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