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 "strings" 24 25 "android/soong/bazel" 26 27 "github.com/google/blueprint" 28 "github.com/google/blueprint/proptools" 29 30 "android/soong/android" 31) 32 33func init() { 34 registerPythonMutators(android.InitRegistrationContext) 35} 36 37func registerPythonMutators(ctx android.RegistrationContext) { 38 ctx.PreDepsMutators(RegisterPythonPreDepsMutators) 39} 40 41// Exported to support other packages using Python modules in tests. 42func RegisterPythonPreDepsMutators(ctx android.RegisterMutatorsContext) { 43 ctx.BottomUp("python_version", versionSplitMutator()).Parallel() 44} 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 false. 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 // list of java modules that provide data that should be installed alongside the test. 96 Java_data []string 97 98 // list of the Python libraries compatible both with Python2 and Python3. 99 Libs []string `android:"arch_variant"` 100 101 Version struct { 102 // Python2-specific properties, including whether Python2 is supported for this module 103 // and version-specific sources, exclusions and dependencies. 104 Py2 VersionProperties `android:"arch_variant"` 105 106 // Python3-specific properties, including whether Python3 is supported for this module 107 // and version-specific sources, exclusions and dependencies. 108 Py3 VersionProperties `android:"arch_variant"` 109 } `android:"arch_variant"` 110 111 // the actual version each module uses after variations created. 112 // this property name is hidden from users' perspectives, and soong will populate it during 113 // runtime. 114 Actual_version string `blueprint:"mutated"` 115 116 // whether the module is required to be built with actual_version. 117 // this is set by the python version mutator based on version-specific properties 118 Enabled *bool `blueprint:"mutated"` 119 120 // whether the binary is required to be built with embedded launcher for this actual_version. 121 // this is set by the python version mutator based on version-specific properties 122 Embedded_launcher *bool `blueprint:"mutated"` 123} 124 125type baseAttributes struct { 126 // TODO(b/200311466): Probably not translate b/c Bazel has no good equiv 127 //Pkg_path bazel.StringAttribute 128 // TODO: Related to Pkg_bath and similarLy gated 129 //Is_internal bazel.BoolAttribute 130 // Combines Srcs and Exclude_srcs 131 Srcs bazel.LabelListAttribute 132 Deps bazel.LabelListAttribute 133 // Combines Data and Java_data (invariant) 134 Data bazel.LabelListAttribute 135} 136 137// Used to store files of current module after expanding dependencies 138type pathMapping struct { 139 dest string 140 src android.Path 141} 142 143type Module struct { 144 android.ModuleBase 145 android.DefaultableModuleBase 146 android.BazelModuleBase 147 148 properties BaseProperties 149 protoProperties android.ProtoProperties 150 151 // initialize before calling Init 152 hod android.HostOrDeviceSupported 153 multilib android.Multilib 154 155 // interface used to bootstrap .par executable when embedded_launcher is true 156 // this should be set by Python modules which are runnable, e.g. binaries and tests 157 // bootstrapper might be nil (e.g. Python library module). 158 bootstrapper bootstrapper 159 160 // interface that implements functions required for installation 161 // this should be set by Python modules which are runnable, e.g. binaries and tests 162 // installer might be nil (e.g. Python library module). 163 installer installer 164 165 // the Python files of current module after expanding source dependencies. 166 // pathMapping: <dest: runfile_path, src: source_path> 167 srcsPathMappings []pathMapping 168 169 // the data files of current module after expanding source dependencies. 170 // pathMapping: <dest: runfile_path, src: source_path> 171 dataPathMappings []pathMapping 172 173 // the zip filepath for zipping current module source/data files. 174 srcsZip android.Path 175 176 // dependency modules' zip filepath for zipping current module source/data files. 177 depsSrcsZips android.Paths 178 179 // (.intermediate) module output path as installation source. 180 installSource android.OptionalPath 181 182 // Map to ensure sub-part of the AndroidMk for this module is only added once 183 subAndroidMkOnce map[subAndroidMkProvider]bool 184} 185 186// newModule generates new Python base module 187func newModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *Module { 188 return &Module{ 189 hod: hod, 190 multilib: multilib, 191 } 192} 193 194func (m *Module) makeArchVariantBaseAttributes(ctx android.TopDownMutatorContext) baseAttributes { 195 var attrs baseAttributes 196 archVariantBaseProps := m.GetArchVariantProperties(ctx, &BaseProperties{}) 197 for axis, configToProps := range archVariantBaseProps { 198 for config, props := range configToProps { 199 if baseProps, ok := props.(*BaseProperties); ok { 200 attrs.Srcs.SetSelectValue(axis, config, 201 android.BazelLabelForModuleSrcExcludes(ctx, baseProps.Srcs, baseProps.Exclude_srcs)) 202 attrs.Deps.SetSelectValue(axis, config, 203 android.BazelLabelForModuleDeps(ctx, baseProps.Libs)) 204 data := android.BazelLabelForModuleSrc(ctx, baseProps.Data) 205 data.Append(android.BazelLabelForModuleSrc(ctx, baseProps.Java_data)) 206 attrs.Data.SetSelectValue(axis, config, data) 207 } 208 } 209 } 210 return attrs 211} 212 213// bootstrapper interface should be implemented for runnable modules, e.g. binary and test 214type bootstrapper interface { 215 bootstrapperProps() []interface{} 216 bootstrap(ctx android.ModuleContext, ActualVersion string, embeddedLauncher bool, 217 srcsPathMappings []pathMapping, srcsZip android.Path, 218 depsSrcsZips android.Paths) android.OptionalPath 219 220 autorun() bool 221} 222 223// installer interface should be implemented for installable modules, e.g. binary and test 224type installer interface { 225 install(ctx android.ModuleContext, path android.Path) 226 setAndroidMkSharedLibs(sharedLibs []string) 227} 228 229// interface implemented by Python modules to provide source and data mappings and zip to python 230// modules that depend on it 231type pythonDependency interface { 232 getSrcsPathMappings() []pathMapping 233 getDataPathMappings() []pathMapping 234 getSrcsZip() android.Path 235} 236 237// getSrcsPathMappings gets this module's path mapping of src source path : runfiles destination 238func (p *Module) getSrcsPathMappings() []pathMapping { 239 return p.srcsPathMappings 240} 241 242// getSrcsPathMappings gets this module's path mapping of data source path : runfiles destination 243func (p *Module) getDataPathMappings() []pathMapping { 244 return p.dataPathMappings 245} 246 247// getSrcsZip returns the filepath where the current module's source/data files are zipped. 248func (p *Module) getSrcsZip() android.Path { 249 return p.srcsZip 250} 251 252var _ pythonDependency = (*Module)(nil) 253 254var _ android.AndroidMkEntriesProvider = (*Module)(nil) 255 256func (p *Module) init(additionalProps ...interface{}) android.Module { 257 p.AddProperties(&p.properties, &p.protoProperties) 258 259 // Add additional properties for bootstrapping/installation 260 // This is currently tied to the bootstrapper interface; 261 // however, these are a combination of properties for the installation and bootstrapping of a module 262 if p.bootstrapper != nil { 263 p.AddProperties(p.bootstrapper.bootstrapperProps()...) 264 } 265 266 android.InitAndroidArchModule(p, p.hod, p.multilib) 267 android.InitDefaultableModule(p) 268 269 return p 270} 271 272// Python-specific tag to transfer information on the purpose of a dependency. 273// This is used when adding a dependency on a module, which can later be accessed when visiting 274// dependencies. 275type dependencyTag struct { 276 blueprint.BaseDependencyTag 277 name string 278} 279 280// Python-specific tag that indicates that installed files of this module should depend on installed 281// files of the dependency 282type installDependencyTag struct { 283 blueprint.BaseDependencyTag 284 // embedding this struct provides the installation dependency requirement 285 android.InstallAlwaysNeededDependencyTag 286 name string 287} 288 289var ( 290 pythonLibTag = dependencyTag{name: "pythonLib"} 291 javaDataTag = dependencyTag{name: "javaData"} 292 launcherTag = dependencyTag{name: "launcher"} 293 launcherSharedLibTag = installDependencyTag{name: "launcherSharedLib"} 294 pathComponentRegexp = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_-]*$`) 295 pyExt = ".py" 296 protoExt = ".proto" 297 pyVersion2 = "PY2" 298 pyVersion3 = "PY3" 299 initFileName = "__init__.py" 300 mainFileName = "__main__.py" 301 entryPointFile = "entry_point.txt" 302 parFileExt = ".zip" 303 internalPath = "internal" 304) 305 306// versionSplitMutator creates version variants for modules and appends the version-specific 307// properties for a given variant to the properties in the variant module 308func versionSplitMutator() func(android.BottomUpMutatorContext) { 309 return func(mctx android.BottomUpMutatorContext) { 310 if base, ok := mctx.Module().(*Module); ok { 311 versionNames := []string{} 312 // collect version specific properties, so that we can merge version-specific properties 313 // into the module's overall properties 314 versionProps := []VersionProperties{} 315 // PY3 is first so that we alias the PY3 variant rather than PY2 if both 316 // are available 317 if proptools.BoolDefault(base.properties.Version.Py3.Enabled, true) { 318 versionNames = append(versionNames, pyVersion3) 319 versionProps = append(versionProps, base.properties.Version.Py3) 320 } 321 if proptools.BoolDefault(base.properties.Version.Py2.Enabled, false) { 322 versionNames = append(versionNames, pyVersion2) 323 versionProps = append(versionProps, base.properties.Version.Py2) 324 } 325 modules := mctx.CreateLocalVariations(versionNames...) 326 // Alias module to the first variant 327 if len(versionNames) > 0 { 328 mctx.AliasVariation(versionNames[0]) 329 } 330 for i, v := range versionNames { 331 // set the actual version for Python module. 332 modules[i].(*Module).properties.Actual_version = v 333 // append versioned properties for the Python module to the overall properties 334 err := proptools.AppendMatchingProperties([]interface{}{&modules[i].(*Module).properties}, &versionProps[i], nil) 335 if err != nil { 336 panic(err) 337 } 338 } 339 } 340 } 341} 342 343// HostToolPath returns a path if appropriate such that this module can be used as a host tool, 344// fulfilling HostToolProvider interface. 345func (p *Module) HostToolPath() android.OptionalPath { 346 if p.installer != nil { 347 if bin, ok := p.installer.(*binaryDecorator); ok { 348 // TODO: This should only be set when building host binaries -- tests built for device would be 349 // setting this incorrectly. 350 return android.OptionalPathForPath(bin.path) 351 } 352 } 353 354 return android.OptionalPath{} 355 356} 357 358// OutputFiles returns output files based on given tag, returns an error if tag is unsupported. 359func (p *Module) OutputFiles(tag string) (android.Paths, error) { 360 switch tag { 361 case "": 362 if outputFile := p.installSource; outputFile.Valid() { 363 return android.Paths{outputFile.Path()}, nil 364 } 365 return android.Paths{}, nil 366 default: 367 return nil, fmt.Errorf("unsupported module reference tag %q", tag) 368 } 369} 370 371func (p *Module) isEmbeddedLauncherEnabled() bool { 372 return p.installer != nil && Bool(p.properties.Embedded_launcher) 373} 374 375func anyHasExt(paths []string, ext string) bool { 376 for _, p := range paths { 377 if filepath.Ext(p) == ext { 378 return true 379 } 380 } 381 382 return false 383} 384 385func (p *Module) anySrcHasExt(ctx android.BottomUpMutatorContext, ext string) bool { 386 return anyHasExt(p.properties.Srcs, ext) 387} 388 389// DepsMutator mutates dependencies for this module: 390// * handles proto dependencies, 391// * if required, specifies launcher and adds launcher dependencies, 392// * applies python version mutations to Python dependencies 393func (p *Module) DepsMutator(ctx android.BottomUpMutatorContext) { 394 android.ProtoDeps(ctx, &p.protoProperties) 395 396 versionVariation := []blueprint.Variation{ 397 {"python_version", p.properties.Actual_version}, 398 } 399 400 // If sources contain a proto file, add dependency on libprotobuf-python 401 if p.anySrcHasExt(ctx, protoExt) && p.Name() != "libprotobuf-python" { 402 ctx.AddVariationDependencies(versionVariation, pythonLibTag, "libprotobuf-python") 403 } 404 405 // Add python library dependencies for this python version variation 406 ctx.AddVariationDependencies(versionVariation, pythonLibTag, android.LastUniqueStrings(p.properties.Libs)...) 407 408 // If this module will be installed and has an embedded launcher, we need to add dependencies for: 409 // * standard library 410 // * launcher 411 // * shared dependencies of the launcher 412 if p.installer != nil && p.isEmbeddedLauncherEnabled() { 413 var stdLib string 414 var launcherModule string 415 // Add launcher shared lib dependencies. Ideally, these should be 416 // derived from the `shared_libs` property of the launcher. However, we 417 // cannot read the property at this stage and it will be too late to add 418 // dependencies later. 419 launcherSharedLibDeps := []string{ 420 "libsqlite", 421 } 422 // Add launcher-specific dependencies for bionic 423 if ctx.Target().Os.Bionic() { 424 launcherSharedLibDeps = append(launcherSharedLibDeps, "libc", "libdl", "libm") 425 } 426 if ctx.Target().Os == android.LinuxMusl && !ctx.Config().HostStaticBinaries() { 427 launcherSharedLibDeps = append(launcherSharedLibDeps, "libc_musl") 428 } 429 430 switch p.properties.Actual_version { 431 case pyVersion2: 432 stdLib = "py2-stdlib" 433 434 launcherModule = "py2-launcher" 435 if p.bootstrapper.autorun() { 436 launcherModule = "py2-launcher-autorun" 437 } 438 439 launcherSharedLibDeps = append(launcherSharedLibDeps, "libc++") 440 441 case pyVersion3: 442 stdLib = "py3-stdlib" 443 444 launcherModule = "py3-launcher" 445 if p.bootstrapper.autorun() { 446 launcherModule = "py3-launcher-autorun" 447 } 448 if ctx.Config().HostStaticBinaries() && ctx.Target().Os == android.LinuxMusl { 449 launcherModule += "-static" 450 } 451 452 if ctx.Device() { 453 launcherSharedLibDeps = append(launcherSharedLibDeps, "liblog") 454 } 455 default: 456 panic(fmt.Errorf("unknown Python Actual_version: %q for module: %q.", 457 p.properties.Actual_version, ctx.ModuleName())) 458 } 459 ctx.AddVariationDependencies(versionVariation, pythonLibTag, stdLib) 460 ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherTag, launcherModule) 461 ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherSharedLibTag, launcherSharedLibDeps...) 462 } 463 464 // Emulate the data property for java_data but with the arch variation overridden to "common" 465 // so that it can point to java modules. 466 javaDataVariation := []blueprint.Variation{{"arch", android.Common.String()}} 467 ctx.AddVariationDependencies(javaDataVariation, javaDataTag, p.properties.Java_data...) 468} 469 470func (p *Module) GenerateAndroidBuildActions(ctx android.ModuleContext) { 471 p.generatePythonBuildActions(ctx) 472 473 // Only Python binary and test modules have non-empty bootstrapper. 474 if p.bootstrapper != nil { 475 // if the module is being installed, we need to collect all transitive dependencies to embed in 476 // the final par 477 p.collectPathsFromTransitiveDeps(ctx) 478 // bootstrap the module, including resolving main file, getting launcher path, and 479 // registering actions to build the par file 480 // bootstrap returns the binary output path 481 p.installSource = p.bootstrapper.bootstrap(ctx, p.properties.Actual_version, 482 p.isEmbeddedLauncherEnabled(), p.srcsPathMappings, p.srcsZip, p.depsSrcsZips) 483 } 484 485 // Only Python binary and test modules have non-empty installer. 486 if p.installer != nil { 487 var sharedLibs []string 488 // if embedded launcher is enabled, we need to collect the shared library depenendencies of the 489 // launcher 490 for _, dep := range ctx.GetDirectDepsWithTag(launcherSharedLibTag) { 491 sharedLibs = append(sharedLibs, ctx.OtherModuleName(dep)) 492 } 493 494 p.installer.setAndroidMkSharedLibs(sharedLibs) 495 496 // Install the par file from installSource 497 if p.installSource.Valid() { 498 p.installer.install(ctx, p.installSource.Path()) 499 } 500 } 501} 502 503// generatePythonBuildActions performs build actions common to all Python modules 504func (p *Module) generatePythonBuildActions(ctx android.ModuleContext) { 505 expandedSrcs := android.PathsForModuleSrcExcludes(ctx, p.properties.Srcs, p.properties.Exclude_srcs) 506 requiresSrcs := true 507 if p.bootstrapper != nil && !p.bootstrapper.autorun() { 508 requiresSrcs = false 509 } 510 if len(expandedSrcs) == 0 && requiresSrcs { 511 ctx.ModuleErrorf("doesn't have any source files!") 512 } 513 514 // expand data files from "data" property. 515 expandedData := android.PathsForModuleSrc(ctx, p.properties.Data) 516 517 // Emulate the data property for java_data dependencies. 518 for _, javaData := range ctx.GetDirectDepsWithTag(javaDataTag) { 519 expandedData = append(expandedData, android.OutputFilesForModule(ctx, javaData, "")...) 520 } 521 522 // Validate pkg_path property 523 pkgPath := String(p.properties.Pkg_path) 524 if pkgPath != "" { 525 // TODO: export validation from android/paths.go handling to replace this duplicated functionality 526 pkgPath = filepath.Clean(String(p.properties.Pkg_path)) 527 if pkgPath == ".." || strings.HasPrefix(pkgPath, "../") || 528 strings.HasPrefix(pkgPath, "/") { 529 ctx.PropertyErrorf("pkg_path", 530 "%q must be a relative path contained in par file.", 531 String(p.properties.Pkg_path)) 532 return 533 } 534 } 535 // If property Is_internal is set, prepend pkgPath with internalPath 536 if proptools.BoolDefault(p.properties.Is_internal, false) { 537 pkgPath = filepath.Join(internalPath, pkgPath) 538 } 539 540 // generate src:destination path mappings for this module 541 p.genModulePathMappings(ctx, pkgPath, expandedSrcs, expandedData) 542 543 // generate the zipfile of all source and data files 544 p.srcsZip = p.createSrcsZip(ctx, pkgPath) 545} 546 547func isValidPythonPath(path string) error { 548 identifiers := strings.Split(strings.TrimSuffix(path, filepath.Ext(path)), "/") 549 for _, token := range identifiers { 550 if !pathComponentRegexp.MatchString(token) { 551 return fmt.Errorf("the path %q contains invalid subpath %q. "+ 552 "Subpaths must be at least one character long. "+ 553 "The first character must an underscore or letter. "+ 554 "Following characters may be any of: letter, digit, underscore, hyphen.", 555 path, token) 556 } 557 } 558 return nil 559} 560 561// For this module, generate unique pathMappings: <dest: runfiles_path, src: source_path> 562// for python/data files expanded from properties. 563func (p *Module) genModulePathMappings(ctx android.ModuleContext, pkgPath string, 564 expandedSrcs, expandedData android.Paths) { 565 // fetch <runfiles_path, source_path> pairs from "src" and "data" properties to 566 // check current module duplicates. 567 destToPySrcs := make(map[string]string) 568 destToPyData := make(map[string]string) 569 570 for _, s := range expandedSrcs { 571 if s.Ext() != pyExt && s.Ext() != protoExt { 572 ctx.PropertyErrorf("srcs", "found non (.py|.proto) file: %q!", s.String()) 573 continue 574 } 575 runfilesPath := filepath.Join(pkgPath, s.Rel()) 576 if err := isValidPythonPath(runfilesPath); err != nil { 577 ctx.PropertyErrorf("srcs", err.Error()) 578 } 579 if !checkForDuplicateOutputPath(ctx, destToPySrcs, runfilesPath, s.String(), p.Name(), p.Name()) { 580 p.srcsPathMappings = append(p.srcsPathMappings, pathMapping{dest: runfilesPath, src: s}) 581 } 582 } 583 584 for _, d := range expandedData { 585 if d.Ext() == pyExt || d.Ext() == protoExt { 586 ctx.PropertyErrorf("data", "found (.py|.proto) file: %q!", d.String()) 587 continue 588 } 589 runfilesPath := filepath.Join(pkgPath, d.Rel()) 590 if !checkForDuplicateOutputPath(ctx, destToPyData, runfilesPath, d.String(), p.Name(), p.Name()) { 591 p.dataPathMappings = append(p.dataPathMappings, 592 pathMapping{dest: runfilesPath, src: d}) 593 } 594 } 595} 596 597// createSrcsZip registers build actions to zip current module's sources and data. 598func (p *Module) createSrcsZip(ctx android.ModuleContext, pkgPath string) android.Path { 599 relativeRootMap := make(map[string]android.Paths) 600 pathMappings := append(p.srcsPathMappings, p.dataPathMappings...) 601 602 var protoSrcs android.Paths 603 // "srcs" or "data" properties may contain filegroup so it might happen that 604 // the root directory for each source path is different. 605 for _, path := range pathMappings { 606 // handle proto sources separately 607 if path.src.Ext() == protoExt { 608 protoSrcs = append(protoSrcs, path.src) 609 } else { 610 var relativeRoot string 611 relativeRoot = strings.TrimSuffix(path.src.String(), path.src.Rel()) 612 if v, found := relativeRootMap[relativeRoot]; found { 613 relativeRootMap[relativeRoot] = append(v, path.src) 614 } else { 615 relativeRootMap[relativeRoot] = android.Paths{path.src} 616 } 617 } 618 } 619 var zips android.Paths 620 if len(protoSrcs) > 0 { 621 protoFlags := android.GetProtoFlags(ctx, &p.protoProperties) 622 protoFlags.OutTypeFlag = "--python_out" 623 624 for _, srcFile := range protoSrcs { 625 zip := genProto(ctx, srcFile, protoFlags, pkgPath) 626 zips = append(zips, zip) 627 } 628 } 629 630 if len(relativeRootMap) > 0 { 631 // in order to keep stable order of soong_zip params, we sort the keys here. 632 roots := android.SortedStringKeys(relativeRootMap) 633 634 parArgs := []string{} 635 if pkgPath != "" { 636 // use package path as path prefix 637 parArgs = append(parArgs, `-P `+pkgPath) 638 } 639 paths := android.Paths{} 640 for _, root := range roots { 641 // specify relative root of file in following -f arguments 642 parArgs = append(parArgs, `-C `+root) 643 for _, path := range relativeRootMap[root] { 644 parArgs = append(parArgs, `-f `+path.String()) 645 paths = append(paths, path) 646 } 647 } 648 649 origSrcsZip := android.PathForModuleOut(ctx, ctx.ModuleName()+".py.srcszip") 650 ctx.Build(pctx, android.BuildParams{ 651 Rule: zip, 652 Description: "python library archive", 653 Output: origSrcsZip, 654 // as zip rule does not use $in, there is no real need to distinguish between Inputs and Implicits 655 Implicits: paths, 656 Args: map[string]string{ 657 "args": strings.Join(parArgs, " "), 658 }, 659 }) 660 zips = append(zips, origSrcsZip) 661 } 662 // we may have multiple zips due to separate handling of proto source files 663 if len(zips) == 1 { 664 return zips[0] 665 } else { 666 combinedSrcsZip := android.PathForModuleOut(ctx, ctx.ModuleName()+".srcszip") 667 ctx.Build(pctx, android.BuildParams{ 668 Rule: combineZip, 669 Description: "combine python library archive", 670 Output: combinedSrcsZip, 671 Inputs: zips, 672 }) 673 return combinedSrcsZip 674 } 675} 676 677// isPythonLibModule returns whether the given module is a Python library Module or not 678func isPythonLibModule(module blueprint.Module) bool { 679 if m, ok := module.(*Module); ok { 680 return m.isLibrary() 681 } 682 return false 683} 684 685// This is distinguished by the fact that Python libraries are not installable, while other Python 686// modules are. 687func (p *Module) isLibrary() bool { 688 // Python library has no bootstrapper or installer 689 return p.bootstrapper == nil && p.installer == nil 690} 691 692func (p *Module) isBinary() bool { 693 _, ok := p.bootstrapper.(*binaryDecorator) 694 return ok 695} 696 697// collectPathsFromTransitiveDeps checks for source/data files for duplicate paths 698// for module and its transitive dependencies and collects list of data/source file 699// zips for transitive dependencies. 700func (p *Module) collectPathsFromTransitiveDeps(ctx android.ModuleContext) { 701 // fetch <runfiles_path, source_path> pairs from "src" and "data" properties to 702 // check duplicates. 703 destToPySrcs := make(map[string]string) 704 destToPyData := make(map[string]string) 705 for _, path := range p.srcsPathMappings { 706 destToPySrcs[path.dest] = path.src.String() 707 } 708 for _, path := range p.dataPathMappings { 709 destToPyData[path.dest] = path.src.String() 710 } 711 712 seen := make(map[android.Module]bool) 713 714 // visit all its dependencies in depth first. 715 ctx.WalkDeps(func(child, parent android.Module) bool { 716 // we only collect dependencies tagged as python library deps 717 if ctx.OtherModuleDependencyTag(child) != pythonLibTag { 718 return false 719 } 720 if seen[child] { 721 return false 722 } 723 seen[child] = true 724 // Python modules only can depend on Python libraries. 725 if !isPythonLibModule(child) { 726 ctx.PropertyErrorf("libs", 727 "the dependency %q of module %q is not Python library!", 728 ctx.OtherModuleName(child), ctx.ModuleName()) 729 } 730 // collect source and data paths, checking that there are no duplicate output file conflicts 731 if dep, ok := child.(pythonDependency); ok { 732 srcs := dep.getSrcsPathMappings() 733 for _, path := range srcs { 734 checkForDuplicateOutputPath(ctx, destToPySrcs, 735 path.dest, path.src.String(), ctx.ModuleName(), ctx.OtherModuleName(child)) 736 } 737 data := dep.getDataPathMappings() 738 for _, path := range data { 739 checkForDuplicateOutputPath(ctx, destToPyData, 740 path.dest, path.src.String(), ctx.ModuleName(), ctx.OtherModuleName(child)) 741 } 742 p.depsSrcsZips = append(p.depsSrcsZips, dep.getSrcsZip()) 743 } 744 return true 745 }) 746} 747 748// chckForDuplicateOutputPath checks whether outputPath has already been included in map m, which 749// would result in two files being placed in the same location. 750// If there is a duplicate path, an error is thrown and true is returned 751// Otherwise, outputPath: srcPath is added to m and returns false 752func checkForDuplicateOutputPath(ctx android.ModuleContext, m map[string]string, outputPath, srcPath, curModule, otherModule string) bool { 753 if oldSrcPath, found := m[outputPath]; found { 754 ctx.ModuleErrorf("found two files to be placed at the same location within zip %q."+ 755 " First file: in module %s at path %q."+ 756 " Second file: in module %s at path %q.", 757 outputPath, curModule, oldSrcPath, otherModule, srcPath) 758 return true 759 } 760 m[outputPath] = srcPath 761 762 return false 763} 764 765// InstallInData returns true as Python is not supported in the system partition 766func (p *Module) InstallInData() bool { 767 return true 768} 769 770func (p *Module) ConvertWithBp2build(ctx android.TopDownMutatorContext) { 771 if p.isLibrary() { 772 pythonLibBp2Build(ctx, p) 773 } else if p.isBinary() { 774 pythonBinaryBp2Build(ctx, p) 775 } 776} 777 778var Bool = proptools.Bool 779var BoolDefault = proptools.BoolDefault 780var String = proptools.String 781