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