1// 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" 21 22 "android/soong/android" 23 "android/soong/bazel" 24 25 "github.com/google/blueprint/proptools" 26) 27 28func init() { 29 registerPythonBinaryComponents(android.InitRegistrationContext) 30} 31 32func registerPythonBinaryComponents(ctx android.RegistrationContext) { 33 ctx.RegisterModuleType("python_binary_host", PythonBinaryHostFactory) 34} 35 36type bazelPythonBinaryAttributes struct { 37 Main *string 38 Srcs bazel.LabelListAttribute 39 Deps bazel.LabelListAttribute 40 Python_version *string 41} 42 43func pythonBinaryBp2Build(ctx android.TopDownMutatorContext, m *Module) { 44 var main *string 45 for _, propIntf := range m.GetProperties() { 46 if props, ok := propIntf.(*BinaryProperties); ok { 47 // main is optional. 48 if props.Main != nil { 49 main = props.Main 50 break 51 } 52 } 53 } 54 55 // TODO(b/182306917): this doesn't fully handle all nested props versioned 56 // by the python version, which would have been handled by the version split 57 // mutator. This is sufficient for very simple python_binary_host modules 58 // under Bionic. 59 py3Enabled := proptools.BoolDefault(m.properties.Version.Py3.Enabled, false) 60 py2Enabled := proptools.BoolDefault(m.properties.Version.Py2.Enabled, false) 61 var python_version *string 62 if py3Enabled && py2Enabled { 63 panic(fmt.Errorf( 64 "error for '%s' module: bp2build's python_binary_host converter does not support "+ 65 "converting a module that is enabled for both Python 2 and 3 at the same time.", m.Name())) 66 } else if py2Enabled { 67 python_version = &pyVersion2 68 } else { 69 // do nothing, since python_version defaults to PY3. 70 } 71 72 baseAttrs := m.makeArchVariantBaseAttributes(ctx) 73 attrs := &bazelPythonBinaryAttributes{ 74 Main: main, 75 Srcs: baseAttrs.Srcs, 76 Deps: baseAttrs.Deps, 77 Python_version: python_version, 78 } 79 80 props := bazel.BazelTargetModuleProperties{ 81 // Use the native py_binary rule. 82 Rule_class: "py_binary", 83 } 84 85 ctx.CreateBazelTargetModule(props, android.CommonAttributes{ 86 Name: m.Name(), 87 Data: baseAttrs.Data, 88 }, attrs) 89} 90 91type BinaryProperties struct { 92 // the name of the source file that is the main entry point of the program. 93 // this file must also be listed in srcs. 94 // If left unspecified, module name is used instead. 95 // If name doesn’t match any filename in srcs, main must be specified. 96 Main *string `android:"arch_variant"` 97 98 // set the name of the output binary. 99 Stem *string `android:"arch_variant"` 100 101 // append to the name of the output binary. 102 Suffix *string `android:"arch_variant"` 103 104 // list of compatibility suites (for example "cts", "vts") that the module should be 105 // installed into. 106 Test_suites []string `android:"arch_variant"` 107 108 // whether to use `main` when starting the executable. The default is true, when set to 109 // false it will act much like the normal `python` executable, but with the sources and 110 // libraries automatically included in the PYTHONPATH. 111 Autorun *bool `android:"arch_variant"` 112 113 // Flag to indicate whether or not to create test config automatically. If AndroidTest.xml 114 // doesn't exist next to the Android.bp, this attribute doesn't need to be set to true 115 // explicitly. 116 Auto_gen_config *bool 117} 118 119type binaryDecorator struct { 120 binaryProperties BinaryProperties 121 122 *pythonInstaller 123} 124 125type IntermPathProvider interface { 126 IntermPathForModuleOut() android.OptionalPath 127} 128 129var ( 130 StubTemplateHost = "build/soong/python/scripts/stub_template_host.txt" 131) 132 133func NewBinary(hod android.HostOrDeviceSupported) (*Module, *binaryDecorator) { 134 module := newModule(hod, android.MultilibFirst) 135 decorator := &binaryDecorator{pythonInstaller: NewPythonInstaller("bin", "")} 136 137 module.bootstrapper = decorator 138 module.installer = decorator 139 140 return module, decorator 141} 142 143func PythonBinaryHostFactory() android.Module { 144 module, _ := NewBinary(android.HostSupported) 145 146 android.InitBazelModule(module) 147 148 return module.init() 149} 150 151func (binary *binaryDecorator) autorun() bool { 152 return BoolDefault(binary.binaryProperties.Autorun, true) 153} 154 155func (binary *binaryDecorator) bootstrapperProps() []interface{} { 156 return []interface{}{&binary.binaryProperties} 157} 158 159func (binary *binaryDecorator) bootstrap(ctx android.ModuleContext, actualVersion string, 160 embeddedLauncher bool, srcsPathMappings []pathMapping, srcsZip android.Path, 161 depsSrcsZips android.Paths) android.OptionalPath { 162 163 main := "" 164 if binary.autorun() { 165 main = binary.getPyMainFile(ctx, srcsPathMappings) 166 } 167 168 var launcherPath android.OptionalPath 169 if embeddedLauncher { 170 ctx.VisitDirectDepsWithTag(launcherTag, func(m android.Module) { 171 if provider, ok := m.(IntermPathProvider); ok { 172 if launcherPath.Valid() { 173 panic(fmt.Errorf("launcher path was found before: %q", 174 launcherPath)) 175 } 176 launcherPath = provider.IntermPathForModuleOut() 177 } 178 }) 179 } 180 181 binFile := registerBuildActionForParFile(ctx, embeddedLauncher, launcherPath, 182 binary.getHostInterpreterName(ctx, actualVersion), 183 main, binary.getStem(ctx), append(android.Paths{srcsZip}, depsSrcsZips...)) 184 185 return android.OptionalPathForPath(binFile) 186} 187 188// get host interpreter name. 189func (binary *binaryDecorator) getHostInterpreterName(ctx android.ModuleContext, 190 actualVersion string) string { 191 var interp string 192 switch actualVersion { 193 case pyVersion2: 194 interp = "python2.7" 195 case pyVersion3: 196 interp = "python3" 197 default: 198 panic(fmt.Errorf("unknown Python actualVersion: %q for module: %q.", 199 actualVersion, ctx.ModuleName())) 200 } 201 202 return interp 203} 204 205// find main program path within runfiles tree. 206func (binary *binaryDecorator) getPyMainFile(ctx android.ModuleContext, 207 srcsPathMappings []pathMapping) string { 208 var main string 209 if String(binary.binaryProperties.Main) == "" { 210 main = ctx.ModuleName() + pyExt 211 } else { 212 main = String(binary.binaryProperties.Main) 213 } 214 215 for _, path := range srcsPathMappings { 216 if main == path.src.Rel() { 217 return path.dest 218 } 219 } 220 ctx.PropertyErrorf("main", "%q is not listed in srcs.", main) 221 222 return "" 223} 224 225func (binary *binaryDecorator) getStem(ctx android.ModuleContext) string { 226 stem := ctx.ModuleName() 227 if String(binary.binaryProperties.Stem) != "" { 228 stem = String(binary.binaryProperties.Stem) 229 } 230 231 return stem + String(binary.binaryProperties.Suffix) 232} 233