blob: ae2693b982ded50930c541a09473642e9f505a10 [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
Nan Zhangdb0b9a32017-02-27 10:12:13 -080024 "android/soong/android"
25)
26
27func init() {
28 android.RegisterModuleType("python_binary_host", PythonBinaryHostFactory)
29}
30
Nan Zhang5323f8e2017-05-10 13:37:54 -070031type PythonBinaryBaseProperties struct {
Nan Zhangdb0b9a32017-02-27 10:12:13 -080032 // the name of the source file that is the main entry point of the program.
33 // this file must also be listed in srcs.
34 // If left unspecified, module name is used instead.
35 // If name doesn’t match any filename in srcs, main must be specified.
36 Main string
37
38 // set the name of the output binary.
39 Stem string
40
41 // append to the name of the output binary.
42 Suffix string
43}
44
Nan Zhang5323f8e2017-05-10 13:37:54 -070045type pythonBinaryBase struct {
Nan Zhangdb0b9a32017-02-27 10:12:13 -080046 pythonBaseModule
47
Nan Zhang5323f8e2017-05-10 13:37:54 -070048 binaryProperties PythonBinaryBaseProperties
Nan Zhangdb0b9a32017-02-27 10:12:13 -080049
50 // soong_zip arguments from all its dependencies.
51 depsParSpecs []parSpec
52
53 // Python runfiles paths from all its dependencies.
54 depsPyRunfiles []string
Nan Zhangdb0b9a32017-02-27 10:12:13 -080055}
56
Nan Zhang5323f8e2017-05-10 13:37:54 -070057type PythonBinaryHost struct {
58 pythonBinaryBase
59}
60
61var _ PythonSubModule = (*PythonBinaryHost)(nil)
62
63type pythonBinaryHostDecorator struct {
64 pythonDecorator
65}
66
67func (p *pythonBinaryHostDecorator) install(ctx android.ModuleContext, file android.Path) {
68 p.pythonDecorator.baseInstaller.install(ctx, file)
69}
Nan Zhangdb0b9a32017-02-27 10:12:13 -080070
71var (
72 stubTemplateHost = "build/soong/python/scripts/stub_template_host.txt"
73)
74
Colin Cross36242852017-06-23 15:06:31 -070075func PythonBinaryHostFactory() android.Module {
Nan Zhang5323f8e2017-05-10 13:37:54 -070076 decorator := &pythonBinaryHostDecorator{
77 pythonDecorator: pythonDecorator{baseInstaller: NewPythonInstaller("bin")}}
Nan Zhangdb0b9a32017-02-27 10:12:13 -080078
Nan Zhang5323f8e2017-05-10 13:37:54 -070079 module := &PythonBinaryHost{}
80 module.pythonBaseModule.installer = decorator
Colin Cross36242852017-06-23 15:06:31 -070081 module.AddProperties(&module.binaryProperties)
Nan Zhang5323f8e2017-05-10 13:37:54 -070082
83 return InitPythonBaseModule(&module.pythonBinaryBase.pythonBaseModule,
Colin Cross36242852017-06-23 15:06:31 -070084 &module.pythonBinaryBase, android.HostSupportedNoCross)
Nan Zhangdb0b9a32017-02-27 10:12:13 -080085}
86
Nan Zhang5323f8e2017-05-10 13:37:54 -070087func (p *pythonBinaryBase) GeneratePythonBuildActions(ctx android.ModuleContext) android.OptionalPath {
Nan Zhangdb0b9a32017-02-27 10:12:13 -080088 p.pythonBaseModule.GeneratePythonBuildActions(ctx)
89
90 // no Python source file for compiling par file.
91 if len(p.pythonBaseModule.srcsPathMappings) == 0 && len(p.depsPyRunfiles) == 0 {
Nan Zhang5323f8e2017-05-10 13:37:54 -070092 return android.OptionalPath{}
Nan Zhangdb0b9a32017-02-27 10:12:13 -080093 }
94
95 // the runfiles packages needs to be populated with "__init__.py".
96 newPyPkgs := []string{}
97 // the set to de-duplicate the new Python packages above.
98 newPyPkgSet := make(map[string]bool)
99 // the runfiles dirs have been treated as packages.
100 existingPyPkgSet := make(map[string]bool)
101
102 wholePyRunfiles := []string{}
103 for _, path := range p.pythonBaseModule.srcsPathMappings {
104 wholePyRunfiles = append(wholePyRunfiles, path.dest)
105 }
106 wholePyRunfiles = append(wholePyRunfiles, p.depsPyRunfiles...)
107
108 // find all the runfiles dirs which have been treated as packages.
109 for _, path := range wholePyRunfiles {
110 if filepath.Base(path) != initFileName {
111 continue
112 }
113 existingPyPkg := PathBeforeLastSlash(path)
114 if _, found := existingPyPkgSet[existingPyPkg]; found {
115 panic(fmt.Errorf("found init file path duplicates: %q for module: %q.",
116 path, ctx.ModuleName()))
117 } else {
118 existingPyPkgSet[existingPyPkg] = true
119 }
120 parentPath := PathBeforeLastSlash(existingPyPkg)
121 populateNewPyPkgs(parentPath, existingPyPkgSet, newPyPkgSet, &newPyPkgs)
122 }
123
124 // create new packages under runfiles tree.
125 for _, path := range wholePyRunfiles {
126 if filepath.Base(path) == initFileName {
127 continue
128 }
129 parentPath := PathBeforeLastSlash(path)
130 populateNewPyPkgs(parentPath, existingPyPkgSet, newPyPkgSet, &newPyPkgs)
131 }
132
133 main := p.getPyMainFile(ctx)
134 if main == "" {
Nan Zhang5323f8e2017-05-10 13:37:54 -0700135 return android.OptionalPath{}
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800136 }
137 interp := p.getInterpreter(ctx)
138 if interp == "" {
Nan Zhang5323f8e2017-05-10 13:37:54 -0700139 return android.OptionalPath{}
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800140 }
141
142 // we need remove "runfiles/" suffix since stub script starts
143 // searching for main file in each sub-dir of "runfiles" directory tree.
144 binFile := registerBuildActionForParFile(ctx, p.getInterpreter(ctx),
145 strings.TrimPrefix(main, runFiles+"/"), p.getStem(ctx),
146 newPyPkgs, append(p.depsParSpecs, p.pythonBaseModule.parSpec))
147
Nan Zhang5323f8e2017-05-10 13:37:54 -0700148 return android.OptionalPathForPath(binFile)
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800149}
150
151// get interpreter path.
Nan Zhang5323f8e2017-05-10 13:37:54 -0700152func (p *pythonBinaryBase) getInterpreter(ctx android.ModuleContext) string {
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800153 var interp string
154 switch p.pythonBaseModule.properties.ActualVersion {
155 case pyVersion2:
156 interp = "python2"
157 case pyVersion3:
158 interp = "python3"
159 default:
160 panic(fmt.Errorf("unknown Python actualVersion: %q for module: %q.",
161 p.properties.ActualVersion, ctx.ModuleName()))
162 }
163
164 return interp
165}
166
167// find main program path within runfiles tree.
Nan Zhang5323f8e2017-05-10 13:37:54 -0700168func (p *pythonBinaryBase) getPyMainFile(ctx android.ModuleContext) string {
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800169 var main string
170 if p.binaryProperties.Main == "" {
171 main = p.BaseModuleName() + pyExt
172 } else {
173 main = p.binaryProperties.Main
174 }
175
176 for _, path := range p.pythonBaseModule.srcsPathMappings {
177 if main == path.src.Rel() {
178 return path.dest
179 }
180 }
181 ctx.PropertyErrorf("main", "%q is not listed in srcs.", main)
182
183 return ""
184}
185
Nan Zhang5323f8e2017-05-10 13:37:54 -0700186func (p *pythonBinaryBase) getStem(ctx android.ModuleContext) string {
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800187 stem := ctx.ModuleName()
188 if p.binaryProperties.Stem != "" {
189 stem = p.binaryProperties.Stem
190 }
191
192 return stem + p.binaryProperties.Suffix
193}
194
195// Sets the given directory and all its ancestor directories as Python packages.
196func populateNewPyPkgs(pkgPath string, existingPyPkgSet,
197 newPyPkgSet map[string]bool, newPyPkgs *[]string) {
198 for pkgPath != "" {
199 if _, found := existingPyPkgSet[pkgPath]; found {
200 break
201 }
202 if _, found := newPyPkgSet[pkgPath]; !found {
203 newPyPkgSet[pkgPath] = true
204 *newPyPkgs = append(*newPyPkgs, pkgPath)
205 // Gets its ancestor directory by trimming last slash.
206 pkgPath = PathBeforeLastSlash(pkgPath)
207 } else {
208 break
209 }
210 }
211}
212
213// filepath.Dir("abc") -> "." and filepath.Dir("/abc") -> "/". However,
214// the PathBeforeLastSlash() will return "" for both cases above.
215func PathBeforeLastSlash(path string) string {
216 if idx := strings.LastIndex(path, "/"); idx != -1 {
217 return path[:idx]
218 }
219 return ""
220}