blob: 457c7fa0a7c97ac4cfa8216fb70885937aec22bc [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 Zhangd4e641b2017-07-12 12:55:28 -070031type BinaryProperties 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.
Nan Zhangea568a42017-11-08 21:20:04 -080036 Main *string `android:"arch_variant"`
Nan Zhangdb0b9a32017-02-27 10:12:13 -080037
38 // set the name of the output binary.
Nan Zhangea568a42017-11-08 21:20:04 -080039 Stem *string `android:"arch_variant"`
Nan Zhangdb0b9a32017-02-27 10:12:13 -080040
41 // append to the name of the output binary.
Nan Zhangea568a42017-11-08 21:20:04 -080042 Suffix *string `android:"arch_variant"`
Nan Zhangc9c6cb72017-11-03 16:54:05 -070043
44 // list of compatibility suites (for example "cts", "vts") that the module should be
45 // installed into.
46 Test_suites []string `android:"arch_variant"`
Nan Zhangdb0b9a32017-02-27 10:12:13 -080047}
48
Nan Zhangd4e641b2017-07-12 12:55:28 -070049type binaryDecorator struct {
50 binaryProperties BinaryProperties
Nan Zhangdb0b9a32017-02-27 10:12:13 -080051
Nan Zhangd9ec5e72017-12-01 20:00:31 +000052 *pythonInstaller
Nan Zhangdb0b9a32017-02-27 10:12:13 -080053}
54
Nan Zhangd4e641b2017-07-12 12:55:28 -070055type IntermPathProvider interface {
56 IntermPathForModuleOut() android.OptionalPath
Nan Zhang5323f8e2017-05-10 13:37:54 -070057}
58
Nan Zhangdb0b9a32017-02-27 10:12:13 -080059var (
60 stubTemplateHost = "build/soong/python/scripts/stub_template_host.txt"
61)
62
Nan Zhangd4e641b2017-07-12 12:55:28 -070063func NewBinary(hod android.HostOrDeviceSupported) (*Module, *binaryDecorator) {
64 module := newModule(hod, android.MultilibFirst)
Nan Zhangd9ec5e72017-12-01 20:00:31 +000065 decorator := &binaryDecorator{pythonInstaller: NewPythonInstaller("bin", "")}
Nan Zhangdb0b9a32017-02-27 10:12:13 -080066
Nan Zhangd4e641b2017-07-12 12:55:28 -070067 module.bootstrapper = decorator
68 module.installer = decorator
Nan Zhang5323f8e2017-05-10 13:37:54 -070069
Nan Zhangd4e641b2017-07-12 12:55:28 -070070 return module, decorator
Nan Zhangdb0b9a32017-02-27 10:12:13 -080071}
72
Nan Zhangd4e641b2017-07-12 12:55:28 -070073func PythonBinaryHostFactory() android.Module {
74 module, _ := NewBinary(android.HostSupportedNoCross)
Nan Zhangdb0b9a32017-02-27 10:12:13 -080075
Nan Zhangd4e641b2017-07-12 12:55:28 -070076 return module.Init()
77}
78
79func (binary *binaryDecorator) bootstrapperProps() []interface{} {
80 return []interface{}{&binary.binaryProperties}
81}
82
83func (binary *binaryDecorator) bootstrap(ctx android.ModuleContext, actual_version string,
84 embedded_launcher bool, srcsPathMappings []pathMapping, parSpec parSpec,
85 depsPyRunfiles []string, depsParSpecs []parSpec) android.OptionalPath {
86 // no Python source file for compiling .par file.
87 if len(srcsPathMappings) == 0 {
Nan Zhang5323f8e2017-05-10 13:37:54 -070088 return android.OptionalPath{}
Nan Zhangdb0b9a32017-02-27 10:12:13 -080089 }
90
91 // the runfiles packages needs to be populated with "__init__.py".
92 newPyPkgs := []string{}
93 // the set to de-duplicate the new Python packages above.
94 newPyPkgSet := make(map[string]bool)
95 // the runfiles dirs have been treated as packages.
96 existingPyPkgSet := make(map[string]bool)
97
98 wholePyRunfiles := []string{}
Nan Zhangd4e641b2017-07-12 12:55:28 -070099 for _, path := range srcsPathMappings {
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800100 wholePyRunfiles = append(wholePyRunfiles, path.dest)
101 }
Nan Zhangd4e641b2017-07-12 12:55:28 -0700102 wholePyRunfiles = append(wholePyRunfiles, depsPyRunfiles...)
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800103
104 // find all the runfiles dirs which have been treated as packages.
105 for _, path := range wholePyRunfiles {
106 if filepath.Base(path) != initFileName {
107 continue
108 }
109 existingPyPkg := PathBeforeLastSlash(path)
110 if _, found := existingPyPkgSet[existingPyPkg]; found {
111 panic(fmt.Errorf("found init file path duplicates: %q for module: %q.",
112 path, ctx.ModuleName()))
113 } else {
114 existingPyPkgSet[existingPyPkg] = true
115 }
116 parentPath := PathBeforeLastSlash(existingPyPkg)
117 populateNewPyPkgs(parentPath, existingPyPkgSet, newPyPkgSet, &newPyPkgs)
118 }
119
120 // create new packages under runfiles tree.
121 for _, path := range wholePyRunfiles {
122 if filepath.Base(path) == initFileName {
123 continue
124 }
125 parentPath := PathBeforeLastSlash(path)
126 populateNewPyPkgs(parentPath, existingPyPkgSet, newPyPkgSet, &newPyPkgs)
127 }
128
Nan Zhangd4e641b2017-07-12 12:55:28 -0700129 main := binary.getPyMainFile(ctx, srcsPathMappings)
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800130 if main == "" {
Nan Zhang5323f8e2017-05-10 13:37:54 -0700131 return android.OptionalPath{}
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800132 }
Nan Zhangd4e641b2017-07-12 12:55:28 -0700133
134 var launcher_path android.Path
135 if embedded_launcher {
Colin Crossd11fcda2017-10-23 17:59:01 -0700136 ctx.VisitDirectDeps(func(m android.Module) {
Nan Zhangd4e641b2017-07-12 12:55:28 -0700137 if ctx.OtherModuleDependencyTag(m) != launcherTag {
138 return
139 }
140 if provider, ok := m.(IntermPathProvider); ok {
141 if launcher_path != nil {
142 panic(fmt.Errorf("launcher path was found before: %q",
143 launcher_path))
144 }
145 launcher_path = provider.IntermPathForModuleOut().Path()
146 }
147 })
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800148 }
149
Nan Zhangd4e641b2017-07-12 12:55:28 -0700150 binFile := registerBuildActionForParFile(ctx, embedded_launcher, launcher_path,
151 binary.getHostInterpreterName(ctx, actual_version),
152 main, binary.getStem(ctx), newPyPkgs, append(depsParSpecs, parSpec))
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800153
Nan Zhang5323f8e2017-05-10 13:37:54 -0700154 return android.OptionalPathForPath(binFile)
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800155}
156
Nan Zhangd4e641b2017-07-12 12:55:28 -0700157// get host interpreter name.
158func (binary *binaryDecorator) getHostInterpreterName(ctx android.ModuleContext,
159 actual_version string) string {
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800160 var interp string
Nan Zhangd4e641b2017-07-12 12:55:28 -0700161 switch actual_version {
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800162 case pyVersion2:
Dan Willemsen7d1681a2017-09-25 13:47:40 -0700163 interp = "python2.7"
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800164 case pyVersion3:
165 interp = "python3"
166 default:
167 panic(fmt.Errorf("unknown Python actualVersion: %q for module: %q.",
Nan Zhangd4e641b2017-07-12 12:55:28 -0700168 actual_version, ctx.ModuleName()))
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800169 }
170
171 return interp
172}
173
174// find main program path within runfiles tree.
Nan Zhangd4e641b2017-07-12 12:55:28 -0700175func (binary *binaryDecorator) getPyMainFile(ctx android.ModuleContext,
176 srcsPathMappings []pathMapping) string {
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800177 var main string
Nan Zhangea568a42017-11-08 21:20:04 -0800178 if String(binary.binaryProperties.Main) == "" {
Nan Zhangd4e641b2017-07-12 12:55:28 -0700179 main = ctx.ModuleName() + pyExt
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800180 } else {
Nan Zhangea568a42017-11-08 21:20:04 -0800181 main = String(binary.binaryProperties.Main)
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800182 }
183
Nan Zhangd4e641b2017-07-12 12:55:28 -0700184 for _, path := range srcsPathMappings {
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800185 if main == path.src.Rel() {
186 return path.dest
187 }
188 }
189 ctx.PropertyErrorf("main", "%q is not listed in srcs.", main)
190
191 return ""
192}
193
Nan Zhangd4e641b2017-07-12 12:55:28 -0700194func (binary *binaryDecorator) getStem(ctx android.ModuleContext) string {
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800195 stem := ctx.ModuleName()
Nan Zhangea568a42017-11-08 21:20:04 -0800196 if String(binary.binaryProperties.Stem) != "" {
197 stem = String(binary.binaryProperties.Stem)
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800198 }
199
Nan Zhangea568a42017-11-08 21:20:04 -0800200 return stem + String(binary.binaryProperties.Suffix)
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800201}
202
203// Sets the given directory and all its ancestor directories as Python packages.
204func populateNewPyPkgs(pkgPath string, existingPyPkgSet,
205 newPyPkgSet map[string]bool, newPyPkgs *[]string) {
206 for pkgPath != "" {
207 if _, found := existingPyPkgSet[pkgPath]; found {
208 break
209 }
210 if _, found := newPyPkgSet[pkgPath]; !found {
211 newPyPkgSet[pkgPath] = true
212 *newPyPkgs = append(*newPyPkgs, pkgPath)
213 // Gets its ancestor directory by trimming last slash.
214 pkgPath = PathBeforeLastSlash(pkgPath)
215 } else {
216 break
217 }
218 }
219}
220
221// filepath.Dir("abc") -> "." and filepath.Dir("/abc") -> "/". However,
222// the PathBeforeLastSlash() will return "" for both cases above.
223func PathBeforeLastSlash(path string) string {
224 if idx := strings.LastIndex(path, "/"); idx != -1 {
225 return path[:idx]
226 }
227 return ""
228}