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 "Base" module type for building Python program. 18 19import ( 20 "fmt" 21 "path/filepath" 22 "regexp" 23 "sort" 24 "strings" 25 26 "android/soong/cc" 27 28 "github.com/google/blueprint" 29 "github.com/google/blueprint/depset" 30 "github.com/google/blueprint/proptools" 31 32 "android/soong/android" 33) 34 35type PythonLibraryInfo struct { 36 SrcsPathMappings []pathMapping 37 DataPathMappings []pathMapping 38 SrcsZip android.Path 39 PrecompiledSrcsZip android.Path 40 PkgPath string 41 BundleSharedLibs android.Paths 42} 43 44var PythonLibraryInfoProvider = blueprint.NewProvider[PythonLibraryInfo]() 45 46// the version-specific properties that apply to python modules. 47type VersionProperties struct { 48 // whether the module is required to be built with this version. 49 // Defaults to true for Python 3, and false otherwise. 50 Enabled *bool 51 52 // list of source files specific to this Python version. 53 // Using the syntax ":module", srcs may reference the outputs of other modules that produce source files, 54 // e.g. genrule or filegroup. 55 Srcs []string `android:"path,arch_variant"` 56 57 // list of source files that should not be used to build the Python module for this version. 58 // This is most useful to remove files that are not common to all Python versions. 59 Exclude_srcs []string `android:"path,arch_variant"` 60 61 // list of the Python libraries used only for this Python version. 62 Libs []string `android:"arch_variant"` 63 64 // whether the binary is required to be built with embedded launcher for this version, defaults to true. 65 Embedded_launcher *bool // TODO(b/174041232): Remove this property 66} 67 68// properties that apply to all python modules 69type BaseProperties struct { 70 // the package path prefix within the output artifact at which to place the source/data 71 // files of the current module. 72 // eg. Pkg_path = "a/b/c"; Other packages can reference this module by using 73 // (from a.b.c import ...) statement. 74 // if left unspecified, all the source/data files path is unchanged within zip file. 75 Pkg_path *string 76 77 // true, if the Python module is used internally, eg, Python std libs. 78 Is_internal *bool 79 80 // list of source (.py) files compatible both with Python2 and Python3 used to compile the 81 // Python module. 82 // srcs may reference the outputs of other modules that produce source files like genrule 83 // or filegroup using the syntax ":module". 84 // Srcs has to be non-empty. 85 Srcs []string `android:"path,arch_variant"` 86 87 // list of source files that should not be used to build the C/C++ module. 88 // This is most useful in the arch/multilib variants to remove non-common files 89 Exclude_srcs []string `android:"path,arch_variant"` 90 91 // list of files or filegroup modules that provide data that should be installed alongside 92 // the test. the file extension can be arbitrary except for (.py). 93 Data []string `android:"path,arch_variant"` 94 95 // Same as data, but will add dependencies on modules using the device's os variation and 96 // the common arch variation. Useful for a host test that wants to embed a module built for 97 // device. 98 Device_common_data []string `android:"path_device_common"` 99 100 // Same as data, but will add dependencies on modules via a device os variation and the 101 // device's first supported arch's variation. Useful for a host test that wants to embed a 102 // module built for device. 103 Device_first_data []string `android:"path_device_first"` 104 105 // list of java modules that provide data that should be installed alongside the test. 106 Java_data []string 107 108 // list of the Python libraries compatible both with Python2 and Python3. 109 Libs []string `android:"arch_variant"` 110 111 // TODO: b/403060602 - add unit tests for this property and related code 112 // list of shared libraries that should be packaged with the python code for this module. 113 Shared_libs []string `android:"arch_variant"` 114 115 Version struct { 116 // Python2-specific properties, including whether Python2 is supported for this module 117 // and version-specific sources, exclusions and dependencies. 118 Py2 VersionProperties `android:"arch_variant"` 119 120 // Python3-specific properties, including whether Python3 is supported for this module 121 // and version-specific sources, exclusions and dependencies. 122 Py3 VersionProperties `android:"arch_variant"` 123 } `android:"arch_variant"` 124 125 // This enabled property is to accept the collapsed enabled property from the VersionProperties. 126 // It is unused now, as all builds should be python3. 127 Enabled *bool `blueprint:"mutated"` 128 129 // whether the binary is required to be built with an embedded python interpreter, defaults to 130 // true. This allows taking the resulting binary outside of the build and running it on machines 131 // that don't have python installed or may have an older version of python. 132 Embedded_launcher *bool 133} 134 135// Used to store files of current module after expanding dependencies 136type pathMapping struct { 137 dest string 138 src android.Path 139} 140 141type PythonLibraryModule struct { 142 android.ModuleBase 143 android.DefaultableModuleBase 144 145 properties BaseProperties 146 protoProperties android.ProtoProperties 147 148 // initialize before calling Init 149 hod android.HostOrDeviceSupported 150 multilib android.Multilib 151 152 // the Python files of current module after expanding source dependencies. 153 // pathMapping: <dest: runfile_path, src: source_path> 154 srcsPathMappings []pathMapping 155 156 // the data files of current module after expanding source dependencies. 157 // pathMapping: <dest: runfile_path, src: source_path> 158 dataPathMappings []pathMapping 159 160 // The zip file containing the current module's source/data files. 161 srcsZip android.Path 162 163 // The zip file containing the current module's source/data files, with the 164 // source files precompiled. 165 precompiledSrcsZip android.Path 166 167 sourceProperties android.SourceProperties 168 169 // The shared libraries that should be bundled with the python code for 170 // any standalone python binaries that depend on this module. 171 bundleSharedLibs android.Paths 172} 173 174// newModule generates new Python base module 175func newModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *PythonLibraryModule { 176 return &PythonLibraryModule{ 177 hod: hod, 178 multilib: multilib, 179 } 180} 181 182// getSrcsPathMappings gets this module's path mapping of src source path : runfiles destination 183func (p *PythonLibraryModule) getSrcsPathMappings() []pathMapping { 184 return p.srcsPathMappings 185} 186 187// getSrcsPathMappings gets this module's path mapping of data source path : runfiles destination 188func (p *PythonLibraryModule) getDataPathMappings() []pathMapping { 189 return p.dataPathMappings 190} 191 192// getSrcsZip returns the filepath where the current module's source/data files are zipped. 193func (p *PythonLibraryModule) getSrcsZip() android.Path { 194 return p.srcsZip 195} 196 197// getSrcsZip returns the filepath where the current module's source/data files are zipped. 198func (p *PythonLibraryModule) getPrecompiledSrcsZip() android.Path { 199 return p.precompiledSrcsZip 200} 201 202// getPkgPath returns the pkg_path value 203func (p *PythonLibraryModule) getPkgPath() string { 204 return String(p.properties.Pkg_path) 205} 206 207func (p *PythonLibraryModule) getBaseProperties() *BaseProperties { 208 return &p.properties 209} 210 211func (p *PythonLibraryModule) getBundleSharedLibs() android.Paths { 212 return p.bundleSharedLibs 213} 214 215func (p *PythonLibraryModule) init() android.Module { 216 p.AddProperties(&p.properties, &p.protoProperties, &p.sourceProperties) 217 android.InitAndroidArchModule(p, p.hod, p.multilib) 218 android.InitDefaultableModule(p) 219 return p 220} 221 222// Python-specific tag to transfer information on the purpose of a dependency. 223// This is used when adding a dependency on a module, which can later be accessed when visiting 224// dependencies. 225type dependencyTag struct { 226 blueprint.BaseDependencyTag 227 name string 228} 229 230// Python-specific tag that indicates that installed files of this module should depend on installed 231// files of the dependency 232type installDependencyTag struct { 233 blueprint.BaseDependencyTag 234 // embedding this struct provides the installation dependency requirement 235 android.InstallAlwaysNeededDependencyTag 236 name string 237} 238 239var ( 240 pythonLibTag = dependencyTag{name: "pythonLib"} 241 javaDataTag = dependencyTag{name: "javaData"} 242 sharedLibTag = dependencyTag{name: "sharedLib"} 243 // The python interpreter, with soong module name "py3-launcher" or "py3-launcher-autorun". 244 launcherTag = dependencyTag{name: "launcher"} 245 launcherSharedLibTag = installDependencyTag{name: "launcherSharedLib"} 246 // The python interpreter built for host so that we can precompile python sources. 247 // This only works because the precompiled sources don't vary by architecture. 248 // The soong module name is "py3-launcher". 249 hostLauncherTag = dependencyTag{name: "hostLauncher"} 250 hostlauncherSharedLibTag = dependencyTag{name: "hostlauncherSharedLib"} 251 hostStdLibTag = dependencyTag{name: "hostStdLib"} 252 pathComponentRegexp = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_-]*$`) 253 pyExt = ".py" 254 protoExt = ".proto" 255 internalPath = "internal" 256) 257 258type basePropertiesProvider interface { 259 getBaseProperties() *BaseProperties 260} 261 262func anyHasExt(paths []string, ext string) bool { 263 for _, p := range paths { 264 if filepath.Ext(p) == ext { 265 return true 266 } 267 } 268 269 return false 270} 271 272func (p *PythonLibraryModule) anySrcHasExt(ctx android.BottomUpMutatorContext, ext string) bool { 273 return anyHasExt(p.properties.Srcs, ext) 274} 275 276// DepsMutator mutates dependencies for this module: 277// - handles proto dependencies, 278// - if required, specifies launcher and adds launcher dependencies, 279// - applies python version mutations to Python dependencies 280func (p *PythonLibraryModule) DepsMutator(ctx android.BottomUpMutatorContext) { 281 // Flatten the version.py3 props down into the main property struct. Leftover from when 282 // there was both python2 and 3 in the build, and properties could be different between them. 283 if base, ok := ctx.Module().(basePropertiesProvider); ok { 284 props := base.getBaseProperties() 285 286 err := proptools.AppendMatchingProperties([]interface{}{props}, &props.Version.Py3, nil) 287 if err != nil { 288 panic(err) 289 } 290 } 291 292 android.ProtoDeps(ctx, &p.protoProperties) 293 294 // If sources contain a proto file, add dependency on libprotobuf-python 295 if p.anySrcHasExt(ctx, protoExt) && p.Name() != "libprotobuf-python" { 296 ctx.AddDependency(ctx.Module(), pythonLibTag, "libprotobuf-python") 297 } 298 299 // Add python library dependencies for this python version variation 300 ctx.AddDependency(ctx.Module(), pythonLibTag, android.LastUniqueStrings(p.properties.Libs)...) 301 302 // Emulate the data property for java_data but with the arch variation overridden to "common" 303 // so that it can point to java modules. 304 javaDataVariation := []blueprint.Variation{{"arch", android.Common.String()}} 305 ctx.AddVariationDependencies(javaDataVariation, javaDataTag, p.properties.Java_data...) 306 307 if ctx.Host() { 308 ctx.AddVariationDependencies(ctx.Config().BuildOSTarget.Variations(), sharedLibTag, p.properties.Shared_libs...) 309 } else if len(p.properties.Shared_libs) > 0 { 310 ctx.PropertyErrorf("shared_libs", "shared_libs is not supported for device builds") 311 } 312 313 p.AddDepsOnPythonLauncherAndStdlib(ctx, hostStdLibTag, hostLauncherTag, hostlauncherSharedLibTag, false, ctx.Config().BuildOSTarget) 314} 315 316// AddDepsOnPythonLauncherAndStdlib will make the current module depend on the python stdlib, 317// launcher (interpreter), and the launcher's shared libraries. If autorun is true, it will use 318// the autorun launcher instead of the regular one. This function acceps a targetForDeps argument 319// as the target to use for these dependencies. For embedded launcher python binaries, the launcher 320// that will be embedded will be under the same target as the python module itself. But when 321// precompiling python code, we need to get the python launcher built for host, even if we're 322// compiling the python module for device, so we pass a different target to this function. 323func (p *PythonLibraryModule) AddDepsOnPythonLauncherAndStdlib(ctx android.BottomUpMutatorContext, 324 stdLibTag, launcherTag, launcherSharedLibTag blueprint.DependencyTag, 325 autorun bool, targetForDeps android.Target) { 326 var stdLib string 327 var launcherModule string 328 // Add launcher shared lib dependencies. Ideally, these should be 329 // derived from the `shared_libs` property of the launcher. TODO: read these from 330 // the python launcher itself using ctx.OtherModuleProvider() or similar on the result 331 // of ctx.AddFarVariationDependencies() 332 launcherSharedLibDeps := []string{ 333 "libsqlite", 334 } 335 // Add launcher-specific dependencies for bionic 336 if targetForDeps.Os.Bionic() { 337 launcherSharedLibDeps = append(launcherSharedLibDeps, "libc", "libdl", "libm") 338 } 339 if targetForDeps.Os == android.LinuxMusl && !ctx.Config().HostStaticBinaries() { 340 launcherSharedLibDeps = append(launcherSharedLibDeps, "libc_musl") 341 } 342 343 var prebuiltStdLib bool 344 if targetForDeps.Os.Bionic() { 345 prebuiltStdLib = false 346 } else if ctx.Config().VendorConfig("cpython3").Bool("force_build_host") { 347 prebuiltStdLib = false 348 } else { 349 prebuiltStdLib = true 350 } 351 352 if prebuiltStdLib { 353 stdLib = "py3-stdlib-prebuilt" 354 } else { 355 stdLib = "py3-stdlib" 356 } 357 358 launcherModule = "py3-launcher" 359 if autorun { 360 launcherModule = "py3-launcher-autorun" 361 } 362 if ctx.Config().HostStaticBinaries() && targetForDeps.Os == android.LinuxMusl { 363 launcherModule += "-static" 364 } 365 if ctx.Device() { 366 launcherSharedLibDeps = append(launcherSharedLibDeps, "liblog") 367 } 368 369 targetVariations := targetForDeps.Variations() 370 if ctx.ModuleName() != stdLib { 371 // Using AddFarVariationDependencies for all of these because they can be for a different 372 // platform, like if the python module itself was being compiled for device, we may want 373 // the python interpreter built for host so that we can precompile python sources. 374 ctx.AddFarVariationDependencies(targetVariations, stdLibTag, stdLib) 375 } 376 ctx.AddFarVariationDependencies(targetVariations, launcherTag, launcherModule) 377 ctx.AddFarVariationDependencies(targetVariations, launcherSharedLibTag, launcherSharedLibDeps...) 378} 379 380// GenerateAndroidBuildActions performs build actions common to all Python modules 381func (p *PythonLibraryModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { 382 if proptools.BoolDefault(p.properties.Version.Py2.Enabled, false) { 383 ctx.PropertyErrorf("version.py2.enabled", "Python 2 is no longer supported, please convert to python 3.") 384 } 385 expandedSrcs := android.PathsForModuleSrcExcludes(ctx, p.properties.Srcs, p.properties.Exclude_srcs) 386 // Keep before any early returns. 387 android.SetProvider(ctx, android.TestOnlyProviderKey, android.TestModuleInformation{ 388 TestOnly: Bool(p.sourceProperties.Test_only), 389 TopLevelTarget: p.sourceProperties.Top_level_test_target, 390 }) 391 392 // expand data files from "data" property. 393 expandedData := android.PathsForModuleSrc(ctx, p.properties.Data) 394 expandedData = append(expandedData, android.PathsForModuleSrc(ctx, p.properties.Device_common_data)...) 395 expandedData = append(expandedData, android.PathsForModuleSrc(ctx, p.properties.Device_first_data)...) 396 397 // Emulate the data property for java_data dependencies. 398 for _, javaData := range ctx.GetDirectDepsProxyWithTag(javaDataTag) { 399 expandedData = append(expandedData, android.OutputFilesForModule(ctx, javaData, "")...) 400 } 401 402 var directImplementationDeps android.Paths 403 var transitiveImplementationDeps []depset.DepSet[android.Path] 404 ctx.VisitDirectDepsProxyWithTag(sharedLibTag, func(dep android.ModuleProxy) { 405 sharedLibInfo, _ := android.OtherModuleProvider(ctx, dep, cc.SharedLibraryInfoProvider) 406 if sharedLibInfo.SharedLibrary != nil { 407 expandedData = append(expandedData, android.OutputFilesForModule(ctx, dep, "")...) 408 directImplementationDeps = append(directImplementationDeps, android.OutputFilesForModule(ctx, dep, "")...) 409 if info, ok := android.OtherModuleProvider(ctx, dep, cc.ImplementationDepInfoProvider); ok { 410 transitiveImplementationDeps = append(transitiveImplementationDeps, info.ImplementationDeps) 411 p.bundleSharedLibs = append(p.bundleSharedLibs, info.ImplementationDeps.ToList()...) 412 } 413 } else { 414 ctx.PropertyErrorf("shared_libs", "%q of type %q is not supported", dep.Name(), ctx.OtherModuleType(dep)) 415 } 416 }) 417 android.SetProvider(ctx, cc.ImplementationDepInfoProvider, &cc.ImplementationDepInfo{ 418 ImplementationDeps: depset.New(depset.PREORDER, directImplementationDeps, transitiveImplementationDeps), 419 }) 420 421 // Validate pkg_path property 422 pkgPath := String(p.properties.Pkg_path) 423 if pkgPath != "" { 424 // TODO: export validation from android/paths.go handling to replace this duplicated functionality 425 pkgPath = filepath.Clean(String(p.properties.Pkg_path)) 426 if pkgPath == ".." || strings.HasPrefix(pkgPath, "../") || 427 strings.HasPrefix(pkgPath, "/") { 428 ctx.PropertyErrorf("pkg_path", 429 "%q must be a relative path contained in par file.", 430 String(p.properties.Pkg_path)) 431 return 432 } 433 } 434 // If property Is_internal is set, prepend pkgPath with internalPath 435 if proptools.BoolDefault(p.properties.Is_internal, false) { 436 pkgPath = filepath.Join(internalPath, pkgPath) 437 } 438 439 // generate src:destination path mappings for this module 440 p.genModulePathMappings(ctx, pkgPath, expandedSrcs, expandedData) 441 442 // generate the zipfile of all source and data files 443 p.srcsZip = p.createSrcsZip(ctx, pkgPath) 444 p.precompiledSrcsZip = p.precompileSrcs(ctx) 445 446 android.SetProvider(ctx, PythonLibraryInfoProvider, PythonLibraryInfo{ 447 SrcsPathMappings: p.getSrcsPathMappings(), 448 DataPathMappings: p.getDataPathMappings(), 449 SrcsZip: p.getSrcsZip(), 450 PkgPath: p.getPkgPath(), 451 PrecompiledSrcsZip: p.getPrecompiledSrcsZip(), 452 BundleSharedLibs: p.getBundleSharedLibs(), 453 }) 454} 455 456func isValidPythonPath(path string) error { 457 identifiers := strings.Split(strings.TrimSuffix(path, filepath.Ext(path)), "/") 458 for _, token := range identifiers { 459 if !pathComponentRegexp.MatchString(token) { 460 return fmt.Errorf("the path %q contains invalid subpath %q. "+ 461 "Subpaths must be at least one character long. "+ 462 "The first character must an underscore or letter. "+ 463 "Following characters may be any of: letter, digit, underscore, hyphen.", 464 path, token) 465 } 466 } 467 return nil 468} 469 470// For this module, generate unique pathMappings: <dest: runfiles_path, src: source_path> 471// for python/data files expanded from properties. 472func (p *PythonLibraryModule) genModulePathMappings(ctx android.ModuleContext, pkgPath string, 473 expandedSrcs, expandedData android.Paths) { 474 // fetch <runfiles_path, source_path> pairs from "src" and "data" properties to 475 // check current module duplicates. 476 destToPySrcs := make(map[string]string) 477 destToPyData := make(map[string]string) 478 479 // Disable path checks for the stdlib, as it includes a "." in the version string 480 isInternal := proptools.BoolDefault(p.properties.Is_internal, false) 481 482 for _, s := range expandedSrcs { 483 if s.Ext() != pyExt && s.Ext() != protoExt { 484 ctx.PropertyErrorf("srcs", "found non (.py|.proto) file: %q!", s.String()) 485 continue 486 } 487 runfilesPath := filepath.Join(pkgPath, s.Rel()) 488 if !isInternal { 489 if err := isValidPythonPath(runfilesPath); err != nil { 490 ctx.PropertyErrorf("srcs", err.Error()) 491 } 492 } 493 if !checkForDuplicateOutputPath(ctx, destToPySrcs, runfilesPath, s.String(), p.Name(), p.Name()) { 494 p.srcsPathMappings = append(p.srcsPathMappings, pathMapping{dest: runfilesPath, src: s}) 495 } 496 } 497 498 for _, d := range expandedData { 499 if d.Ext() == pyExt { 500 ctx.PropertyErrorf("data", "found (.py) file: %q!", d.String()) 501 continue 502 } 503 runfilesPath := filepath.Join(pkgPath, d.Rel()) 504 if !checkForDuplicateOutputPath(ctx, destToPyData, runfilesPath, d.String(), p.Name(), p.Name()) { 505 p.dataPathMappings = append(p.dataPathMappings, 506 pathMapping{dest: runfilesPath, src: d}) 507 } 508 } 509} 510 511// createSrcsZip registers build actions to zip current module's sources and data. 512func (p *PythonLibraryModule) createSrcsZip(ctx android.ModuleContext, pkgPath string) android.Path { 513 relativeRootMap := make(map[string]android.Paths) 514 var protoSrcs android.Paths 515 addPathMapping := func(path pathMapping) { 516 relativeRoot := strings.TrimSuffix(path.src.String(), path.src.Rel()) 517 relativeRootMap[relativeRoot] = append(relativeRootMap[relativeRoot], path.src) 518 } 519 520 // "srcs" or "data" properties may contain filegroups so it might happen that 521 // the root directory for each source path is different. 522 for _, path := range p.srcsPathMappings { 523 // handle proto sources separately 524 if path.src.Ext() == protoExt { 525 protoSrcs = append(protoSrcs, path.src) 526 } else { 527 addPathMapping(path) 528 } 529 } 530 for _, path := range p.dataPathMappings { 531 addPathMapping(path) 532 } 533 534 var zips android.Paths 535 if len(protoSrcs) > 0 { 536 protoFlags := android.GetProtoFlags(ctx, &p.protoProperties) 537 protoFlags.OutTypeFlag = "--python_out" 538 539 if pkgPath != "" { 540 pkgPathStagingDir := android.PathForModuleGen(ctx, "protos_staged_for_pkg_path") 541 rule := android.NewRuleBuilder(pctx, ctx) 542 var stagedProtoSrcs android.Paths 543 for _, srcFile := range protoSrcs { 544 stagedProtoSrc := pkgPathStagingDir.Join(ctx, pkgPath, srcFile.Rel()) 545 rule.Command().Text("cp -f").Input(srcFile).Output(stagedProtoSrc) 546 stagedProtoSrcs = append(stagedProtoSrcs, stagedProtoSrc) 547 } 548 rule.Build("stage_protos_for_pkg_path", "Stage protos for pkg_path") 549 protoSrcs = stagedProtoSrcs 550 } 551 552 for _, srcFile := range protoSrcs { 553 zip := genProto(ctx, srcFile, protoFlags) 554 zips = append(zips, zip) 555 } 556 } 557 558 if len(relativeRootMap) > 0 { 559 // in order to keep stable order of soong_zip params, we sort the keys here. 560 roots := android.SortedKeys(relativeRootMap) 561 562 // Use -symlinks=false so that the symlinks in the bazel output directory are followed 563 parArgs := []string{"-symlinks=false"} 564 if pkgPath != "" { 565 // use package path as path prefix 566 parArgs = append(parArgs, `-P `+pkgPath) 567 } 568 paths := android.Paths{} 569 for _, root := range roots { 570 // specify relative root of file in following -f arguments 571 parArgs = append(parArgs, `-C `+root) 572 for _, path := range relativeRootMap[root] { 573 parArgs = append(parArgs, `-f `+path.String()) 574 paths = append(paths, path) 575 } 576 } 577 578 origSrcsZip := android.PathForModuleOut(ctx, ctx.ModuleName()+".py.srcszip") 579 ctx.Build(pctx, android.BuildParams{ 580 Rule: zip, 581 Description: "python library archive", 582 Output: origSrcsZip, 583 // as zip rule does not use $in, there is no real need to distinguish between Inputs and Implicits 584 Implicits: paths, 585 Args: map[string]string{ 586 "args": strings.Join(parArgs, " "), 587 }, 588 }) 589 zips = append(zips, origSrcsZip) 590 } 591 // we may have multiple zips due to separate handling of proto source files 592 if len(zips) == 1 { 593 return zips[0] 594 } else { 595 combinedSrcsZip := android.PathForModuleOut(ctx, ctx.ModuleName()+".srcszip") 596 ctx.Build(pctx, android.BuildParams{ 597 Rule: combineZip, 598 Description: "combine python library archive", 599 Output: combinedSrcsZip, 600 Inputs: zips, 601 }) 602 return combinedSrcsZip 603 } 604} 605 606func (p *PythonLibraryModule) precompileSrcs(ctx android.ModuleContext) android.Path { 607 // To precompile the python sources, we need a python interpreter and stdlib built 608 // for host. We then use those to compile the python sources, which may be used on either 609 // host of device. Python bytecode is architecture agnostic, so we're essentially 610 // "cross compiling" for device here purely by virtue of host and device python bytecode 611 // being the same. 612 var stdLib android.Path 613 var stdLibPkg string 614 var launcher android.Path 615 if proptools.BoolDefault(p.properties.Is_internal, false) { 616 stdLib = p.srcsZip 617 stdLibPkg = p.getPkgPath() 618 } else { 619 ctx.VisitDirectDepsProxyWithTag(hostStdLibTag, func(module android.ModuleProxy) { 620 if dep, ok := android.OtherModuleProvider(ctx, module, PythonLibraryInfoProvider); ok { 621 stdLib = dep.PrecompiledSrcsZip 622 stdLibPkg = dep.PkgPath 623 } 624 }) 625 } 626 ctx.VisitDirectDepsProxyWithTag(hostLauncherTag, func(module android.ModuleProxy) { 627 if dep, ok := android.OtherModuleProvider(ctx, module, cc.LinkableInfoProvider); ok { 628 optionalLauncher := dep.OutputFile 629 if optionalLauncher.Valid() { 630 launcher = optionalLauncher.Path() 631 } 632 } 633 }) 634 var launcherSharedLibs android.Paths 635 var ldLibraryPath []string 636 ctx.VisitDirectDepsProxyWithTag(hostlauncherSharedLibTag, func(module android.ModuleProxy) { 637 if dep, ok := android.OtherModuleProvider(ctx, module, cc.LinkableInfoProvider); ok { 638 optionalPath := dep.OutputFile 639 if optionalPath.Valid() { 640 launcherSharedLibs = append(launcherSharedLibs, optionalPath.Path()) 641 ldLibraryPath = append(ldLibraryPath, filepath.Dir(optionalPath.Path().String())) 642 } 643 } 644 }) 645 646 out := android.PathForModuleOut(ctx, ctx.ModuleName()+".srcszipprecompiled") 647 if stdLib == nil || launcher == nil { 648 // This shouldn't happen in a real build because we'll error out when adding dependencies 649 // on the stdlib and launcher if they don't exist. But some tests set 650 // AllowMissingDependencies. 651 return out 652 } 653 ctx.Build(pctx, android.BuildParams{ 654 Rule: precompile, 655 Input: p.srcsZip, 656 Output: out, 657 Implicits: launcherSharedLibs, 658 Description: "Precompile the python sources of " + ctx.ModuleName(), 659 Args: map[string]string{ 660 "stdlibZip": stdLib.String(), 661 "stdlibPkg": stdLibPkg, 662 "launcher": launcher.String(), 663 "ldLibraryPath": strings.Join(ldLibraryPath, ":"), 664 }, 665 }) 666 return out 667} 668 669// collectPathsFromTransitiveDeps checks for source/data files for duplicate paths 670// for module and its transitive dependencies and collects list of data/source file 671// zips for transitive dependencies. 672func (p *PythonLibraryModule) collectPathsFromTransitiveDeps(ctx android.ModuleContext, precompiled bool) android.Paths { 673 // fetch <runfiles_path, source_path> pairs from "src" and "data" properties to 674 // check duplicates. 675 destToPySrcs := make(map[string]string) 676 destToPyData := make(map[string]string) 677 for _, path := range p.srcsPathMappings { 678 destToPySrcs[path.dest] = path.src.String() 679 } 680 for _, path := range p.dataPathMappings { 681 destToPyData[path.dest] = path.src.String() 682 } 683 684 seen := make(map[android.Module]bool) 685 686 var result android.Paths 687 688 // visit all its dependencies in depth first. 689 ctx.WalkDepsProxy(func(child, _ android.ModuleProxy) bool { 690 // we only collect dependencies tagged as python library deps 691 if ctx.OtherModuleDependencyTag(child) != pythonLibTag { 692 return false 693 } 694 if seen[child] { 695 return false 696 } 697 seen[child] = true 698 // Python modules only can depend on Python libraries. 699 dep, isLibrary := android.OtherModuleProvider(ctx, child, PythonLibraryInfoProvider) 700 _, isBinary := android.OtherModuleProvider(ctx, child, PythonBinaryInfoProvider) 701 if !isLibrary || isBinary { 702 ctx.PropertyErrorf("libs", 703 "the dependency %q of module %q is not Python library!", 704 ctx.OtherModuleName(child), ctx.ModuleName()) 705 } 706 // collect source and data paths, checking that there are no duplicate output file conflicts 707 if isLibrary { 708 srcs := dep.SrcsPathMappings 709 for _, path := range srcs { 710 checkForDuplicateOutputPath(ctx, destToPySrcs, 711 path.dest, path.src.String(), ctx.ModuleName(), ctx.OtherModuleName(child)) 712 } 713 data := dep.DataPathMappings 714 for _, path := range data { 715 checkForDuplicateOutputPath(ctx, destToPyData, 716 path.dest, path.src.String(), ctx.ModuleName(), ctx.OtherModuleName(child)) 717 } 718 if precompiled { 719 result = append(result, dep.PrecompiledSrcsZip) 720 } else { 721 result = append(result, dep.SrcsZip) 722 } 723 } 724 return true 725 }) 726 return result 727} 728 729func (p *PythonLibraryModule) collectSharedLibDeps(ctx android.ModuleContext) android.Paths { 730 seen := make(map[android.Module]bool) 731 732 var result android.Paths 733 734 ctx.WalkDepsProxy(func(child, _ android.ModuleProxy) bool { 735 // we only collect dependencies tagged as python library deps 736 if ctx.OtherModuleDependencyTag(child) != pythonLibTag { 737 return false 738 } 739 if seen[child] { 740 return false 741 } 742 seen[child] = true 743 dep, isLibrary := android.OtherModuleProvider(ctx, child, PythonLibraryInfoProvider) 744 if isLibrary { 745 result = append(result, dep.BundleSharedLibs...) 746 } 747 return true 748 }) 749 return result 750} 751 752func (p *PythonLibraryModule) zipSharedLibs(ctx android.ModuleContext, bundleSharedLibs android.Paths) android.Path { 753 // sort the paths to keep the output deterministic 754 sort.Slice(bundleSharedLibs, func(i, j int) bool { 755 return bundleSharedLibs[i].String() < bundleSharedLibs[j].String() 756 }) 757 758 parArgs := []string{"-symlinks=false", "-P lib64"} 759 paths := android.Paths{} 760 for _, path := range bundleSharedLibs { 761 // specify relative root of file in following -f arguments 762 parArgs = append(parArgs, `-C `+filepath.Dir(path.String())) 763 parArgs = append(parArgs, `-f `+path.String()) 764 paths = append(paths, path) 765 } 766 srcsZip := android.PathForModuleOut(ctx, ctx.ModuleName()+".sharedlibs.srcszip") 767 ctx.Build(pctx, android.BuildParams{ 768 Rule: zip, 769 Description: "bundle shared libraries for python binary", 770 Output: srcsZip, 771 Implicits: paths, 772 Args: map[string]string{ 773 "args": strings.Join(parArgs, " "), 774 }, 775 }) 776 return srcsZip 777} 778 779// chckForDuplicateOutputPath checks whether outputPath has already been included in map m, which 780// would result in two files being placed in the same location. 781// If there is a duplicate path, an error is thrown and true is returned 782// Otherwise, outputPath: srcPath is added to m and returns false 783func checkForDuplicateOutputPath(ctx android.ModuleContext, m map[string]string, outputPath, srcPath, curModule, otherModule string) bool { 784 if oldSrcPath, found := m[outputPath]; found { 785 ctx.ModuleErrorf("found two files to be placed at the same location within zip %q."+ 786 " First file: in module %s at path %q."+ 787 " Second file: in module %s at path %q.", 788 outputPath, curModule, oldSrcPath, otherModule, srcPath) 789 return true 790 } 791 m[outputPath] = srcPath 792 793 return false 794} 795 796// InstallInData returns true as Python is not supported in the system partition 797func (p *PythonLibraryModule) InstallInData() bool { 798 return true 799} 800 801var Bool = proptools.Bool 802var BoolDefault = proptools.BoolDefault 803var String = proptools.String 804