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 "path/filepath" 22 "strings" 23 24 "android/soong/android" 25) 26 27func init() { 28 registerPythonBinaryComponents(android.InitRegistrationContext) 29} 30 31func registerPythonBinaryComponents(ctx android.RegistrationContext) { 32 ctx.RegisterModuleType("python_binary_host", PythonBinaryHostFactory) 33} 34 35type BinaryProperties struct { 36 // the name of the source file that is the main entry point of the program. 37 // this file must also be listed in srcs. 38 // If left unspecified, module name is used instead. 39 // If name doesn’t match any filename in srcs, main must be specified. 40 Main *string 41 42 // set the name of the output binary. 43 Stem *string `android:"arch_variant"` 44 45 // append to the name of the output binary. 46 Suffix *string `android:"arch_variant"` 47 48 // list of compatibility suites (for example "cts", "vts") that the module should be 49 // installed into. 50 Test_suites []string `android:"arch_variant"` 51 52 // whether to use `main` when starting the executable. The default is true, when set to 53 // false it will act much like the normal `python` executable, but with the sources and 54 // libraries automatically included in the PYTHONPATH. 55 Autorun *bool `android:"arch_variant"` 56 57 // Flag to indicate whether or not to create test config automatically. If AndroidTest.xml 58 // doesn't exist next to the Android.bp, this attribute doesn't need to be set to true 59 // explicitly. 60 Auto_gen_config *bool 61} 62 63type PythonBinaryModule struct { 64 PythonLibraryModule 65 binaryProperties BinaryProperties 66 67 // (.intermediate) module output path as installation source. 68 installSource android.Path 69 70 // Final installation path. 71 installedDest android.Path 72 73 androidMkSharedLibs []string 74} 75 76var _ android.AndroidMkEntriesProvider = (*PythonBinaryModule)(nil) 77var _ android.Module = (*PythonBinaryModule)(nil) 78 79type IntermPathProvider interface { 80 IntermPathForModuleOut() android.OptionalPath 81} 82 83func NewBinary(hod android.HostOrDeviceSupported) *PythonBinaryModule { 84 return &PythonBinaryModule{ 85 PythonLibraryModule: *newModule(hod, android.MultilibFirst), 86 } 87} 88 89func PythonBinaryHostFactory() android.Module { 90 return NewBinary(android.HostSupported).init() 91} 92 93func (p *PythonBinaryModule) init() android.Module { 94 p.AddProperties(&p.properties, &p.protoProperties) 95 p.AddProperties(&p.binaryProperties) 96 android.InitAndroidArchModule(p, p.hod, p.multilib) 97 android.InitDefaultableModule(p) 98 return p 99} 100 101func (p *PythonBinaryModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { 102 p.PythonLibraryModule.GenerateAndroidBuildActions(ctx) 103 p.buildBinary(ctx) 104 p.installedDest = ctx.InstallFile(installDir(ctx, "bin", "", ""), 105 p.installSource.Base(), p.installSource) 106} 107 108func (p *PythonBinaryModule) buildBinary(ctx android.ModuleContext) { 109 embeddedLauncher := p.isEmbeddedLauncherEnabled() 110 depsSrcsZips := p.collectPathsFromTransitiveDeps(ctx, embeddedLauncher) 111 main := "" 112 if p.autorun() { 113 main = p.getPyMainFile(ctx, p.srcsPathMappings) 114 } 115 116 var launcherPath android.OptionalPath 117 if embeddedLauncher { 118 ctx.VisitDirectDepsWithTag(launcherTag, func(m android.Module) { 119 if provider, ok := m.(IntermPathProvider); ok { 120 if launcherPath.Valid() { 121 panic(fmt.Errorf("launcher path was found before: %q", 122 launcherPath)) 123 } 124 launcherPath = provider.IntermPathForModuleOut() 125 } 126 }) 127 } 128 srcsZips := make(android.Paths, 0, len(depsSrcsZips)+1) 129 if embeddedLauncher { 130 srcsZips = append(srcsZips, p.precompiledSrcsZip) 131 } else { 132 srcsZips = append(srcsZips, p.srcsZip) 133 } 134 srcsZips = append(srcsZips, depsSrcsZips...) 135 p.installSource = registerBuildActionForParFile(ctx, embeddedLauncher, launcherPath, 136 p.getHostInterpreterName(ctx, p.properties.Actual_version), 137 main, p.getStem(ctx), srcsZips) 138 139 var sharedLibs []string 140 // if embedded launcher is enabled, we need to collect the shared library dependencies of the 141 // launcher 142 for _, dep := range ctx.GetDirectDepsWithTag(launcherSharedLibTag) { 143 sharedLibs = append(sharedLibs, ctx.OtherModuleName(dep)) 144 } 145 p.androidMkSharedLibs = sharedLibs 146} 147 148func (p *PythonBinaryModule) AndroidMkEntries() []android.AndroidMkEntries { 149 entries := android.AndroidMkEntries{OutputFile: android.OptionalPathForPath(p.installSource)} 150 151 entries.Class = "EXECUTABLES" 152 153 entries.ExtraEntries = append(entries.ExtraEntries, 154 func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { 155 entries.AddCompatibilityTestSuites(p.binaryProperties.Test_suites...) 156 }) 157 158 entries.Required = append(entries.Required, "libc++") 159 entries.ExtraEntries = append(entries.ExtraEntries, 160 func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { 161 path, file := filepath.Split(p.installedDest.String()) 162 stem := strings.TrimSuffix(file, filepath.Ext(file)) 163 164 entries.SetString("LOCAL_MODULE_SUFFIX", filepath.Ext(file)) 165 entries.SetString("LOCAL_MODULE_PATH", path) 166 entries.SetString("LOCAL_MODULE_STEM", stem) 167 entries.AddStrings("LOCAL_SHARED_LIBRARIES", p.androidMkSharedLibs...) 168 entries.SetBool("LOCAL_CHECK_ELF_FILES", false) 169 }) 170 171 return []android.AndroidMkEntries{entries} 172} 173 174func (p *PythonBinaryModule) DepsMutator(ctx android.BottomUpMutatorContext) { 175 p.PythonLibraryModule.DepsMutator(ctx) 176 177 if p.isEmbeddedLauncherEnabled() { 178 p.AddDepsOnPythonLauncherAndStdlib(ctx, pythonLibTag, launcherTag, launcherSharedLibTag, p.autorun(), ctx.Target()) 179 } 180} 181 182// HostToolPath returns a path if appropriate such that this module can be used as a host tool, 183// fulfilling the android.HostToolProvider interface. 184func (p *PythonBinaryModule) HostToolPath() android.OptionalPath { 185 // TODO: This should only be set when building host binaries -- tests built for device would be 186 // setting this incorrectly. 187 return android.OptionalPathForPath(p.installedDest) 188} 189 190// OutputFiles returns output files based on given tag, returns an error if tag is unsupported. 191func (p *PythonBinaryModule) OutputFiles(tag string) (android.Paths, error) { 192 switch tag { 193 case "": 194 return android.Paths{p.installSource}, nil 195 default: 196 return nil, fmt.Errorf("unsupported module reference tag %q", tag) 197 } 198} 199 200func (p *PythonBinaryModule) isEmbeddedLauncherEnabled() bool { 201 return BoolDefault(p.properties.Embedded_launcher, true) 202} 203 204func (b *PythonBinaryModule) autorun() bool { 205 return BoolDefault(b.binaryProperties.Autorun, true) 206} 207 208// get host interpreter name. 209func (p *PythonBinaryModule) getHostInterpreterName(ctx android.ModuleContext, 210 actualVersion string) string { 211 var interp string 212 switch actualVersion { 213 case pyVersion2: 214 interp = "python2.7" 215 case pyVersion3: 216 interp = "python3" 217 default: 218 panic(fmt.Errorf("unknown Python actualVersion: %q for module: %q.", 219 actualVersion, ctx.ModuleName())) 220 } 221 222 return interp 223} 224 225// find main program path within runfiles tree. 226func (p *PythonBinaryModule) getPyMainFile(ctx android.ModuleContext, 227 srcsPathMappings []pathMapping) string { 228 var main string 229 if String(p.binaryProperties.Main) == "" { 230 main = ctx.ModuleName() + pyExt 231 } else { 232 main = String(p.binaryProperties.Main) 233 } 234 235 for _, path := range srcsPathMappings { 236 if main == path.src.Rel() { 237 return path.dest 238 } 239 } 240 ctx.PropertyErrorf("main", "%q is not listed in srcs.", main) 241 242 return "" 243} 244 245func (p *PythonBinaryModule) getStem(ctx android.ModuleContext) string { 246 stem := ctx.ModuleName() 247 if String(p.binaryProperties.Stem) != "" { 248 stem = String(p.binaryProperties.Stem) 249 } 250 251 return stem + String(p.binaryProperties.Suffix) 252} 253 254func installDir(ctx android.ModuleContext, dir, dir64, relative string) android.InstallPath { 255 if ctx.Arch().ArchType.Multilib == "lib64" && dir64 != "" { 256 dir = dir64 257 } 258 if !ctx.Host() && ctx.Config().HasMultilibConflict(ctx.Arch().ArchType) { 259 dir = filepath.Join(dir, ctx.Arch().ArchType.String()) 260 } 261 return android.PathForModuleInstall(ctx, dir, relative) 262} 263