blob: 0314edb8c1031e19976cf3cc0ab8efe7f0764ff3 [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 Crossee6143c2017-12-30 17:54:27 -0800136 ctx.VisitDirectDepsWithTag(launcherTag, func(m android.Module) {
Nan Zhangd4e641b2017-07-12 12:55:28 -0700137 if provider, ok := m.(IntermPathProvider); ok {
138 if launcher_path != nil {
139 panic(fmt.Errorf("launcher path was found before: %q",
140 launcher_path))
141 }
142 launcher_path = provider.IntermPathForModuleOut().Path()
143 }
144 })
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800145 }
146
Nan Zhangd4e641b2017-07-12 12:55:28 -0700147 binFile := registerBuildActionForParFile(ctx, embedded_launcher, launcher_path,
148 binary.getHostInterpreterName(ctx, actual_version),
149 main, binary.getStem(ctx), newPyPkgs, append(depsParSpecs, parSpec))
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800150
Nan Zhang5323f8e2017-05-10 13:37:54 -0700151 return android.OptionalPathForPath(binFile)
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800152}
153
Nan Zhangd4e641b2017-07-12 12:55:28 -0700154// get host interpreter name.
155func (binary *binaryDecorator) getHostInterpreterName(ctx android.ModuleContext,
156 actual_version string) string {
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800157 var interp string
Nan Zhangd4e641b2017-07-12 12:55:28 -0700158 switch actual_version {
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800159 case pyVersion2:
Dan Willemsen7d1681a2017-09-25 13:47:40 -0700160 interp = "python2.7"
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800161 case pyVersion3:
162 interp = "python3"
163 default:
164 panic(fmt.Errorf("unknown Python actualVersion: %q for module: %q.",
Nan Zhangd4e641b2017-07-12 12:55:28 -0700165 actual_version, ctx.ModuleName()))
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800166 }
167
168 return interp
169}
170
171// find main program path within runfiles tree.
Nan Zhangd4e641b2017-07-12 12:55:28 -0700172func (binary *binaryDecorator) getPyMainFile(ctx android.ModuleContext,
173 srcsPathMappings []pathMapping) string {
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800174 var main string
Nan Zhangea568a42017-11-08 21:20:04 -0800175 if String(binary.binaryProperties.Main) == "" {
Nan Zhangd4e641b2017-07-12 12:55:28 -0700176 main = ctx.ModuleName() + pyExt
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800177 } else {
Nan Zhangea568a42017-11-08 21:20:04 -0800178 main = String(binary.binaryProperties.Main)
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800179 }
180
Nan Zhangd4e641b2017-07-12 12:55:28 -0700181 for _, path := range srcsPathMappings {
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800182 if main == path.src.Rel() {
183 return path.dest
184 }
185 }
186 ctx.PropertyErrorf("main", "%q is not listed in srcs.", main)
187
188 return ""
189}
190
Nan Zhangd4e641b2017-07-12 12:55:28 -0700191func (binary *binaryDecorator) getStem(ctx android.ModuleContext) string {
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800192 stem := ctx.ModuleName()
Nan Zhangea568a42017-11-08 21:20:04 -0800193 if String(binary.binaryProperties.Stem) != "" {
194 stem = String(binary.binaryProperties.Stem)
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800195 }
196
Nan Zhangea568a42017-11-08 21:20:04 -0800197 return stem + String(binary.binaryProperties.Suffix)
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800198}
199
200// Sets the given directory and all its ancestor directories as Python packages.
201func populateNewPyPkgs(pkgPath string, existingPyPkgSet,
202 newPyPkgSet map[string]bool, newPyPkgs *[]string) {
203 for pkgPath != "" {
204 if _, found := existingPyPkgSet[pkgPath]; found {
205 break
206 }
207 if _, found := newPyPkgSet[pkgPath]; !found {
208 newPyPkgSet[pkgPath] = true
209 *newPyPkgs = append(*newPyPkgs, pkgPath)
210 // Gets its ancestor directory by trimming last slash.
211 pkgPath = PathBeforeLastSlash(pkgPath)
212 } else {
213 break
214 }
215 }
216}
217
218// filepath.Dir("abc") -> "." and filepath.Dir("/abc") -> "/". However,
219// the PathBeforeLastSlash() will return "" for both cases above.
220func PathBeforeLastSlash(path string) string {
221 if idx := strings.LastIndex(path, "/"); idx != -1 {
222 return path[:idx]
223 }
224 return ""
225}