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