blob: 81e8bd94490427e30727260c40db0aa369eb6fcc [file] [log] [blame]
Nan Zhangdb0b9a32017-02-27 10:12:13 -08001// Copyright 2017 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package python
16
17// This file contains the module types for building Python binary.
18
19import (
20 "fmt"
Nan Zhangdb0b9a32017-02-27 10:12:13 -080021 "path/filepath"
22 "strings"
23
24 "github.com/google/blueprint"
25
26 "android/soong/android"
27)
28
29func init() {
30 android.RegisterModuleType("python_binary_host", PythonBinaryHostFactory)
31}
32
Nan Zhang5323f8e2017-05-10 13:37:54 -070033type PythonBinaryBaseProperties struct {
Nan Zhangdb0b9a32017-02-27 10:12:13 -080034 // the name of the source file that is the main entry point of the program.
35 // this file must also be listed in srcs.
36 // If left unspecified, module name is used instead.
37 // If name doesn’t match any filename in srcs, main must be specified.
38 Main string
39
40 // set the name of the output binary.
41 Stem string
42
43 // append to the name of the output binary.
44 Suffix string
45}
46
Nan Zhang5323f8e2017-05-10 13:37:54 -070047type pythonBinaryBase struct {
Nan Zhangdb0b9a32017-02-27 10:12:13 -080048 pythonBaseModule
49
Nan Zhang5323f8e2017-05-10 13:37:54 -070050 binaryProperties PythonBinaryBaseProperties
Nan Zhangdb0b9a32017-02-27 10:12:13 -080051
52 // soong_zip arguments from all its dependencies.
53 depsParSpecs []parSpec
54
55 // Python runfiles paths from all its dependencies.
56 depsPyRunfiles []string
Nan Zhangdb0b9a32017-02-27 10:12:13 -080057}
58
Nan Zhang5323f8e2017-05-10 13:37:54 -070059type PythonBinaryHost struct {
60 pythonBinaryBase
61}
62
63var _ PythonSubModule = (*PythonBinaryHost)(nil)
64
65type pythonBinaryHostDecorator struct {
66 pythonDecorator
67}
68
69func (p *pythonBinaryHostDecorator) install(ctx android.ModuleContext, file android.Path) {
70 p.pythonDecorator.baseInstaller.install(ctx, file)
71}
Nan Zhangdb0b9a32017-02-27 10:12:13 -080072
73var (
74 stubTemplateHost = "build/soong/python/scripts/stub_template_host.txt"
75)
76
77func PythonBinaryHostFactory() (blueprint.Module, []interface{}) {
Nan Zhang5323f8e2017-05-10 13:37:54 -070078 decorator := &pythonBinaryHostDecorator{
79 pythonDecorator: pythonDecorator{baseInstaller: NewPythonInstaller("bin")}}
Nan Zhangdb0b9a32017-02-27 10:12:13 -080080
Nan Zhang5323f8e2017-05-10 13:37:54 -070081 module := &PythonBinaryHost{}
82 module.pythonBaseModule.installer = decorator
83
84 return InitPythonBaseModule(&module.pythonBinaryBase.pythonBaseModule,
85 &module.pythonBinaryBase, android.HostSupportedNoCross, &module.binaryProperties)
Nan Zhangdb0b9a32017-02-27 10:12:13 -080086}
87
Nan Zhang5323f8e2017-05-10 13:37:54 -070088func (p *pythonBinaryBase) GeneratePythonBuildActions(ctx android.ModuleContext) android.OptionalPath {
Nan Zhangdb0b9a32017-02-27 10:12:13 -080089 p.pythonBaseModule.GeneratePythonBuildActions(ctx)
90
91 // no Python source file for compiling par file.
92 if len(p.pythonBaseModule.srcsPathMappings) == 0 && len(p.depsPyRunfiles) == 0 {
Nan Zhang5323f8e2017-05-10 13:37:54 -070093 return android.OptionalPath{}
Nan Zhangdb0b9a32017-02-27 10:12:13 -080094 }
95
96 // the runfiles packages needs to be populated with "__init__.py".
97 newPyPkgs := []string{}
98 // the set to de-duplicate the new Python packages above.
99 newPyPkgSet := make(map[string]bool)
100 // the runfiles dirs have been treated as packages.
101 existingPyPkgSet := make(map[string]bool)
102
103 wholePyRunfiles := []string{}
104 for _, path := range p.pythonBaseModule.srcsPathMappings {
105 wholePyRunfiles = append(wholePyRunfiles, path.dest)
106 }
107 wholePyRunfiles = append(wholePyRunfiles, p.depsPyRunfiles...)
108
109 // find all the runfiles dirs which have been treated as packages.
110 for _, path := range wholePyRunfiles {
111 if filepath.Base(path) != initFileName {
112 continue
113 }
114 existingPyPkg := PathBeforeLastSlash(path)
115 if _, found := existingPyPkgSet[existingPyPkg]; found {
116 panic(fmt.Errorf("found init file path duplicates: %q for module: %q.",
117 path, ctx.ModuleName()))
118 } else {
119 existingPyPkgSet[existingPyPkg] = true
120 }
121 parentPath := PathBeforeLastSlash(existingPyPkg)
122 populateNewPyPkgs(parentPath, existingPyPkgSet, newPyPkgSet, &newPyPkgs)
123 }
124
125 // create new packages under runfiles tree.
126 for _, path := range wholePyRunfiles {
127 if filepath.Base(path) == initFileName {
128 continue
129 }
130 parentPath := PathBeforeLastSlash(path)
131 populateNewPyPkgs(parentPath, existingPyPkgSet, newPyPkgSet, &newPyPkgs)
132 }
133
134 main := p.getPyMainFile(ctx)
135 if main == "" {
Nan Zhang5323f8e2017-05-10 13:37:54 -0700136 return android.OptionalPath{}
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800137 }
138 interp := p.getInterpreter(ctx)
139 if interp == "" {
Nan Zhang5323f8e2017-05-10 13:37:54 -0700140 return android.OptionalPath{}
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800141 }
142
143 // we need remove "runfiles/" suffix since stub script starts
144 // searching for main file in each sub-dir of "runfiles" directory tree.
145 binFile := registerBuildActionForParFile(ctx, p.getInterpreter(ctx),
146 strings.TrimPrefix(main, runFiles+"/"), p.getStem(ctx),
147 newPyPkgs, append(p.depsParSpecs, p.pythonBaseModule.parSpec))
148
Nan Zhang5323f8e2017-05-10 13:37:54 -0700149 return android.OptionalPathForPath(binFile)
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800150}
151
152// get interpreter path.
Nan Zhang5323f8e2017-05-10 13:37:54 -0700153func (p *pythonBinaryBase) getInterpreter(ctx android.ModuleContext) string {
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800154 var interp string
155 switch p.pythonBaseModule.properties.ActualVersion {
156 case pyVersion2:
157 interp = "python2"
158 case pyVersion3:
159 interp = "python3"
160 default:
161 panic(fmt.Errorf("unknown Python actualVersion: %q for module: %q.",
162 p.properties.ActualVersion, ctx.ModuleName()))
163 }
164
165 return interp
166}
167
168// find main program path within runfiles tree.
Nan Zhang5323f8e2017-05-10 13:37:54 -0700169func (p *pythonBinaryBase) getPyMainFile(ctx android.ModuleContext) string {
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800170 var main string
171 if p.binaryProperties.Main == "" {
172 main = p.BaseModuleName() + pyExt
173 } else {
174 main = p.binaryProperties.Main
175 }
176
177 for _, path := range p.pythonBaseModule.srcsPathMappings {
178 if main == path.src.Rel() {
179 return path.dest
180 }
181 }
182 ctx.PropertyErrorf("main", "%q is not listed in srcs.", main)
183
184 return ""
185}
186
Nan Zhang5323f8e2017-05-10 13:37:54 -0700187func (p *pythonBinaryBase) getStem(ctx android.ModuleContext) string {
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800188 stem := ctx.ModuleName()
189 if p.binaryProperties.Stem != "" {
190 stem = p.binaryProperties.Stem
191 }
192
193 return stem + p.binaryProperties.Suffix
194}
195
196// Sets the given directory and all its ancestor directories as Python packages.
197func populateNewPyPkgs(pkgPath string, existingPyPkgSet,
198 newPyPkgSet map[string]bool, newPyPkgs *[]string) {
199 for pkgPath != "" {
200 if _, found := existingPyPkgSet[pkgPath]; found {
201 break
202 }
203 if _, found := newPyPkgSet[pkgPath]; !found {
204 newPyPkgSet[pkgPath] = true
205 *newPyPkgs = append(*newPyPkgs, pkgPath)
206 // Gets its ancestor directory by trimming last slash.
207 pkgPath = PathBeforeLastSlash(pkgPath)
208 } else {
209 break
210 }
211 }
212}
213
214// filepath.Dir("abc") -> "." and filepath.Dir("/abc") -> "/". However,
215// the PathBeforeLastSlash() will return "" for both cases above.
216func PathBeforeLastSlash(path string) string {
217 if idx := strings.LastIndex(path, "/"); idx != -1 {
218 return path[:idx]
219 }
220 return ""
221}