1// Copyright 2015 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 android 16 17import ( 18 "fmt" 19 "os" 20 "path/filepath" 21 "reflect" 22 "regexp" 23 "sort" 24 "strings" 25 26 "github.com/google/blueprint" 27 "github.com/google/blueprint/gobtools" 28 "github.com/google/blueprint/pathtools" 29) 30 31var absSrcDir string 32 33// PathContext is the subset of a (Module|Singleton)Context required by the 34// Path methods. 35type PathContext interface { 36 Config() Config 37 AddNinjaFileDeps(deps ...string) 38} 39 40type PathGlobContext interface { 41 PathContext 42 GlobWithDeps(globPattern string, excludes []string) ([]string, error) 43} 44 45var _ PathContext = SingletonContext(nil) 46var _ PathContext = ModuleContext(nil) 47 48// "Null" path context is a minimal path context for a given config. 49type NullPathContext struct { 50 config Config 51} 52 53func (NullPathContext) AddNinjaFileDeps(...string) {} 54func (ctx NullPathContext) Config() Config { return ctx.config } 55 56// EarlyModulePathContext is a subset of EarlyModuleContext methods required by the 57// Path methods. These path methods can be called before any mutators have run. 58type EarlyModulePathContext interface { 59 PathGlobContext 60 61 ModuleDir() string 62 ModuleErrorf(fmt string, args ...interface{}) 63 OtherModulePropertyErrorf(module Module, property, fmt string, args ...interface{}) 64} 65 66var _ EarlyModulePathContext = ModuleContext(nil) 67 68// Glob globs files and directories matching globPattern relative to ModuleDir(), 69// paths in the excludes parameter will be omitted. 70func Glob(ctx EarlyModulePathContext, globPattern string, excludes []string) Paths { 71 ret, err := ctx.GlobWithDeps(globPattern, excludes) 72 if err != nil { 73 ctx.ModuleErrorf("glob: %s", err.Error()) 74 } 75 return pathsForModuleSrcFromFullPath(ctx, ret, true) 76} 77 78// GlobFiles globs *only* files (not directories) matching globPattern relative to ModuleDir(). 79// Paths in the excludes parameter will be omitted. 80func GlobFiles(ctx EarlyModulePathContext, globPattern string, excludes []string) Paths { 81 ret, err := ctx.GlobWithDeps(globPattern, excludes) 82 if err != nil { 83 ctx.ModuleErrorf("glob: %s", err.Error()) 84 } 85 return pathsForModuleSrcFromFullPath(ctx, ret, false) 86} 87 88// ModuleWithDepsPathContext is a subset of *ModuleContext methods required by 89// the Path methods that rely on module dependencies having been resolved. 90type ModuleWithDepsPathContext interface { 91 EarlyModulePathContext 92 OtherModuleProviderContext 93 VisitDirectDeps(visit func(Module)) 94 VisitDirectDepsProxy(visit func(ModuleProxy)) 95 VisitDirectDepsProxyWithTag(tag blueprint.DependencyTag, visit func(ModuleProxy)) 96 OtherModuleDependencyTag(m blueprint.Module) blueprint.DependencyTag 97 HasMutatorFinished(mutatorName string) bool 98} 99 100// ModuleMissingDepsPathContext is a subset of *ModuleContext methods required by 101// the Path methods that rely on module dependencies having been resolved and ability to report 102// missing dependency errors. 103type ModuleMissingDepsPathContext interface { 104 ModuleWithDepsPathContext 105 AddMissingDependencies(missingDeps []string) 106} 107 108type ModuleInstallPathContext interface { 109 BaseModuleContext 110 111 InstallInData() bool 112 InstallInTestcases() bool 113 InstallInSanitizerDir() bool 114 InstallInRamdisk() bool 115 InstallInVendorRamdisk() bool 116 InstallInDebugRamdisk() bool 117 InstallInRecovery() bool 118 InstallInRoot() bool 119 InstallInOdm() bool 120 InstallInProduct() bool 121 InstallInVendor() bool 122 InstallInSystemDlkm() bool 123 InstallInVendorDlkm() bool 124 InstallInOdmDlkm() bool 125 InstallForceOS() (*OsType, *ArchType) 126} 127 128var _ ModuleInstallPathContext = ModuleContext(nil) 129 130type baseModuleContextToModuleInstallPathContext struct { 131 BaseModuleContext 132} 133 134func (ctx *baseModuleContextToModuleInstallPathContext) InstallInData() bool { 135 return ctx.Module().InstallInData() 136} 137 138func (ctx *baseModuleContextToModuleInstallPathContext) InstallInTestcases() bool { 139 return ctx.Module().InstallInTestcases() 140} 141 142func (ctx *baseModuleContextToModuleInstallPathContext) InstallInSanitizerDir() bool { 143 return ctx.Module().InstallInSanitizerDir() 144} 145 146func (ctx *baseModuleContextToModuleInstallPathContext) InstallInRamdisk() bool { 147 return ctx.Module().InstallInRamdisk() 148} 149 150func (ctx *baseModuleContextToModuleInstallPathContext) InstallInVendorRamdisk() bool { 151 return ctx.Module().InstallInVendorRamdisk() 152} 153 154func (ctx *baseModuleContextToModuleInstallPathContext) InstallInDebugRamdisk() bool { 155 return ctx.Module().InstallInDebugRamdisk() 156} 157 158func (ctx *baseModuleContextToModuleInstallPathContext) InstallInRecovery() bool { 159 return ctx.Module().InstallInRecovery() 160} 161 162func (ctx *baseModuleContextToModuleInstallPathContext) InstallInRoot() bool { 163 return ctx.Module().InstallInRoot() 164} 165 166func (ctx *baseModuleContextToModuleInstallPathContext) InstallInOdm() bool { 167 return ctx.Module().InstallInOdm() 168} 169 170func (ctx *baseModuleContextToModuleInstallPathContext) InstallInProduct() bool { 171 return ctx.Module().InstallInProduct() 172} 173 174func (ctx *baseModuleContextToModuleInstallPathContext) InstallInVendor() bool { 175 return ctx.Module().InstallInVendor() 176} 177 178func (ctx *baseModuleContextToModuleInstallPathContext) InstallInSystemDlkm() bool { 179 return ctx.Module().InstallInSystemDlkm() 180} 181 182func (ctx *baseModuleContextToModuleInstallPathContext) InstallInVendorDlkm() bool { 183 return ctx.Module().InstallInVendorDlkm() 184} 185 186func (ctx *baseModuleContextToModuleInstallPathContext) InstallInOdmDlkm() bool { 187 return ctx.Module().InstallInOdmDlkm() 188} 189 190func (ctx *baseModuleContextToModuleInstallPathContext) InstallForceOS() (*OsType, *ArchType) { 191 return ctx.Module().InstallForceOS() 192} 193 194var _ ModuleInstallPathContext = (*baseModuleContextToModuleInstallPathContext)(nil) 195 196// errorfContext is the interface containing the Errorf method matching the 197// Errorf method in blueprint.SingletonContext. 198type errorfContext interface { 199 Errorf(format string, args ...interface{}) 200} 201 202var _ errorfContext = blueprint.SingletonContext(nil) 203 204// ModuleErrorfContext is the interface containing the ModuleErrorf method matching 205// the ModuleErrorf method in blueprint.ModuleContext. 206type ModuleErrorfContext interface { 207 ModuleErrorf(format string, args ...interface{}) 208} 209 210var _ ModuleErrorfContext = blueprint.ModuleContext(nil) 211 212type AddMissingDependenciesContext interface { 213 AddMissingDependencies([]string) 214} 215 216// reportPathError will register an error with the attached context. It 217// attempts ctx.ModuleErrorf for a better error message first, then falls 218// back to ctx.Errorf. 219func reportPathError(ctx PathContext, err error) { 220 ReportPathErrorf(ctx, "%s", err.Error()) 221} 222 223// ReportPathErrorf will register an error with the attached context. It 224// attempts ctx.ModuleErrorf for a better error message first, then falls 225// back to ctx.Errorf. 226func ReportPathErrorf(ctx PathContext, format string, args ...interface{}) { 227 if mctx, ok := ctx.(AddMissingDependenciesContext); ok && ctx.Config().AllowMissingDependencies() { 228 mctx.AddMissingDependencies([]string{fmt.Sprintf(format, args...)}) 229 } else if mctx, ok := ctx.(ModuleErrorfContext); ok { 230 mctx.ModuleErrorf(format, args...) 231 } else if ectx, ok := ctx.(errorfContext); ok { 232 ectx.Errorf(format, args...) 233 } else { 234 panic(fmt.Sprintf(format, args...)) 235 } 236} 237 238// TODO(b/397766191): Change the signature to take ModuleProxy 239// Please only access the module's internal data through providers. 240func pathContextName(ctx PathContext, module blueprint.Module) string { 241 if x, ok := ctx.(interface{ ModuleName(blueprint.Module) string }); ok { 242 return x.ModuleName(module) 243 } else if x, ok := ctx.(interface{ OtherModuleName(blueprint.Module) string }); ok { 244 return x.OtherModuleName(module) 245 } 246 return "unknown" 247} 248 249type Path interface { 250 // Returns the path in string form 251 String() string 252 253 // Ext returns the extension of the last element of the path 254 Ext() string 255 256 // Base returns the last element of the path 257 Base() string 258 259 // Rel returns the portion of the path relative to the directory it was created from. For 260 // example, Rel on a PathsForModuleSrc would return the path relative to the module source 261 // directory, and OutputPath.Join("foo").Rel() would return "foo". 262 Rel() string 263 264 // WithoutRel returns a new Path with no relative path, i.e. Rel() will return the same value as Base(). 265 WithoutRel() Path 266 267 // RelativeToTop returns a new path relative to the top, it is provided solely for use in tests. 268 // 269 // It is guaranteed to always return the same type as it is called on, e.g. if called on an 270 // InstallPath then the returned value can be converted to an InstallPath. 271 // 272 // A standard build has the following structure: 273 // ../top/ 274 // out/ - make install files go here. 275 // out/soong - this is the outDir passed to NewTestConfig() 276 // ... - the source files 277 // 278 // This function converts a path so that it appears relative to the ../top/ directory, i.e. 279 // * Make install paths, which have the pattern "outDir/../<path>" are converted into the top 280 // relative path "out/<path>" 281 // * Soong install paths and other writable paths, which have the pattern "outDir/soong/<path>" are 282 // converted into the top relative path "out/soong/<path>". 283 // * Source paths are already relative to the top. 284 // * Phony paths are not relative to anything. 285 // * toolDepPath have an absolute but known value in so don't need making relative to anything in 286 // order to test. 287 RelativeToTop() Path 288} 289 290const ( 291 testOutDir = "out" 292 testOutSoongSubDir = "/soong" 293 TestOutSoongDir = testOutDir + testOutSoongSubDir 294) 295 296// WritablePath is a type of path that can be used as an output for build rules. 297type WritablePath interface { 298 Path 299 300 // return the path to the build directory. 301 getSoongOutDir() string 302 303 // the writablePath method doesn't directly do anything, 304 // but it allows a struct to distinguish between whether or not it implements the WritablePath interface 305 writablePath() 306 307 ReplaceExtension(ctx PathContext, ext string) OutputPath 308} 309 310type genPathProvider interface { 311 genPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleGenPath 312 genPathWithExtAndTrimExt(ctx ModuleOutPathContext, subdir, ext string, trimExt string) ModuleGenPath 313} 314type objPathProvider interface { 315 objPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleObjPath 316} 317type resPathProvider interface { 318 resPathWithName(ctx ModuleOutPathContext, name string) ModuleResPath 319} 320 321// GenPathWithExt derives a new file path in ctx's generated sources directory 322// from the current path, but with the new extension. 323func GenPathWithExt(ctx ModuleOutPathContext, subdir string, p Path, ext string) ModuleGenPath { 324 if path, ok := p.(genPathProvider); ok { 325 return path.genPathWithExt(ctx, subdir, ext) 326 } 327 ReportPathErrorf(ctx, "Tried to create generated file from unsupported path: %s(%s)", reflect.TypeOf(p).Name(), p) 328 return PathForModuleGen(ctx) 329} 330 331// GenPathWithExtAndTrimExt derives a new file path in ctx's generated sources directory 332// from the current path, but with the new extension and trim the suffix. 333func GenPathWithExtAndTrimExt(ctx ModuleOutPathContext, subdir string, p Path, ext string, trimExt string) ModuleGenPath { 334 if path, ok := p.(genPathProvider); ok { 335 return path.genPathWithExtAndTrimExt(ctx, subdir, ext, trimExt) 336 } 337 ReportPathErrorf(ctx, "Tried to create generated file from unsupported path: %s(%s)", reflect.TypeOf(p).Name(), p) 338 return PathForModuleGen(ctx) 339} 340 341// ObjPathWithExt derives a new file path in ctx's object directory from the 342// current path, but with the new extension. 343func ObjPathWithExt(ctx ModuleOutPathContext, subdir string, p Path, ext string) ModuleObjPath { 344 if path, ok := p.(objPathProvider); ok { 345 return path.objPathWithExt(ctx, subdir, ext) 346 } 347 ReportPathErrorf(ctx, "Tried to create object file from unsupported path: %s (%s)", reflect.TypeOf(p).Name(), p) 348 return PathForModuleObj(ctx) 349} 350 351// ResPathWithName derives a new path in ctx's output resource directory, using 352// the current path to create the directory name, and the `name` argument for 353// the filename. 354func ResPathWithName(ctx ModuleOutPathContext, p Path, name string) ModuleResPath { 355 if path, ok := p.(resPathProvider); ok { 356 return path.resPathWithName(ctx, name) 357 } 358 ReportPathErrorf(ctx, "Tried to create res file from unsupported path: %s (%s)", reflect.TypeOf(p).Name(), p) 359 return PathForModuleRes(ctx) 360} 361 362// OptionalPath is a container that may or may not contain a valid Path. 363type OptionalPath struct { 364 path Path // nil if invalid. 365 invalidReason string // Not applicable if path != nil. "" if the reason is unknown. 366} 367 368type optionalPathGob struct { 369 Path Path 370 InvalidReason string 371} 372 373// OptionalPathForPath returns an OptionalPath containing the path. 374func OptionalPathForPath(path Path) OptionalPath { 375 return OptionalPath{path: path} 376} 377 378// InvalidOptionalPath returns an OptionalPath that is invalid with the given reason. 379func InvalidOptionalPath(reason string) OptionalPath { 380 381 return OptionalPath{invalidReason: reason} 382} 383 384func (p *OptionalPath) ToGob() *optionalPathGob { 385 return &optionalPathGob{ 386 Path: p.path, 387 InvalidReason: p.invalidReason, 388 } 389} 390 391func (p *OptionalPath) FromGob(data *optionalPathGob) { 392 p.path = data.Path 393 p.invalidReason = data.InvalidReason 394} 395 396func (p OptionalPath) GobEncode() ([]byte, error) { 397 return gobtools.CustomGobEncode[optionalPathGob](&p) 398} 399 400func (p *OptionalPath) GobDecode(data []byte) error { 401 return gobtools.CustomGobDecode[optionalPathGob](data, p) 402} 403 404// Valid returns whether there is a valid path 405func (p OptionalPath) Valid() bool { 406 return p.path != nil 407} 408 409// Path returns the Path embedded in this OptionalPath. You must be sure that 410// there is a valid path, since this method will panic if there is not. 411func (p OptionalPath) Path() Path { 412 if p.path == nil { 413 msg := "Requesting an invalid path" 414 if p.invalidReason != "" { 415 msg += ": " + p.invalidReason 416 } 417 panic(msg) 418 } 419 return p.path 420} 421 422// InvalidReason returns the reason that the optional path is invalid, or "" if it is valid. 423func (p OptionalPath) InvalidReason() string { 424 if p.path != nil { 425 return "" 426 } 427 if p.invalidReason == "" { 428 return "unknown" 429 } 430 return p.invalidReason 431} 432 433// AsPaths converts the OptionalPath into Paths. 434// 435// It returns nil if this is not valid, or a single length slice containing the Path embedded in 436// this OptionalPath. 437func (p OptionalPath) AsPaths() Paths { 438 if p.path == nil { 439 return nil 440 } 441 return Paths{p.path} 442} 443 444// RelativeToTop returns an OptionalPath with the path that was embedded having been replaced by the 445// result of calling Path.RelativeToTop on it. 446func (p OptionalPath) RelativeToTop() OptionalPath { 447 if p.path == nil { 448 return p 449 } 450 p.path = p.path.RelativeToTop() 451 return p 452} 453 454// String returns the string version of the Path, or "" if it isn't valid. 455func (p OptionalPath) String() string { 456 if p.path != nil { 457 return p.path.String() 458 } else { 459 return "" 460 } 461} 462 463// Paths is a slice of Path objects, with helpers to operate on the collection. 464type Paths []Path 465 466// RelativeToTop creates a new Paths containing the result of calling Path.RelativeToTop on each 467// item in this slice. 468func (p Paths) RelativeToTop() Paths { 469 ensureTestOnly() 470 if p == nil { 471 return p 472 } 473 ret := make(Paths, len(p)) 474 for i, path := range p { 475 ret[i] = path.RelativeToTop() 476 } 477 return ret 478} 479 480func (paths Paths) containsPath(path Path) bool { 481 for _, p := range paths { 482 if p == path { 483 return true 484 } 485 } 486 return false 487} 488 489// PathsForSource returns Paths rooted from SrcDir, *not* rooted from the module's local source 490// directory 491func PathsForSource(ctx PathContext, paths []string) Paths { 492 ret := make(Paths, len(paths)) 493 for i, path := range paths { 494 ret[i] = PathForSource(ctx, path) 495 } 496 return ret 497} 498 499// ExistentPathsForSources returns a list of Paths rooted from SrcDir, *not* rooted from the 500// module's local source directory, that are found in the tree. If any are not found, they are 501// omitted from the list, and dependencies are added so that we're re-run when they are added. 502func ExistentPathsForSources(ctx PathGlobContext, paths []string) Paths { 503 ret := make(Paths, 0, len(paths)) 504 for _, path := range paths { 505 p := ExistentPathForSource(ctx, path) 506 if p.Valid() { 507 ret = append(ret, p.Path()) 508 } 509 } 510 return ret 511} 512 513// PathsForModuleSrc returns a Paths{} containing the resolved references in paths: 514// - filepath, relative to local module directory, resolves as a filepath relative to the local 515// source directory 516// - glob, relative to the local module directory, resolves as filepath(s), relative to the local 517// source directory. 518// - other modules using the ":name{.tag}" syntax. These modules must implement SourceFileProducer 519// or set the OutputFilesProvider. These resolve as a filepath to an output filepath or generated 520// source filepath. 521// 522// Properties passed as the paths argument must have been annotated with struct tag 523// `android:"path"` so that dependencies on SourceFileProducer modules will have already been handled by the 524// pathdeps mutator. 525// If a requested module is not found as a dependency: 526// - if ctx.Config().AllowMissingDependencies() is true, this module to be marked as having 527// missing dependencies 528// - otherwise, a ModuleError is thrown. 529func PathsForModuleSrc(ctx ModuleMissingDepsPathContext, paths []string) Paths { 530 return PathsForModuleSrcExcludes(ctx, paths, nil) 531} 532 533type SourceInput struct { 534 Context ModuleMissingDepsPathContext 535 Paths []string 536 ExcludePaths []string 537 IncludeDirs bool 538} 539 540// PathsForModuleSrcExcludes returns a Paths{} containing the resolved references in paths, minus 541// those listed in excludes. Elements of paths and excludes are resolved as: 542// - filepath, relative to local module directory, resolves as a filepath relative to the local 543// source directory 544// - glob, relative to the local module directory, resolves as filepath(s), relative to the local 545// source directory. Not valid in excludes. 546// - other modules using the ":name{.tag}" syntax. These modules must implement SourceFileProducer 547// or set the OutputFilesProvider. These resolve as a filepath to an output filepath or generated 548// source filepath. 549// 550// excluding the items (similarly resolved 551// Properties passed as the paths argument must have been annotated with struct tag 552// `android:"path"` so that dependencies on SourceFileProducer modules will have already been handled by the 553// pathdeps mutator. 554// If a requested module is not found as a dependency: 555// - if ctx.Config().AllowMissingDependencies() is true, this module to be marked as having 556// missing dependencies 557// - otherwise, a ModuleError is thrown. 558func PathsForModuleSrcExcludes(ctx ModuleMissingDepsPathContext, paths, excludes []string) Paths { 559 return PathsRelativeToModuleSourceDir(SourceInput{ 560 Context: ctx, 561 Paths: paths, 562 ExcludePaths: excludes, 563 IncludeDirs: true, 564 }) 565} 566 567func PathsRelativeToModuleSourceDir(input SourceInput) Paths { 568 ret, missingDeps := PathsAndMissingDepsRelativeToModuleSourceDir(input) 569 if input.Context.Config().AllowMissingDependencies() { 570 input.Context.AddMissingDependencies(missingDeps) 571 } else { 572 for _, m := range missingDeps { 573 input.Context.ModuleErrorf(`missing dependency on %q, is the property annotated with android:"path"?`, m) 574 } 575 } 576 return ret 577} 578 579type directoryPath struct { 580 basePath 581} 582 583func (d *directoryPath) String() string { 584 return d.basePath.String() 585} 586 587func (d *directoryPath) base() basePath { 588 return d.basePath 589} 590 591// DirectoryPath represents a source path for directories. Incompatible with Path by design. 592type DirectoryPath interface { 593 String() string 594 base() basePath 595} 596 597var _ DirectoryPath = (*directoryPath)(nil) 598 599type DirectoryPaths []DirectoryPath 600 601// DirectoryPathsForModuleSrcExcludes returns a Paths{} containing the resolved references in 602// directory paths. Elements of paths are resolved as: 603// - filepath, relative to local module directory, resolves as a filepath relative to the local 604// source directory 605// - other modules using the ":name" syntax. These modules must implement DirProvider. 606func DirectoryPathsForModuleSrc(ctx ModuleMissingDepsPathContext, paths []string) DirectoryPaths { 607 var ret DirectoryPaths 608 609 for _, path := range paths { 610 if m, t := SrcIsModuleWithTag(path); m != "" { 611 module := GetModuleProxyFromPathDep(ctx, m, t) 612 if module == nil { 613 ctx.ModuleErrorf(`missing dependency on %q, is the property annotated with android:"path"?`, m) 614 continue 615 } 616 if t != "" { 617 ctx.ModuleErrorf("DirProvider dependency %q does not support the tag %q", module, t) 618 continue 619 } 620 mctx, ok := ctx.(OtherModuleProviderContext) 621 if !ok { 622 panic(fmt.Errorf("%s is not an OtherModuleProviderContext", ctx)) 623 } 624 if dirProvider, ok := OtherModuleProvider(mctx, *module, DirProvider); ok { 625 ret = append(ret, dirProvider.Dirs...) 626 } else { 627 ReportPathErrorf(ctx, "module %q does not implement DirProvider", module) 628 } 629 } else { 630 p := pathForModuleSrc(ctx, path) 631 if isDir, err := ctx.Config().fs.IsDir(p.String()); err != nil { 632 ReportPathErrorf(ctx, "%s: %s", p, err.Error()) 633 } else if !isDir { 634 ReportPathErrorf(ctx, "module directory path %q is not a directory", p) 635 } else { 636 ret = append(ret, &directoryPath{basePath{path: p.path, rel: p.rel}}) 637 } 638 } 639 } 640 641 seen := make(map[DirectoryPath]bool, len(ret)) 642 for _, path := range ret { 643 if seen[path] { 644 ReportPathErrorf(ctx, "duplicated path %q", path) 645 } 646 seen[path] = true 647 } 648 return ret 649} 650 651// OutputPaths is a slice of OutputPath objects, with helpers to operate on the collection. 652type OutputPaths []OutputPath 653 654// Paths returns the OutputPaths as a Paths 655func (p OutputPaths) Paths() Paths { 656 if p == nil { 657 return nil 658 } 659 ret := make(Paths, len(p)) 660 for i, path := range p { 661 ret[i] = path 662 } 663 return ret 664} 665 666// Strings returns the string forms of the writable paths. 667func (p OutputPaths) Strings() []string { 668 if p == nil { 669 return nil 670 } 671 ret := make([]string, len(p)) 672 for i, path := range p { 673 ret[i] = path.String() 674 } 675 return ret 676} 677 678// Expands Paths to a SourceFileProducer or OutputFileProducer module dependency referenced via ":name" or ":name{.tag}" syntax. 679// If the dependency is not found, a missingErrorDependency is returned. 680// If the module dependency is not a SourceFileProducer or OutputFileProducer, appropriate errors will be returned. 681func getPathsFromModuleDep(ctx ModuleWithDepsPathContext, path, moduleName, tag string) (Paths, error) { 682 module := GetModuleProxyFromPathDep(ctx, moduleName, tag) 683 if module == nil { 684 return nil, missingDependencyError{[]string{moduleName}} 685 } 686 if !OtherModulePointerProviderOrDefault(ctx, *module, CommonModuleInfoProvider).Enabled { 687 return nil, missingDependencyError{[]string{moduleName}} 688 } 689 690 outputFiles, err := outputFilesForModule(ctx, *module, tag) 691 if outputFiles != nil && err == nil { 692 return outputFiles, nil 693 } else { 694 return nil, err 695 } 696} 697 698// GetModuleProxyFromPathDep will return the module that was added as a dependency automatically for 699// properties tagged with `android:"path"` or manually using ExtractSourceDeps or 700// ExtractSourcesDeps. 701// 702// The moduleName and tag supplied to this should be the values returned from SrcIsModuleWithTag. 703// Or, if no tag is expected then the moduleName should be the value returned by SrcIsModule and 704// the tag must be "". 705// 706// If tag is "" then the returned module will be the dependency that was added for ":moduleName". 707// Otherwise, it is the dependency that was added for ":moduleName{tag}". 708func GetModuleProxyFromPathDep(ctx ModuleWithDepsPathContext, moduleName, tag string) *ModuleProxy { 709 var found *ModuleProxy 710 // The sourceOrOutputDepTag uniquely identifies the module dependency as it contains both the 711 // module name and the tag. Dependencies added automatically for properties tagged with 712 // `android:"path"` are deduped so are guaranteed to be unique. It is possible for duplicate 713 // dependencies to be added manually using ExtractSourcesDeps or ExtractSourceDeps but even then 714 // it will always be the case that the dependencies will be identical, i.e. the same tag and same 715 // moduleName referring to the same dependency module. 716 // 717 // It does not matter whether the moduleName is a fully qualified name or if the module 718 // dependency is a prebuilt module. All that matters is the same information is supplied to 719 // create the tag here as was supplied to create the tag when the dependency was added so that 720 // this finds the matching dependency module. 721 expectedTag := sourceOrOutputDepTag(moduleName, tag) 722 ctx.VisitDirectDepsProxyWithTag(expectedTag, func(module ModuleProxy) { 723 found = &module 724 }) 725 return found 726} 727 728// Deprecated: use GetModuleProxyFromPathDep 729func GetModuleFromPathDep(ctx ModuleWithDepsPathContext, moduleName, tag string) blueprint.Module { 730 var found blueprint.Module 731 // The sourceOrOutputDepTag uniquely identifies the module dependency as it contains both the 732 // module name and the tag. Dependencies added automatically for properties tagged with 733 // `android:"path"` are deduped so are guaranteed to be unique. It is possible for duplicate 734 // dependencies to be added manually using ExtractSourcesDeps or ExtractSourceDeps but even then 735 // it will always be the case that the dependencies will be identical, i.e. the same tag and same 736 // moduleName referring to the same dependency module. 737 // 738 // It does not matter whether the moduleName is a fully qualified name or if the module 739 // dependency is a prebuilt module. All that matters is the same information is supplied to 740 // create the tag here as was supplied to create the tag when the dependency was added so that 741 // this finds the matching dependency module. 742 expectedTag := sourceOrOutputDepTag(moduleName, tag) 743 ctx.VisitDirectDeps(func(module Module) { 744 depTag := ctx.OtherModuleDependencyTag(module) 745 if depTag == expectedTag { 746 found = module 747 } 748 }) 749 return found 750} 751 752// PathsAndMissingDepsForModuleSrcExcludes returns a Paths{} containing the resolved references in 753// paths, minus those listed in excludes. Elements of paths and excludes are resolved as: 754// - filepath, relative to local module directory, resolves as a filepath relative to the local 755// source directory 756// - glob, relative to the local module directory, resolves as filepath(s), relative to the local 757// source directory. Not valid in excludes. 758// - other modules using the ":name{.tag}" syntax. These modules must implement SourceFileProducer 759// or set the OutputFilesProvider. These resolve as a filepath to an output filepath or generated 760// source filepath. 761// 762// and a list of the module names of missing module dependencies are returned as the second return. 763// Properties passed as the paths argument must have been annotated with struct tag 764// `android:"path"` so that dependencies on SourceFileProducer modules will have already been handled by the 765// pathdeps mutator. 766func PathsAndMissingDepsForModuleSrcExcludes(ctx ModuleMissingDepsPathContext, paths, excludes []string) (Paths, []string) { 767 return PathsAndMissingDepsRelativeToModuleSourceDir(SourceInput{ 768 Context: ctx, 769 Paths: paths, 770 ExcludePaths: excludes, 771 IncludeDirs: true, 772 }) 773} 774 775func PathsAndMissingDepsRelativeToModuleSourceDir(input SourceInput) (Paths, []string) { 776 prefix := pathForModuleSrc(input.Context).String() 777 778 var expandedExcludes []string 779 if input.ExcludePaths != nil { 780 expandedExcludes = make([]string, 0, len(input.ExcludePaths)) 781 } 782 783 var missingExcludeDeps []string 784 for _, e := range input.ExcludePaths { 785 if m, t := SrcIsModuleWithTag(e); m != "" { 786 modulePaths, err := getPathsFromModuleDep(input.Context, e, m, t) 787 if m, ok := err.(missingDependencyError); ok { 788 missingExcludeDeps = append(missingExcludeDeps, m.missingDeps...) 789 } else if err != nil { 790 reportPathError(input.Context, err) 791 } else { 792 expandedExcludes = append(expandedExcludes, modulePaths.Strings()...) 793 } 794 } else { 795 expandedExcludes = append(expandedExcludes, filepath.Join(prefix, e)) 796 } 797 } 798 799 if input.Paths == nil { 800 return nil, missingExcludeDeps 801 } 802 803 var missingDeps []string 804 805 expandedSrcFiles := make(Paths, 0, len(input.Paths)) 806 for _, s := range input.Paths { 807 srcFiles, err := expandOneSrcPath(sourcePathInput{ 808 context: input.Context, 809 path: s, 810 expandedExcludes: expandedExcludes, 811 includeDirs: input.IncludeDirs, 812 }) 813 if depErr, ok := err.(missingDependencyError); ok { 814 missingDeps = append(missingDeps, depErr.missingDeps...) 815 } else if err != nil { 816 reportPathError(input.Context, err) 817 } 818 expandedSrcFiles = append(expandedSrcFiles, srcFiles...) 819 } 820 821 // TODO: b/334169722 - Replace with an error instead of implicitly removing duplicates. 822 return FirstUniquePaths(expandedSrcFiles), append(missingDeps, missingExcludeDeps...) 823} 824 825type missingDependencyError struct { 826 missingDeps []string 827} 828 829func (e missingDependencyError) Error() string { 830 return "missing dependencies: " + strings.Join(e.missingDeps, ", ") 831} 832 833type sourcePathInput struct { 834 context ModuleWithDepsPathContext 835 path string 836 expandedExcludes []string 837 includeDirs bool 838} 839 840// Expands one path string to Paths rooted from the module's local source 841// directory, excluding those listed in the expandedExcludes. 842// Expands globs, references to SourceFileProducer or OutputFileProducer modules using the ":name" and ":name{.tag}" syntax. 843func expandOneSrcPath(input sourcePathInput) (Paths, error) { 844 excludePaths := func(paths Paths) Paths { 845 if len(input.expandedExcludes) == 0 { 846 return paths 847 } 848 remainder := make(Paths, 0, len(paths)) 849 for _, p := range paths { 850 if !InList(p.String(), input.expandedExcludes) { 851 remainder = append(remainder, p) 852 } 853 } 854 return remainder 855 } 856 if m, t := SrcIsModuleWithTag(input.path); m != "" { 857 modulePaths, err := getPathsFromModuleDep(input.context, input.path, m, t) 858 if err != nil { 859 return nil, err 860 } else { 861 return excludePaths(modulePaths), nil 862 } 863 } else { 864 p := pathForModuleSrc(input.context, input.path) 865 if pathtools.IsGlob(input.path) { 866 paths := GlobFiles(input.context, p.String(), input.expandedExcludes) 867 return PathsWithModuleSrcSubDir(input.context, paths, ""), nil 868 } else { 869 if exists, _, err := input.context.Config().fs.Exists(p.String()); err != nil { 870 ReportPathErrorf(input.context, "%s: %s", p, err.Error()) 871 } else if !exists && !input.context.Config().TestAllowNonExistentPaths { 872 ReportPathErrorf(input.context, "module source path %q does not exist", p) 873 } else if !input.includeDirs { 874 if isDir, err := input.context.Config().fs.IsDir(p.String()); exists && err != nil { 875 ReportPathErrorf(input.context, "%s: %s", p, err.Error()) 876 } else if isDir { 877 ReportPathErrorf(input.context, "module source path %q is a directory", p) 878 } 879 } 880 881 if InList(p.String(), input.expandedExcludes) { 882 return nil, nil 883 } 884 return Paths{p}, nil 885 } 886 } 887} 888 889// pathsForModuleSrcFromFullPath returns Paths rooted from the module's local 890// source directory, but strip the local source directory from the beginning of 891// each string. If incDirs is false, strip paths with a trailing '/' from the list. 892// It intended for use in globs that only list files that exist, so it allows '$' in 893// filenames. 894func pathsForModuleSrcFromFullPath(ctx EarlyModulePathContext, paths []string, incDirs bool) Paths { 895 prefix := ctx.ModuleDir() + "/" 896 if prefix == "./" { 897 prefix = "" 898 } 899 ret := make(Paths, 0, len(paths)) 900 for _, p := range paths { 901 if !incDirs && strings.HasSuffix(p, "/") { 902 continue 903 } 904 path := filepath.Clean(p) 905 if !strings.HasPrefix(path, prefix) { 906 ReportPathErrorf(ctx, "Path %q is not in module source directory %q", p, prefix) 907 continue 908 } 909 910 srcPath, err := safePathForSource(ctx, ctx.ModuleDir(), path[len(prefix):]) 911 if err != nil { 912 reportPathError(ctx, err) 913 continue 914 } 915 916 srcPath.basePath.rel = srcPath.path 917 918 ret = append(ret, srcPath) 919 } 920 return ret 921} 922 923// PathsWithOptionalDefaultForModuleSrc returns Paths rooted from the module's local source 924// directory. If input is nil, use the default if it exists. If input is empty, returns nil. 925func PathsWithOptionalDefaultForModuleSrc(ctx ModuleMissingDepsPathContext, input []string, def string) Paths { 926 if input != nil { 927 return PathsForModuleSrc(ctx, input) 928 } 929 // Use Glob so that if the default doesn't exist, a dependency is added so that when it 930 // is created, we're run again. 931 path := filepath.Join(ctx.ModuleDir(), def) 932 return Glob(ctx, path, nil) 933} 934 935// Strings returns the Paths in string form 936func (p Paths) Strings() []string { 937 if p == nil { 938 return nil 939 } 940 ret := make([]string, len(p)) 941 for i, path := range p { 942 ret[i] = path.String() 943 } 944 return ret 945} 946 947func CopyOfPaths(paths Paths) Paths { 948 return append(Paths(nil), paths...) 949} 950 951// FirstUniquePaths returns all unique elements of a Paths, keeping the first copy of each. It 952// modifies the Paths slice contents in place, and returns a subslice of the original slice. 953func FirstUniquePaths(list Paths) Paths { 954 // 128 was chosen based on BenchmarkFirstUniquePaths results. 955 if len(list) > 128 { 956 return firstUniquePathsMap(list) 957 } 958 return firstUniquePathsList(list) 959} 960 961// SortedUniquePaths returns all unique elements of a Paths in sorted order. It modifies the 962// Paths slice contents in place, and returns a subslice of the original slice. 963func SortedUniquePaths(list Paths) Paths { 964 unique := FirstUniquePaths(list) 965 sort.Slice(unique, func(i, j int) bool { 966 return unique[i].String() < unique[j].String() 967 }) 968 return unique 969} 970 971func firstUniquePathsList(list Paths) Paths { 972 k := 0 973outer: 974 for i := 0; i < len(list); i++ { 975 for j := 0; j < k; j++ { 976 if list[i] == list[j] { 977 continue outer 978 } 979 } 980 list[k] = list[i] 981 k++ 982 } 983 return list[:k] 984} 985 986func firstUniquePathsMap(list Paths) Paths { 987 k := 0 988 seen := make(map[Path]bool, len(list)) 989 for i := 0; i < len(list); i++ { 990 if seen[list[i]] { 991 continue 992 } 993 seen[list[i]] = true 994 list[k] = list[i] 995 k++ 996 } 997 return list[:k] 998} 999 1000// FirstUniqueInstallPaths returns all unique elements of an InstallPaths, keeping the first copy of each. It 1001// modifies the InstallPaths slice contents in place, and returns a subslice of the original slice. 1002func FirstUniqueInstallPaths(list InstallPaths) InstallPaths { 1003 // 128 was chosen based on BenchmarkFirstUniquePaths results. 1004 if len(list) > 128 { 1005 return firstUniqueInstallPathsMap(list) 1006 } 1007 return firstUniqueInstallPathsList(list) 1008} 1009 1010func firstUniqueInstallPathsList(list InstallPaths) InstallPaths { 1011 k := 0 1012outer: 1013 for i := 0; i < len(list); i++ { 1014 for j := 0; j < k; j++ { 1015 if list[i] == list[j] { 1016 continue outer 1017 } 1018 } 1019 list[k] = list[i] 1020 k++ 1021 } 1022 return list[:k] 1023} 1024 1025func firstUniqueInstallPathsMap(list InstallPaths) InstallPaths { 1026 k := 0 1027 seen := make(map[InstallPath]bool, len(list)) 1028 for i := 0; i < len(list); i++ { 1029 if seen[list[i]] { 1030 continue 1031 } 1032 seen[list[i]] = true 1033 list[k] = list[i] 1034 k++ 1035 } 1036 return list[:k] 1037} 1038 1039// LastUniquePaths returns all unique elements of a Paths, keeping the last copy of each. It 1040// modifies the Paths slice contents in place, and returns a subslice of the original slice. 1041func LastUniquePaths(list Paths) Paths { 1042 totalSkip := 0 1043 for i := len(list) - 1; i >= totalSkip; i-- { 1044 skip := 0 1045 for j := i - 1; j >= totalSkip; j-- { 1046 if list[i] == list[j] { 1047 skip++ 1048 } else { 1049 list[j+skip] = list[j] 1050 } 1051 } 1052 totalSkip += skip 1053 } 1054 return list[totalSkip:] 1055} 1056 1057// ReversePaths returns a copy of a Paths in reverse order. 1058func ReversePaths(list Paths) Paths { 1059 if list == nil { 1060 return nil 1061 } 1062 ret := make(Paths, len(list)) 1063 for i := range list { 1064 ret[i] = list[len(list)-1-i] 1065 } 1066 return ret 1067} 1068 1069func indexPathList(s Path, list []Path) int { 1070 for i, l := range list { 1071 if l == s { 1072 return i 1073 } 1074 } 1075 1076 return -1 1077} 1078 1079func inPathList(p Path, list []Path) bool { 1080 return indexPathList(p, list) != -1 1081} 1082 1083func FilterPathList(list []Path, filter []Path) (remainder []Path, filtered []Path) { 1084 return FilterPathListPredicate(list, func(p Path) bool { return inPathList(p, filter) }) 1085} 1086 1087func FilterPathListPredicate(list []Path, predicate func(Path) bool) (remainder []Path, filtered []Path) { 1088 for _, l := range list { 1089 if predicate(l) { 1090 filtered = append(filtered, l) 1091 } else { 1092 remainder = append(remainder, l) 1093 } 1094 } 1095 1096 return 1097} 1098 1099// HasExt returns true of any of the paths have extension ext, otherwise false 1100func (p Paths) HasExt(ext string) bool { 1101 for _, path := range p { 1102 if path.Ext() == ext { 1103 return true 1104 } 1105 } 1106 1107 return false 1108} 1109 1110// FilterByExt returns the subset of the paths that have extension ext 1111func (p Paths) FilterByExt(ext string) Paths { 1112 ret := make(Paths, 0, len(p)) 1113 for _, path := range p { 1114 if path.Ext() == ext { 1115 ret = append(ret, path) 1116 } 1117 } 1118 return ret 1119} 1120 1121// FilterOutByExt returns the subset of the paths that do not have extension ext 1122func (p Paths) FilterOutByExt(ext string) Paths { 1123 ret := make(Paths, 0, len(p)) 1124 for _, path := range p { 1125 if path.Ext() != ext { 1126 ret = append(ret, path) 1127 } 1128 } 1129 return ret 1130} 1131 1132// DirectorySortedPaths is a slice of paths that are sorted such that all files in a directory 1133// (including subdirectories) are in a contiguous subslice of the list, and can be found in 1134// O(log(N)) time using a binary search on the directory prefix. 1135type DirectorySortedPaths Paths 1136 1137func PathsToDirectorySortedPaths(paths Paths) DirectorySortedPaths { 1138 ret := append(DirectorySortedPaths(nil), paths...) 1139 sort.Slice(ret, func(i, j int) bool { 1140 return ret[i].String() < ret[j].String() 1141 }) 1142 return ret 1143} 1144 1145// PathsInDirectory returns a subslice of the DirectorySortedPaths as a Paths that contains all entries 1146// that are in the specified directory and its subdirectories. 1147func (p DirectorySortedPaths) PathsInDirectory(dir string) Paths { 1148 prefix := filepath.Clean(dir) + "/" 1149 start := sort.Search(len(p), func(i int) bool { 1150 return prefix < p[i].String() 1151 }) 1152 1153 ret := p[start:] 1154 1155 end := sort.Search(len(ret), func(i int) bool { 1156 return !strings.HasPrefix(ret[i].String(), prefix) 1157 }) 1158 1159 ret = ret[:end] 1160 1161 return Paths(ret) 1162} 1163 1164// WritablePaths is a slice of WritablePath, used for multiple outputs. 1165type WritablePaths []WritablePath 1166 1167// RelativeToTop creates a new WritablePaths containing the result of calling Path.RelativeToTop on 1168// each item in this slice. 1169func (p WritablePaths) RelativeToTop() WritablePaths { 1170 ensureTestOnly() 1171 if p == nil { 1172 return p 1173 } 1174 ret := make(WritablePaths, len(p)) 1175 for i, path := range p { 1176 ret[i] = path.RelativeToTop().(WritablePath) 1177 } 1178 return ret 1179} 1180 1181// Strings returns the string forms of the writable paths. 1182func (p WritablePaths) Strings() []string { 1183 if p == nil { 1184 return nil 1185 } 1186 ret := make([]string, len(p)) 1187 for i, path := range p { 1188 ret[i] = path.String() 1189 } 1190 return ret 1191} 1192 1193// Paths returns the WritablePaths as a Paths 1194func (p WritablePaths) Paths() Paths { 1195 if p == nil { 1196 return nil 1197 } 1198 ret := make(Paths, len(p)) 1199 for i, path := range p { 1200 ret[i] = path 1201 } 1202 return ret 1203} 1204 1205type basePath struct { 1206 path string 1207 rel string 1208} 1209 1210type basePathGob struct { 1211 Path string 1212 Rel string 1213} 1214 1215func (p *basePath) ToGob() *basePathGob { 1216 return &basePathGob{ 1217 Path: p.path, 1218 Rel: p.rel, 1219 } 1220} 1221 1222func (p *basePath) FromGob(data *basePathGob) { 1223 p.path = data.Path 1224 p.rel = data.Rel 1225} 1226 1227func (p basePath) GobEncode() ([]byte, error) { 1228 return gobtools.CustomGobEncode[basePathGob](&p) 1229} 1230 1231func (p *basePath) GobDecode(data []byte) error { 1232 return gobtools.CustomGobDecode[basePathGob](data, p) 1233} 1234 1235func (p basePath) Ext() string { 1236 return filepath.Ext(p.path) 1237} 1238 1239func (p basePath) Base() string { 1240 return filepath.Base(p.path) 1241} 1242 1243func (p basePath) Rel() string { 1244 if p.rel != "" { 1245 return p.rel 1246 } 1247 return p.path 1248} 1249 1250func (p basePath) String() string { 1251 return p.path 1252} 1253 1254func (p basePath) withRel(rel string) basePath { 1255 p.path = filepath.Join(p.path, rel) 1256 p.rel = rel 1257 return p 1258} 1259 1260func (p basePath) withoutRel() basePath { 1261 p.rel = filepath.Base(p.path) 1262 return p 1263} 1264 1265// SourcePath is a Path representing a file path rooted from SrcDir 1266type SourcePath struct { 1267 basePath 1268} 1269 1270var _ Path = SourcePath{} 1271 1272func (p SourcePath) withRel(rel string) SourcePath { 1273 p.basePath = p.basePath.withRel(rel) 1274 return p 1275} 1276 1277func (p SourcePath) RelativeToTop() Path { 1278 ensureTestOnly() 1279 return p 1280} 1281 1282// safePathForSource is for paths that we expect are safe -- only for use by go 1283// code that is embedding ninja variables in paths 1284func safePathForSource(ctx PathContext, pathComponents ...string) (SourcePath, error) { 1285 p, err := validateSafePath(pathComponents...) 1286 ret := SourcePath{basePath{p, ""}} 1287 if err != nil { 1288 return ret, err 1289 } 1290 1291 // absolute path already checked by validateSafePath 1292 // special-case api surface gen files for now 1293 if strings.HasPrefix(ret.String(), ctx.Config().soongOutDir) && !strings.Contains(ret.String(), ctx.Config().soongOutDir+"/.export") { 1294 return ret, fmt.Errorf("source path %q is in output", ret.String()) 1295 } 1296 1297 return ret, err 1298} 1299 1300// pathForSource creates a SourcePath from pathComponents, but does not check that it exists. 1301func pathForSource(ctx PathContext, pathComponents ...string) (SourcePath, error) { 1302 p, err := validatePath(pathComponents...) 1303 ret := SourcePath{basePath{p, ""}} 1304 if err != nil { 1305 return ret, err 1306 } 1307 1308 // absolute path already checked by validatePath 1309 // special-case for now 1310 if strings.HasPrefix(ret.String(), ctx.Config().soongOutDir) && !strings.Contains(ret.String(), ctx.Config().soongOutDir+"/.export") { 1311 return ret, fmt.Errorf("source path %q is in output", ret.String()) 1312 } 1313 1314 return ret, nil 1315} 1316 1317// existsWithDependencies returns true if the path exists, and adds appropriate dependencies to rerun if the 1318// path does not exist. 1319func existsWithDependencies(ctx PathGlobContext, path SourcePath) (exists bool, err error) { 1320 var files []string 1321 1322 // Use glob to produce proper dependencies, even though we only want 1323 // a single file. 1324 files, err = ctx.GlobWithDeps(path.String(), nil) 1325 1326 if err != nil { 1327 return false, fmt.Errorf("glob: %s", err.Error()) 1328 } 1329 1330 return len(files) > 0, nil 1331} 1332 1333// PathForSource joins the provided path components and validates that the result 1334// neither escapes the source dir nor is in the out dir. 1335// On error, it will return a usable, but invalid SourcePath, and report a ModuleError. 1336func PathForSource(ctx PathContext, pathComponents ...string) SourcePath { 1337 path, err := pathForSource(ctx, pathComponents...) 1338 if err != nil { 1339 reportPathError(ctx, err) 1340 } 1341 1342 if pathtools.IsGlob(path.String()) { 1343 ReportPathErrorf(ctx, "path may not contain a glob: %s", path.String()) 1344 } 1345 1346 if modCtx, ok := ctx.(ModuleMissingDepsPathContext); ok && ctx.Config().AllowMissingDependencies() { 1347 exists, err := existsWithDependencies(modCtx, path) 1348 if err != nil { 1349 reportPathError(ctx, err) 1350 } 1351 if !exists { 1352 modCtx.AddMissingDependencies([]string{path.String()}) 1353 } 1354 } else if exists, _, err := ctx.Config().fs.Exists(path.String()); err != nil { 1355 ReportPathErrorf(ctx, "%s: %s", path, err.Error()) 1356 } else if !exists && !ctx.Config().TestAllowNonExistentPaths { 1357 ReportPathErrorf(ctx, "source path %q does not exist", path) 1358 } 1359 return path 1360} 1361 1362// PathForArbitraryOutput creates a path for the given components. Unlike PathForOutput, 1363// the path is relative to the root of the output folder, not the out/soong folder. 1364func PathForArbitraryOutput(ctx PathContext, pathComponents ...string) WritablePath { 1365 path, err := validatePath(pathComponents...) 1366 if err != nil { 1367 reportPathError(ctx, err) 1368 } 1369 fullPath := filepath.Join(ctx.Config().OutDir(), path) 1370 path = fullPath[len(fullPath)-len(path):] 1371 return OutputPath{basePath{path, ""}, ctx.Config().OutDir(), fullPath} 1372} 1373 1374// MaybeExistentPathForSource joins the provided path components and validates that the result 1375// neither escapes the source dir nor is in the out dir. 1376// It does not validate whether the path exists. 1377func MaybeExistentPathForSource(ctx PathContext, pathComponents ...string) SourcePath { 1378 path, err := pathForSource(ctx, pathComponents...) 1379 if err != nil { 1380 reportPathError(ctx, err) 1381 } 1382 1383 if pathtools.IsGlob(path.String()) { 1384 ReportPathErrorf(ctx, "path may not contain a glob: %s", path.String()) 1385 } 1386 return path 1387} 1388 1389// ExistentPathForSource returns an OptionalPath with the SourcePath, rooted from SrcDir, *not* 1390// rooted from the module's local source directory, if the path exists, or an empty OptionalPath if 1391// it doesn't exist. Dependencies are added so that the ninja file will be regenerated if the state 1392// of the path changes. 1393func ExistentPathForSource(ctx PathGlobContext, pathComponents ...string) OptionalPath { 1394 path, err := pathForSource(ctx, pathComponents...) 1395 if err != nil { 1396 reportPathError(ctx, err) 1397 // No need to put the error message into the returned path since it has been reported already. 1398 return OptionalPath{} 1399 } 1400 1401 if pathtools.IsGlob(path.String()) { 1402 ReportPathErrorf(ctx, "path may not contain a glob: %s", path.String()) 1403 return OptionalPath{} 1404 } 1405 1406 exists, err := existsWithDependencies(ctx, path) 1407 if err != nil { 1408 reportPathError(ctx, err) 1409 return OptionalPath{} 1410 } 1411 if !exists { 1412 return InvalidOptionalPath(path.String() + " does not exist") 1413 } 1414 return OptionalPathForPath(path) 1415} 1416 1417func (p SourcePath) String() string { 1418 if p.path == "" { 1419 return "." 1420 } 1421 return p.path 1422} 1423 1424func (p SourcePath) WithoutRel() Path { 1425 p.basePath = p.basePath.withoutRel() 1426 return p 1427} 1428 1429// Join creates a new SourcePath with paths... joined with the current path. The 1430// provided paths... may not use '..' to escape from the current path. 1431func (p SourcePath) Join(ctx PathContext, paths ...string) SourcePath { 1432 path, err := validatePath(paths...) 1433 if err != nil { 1434 reportPathError(ctx, err) 1435 } 1436 return p.withRel(path) 1437} 1438 1439// join is like Join but does less path validation. 1440func (p SourcePath) join(ctx PathContext, paths ...string) SourcePath { 1441 path, err := validateSafePath(paths...) 1442 if err != nil { 1443 reportPathError(ctx, err) 1444 } 1445 return p.withRel(path) 1446} 1447 1448// OutputPath is a Path representing an intermediates file path rooted from the build directory 1449type OutputPath struct { 1450 basePath 1451 1452 // The base out directory for this path, either Config.SoongOutDir() or Config.OutDir() 1453 outDir string 1454 1455 fullPath string 1456} 1457 1458type outputPathGob struct { 1459 BasePath basePath 1460 OutDir string 1461 FullPath string 1462} 1463 1464func (p *OutputPath) ToGob() *outputPathGob { 1465 return &outputPathGob{ 1466 BasePath: p.basePath, 1467 OutDir: p.outDir, 1468 FullPath: p.fullPath, 1469 } 1470} 1471 1472func (p *OutputPath) FromGob(data *outputPathGob) { 1473 p.basePath = data.BasePath 1474 p.outDir = data.OutDir 1475 p.fullPath = data.FullPath 1476} 1477 1478func (p OutputPath) GobEncode() ([]byte, error) { 1479 return gobtools.CustomGobEncode[outputPathGob](&p) 1480} 1481 1482func (p *OutputPath) GobDecode(data []byte) error { 1483 return gobtools.CustomGobDecode[outputPathGob](data, p) 1484} 1485 1486func (p OutputPath) withRel(rel string) OutputPath { 1487 p.basePath = p.basePath.withRel(rel) 1488 p.fullPath = filepath.Join(p.fullPath, rel) 1489 return p 1490} 1491 1492func (p OutputPath) WithoutRel() Path { 1493 p.basePath = p.basePath.withoutRel() 1494 return p 1495} 1496 1497func (p OutputPath) getSoongOutDir() string { 1498 return p.outDir 1499} 1500 1501func (p OutputPath) RelativeToTop() Path { 1502 return p.outputPathRelativeToTop() 1503} 1504 1505func (p OutputPath) outputPathRelativeToTop() OutputPath { 1506 p.fullPath = StringPathRelativeToTop(p.outDir, p.fullPath) 1507 if strings.HasSuffix(p.outDir, testOutSoongSubDir) { 1508 p.outDir = TestOutSoongDir 1509 } else { 1510 // Handle the PathForArbitraryOutput case 1511 p.outDir = testOutDir 1512 } 1513 return p 1514} 1515 1516func (p OutputPath) objPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleObjPath { 1517 return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext)) 1518} 1519 1520var _ Path = OutputPath{} 1521var _ WritablePath = OutputPath{} 1522var _ objPathProvider = OutputPath{} 1523 1524// toolDepPath is a Path representing a dependency of the build tool. 1525type toolDepPath struct { 1526 basePath 1527} 1528 1529func (t toolDepPath) WithoutRel() Path { 1530 t.basePath = t.basePath.withoutRel() 1531 return t 1532} 1533 1534func (t toolDepPath) RelativeToTop() Path { 1535 ensureTestOnly() 1536 return t 1537} 1538 1539var _ Path = toolDepPath{} 1540 1541// pathForBuildToolDep returns a toolDepPath representing the given path string. 1542// There is no validation for the path, as it is "trusted": It may fail 1543// normal validation checks. For example, it may be an absolute path. 1544// Only use this function to construct paths for dependencies of the build 1545// tool invocation. 1546func pathForBuildToolDep(ctx PathContext, path string) toolDepPath { 1547 return toolDepPath{basePath{path, ""}} 1548} 1549 1550// PathForOutput joins the provided paths and returns an OutputPath that is 1551// validated to not escape the build dir. 1552// On error, it will return a usable, but invalid OutputPath, and report a ModuleError. 1553func PathForOutput(ctx PathContext, pathComponents ...string) OutputPath { 1554 path, err := validatePath(pathComponents...) 1555 if err != nil { 1556 reportPathError(ctx, err) 1557 } 1558 fullPath := filepath.Join(ctx.Config().soongOutDir, path) 1559 path = fullPath[len(fullPath)-len(path):] 1560 return OutputPath{basePath{path, ""}, ctx.Config().soongOutDir, fullPath} 1561} 1562 1563// PathsForOutput returns Paths rooted from outDir 1564func PathsForOutput(ctx PathContext, paths []string) WritablePaths { 1565 ret := make(WritablePaths, len(paths)) 1566 for i, path := range paths { 1567 ret[i] = PathForOutput(ctx, path) 1568 } 1569 return ret 1570} 1571 1572func (p OutputPath) writablePath() {} 1573 1574func (p OutputPath) String() string { 1575 return p.fullPath 1576} 1577 1578// Join creates a new OutputPath with paths... joined with the current path. The 1579// provided paths... may not use '..' to escape from the current path. 1580func (p OutputPath) Join(ctx PathContext, paths ...string) OutputPath { 1581 path, err := validatePath(paths...) 1582 if err != nil { 1583 reportPathError(ctx, err) 1584 } 1585 return p.withRel(path) 1586} 1587 1588// ReplaceExtension creates a new OutputPath with the extension replaced with ext. 1589func (p OutputPath) ReplaceExtension(ctx PathContext, ext string) OutputPath { 1590 if strings.Contains(ext, "/") { 1591 ReportPathErrorf(ctx, "extension %q cannot contain /", ext) 1592 } 1593 ret := PathForOutput(ctx, pathtools.ReplaceExtension(p.path, ext)) 1594 ret.rel = pathtools.ReplaceExtension(p.rel, ext) 1595 return ret 1596} 1597 1598// InSameDir creates a new OutputPath from the directory of the current OutputPath joined with the elements in paths. 1599func (p OutputPath) InSameDir(ctx PathContext, paths ...string) OutputPath { 1600 path, err := validatePath(paths...) 1601 if err != nil { 1602 reportPathError(ctx, err) 1603 } 1604 1605 ret := PathForOutput(ctx, filepath.Dir(p.path), path) 1606 ret.rel = filepath.Join(filepath.Dir(p.rel), path) 1607 return ret 1608} 1609 1610// PathForIntermediates returns an OutputPath representing the top-level 1611// intermediates directory. 1612func PathForIntermediates(ctx PathContext, paths ...string) OutputPath { 1613 path, err := validatePath(paths...) 1614 if err != nil { 1615 reportPathError(ctx, err) 1616 } 1617 return PathForOutput(ctx, ".intermediates", path) 1618} 1619 1620var _ genPathProvider = SourcePath{} 1621var _ objPathProvider = SourcePath{} 1622var _ resPathProvider = SourcePath{} 1623 1624// PathForModuleSrc returns a Path representing the paths... under the 1625// module's local source directory. 1626func PathForModuleSrc(ctx ModuleMissingDepsPathContext, pathComponents ...string) Path { 1627 // Just join the components textually just to make sure that it does not corrupt a fully qualified 1628 // module reference, e.g. if the pathComponents is "://other:foo" then using filepath.Join() or 1629 // validatePath() will corrupt it, e.g. replace "//" with "/". If the path is not a module 1630 // reference then it will be validated by expandOneSrcPath anyway when it calls expandOneSrcPath. 1631 p := strings.Join(pathComponents, string(filepath.Separator)) 1632 paths, err := expandOneSrcPath(sourcePathInput{context: ctx, path: p, includeDirs: true}) 1633 if err != nil { 1634 if depErr, ok := err.(missingDependencyError); ok { 1635 if ctx.Config().AllowMissingDependencies() { 1636 ctx.AddMissingDependencies(depErr.missingDeps) 1637 } else { 1638 ctx.ModuleErrorf(`%s, is the property annotated with android:"path"?`, depErr.Error()) 1639 } 1640 } else { 1641 reportPathError(ctx, err) 1642 } 1643 return nil 1644 } else if len(paths) == 0 { 1645 ReportPathErrorf(ctx, "%q produced no files, expected exactly one", p) 1646 return nil 1647 } else if len(paths) > 1 { 1648 ReportPathErrorf(ctx, "%q produced %d files, expected exactly one", p, len(paths)) 1649 } 1650 return paths[0] 1651} 1652 1653func pathForModuleSrc(ctx EarlyModulePathContext, paths ...string) SourcePath { 1654 p, err := validatePath(paths...) 1655 if err != nil { 1656 reportPathError(ctx, err) 1657 } 1658 1659 path, err := pathForSource(ctx, ctx.ModuleDir(), p) 1660 if err != nil { 1661 reportPathError(ctx, err) 1662 } 1663 1664 path.basePath.rel = p 1665 1666 return path 1667} 1668 1669// PathsWithModuleSrcSubDir takes a list of Paths and returns a new list of Paths where Rel() on each path 1670// will return the path relative to subDir in the module's source directory. If any input paths are not located 1671// inside subDir then a path error will be reported. 1672func PathsWithModuleSrcSubDir(ctx EarlyModulePathContext, paths Paths, subDir string) Paths { 1673 paths = append(Paths(nil), paths...) 1674 subDirFullPath := pathForModuleSrc(ctx, subDir) 1675 for i, path := range paths { 1676 rel := Rel(ctx, subDirFullPath.String(), path.String()) 1677 paths[i] = subDirFullPath.join(ctx, rel) 1678 } 1679 return paths 1680} 1681 1682// PathWithModuleSrcSubDir takes a Path and returns a Path where Rel() will return the path relative to subDir in the 1683// module's source directory. If the input path is not located inside subDir then a path error will be reported. 1684func PathWithModuleSrcSubDir(ctx EarlyModulePathContext, path Path, subDir string) Path { 1685 subDirFullPath := pathForModuleSrc(ctx, subDir) 1686 rel := Rel(ctx, subDirFullPath.String(), path.String()) 1687 return subDirFullPath.Join(ctx, rel) 1688} 1689 1690// OptionalPathForModuleSrc returns an OptionalPath. The OptionalPath contains a 1691// valid path if p is non-nil. 1692func OptionalPathForModuleSrc(ctx ModuleMissingDepsPathContext, p *string) OptionalPath { 1693 if p == nil { 1694 return OptionalPath{} 1695 } 1696 return OptionalPathForPath(PathForModuleSrc(ctx, *p)) 1697} 1698 1699func (p SourcePath) genPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleGenPath { 1700 return PathForModuleGen(ctx, subdir, pathtools.ReplaceExtension(p.path, ext)) 1701} 1702 1703func (p SourcePath) genPathWithExtAndTrimExt(ctx ModuleOutPathContext, subdir, ext string, trimExt string) ModuleGenPath { 1704 // If Trim_extension being set, force append Output_extension without replace original extension. 1705 if trimExt != "" { 1706 if ext != "" { 1707 return PathForModuleGen(ctx, subdir, strings.TrimSuffix(p.path, trimExt)+"."+ext) 1708 } 1709 return PathForModuleGen(ctx, subdir, strings.TrimSuffix(p.path, trimExt)) 1710 } 1711 return PathForModuleGen(ctx, subdir, pathtools.ReplaceExtension(p.path, ext)) 1712} 1713 1714func (p SourcePath) objPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleObjPath { 1715 return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext)) 1716} 1717 1718func (p SourcePath) resPathWithName(ctx ModuleOutPathContext, name string) ModuleResPath { 1719 // TODO: Use full directory if the new ctx is not the current ctx? 1720 return PathForModuleRes(ctx, p.path, name) 1721} 1722 1723// ModuleOutPath is a Path representing a module's output directory. 1724type ModuleOutPath struct { 1725 OutputPath 1726} 1727 1728func (p ModuleOutPath) RelativeToTop() Path { 1729 p.OutputPath = p.outputPathRelativeToTop() 1730 return p 1731} 1732 1733var _ Path = ModuleOutPath{} 1734var _ WritablePath = ModuleOutPath{} 1735 1736func (p ModuleOutPath) objPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleObjPath { 1737 return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext)) 1738} 1739 1740// ModuleOutPathContext Subset of ModuleContext functions necessary for output path methods. 1741type ModuleOutPathContext interface { 1742 PathContext 1743 1744 ModuleName() string 1745 ModuleDir() string 1746 ModuleSubDir() string 1747} 1748 1749func pathForModuleOut(ctx ModuleOutPathContext) OutputPath { 1750 return PathForOutput(ctx, ".intermediates", ctx.ModuleDir(), ctx.ModuleName(), ctx.ModuleSubDir()) 1751} 1752 1753// PathForModuleOut returns a Path representing the paths... under the module's 1754// output directory. 1755func PathForModuleOut(ctx ModuleOutPathContext, paths ...string) ModuleOutPath { 1756 p, err := validatePath(paths...) 1757 if err != nil { 1758 reportPathError(ctx, err) 1759 } 1760 return ModuleOutPath{ 1761 OutputPath: pathForModuleOut(ctx).withRel(p), 1762 } 1763} 1764 1765// ModuleGenPath is a Path representing the 'gen' directory in a module's output 1766// directory. Mainly used for generated sources. 1767type ModuleGenPath struct { 1768 ModuleOutPath 1769} 1770 1771func (p ModuleGenPath) RelativeToTop() Path { 1772 p.OutputPath = p.outputPathRelativeToTop() 1773 return p 1774} 1775 1776var _ Path = ModuleGenPath{} 1777var _ WritablePath = ModuleGenPath{} 1778var _ genPathProvider = ModuleGenPath{} 1779var _ objPathProvider = ModuleGenPath{} 1780 1781// PathForModuleGen returns a Path representing the paths... under the module's 1782// `gen' directory. 1783func PathForModuleGen(ctx ModuleOutPathContext, paths ...string) ModuleGenPath { 1784 p, err := validatePath(paths...) 1785 if err != nil { 1786 reportPathError(ctx, err) 1787 } 1788 return ModuleGenPath{ 1789 ModuleOutPath: ModuleOutPath{ 1790 OutputPath: pathForModuleOut(ctx).withRel("gen").withRel(p), 1791 }, 1792 } 1793} 1794 1795func (p ModuleGenPath) genPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleGenPath { 1796 // TODO: make a different path for local vs remote generated files? 1797 return PathForModuleGen(ctx, subdir, pathtools.ReplaceExtension(p.path, ext)) 1798} 1799 1800func (p ModuleGenPath) genPathWithExtAndTrimExt(ctx ModuleOutPathContext, subdir, ext string, trimExt string) ModuleGenPath { 1801 // If Trim_extension being set, force append Output_extension without replace original extension. 1802 if trimExt != "" { 1803 if ext != "" { 1804 return PathForModuleGen(ctx, subdir, strings.TrimSuffix(p.path, trimExt)+"."+ext) 1805 } 1806 return PathForModuleGen(ctx, subdir, strings.TrimSuffix(p.path, trimExt)) 1807 } 1808 return PathForModuleGen(ctx, subdir, pathtools.ReplaceExtension(p.path, ext)) 1809} 1810 1811func (p ModuleGenPath) objPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleObjPath { 1812 return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext)) 1813} 1814 1815// ModuleObjPath is a Path representing the 'obj' directory in a module's output 1816// directory. Used for compiled objects. 1817type ModuleObjPath struct { 1818 ModuleOutPath 1819} 1820 1821func (p ModuleObjPath) RelativeToTop() Path { 1822 p.OutputPath = p.outputPathRelativeToTop() 1823 return p 1824} 1825 1826var _ Path = ModuleObjPath{} 1827var _ WritablePath = ModuleObjPath{} 1828 1829// PathForModuleObj returns a Path representing the paths... under the module's 1830// 'obj' directory. 1831func PathForModuleObj(ctx ModuleOutPathContext, pathComponents ...string) ModuleObjPath { 1832 p, err := validatePath(pathComponents...) 1833 if err != nil { 1834 reportPathError(ctx, err) 1835 } 1836 return ModuleObjPath{PathForModuleOut(ctx, "obj", p)} 1837} 1838 1839// ModuleResPath is a a Path representing the 'res' directory in a module's 1840// output directory. 1841type ModuleResPath struct { 1842 ModuleOutPath 1843} 1844 1845func (p ModuleResPath) RelativeToTop() Path { 1846 p.OutputPath = p.outputPathRelativeToTop() 1847 return p 1848} 1849 1850var _ Path = ModuleResPath{} 1851var _ WritablePath = ModuleResPath{} 1852 1853// PathForModuleRes returns a Path representing the paths... under the module's 1854// 'res' directory. 1855func PathForModuleRes(ctx ModuleOutPathContext, pathComponents ...string) ModuleResPath { 1856 p, err := validatePath(pathComponents...) 1857 if err != nil { 1858 reportPathError(ctx, err) 1859 } 1860 1861 return ModuleResPath{PathForModuleOut(ctx, "res", p)} 1862} 1863 1864// InstallPath is a Path representing a installed file path rooted from the build directory 1865type InstallPath struct { 1866 basePath 1867 1868 // The soong build directory, i.e. Config.SoongOutDir() 1869 soongOutDir string 1870 1871 // partitionDir is the part of the InstallPath that is automatically determined according to the context. 1872 // For example, it is host/<os>-<arch> for host modules, and target/product/<device>/<partition> for device modules. 1873 partitionDir string 1874 1875 partition string 1876 1877 // makePath indicates whether this path is for Soong (false) or Make (true). 1878 makePath bool 1879 1880 fullPath string 1881} 1882 1883type installPathGob struct { 1884 BasePath basePath 1885 SoongOutDir string 1886 PartitionDir string 1887 Partition string 1888 MakePath bool 1889 FullPath string 1890} 1891 1892func (p *InstallPath) ToGob() *installPathGob { 1893 return &installPathGob{ 1894 BasePath: p.basePath, 1895 SoongOutDir: p.soongOutDir, 1896 PartitionDir: p.partitionDir, 1897 Partition: p.partition, 1898 MakePath: p.makePath, 1899 FullPath: p.fullPath, 1900 } 1901} 1902 1903func (p *InstallPath) FromGob(data *installPathGob) { 1904 p.basePath = data.BasePath 1905 p.soongOutDir = data.SoongOutDir 1906 p.partitionDir = data.PartitionDir 1907 p.partition = data.Partition 1908 p.makePath = data.MakePath 1909 p.fullPath = data.FullPath 1910} 1911 1912func (p InstallPath) GobEncode() ([]byte, error) { 1913 return gobtools.CustomGobEncode[installPathGob](&p) 1914} 1915 1916func (p *InstallPath) GobDecode(data []byte) error { 1917 return gobtools.CustomGobDecode[installPathGob](data, p) 1918} 1919 1920// Will panic if called from outside a test environment. 1921func ensureTestOnly() { 1922 if PrefixInList(os.Args, "-test.") { 1923 return 1924 } 1925 panic(fmt.Errorf("Not in test. Command line:\n %s", strings.Join(os.Args, "\n "))) 1926} 1927 1928func (p InstallPath) RelativeToTop() Path { 1929 ensureTestOnly() 1930 if p.makePath { 1931 p.soongOutDir = testOutDir 1932 } else { 1933 p.soongOutDir = TestOutSoongDir 1934 } 1935 p.fullPath = filepath.Join(p.soongOutDir, p.path) 1936 return p 1937} 1938 1939func (p InstallPath) WithoutRel() Path { 1940 p.basePath = p.basePath.withoutRel() 1941 return p 1942} 1943 1944func (p InstallPath) getSoongOutDir() string { 1945 return p.soongOutDir 1946} 1947 1948func (p InstallPath) ReplaceExtension(ctx PathContext, ext string) OutputPath { 1949 panic("Not implemented") 1950} 1951 1952var _ Path = InstallPath{} 1953var _ WritablePath = InstallPath{} 1954 1955func (p InstallPath) writablePath() {} 1956 1957func (p InstallPath) String() string { 1958 return p.fullPath 1959} 1960 1961// PartitionDir returns the path to the partition where the install path is rooted at. It is 1962// out/soong/target/product/<device>/<partition> for device modules, and out/soong/host/<os>-<arch> for host modules. 1963// The ./soong is dropped if the install path is for Make. 1964func (p InstallPath) PartitionDir() string { 1965 if p.makePath { 1966 return filepath.Join(p.soongOutDir, "../", p.partitionDir) 1967 } else { 1968 return filepath.Join(p.soongOutDir, p.partitionDir) 1969 } 1970} 1971 1972func (p InstallPath) Partition() string { 1973 return p.partition 1974} 1975 1976// Join creates a new InstallPath with paths... joined with the current path. The 1977// provided paths... may not use '..' to escape from the current path. 1978func (p InstallPath) Join(ctx PathContext, paths ...string) InstallPath { 1979 path, err := validatePath(paths...) 1980 if err != nil { 1981 reportPathError(ctx, err) 1982 } 1983 return p.withRel(path) 1984} 1985 1986func (p InstallPath) withRel(rel string) InstallPath { 1987 p.basePath = p.basePath.withRel(rel) 1988 p.fullPath = filepath.Join(p.fullPath, rel) 1989 return p 1990} 1991 1992// Deprecated: ToMakePath is a noop, PathForModuleInstall always returns Make paths when building 1993// embedded in Make. 1994func (p InstallPath) ToMakePath() InstallPath { 1995 p.makePath = true 1996 return p 1997} 1998 1999// PathForModuleInstall returns a Path representing the install path for the 2000// module appended with paths... 2001func PathForModuleInstall(ctx ModuleInstallPathContext, pathComponents ...string) InstallPath { 2002 os, arch := osAndArch(ctx) 2003 partition := modulePartition(ctx, os.Class == Device) 2004 return pathForInstall(ctx, os, arch, partition, pathComponents...) 2005} 2006 2007// PathForHostDexInstall returns an InstallPath representing the install path for the 2008// module appended with paths... 2009func PathForHostDexInstall(ctx ModuleInstallPathContext, pathComponents ...string) InstallPath { 2010 return pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, "", pathComponents...) 2011} 2012 2013// PathForModuleInPartitionInstall is similar to PathForModuleInstall but partition is provided by the caller 2014func PathForModuleInPartitionInstall(ctx ModuleInstallPathContext, partition string, pathComponents ...string) InstallPath { 2015 os, arch := osAndArch(ctx) 2016 return pathForInstall(ctx, os, arch, partition, pathComponents...) 2017} 2018 2019func osAndArch(ctx ModuleInstallPathContext) (OsType, ArchType) { 2020 os := ctx.Os() 2021 arch := ctx.Arch().ArchType 2022 forceOS, forceArch := ctx.InstallForceOS() 2023 if forceOS != nil { 2024 os = *forceOS 2025 } 2026 if forceArch != nil { 2027 arch = *forceArch 2028 } 2029 return os, arch 2030} 2031 2032func pathForPartitionInstallDir(ctx PathContext, partition, partitionPath string, makePath bool) InstallPath { 2033 fullPath := ctx.Config().SoongOutDir() 2034 if makePath { 2035 // Make path starts with out/ instead of out/soong. 2036 fullPath = filepath.Join(fullPath, "../", partitionPath) 2037 } else { 2038 fullPath = filepath.Join(fullPath, partitionPath) 2039 } 2040 2041 return InstallPath{ 2042 basePath: basePath{partitionPath, ""}, 2043 soongOutDir: ctx.Config().soongOutDir, 2044 partitionDir: partitionPath, 2045 partition: partition, 2046 makePath: makePath, 2047 fullPath: fullPath, 2048 } 2049} 2050 2051func pathForInstall(ctx PathContext, os OsType, arch ArchType, partition string, 2052 pathComponents ...string) InstallPath { 2053 2054 var partitionPaths []string 2055 2056 if os.Class == Device { 2057 partitionPaths = []string{"target", "product", *ctx.Config().deviceNameToInstall, partition} 2058 } else { 2059 osName := os.String() 2060 if os == Linux { 2061 // instead of linux_glibc 2062 osName = "linux" 2063 } 2064 if os == LinuxMusl && ctx.Config().UseHostMusl() { 2065 // When using musl instead of glibc, use "linux" instead of "linux_musl". When cross 2066 // compiling we will still use "linux_musl". 2067 osName = "linux" 2068 } 2069 2070 // SOONG_HOST_OUT is set to out/host/$(HOST_OS)-$(HOST_PREBUILT_ARCH) 2071 // and HOST_PREBUILT_ARCH is forcibly set to x86 even on x86_64 hosts. We don't seem 2072 // to have a plan to fix it (see the comment in build/make/core/envsetup.mk). 2073 // Let's keep using x86 for the existing cases until we have a need to support 2074 // other architectures. 2075 archName := arch.String() 2076 if os.Class == Host && (arch == X86_64 || arch == Common) { 2077 archName = "x86" 2078 } 2079 partitionPaths = []string{"host", osName + "-" + archName, partition} 2080 } 2081 2082 partitionPath, err := validatePath(partitionPaths...) 2083 if err != nil { 2084 reportPathError(ctx, err) 2085 } 2086 2087 base := pathForPartitionInstallDir(ctx, partition, partitionPath, true) 2088 return base.Join(ctx, pathComponents...) 2089} 2090 2091func PathForNdkInstall(ctx PathContext, paths ...string) OutputPath { 2092 return PathForOutput(ctx, append([]string{"ndk"}, paths...)...) 2093} 2094 2095func PathForMainlineSdksInstall(ctx PathContext, paths ...string) InstallPath { 2096 base := pathForPartitionInstallDir(ctx, "", "mainline-sdks", false) 2097 return base.Join(ctx, paths...) 2098} 2099 2100func PathForSuiteInstall(ctx PathContext, suite string, pathComponents ...string) InstallPath { 2101 return pathForPartitionInstallDir(ctx, "test_suites", "test_suites", false).Join(ctx, suite).Join(ctx, pathComponents...) 2102} 2103 2104func InstallPathToOnDevicePath(ctx PathContext, path InstallPath) string { 2105 rel := Rel(ctx, strings.TrimSuffix(path.PartitionDir(), path.partition), path.String()) 2106 return "/" + rel 2107} 2108 2109func modulePartition(ctx ModuleInstallPathContext, device bool) string { 2110 var partition string 2111 if ctx.InstallInTestcases() { 2112 // "testcases" install directory can be used for host or device modules. 2113 partition = "testcases" 2114 } else if device { 2115 if ctx.InstallInData() { 2116 partition = "data" 2117 } else if ctx.InstallInRamdisk() { 2118 if ctx.DeviceConfig().BoardUsesRecoveryAsBoot() { 2119 partition = "recovery/root/first_stage_ramdisk" 2120 } else { 2121 partition = "ramdisk" 2122 } 2123 if !ctx.InstallInRoot() { 2124 partition += "/system" 2125 } 2126 } else if ctx.InstallInVendorRamdisk() { 2127 // The module is only available after switching root into 2128 // /first_stage_ramdisk. To expose the module before switching root 2129 // on a device without a dedicated recovery partition, install the 2130 // recovery variant. 2131 if ctx.DeviceConfig().BoardMoveRecoveryResourcesToVendorBoot() { 2132 partition = "vendor_ramdisk/first_stage_ramdisk" 2133 } else { 2134 partition = "vendor_ramdisk" 2135 } 2136 if !ctx.InstallInRoot() { 2137 partition += "/system" 2138 } 2139 } else if ctx.InstallInDebugRamdisk() { 2140 partition = "debug_ramdisk" 2141 } else if ctx.InstallInRecovery() { 2142 if ctx.InstallInRoot() { 2143 partition = "recovery/root" 2144 } else { 2145 // the layout of recovery partion is the same as that of system partition 2146 partition = "recovery/root/system" 2147 } 2148 } else if ctx.SocSpecific() || ctx.InstallInVendor() { 2149 partition = ctx.DeviceConfig().VendorPath() 2150 } else if ctx.DeviceSpecific() || ctx.InstallInOdm() { 2151 partition = ctx.DeviceConfig().OdmPath() 2152 } else if ctx.ProductSpecific() || ctx.InstallInProduct() { 2153 partition = ctx.DeviceConfig().ProductPath() 2154 } else if ctx.SystemExtSpecific() { 2155 partition = ctx.DeviceConfig().SystemExtPath() 2156 } else if ctx.InstallInRoot() { 2157 partition = "root" 2158 } else if ctx.InstallInSystemDlkm() { 2159 partition = ctx.DeviceConfig().SystemDlkmPath() 2160 } else if ctx.InstallInVendorDlkm() { 2161 partition = ctx.DeviceConfig().VendorDlkmPath() 2162 } else if ctx.InstallInOdmDlkm() { 2163 partition = ctx.DeviceConfig().OdmDlkmPath() 2164 } else { 2165 partition = "system" 2166 } 2167 if ctx.InstallInSanitizerDir() { 2168 partition = "data/asan/" + partition 2169 } 2170 } 2171 return partition 2172} 2173 2174type InstallPaths []InstallPath 2175 2176// Paths returns the InstallPaths as a Paths 2177func (p InstallPaths) Paths() Paths { 2178 if p == nil { 2179 return nil 2180 } 2181 ret := make(Paths, len(p)) 2182 for i, path := range p { 2183 ret[i] = path 2184 } 2185 return ret 2186} 2187 2188// Strings returns the string forms of the install paths. 2189func (p InstallPaths) Strings() []string { 2190 if p == nil { 2191 return nil 2192 } 2193 ret := make([]string, len(p)) 2194 for i, path := range p { 2195 ret[i] = path.String() 2196 } 2197 return ret 2198} 2199 2200// validatePathInternal ensures that a path does not leave its component, and 2201// optionally doesn't contain Ninja variables. 2202func validatePathInternal(allowNinjaVariables bool, pathComponents ...string) (string, error) { 2203 initialEmpty := 0 2204 finalEmpty := 0 2205 for i, path := range pathComponents { 2206 if !allowNinjaVariables && strings.Contains(path, "$") { 2207 return "", fmt.Errorf("Path contains invalid character($): %s", path) 2208 } 2209 2210 path := filepath.Clean(path) 2211 if path == ".." || strings.HasPrefix(path, "../") || strings.HasPrefix(path, "/") { 2212 return "", fmt.Errorf("Path is outside directory: %s", path) 2213 } 2214 2215 if i == initialEmpty && pathComponents[i] == "" { 2216 initialEmpty++ 2217 } 2218 if i == finalEmpty && pathComponents[len(pathComponents)-1-i] == "" { 2219 finalEmpty++ 2220 } 2221 } 2222 // Optimization: filepath.Join("foo", "") returns a newly allocated copy 2223 // of "foo", while filepath.Join("foo") does not. Strip out any empty 2224 // path components. 2225 if initialEmpty == len(pathComponents) { 2226 return "", nil 2227 } 2228 nonEmptyPathComponents := pathComponents[initialEmpty : len(pathComponents)-finalEmpty] 2229 // TODO: filepath.Join isn't necessarily correct with embedded ninja 2230 // variables. '..' may remove the entire ninja variable, even if it 2231 // will be expanded to multiple nested directories. 2232 return filepath.Join(nonEmptyPathComponents...), nil 2233} 2234 2235// validateSafePath validates a path that we trust (may contain ninja 2236// variables). Ensures that each path component does not attempt to leave its 2237// component. Returns a joined version of each path component. 2238func validateSafePath(pathComponents ...string) (string, error) { 2239 return validatePathInternal(true, pathComponents...) 2240} 2241 2242// validatePath validates that a path does not include ninja variables, and that 2243// each path component does not attempt to leave its component. Returns a joined 2244// version of each path component. 2245func validatePath(pathComponents ...string) (string, error) { 2246 return validatePathInternal(false, pathComponents...) 2247} 2248 2249func PathForPhony(ctx PathContext, phony string) WritablePath { 2250 if strings.ContainsAny(phony, "$/") { 2251 ReportPathErrorf(ctx, "Phony target contains invalid character ($ or /): %s", phony) 2252 } 2253 return PhonyPath{basePath{phony, ""}} 2254} 2255 2256type PhonyPath struct { 2257 basePath 2258} 2259 2260func (p PhonyPath) writablePath() {} 2261 2262func (p PhonyPath) getSoongOutDir() string { 2263 // A phone path cannot contain any / so cannot be relative to the build directory. 2264 return "" 2265} 2266 2267func (p PhonyPath) RelativeToTop() Path { 2268 ensureTestOnly() 2269 // A phony path cannot contain any / so does not have a build directory so switching to a new 2270 // build directory has no effect so just return this path. 2271 return p 2272} 2273 2274func (p PhonyPath) WithoutRel() Path { 2275 p.basePath = p.basePath.withoutRel() 2276 return p 2277} 2278 2279func (p PhonyPath) ReplaceExtension(ctx PathContext, ext string) OutputPath { 2280 panic("Not implemented") 2281} 2282 2283var _ Path = PhonyPath{} 2284var _ WritablePath = PhonyPath{} 2285 2286type testPath struct { 2287 basePath 2288} 2289 2290func (p testPath) RelativeToTop() Path { 2291 ensureTestOnly() 2292 return p 2293} 2294 2295func (p testPath) WithoutRel() Path { 2296 p.basePath = p.basePath.withoutRel() 2297 return p 2298} 2299 2300func (p testPath) String() string { 2301 return p.path 2302} 2303 2304var _ Path = testPath{} 2305 2306// PathForTesting returns a Path constructed from joining the elements of paths with '/'. It should only be used from 2307// within tests. 2308func PathForTesting(paths ...string) Path { 2309 p, err := validateSafePath(paths...) 2310 if err != nil { 2311 panic(err) 2312 } 2313 return testPath{basePath{path: p, rel: p}} 2314} 2315 2316func PathForTestingWithRel(path, rel string) Path { 2317 p, err := validateSafePath(path, rel) 2318 if err != nil { 2319 panic(err) 2320 } 2321 r, err := validatePath(rel) 2322 if err != nil { 2323 panic(err) 2324 } 2325 return testPath{basePath{path: p, rel: r}} 2326} 2327 2328// PathsForTesting returns a Path constructed from each element in strs. It should only be used from within tests. 2329func PathsForTesting(strs ...string) Paths { 2330 p := make(Paths, len(strs)) 2331 for i, s := range strs { 2332 p[i] = PathForTesting(s) 2333 } 2334 2335 return p 2336} 2337 2338type testPathContext struct { 2339 config Config 2340} 2341 2342func (x *testPathContext) Config() Config { return x.config } 2343func (x *testPathContext) AddNinjaFileDeps(...string) {} 2344 2345// PathContextForTesting returns a PathContext that can be used in tests, for example to create an OutputPath with 2346// PathForOutput. 2347func PathContextForTesting(config Config) PathContext { 2348 return &testPathContext{ 2349 config: config, 2350 } 2351} 2352 2353type testModuleInstallPathContext struct { 2354 baseModuleContext 2355 2356 inData bool 2357 inTestcases bool 2358 inSanitizerDir bool 2359 inRamdisk bool 2360 inVendorRamdisk bool 2361 inDebugRamdisk bool 2362 inRecovery bool 2363 inRoot bool 2364 inOdm bool 2365 inProduct bool 2366 inVendor bool 2367 inSystemDlkm bool 2368 inVendorDlkm bool 2369 inOdmDlkm bool 2370 forceOS *OsType 2371 forceArch *ArchType 2372} 2373 2374func (m testModuleInstallPathContext) Config() Config { 2375 return m.baseModuleContext.config 2376} 2377 2378func (testModuleInstallPathContext) AddNinjaFileDeps(deps ...string) {} 2379 2380func (m testModuleInstallPathContext) InstallInData() bool { 2381 return m.inData 2382} 2383 2384func (m testModuleInstallPathContext) InstallInTestcases() bool { 2385 return m.inTestcases 2386} 2387 2388func (m testModuleInstallPathContext) InstallInSanitizerDir() bool { 2389 return m.inSanitizerDir 2390} 2391 2392func (m testModuleInstallPathContext) InstallInRamdisk() bool { 2393 return m.inRamdisk 2394} 2395 2396func (m testModuleInstallPathContext) InstallInVendorRamdisk() bool { 2397 return m.inVendorRamdisk 2398} 2399 2400func (m testModuleInstallPathContext) InstallInDebugRamdisk() bool { 2401 return m.inDebugRamdisk 2402} 2403 2404func (m testModuleInstallPathContext) InstallInRecovery() bool { 2405 return m.inRecovery 2406} 2407 2408func (m testModuleInstallPathContext) InstallInRoot() bool { 2409 return m.inRoot 2410} 2411 2412func (m testModuleInstallPathContext) InstallInOdm() bool { 2413 return m.inOdm 2414} 2415 2416func (m testModuleInstallPathContext) InstallInProduct() bool { 2417 return m.inProduct 2418} 2419 2420func (m testModuleInstallPathContext) InstallInVendor() bool { 2421 return m.inVendor 2422} 2423 2424func (m testModuleInstallPathContext) InstallInSystemDlkm() bool { 2425 return m.inSystemDlkm 2426} 2427 2428func (m testModuleInstallPathContext) InstallInVendorDlkm() bool { 2429 return m.inVendorDlkm 2430} 2431 2432func (m testModuleInstallPathContext) InstallInOdmDlkm() bool { 2433 return m.inOdmDlkm 2434} 2435 2436func (m testModuleInstallPathContext) InstallForceOS() (*OsType, *ArchType) { 2437 return m.forceOS, m.forceArch 2438} 2439 2440// Construct a minimal ModuleInstallPathContext for testing. Note that baseModuleContext is 2441// default-initialized, which leaves blueprint.baseModuleContext set to nil, so methods that are 2442// delegated to it will panic. 2443func ModuleInstallPathContextForTesting(config Config) ModuleInstallPathContext { 2444 ctx := &testModuleInstallPathContext{} 2445 ctx.config = config 2446 ctx.os = Android 2447 return ctx 2448} 2449 2450// Rel performs the same function as filepath.Rel, but reports errors to a PathContext, and reports an error if 2451// targetPath is not inside basePath. 2452func Rel(ctx PathContext, basePath string, targetPath string) string { 2453 rel, isRel := MaybeRel(ctx, basePath, targetPath) 2454 if !isRel { 2455 ReportPathErrorf(ctx, "path %q is not under path %q", targetPath, basePath) 2456 return "" 2457 } 2458 return rel 2459} 2460 2461// MaybeRel performs the same function as filepath.Rel, but reports errors to a PathContext, and returns false if 2462// targetPath is not inside basePath. 2463func MaybeRel(ctx PathContext, basePath string, targetPath string) (string, bool) { 2464 rel, isRel, err := maybeRelErr(basePath, targetPath) 2465 if err != nil { 2466 reportPathError(ctx, err) 2467 } 2468 return rel, isRel 2469} 2470 2471func maybeRelErr(basePath string, targetPath string) (string, bool, error) { 2472 // filepath.Rel returns an error if one path is absolute and the other is not, handle that case first. 2473 if filepath.IsAbs(basePath) != filepath.IsAbs(targetPath) { 2474 return "", false, nil 2475 } 2476 rel, err := filepath.Rel(basePath, targetPath) 2477 if err != nil { 2478 return "", false, err 2479 } else if rel == ".." || strings.HasPrefix(rel, "../") || strings.HasPrefix(rel, "/") { 2480 return "", false, nil 2481 } 2482 return rel, true, nil 2483} 2484 2485// Writes a file to the output directory. Attempting to write directly to the output directory 2486// will fail due to the sandbox of the soong_build process. 2487// Only writes the file if the file doesn't exist or if it has different contents, to prevent 2488// updating the timestamp if no changes would be made. (This is better for incremental 2489// performance.) 2490func WriteFileToOutputDir(path WritablePath, data []byte, perm os.FileMode) error { 2491 absPath := absolutePath(path.String()) 2492 err := os.MkdirAll(filepath.Dir(absPath), 0777) 2493 if err != nil { 2494 return err 2495 } 2496 return pathtools.WriteFileIfChanged(absPath, data, perm) 2497} 2498 2499func RemoveAllOutputDir(path WritablePath) error { 2500 return os.RemoveAll(absolutePath(path.String())) 2501} 2502 2503func CreateOutputDirIfNonexistent(path WritablePath, perm os.FileMode) error { 2504 dir := absolutePath(path.String()) 2505 return createDirIfNonexistent(dir, perm) 2506} 2507 2508func createDirIfNonexistent(dir string, perm os.FileMode) error { 2509 if _, err := os.Stat(dir); os.IsNotExist(err) { 2510 return os.MkdirAll(dir, os.ModePerm) 2511 } else { 2512 return err 2513 } 2514} 2515 2516// absolutePath is deliberately private so that Soong's Go plugins can't use it to find and 2517// read arbitrary files without going through the methods in the current package that track 2518// dependencies. 2519func absolutePath(path string) string { 2520 if filepath.IsAbs(path) { 2521 return path 2522 } 2523 return filepath.Join(absSrcDir, path) 2524} 2525 2526// A DataPath represents the path of a file to be used as data, for example 2527// a test library to be installed alongside a test. 2528// The data file should be installed (copied from `<SrcPath>`) to 2529// `<install_root>/<RelativeInstallPath>/<filename>`, or 2530// `<install_root>/<filename>` if RelativeInstallPath is empty. 2531type DataPath struct { 2532 // The path of the data file that should be copied into the data directory 2533 SrcPath Path 2534 // The install path of the data file, relative to the install root. 2535 RelativeInstallPath string 2536 // If WithoutRel is true, use SrcPath.Base() instead of SrcPath.Rel() as the filename. 2537 WithoutRel bool 2538} 2539 2540func (d *DataPath) ToRelativeInstallPath() string { 2541 relPath := d.SrcPath.Rel() 2542 if d.WithoutRel { 2543 relPath = d.SrcPath.Base() 2544 } 2545 if d.RelativeInstallPath != "" { 2546 relPath = filepath.Join(d.RelativeInstallPath, relPath) 2547 } 2548 return relPath 2549} 2550 2551// PathsIfNonNil returns a Paths containing only the non-nil input arguments. 2552func PathsIfNonNil(paths ...Path) Paths { 2553 if len(paths) == 0 { 2554 // Fast path for empty argument list 2555 return nil 2556 } else if len(paths) == 1 { 2557 // Fast path for a single argument 2558 if paths[0] != nil { 2559 return paths 2560 } else { 2561 return nil 2562 } 2563 } 2564 ret := make(Paths, 0, len(paths)) 2565 for _, path := range paths { 2566 if path != nil { 2567 ret = append(ret, path) 2568 } 2569 } 2570 if len(ret) == 0 { 2571 return nil 2572 } 2573 return ret 2574} 2575 2576var thirdPartyDirPrefixExceptions = []*regexp.Regexp{ 2577 regexp.MustCompile("^vendor/[^/]*google[^/]*/"), 2578 regexp.MustCompile("^hardware/google/"), 2579 regexp.MustCompile("^hardware/interfaces/"), 2580 regexp.MustCompile("^hardware/libhardware[^/]*/"), 2581 regexp.MustCompile("^hardware/ril/"), 2582} 2583 2584func IsThirdPartyPath(path string) bool { 2585 thirdPartyDirPrefixes := []string{"external/", "vendor/", "hardware/"} 2586 2587 if HasAnyPrefix(path, thirdPartyDirPrefixes) { 2588 for _, prefix := range thirdPartyDirPrefixExceptions { 2589 if prefix.MatchString(path) { 2590 return false 2591 } 2592 } 2593 return true 2594 } 2595 return false 2596} 2597 2598// ToRelativeSourcePath converts absolute source path to the path relative to the source root. 2599// This throws an error if the input path is outside of the source root and cannot be converted 2600// to the relative path. 2601// This should be rarely used given that the source path is relative in Soong. 2602func ToRelativeSourcePath(ctx PathContext, path string) string { 2603 ret := path 2604 if filepath.IsAbs(path) { 2605 relPath, err := filepath.Rel(absSrcDir, path) 2606 if err != nil || strings.HasPrefix(relPath, "..") { 2607 ReportPathErrorf(ctx, "%s is outside of the source root", path) 2608 } 2609 ret = relPath 2610 } 2611 return ret 2612} 2613