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