blob: 91b7a548762633fa467afc0211e18367da528a9d [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 Zhangd4e641b2017-07-12 12:55:28 -070024 "github.com/google/blueprint"
25
Nan Zhangdb0b9a32017-02-27 10:12:13 -080026 "android/soong/android"
27)
28
29func init() {
30 android.RegisterModuleType("python_binary_host", PythonBinaryHostFactory)
31}
32
Nan Zhangd4e641b2017-07-12 12:55:28 -070033type BinaryProperties 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.
Nan Zhangd4e641b2017-07-12 12:55:28 -070038 Main string `android:"arch_variant"`
Nan Zhangdb0b9a32017-02-27 10:12:13 -080039
40 // set the name of the output binary.
Nan Zhangd4e641b2017-07-12 12:55:28 -070041 Stem string `android:"arch_variant"`
Nan Zhangdb0b9a32017-02-27 10:12:13 -080042
43 // append to the name of the output binary.
Nan Zhangd4e641b2017-07-12 12:55:28 -070044 Suffix string `android:"arch_variant"`
Nan Zhangdb0b9a32017-02-27 10:12:13 -080045}
46
Nan Zhangd4e641b2017-07-12 12:55:28 -070047type binaryDecorator struct {
48 binaryProperties BinaryProperties
Nan Zhangdb0b9a32017-02-27 10:12:13 -080049
Nan Zhangd4e641b2017-07-12 12:55:28 -070050 baseInstaller *pythonInstaller
Nan Zhangdb0b9a32017-02-27 10:12:13 -080051}
52
Nan Zhangd4e641b2017-07-12 12:55:28 -070053type IntermPathProvider interface {
54 IntermPathForModuleOut() android.OptionalPath
Nan Zhang5323f8e2017-05-10 13:37:54 -070055}
56
Nan Zhangd4e641b2017-07-12 12:55:28 -070057func (binary *binaryDecorator) install(ctx android.ModuleContext, file android.Path) {
58 binary.baseInstaller.install(ctx, file)
Nan Zhang5323f8e2017-05-10 13:37:54 -070059}
Nan Zhangdb0b9a32017-02-27 10:12:13 -080060
61var (
62 stubTemplateHost = "build/soong/python/scripts/stub_template_host.txt"
63)
64
Nan Zhangd4e641b2017-07-12 12:55:28 -070065func NewBinary(hod android.HostOrDeviceSupported) (*Module, *binaryDecorator) {
66 module := newModule(hod, android.MultilibFirst)
67 decorator := &binaryDecorator{baseInstaller: NewPythonInstaller("bin")}
Nan Zhangdb0b9a32017-02-27 10:12:13 -080068
Nan Zhangd4e641b2017-07-12 12:55:28 -070069 module.bootstrapper = decorator
70 module.installer = decorator
Nan Zhang5323f8e2017-05-10 13:37:54 -070071
Nan Zhangd4e641b2017-07-12 12:55:28 -070072 return module, decorator
Nan Zhangdb0b9a32017-02-27 10:12:13 -080073}
74
Nan Zhangd4e641b2017-07-12 12:55:28 -070075func PythonBinaryHostFactory() android.Module {
76 module, _ := NewBinary(android.HostSupportedNoCross)
Nan Zhangdb0b9a32017-02-27 10:12:13 -080077
Nan Zhangd4e641b2017-07-12 12:55:28 -070078 return module.Init()
79}
80
81func (binary *binaryDecorator) bootstrapperProps() []interface{} {
82 return []interface{}{&binary.binaryProperties}
83}
84
85func (binary *binaryDecorator) bootstrap(ctx android.ModuleContext, actual_version string,
86 embedded_launcher bool, srcsPathMappings []pathMapping, parSpec parSpec,
87 depsPyRunfiles []string, depsParSpecs []parSpec) android.OptionalPath {
88 // no Python source file for compiling .par file.
89 if len(srcsPathMappings) == 0 {
Nan Zhang5323f8e2017-05-10 13:37:54 -070090 return android.OptionalPath{}
Nan Zhangdb0b9a32017-02-27 10:12:13 -080091 }
92
93 // the runfiles packages needs to be populated with "__init__.py".
94 newPyPkgs := []string{}
95 // the set to de-duplicate the new Python packages above.
96 newPyPkgSet := make(map[string]bool)
97 // the runfiles dirs have been treated as packages.
98 existingPyPkgSet := make(map[string]bool)
99
100 wholePyRunfiles := []string{}
Nan Zhangd4e641b2017-07-12 12:55:28 -0700101 for _, path := range srcsPathMappings {
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800102 wholePyRunfiles = append(wholePyRunfiles, path.dest)
103 }
Nan Zhangd4e641b2017-07-12 12:55:28 -0700104 wholePyRunfiles = append(wholePyRunfiles, depsPyRunfiles...)
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800105
106 // find all the runfiles dirs which have been treated as packages.
107 for _, path := range wholePyRunfiles {
108 if filepath.Base(path) != initFileName {
109 continue
110 }
111 existingPyPkg := PathBeforeLastSlash(path)
112 if _, found := existingPyPkgSet[existingPyPkg]; found {
113 panic(fmt.Errorf("found init file path duplicates: %q for module: %q.",
114 path, ctx.ModuleName()))
115 } else {
116 existingPyPkgSet[existingPyPkg] = true
117 }
118 parentPath := PathBeforeLastSlash(existingPyPkg)
119 populateNewPyPkgs(parentPath, existingPyPkgSet, newPyPkgSet, &newPyPkgs)
120 }
121
122 // create new packages under runfiles tree.
123 for _, path := range wholePyRunfiles {
124 if filepath.Base(path) == initFileName {
125 continue
126 }
127 parentPath := PathBeforeLastSlash(path)
128 populateNewPyPkgs(parentPath, existingPyPkgSet, newPyPkgSet, &newPyPkgs)
129 }
130
Nan Zhangd4e641b2017-07-12 12:55:28 -0700131 main := binary.getPyMainFile(ctx, srcsPathMappings)
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800132 if main == "" {
Nan Zhang5323f8e2017-05-10 13:37:54 -0700133 return android.OptionalPath{}
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800134 }
Nan Zhangd4e641b2017-07-12 12:55:28 -0700135
136 var launcher_path android.Path
137 if embedded_launcher {
138 ctx.VisitDirectDeps(func(m blueprint.Module) {
139 if ctx.OtherModuleDependencyTag(m) != launcherTag {
140 return
141 }
142 if provider, ok := m.(IntermPathProvider); ok {
143 if launcher_path != nil {
144 panic(fmt.Errorf("launcher path was found before: %q",
145 launcher_path))
146 }
147 launcher_path = provider.IntermPathForModuleOut().Path()
148 }
149 })
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800150 }
151
Nan Zhangd4e641b2017-07-12 12:55:28 -0700152 binFile := registerBuildActionForParFile(ctx, embedded_launcher, launcher_path,
153 binary.getHostInterpreterName(ctx, actual_version),
154 main, binary.getStem(ctx), newPyPkgs, append(depsParSpecs, parSpec))
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800155
Nan Zhang5323f8e2017-05-10 13:37:54 -0700156 return android.OptionalPathForPath(binFile)
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800157}
158
Nan Zhangd4e641b2017-07-12 12:55:28 -0700159// get host interpreter name.
160func (binary *binaryDecorator) getHostInterpreterName(ctx android.ModuleContext,
161 actual_version string) string {
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800162 var interp string
Nan Zhangd4e641b2017-07-12 12:55:28 -0700163 switch actual_version {
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800164 case pyVersion2:
165 interp = "python2"
166 case pyVersion3:
167 interp = "python3"
168 default:
169 panic(fmt.Errorf("unknown Python actualVersion: %q for module: %q.",
Nan Zhangd4e641b2017-07-12 12:55:28 -0700170 actual_version, ctx.ModuleName()))
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800171 }
172
173 return interp
174}
175
176// find main program path within runfiles tree.
Nan Zhangd4e641b2017-07-12 12:55:28 -0700177func (binary *binaryDecorator) getPyMainFile(ctx android.ModuleContext,
178 srcsPathMappings []pathMapping) string {
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800179 var main string
Nan Zhangd4e641b2017-07-12 12:55:28 -0700180 if binary.binaryProperties.Main == "" {
181 main = ctx.ModuleName() + pyExt
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800182 } else {
Nan Zhangd4e641b2017-07-12 12:55:28 -0700183 main = binary.binaryProperties.Main
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800184 }
185
Nan Zhangd4e641b2017-07-12 12:55:28 -0700186 for _, path := range srcsPathMappings {
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800187 if main == path.src.Rel() {
188 return path.dest
189 }
190 }
191 ctx.PropertyErrorf("main", "%q is not listed in srcs.", main)
192
193 return ""
194}
195
Nan Zhangd4e641b2017-07-12 12:55:28 -0700196func (binary *binaryDecorator) getStem(ctx android.ModuleContext) string {
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800197 stem := ctx.ModuleName()
Nan Zhangd4e641b2017-07-12 12:55:28 -0700198 if binary.binaryProperties.Stem != "" {
199 stem = binary.binaryProperties.Stem
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800200 }
201
Nan Zhangd4e641b2017-07-12 12:55:28 -0700202 return stem + binary.binaryProperties.Suffix
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800203}
204
205// Sets the given directory and all its ancestor directories as Python packages.
206func populateNewPyPkgs(pkgPath string, existingPyPkgSet,
207 newPyPkgSet map[string]bool, newPyPkgs *[]string) {
208 for pkgPath != "" {
209 if _, found := existingPyPkgSet[pkgPath]; found {
210 break
211 }
212 if _, found := newPyPkgSet[pkgPath]; !found {
213 newPyPkgSet[pkgPath] = true
214 *newPyPkgs = append(*newPyPkgs, pkgPath)
215 // Gets its ancestor directory by trimming last slash.
216 pkgPath = PathBeforeLastSlash(pkgPath)
217 } else {
218 break
219 }
220 }
221}
222
223// filepath.Dir("abc") -> "." and filepath.Dir("/abc") -> "/". However,
224// the PathBeforeLastSlash() will return "" for both cases above.
225func PathBeforeLastSlash(path string) string {
226 if idx := strings.LastIndex(path, "/"); idx != -1 {
227 return path[:idx]
228 }
229 return ""
230}