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 "io/ioutil" 20 "os" 21 "path/filepath" 22 "reflect" 23 "sort" 24 "strings" 25 26 "github.com/google/blueprint" 27 "github.com/google/blueprint/bootstrap" 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 GlobWithDeps(globPattern string, excludes []string) ([]string, error) 42} 43 44var _ PathContext = SingletonContext(nil) 45var _ PathContext = ModuleContext(nil) 46 47// "Null" path context is a minimal path context for a given config. 48type NullPathContext struct { 49 config Config 50} 51 52func (NullPathContext) AddNinjaFileDeps(...string) {} 53func (ctx NullPathContext) Config() Config { return ctx.config } 54 55// EarlyModulePathContext is a subset of EarlyModuleContext methods required by the 56// Path methods. These path methods can be called before any mutators have run. 57type EarlyModulePathContext interface { 58 PathContext 59 PathGlobContext 60 61 ModuleDir() string 62 ModuleErrorf(fmt string, args ...interface{}) 63} 64 65var _ EarlyModulePathContext = ModuleContext(nil) 66 67// Glob globs files and directories matching globPattern relative to ModuleDir(), 68// paths in the excludes parameter will be omitted. 69func Glob(ctx EarlyModulePathContext, globPattern string, excludes []string) Paths { 70 ret, err := ctx.GlobWithDeps(globPattern, excludes) 71 if err != nil { 72 ctx.ModuleErrorf("glob: %s", err.Error()) 73 } 74 return pathsForModuleSrcFromFullPath(ctx, ret, true) 75} 76 77// GlobFiles globs *only* files (not directories) matching globPattern relative to ModuleDir(). 78// Paths in the excludes parameter will be omitted. 79func GlobFiles(ctx EarlyModulePathContext, globPattern string, excludes []string) Paths { 80 ret, err := ctx.GlobWithDeps(globPattern, excludes) 81 if err != nil { 82 ctx.ModuleErrorf("glob: %s", err.Error()) 83 } 84 return pathsForModuleSrcFromFullPath(ctx, ret, false) 85} 86 87// ModuleWithDepsPathContext is a subset of *ModuleContext methods required by 88// the Path methods that rely on module dependencies having been resolved. 89type ModuleWithDepsPathContext interface { 90 EarlyModulePathContext 91 GetDirectDepWithTag(name string, tag blueprint.DependencyTag) blueprint.Module 92} 93 94// ModuleMissingDepsPathContext is a subset of *ModuleContext methods required by 95// the Path methods that rely on module dependencies having been resolved and ability to report 96// missing dependency errors. 97type ModuleMissingDepsPathContext interface { 98 ModuleWithDepsPathContext 99 AddMissingDependencies(missingDeps []string) 100} 101 102type ModuleInstallPathContext interface { 103 BaseModuleContext 104 105 InstallInData() bool 106 InstallInTestcases() bool 107 InstallInSanitizerDir() bool 108 InstallInRamdisk() bool 109 InstallInVendorRamdisk() bool 110 InstallInDebugRamdisk() bool 111 InstallInRecovery() bool 112 InstallInRoot() bool 113 InstallBypassMake() bool 114 InstallForceOS() (*OsType, *ArchType) 115} 116 117var _ ModuleInstallPathContext = ModuleContext(nil) 118 119// errorfContext is the interface containing the Errorf method matching the 120// Errorf method in blueprint.SingletonContext. 121type errorfContext interface { 122 Errorf(format string, args ...interface{}) 123} 124 125var _ errorfContext = blueprint.SingletonContext(nil) 126 127// moduleErrorf is the interface containing the ModuleErrorf method matching 128// the ModuleErrorf method in blueprint.ModuleContext. 129type moduleErrorf interface { 130 ModuleErrorf(format string, args ...interface{}) 131} 132 133var _ moduleErrorf = blueprint.ModuleContext(nil) 134 135// reportPathError will register an error with the attached context. It 136// attempts ctx.ModuleErrorf for a better error message first, then falls 137// back to ctx.Errorf. 138func reportPathError(ctx PathContext, err error) { 139 ReportPathErrorf(ctx, "%s", err.Error()) 140} 141 142// ReportPathErrorf will register an error with the attached context. It 143// attempts ctx.ModuleErrorf for a better error message first, then falls 144// back to ctx.Errorf. 145func ReportPathErrorf(ctx PathContext, format string, args ...interface{}) { 146 if mctx, ok := ctx.(moduleErrorf); ok { 147 mctx.ModuleErrorf(format, args...) 148 } else if ectx, ok := ctx.(errorfContext); ok { 149 ectx.Errorf(format, args...) 150 } else { 151 panic(fmt.Sprintf(format, args...)) 152 } 153} 154 155func pathContextName(ctx PathContext, module blueprint.Module) string { 156 if x, ok := ctx.(interface{ ModuleName(blueprint.Module) string }); ok { 157 return x.ModuleName(module) 158 } else if x, ok := ctx.(interface{ OtherModuleName(blueprint.Module) string }); ok { 159 return x.OtherModuleName(module) 160 } 161 return "unknown" 162} 163 164type Path interface { 165 // Returns the path in string form 166 String() string 167 168 // Ext returns the extension of the last element of the path 169 Ext() string 170 171 // Base returns the last element of the path 172 Base() string 173 174 // Rel returns the portion of the path relative to the directory it was created from. For 175 // example, Rel on a PathsForModuleSrc would return the path relative to the module source 176 // directory, and OutputPath.Join("foo").Rel() would return "foo". 177 Rel() string 178 179 // RelativeToTop returns a new path relative to the top, it is provided solely for use in tests. 180 // 181 // It is guaranteed to always return the same type as it is called on, e.g. if called on an 182 // InstallPath then the returned value can be converted to an InstallPath. 183 // 184 // A standard build has the following structure: 185 // ../top/ 186 // out/ - make install files go here. 187 // out/soong - this is the buildDir passed to NewTestConfig() 188 // ... - the source files 189 // 190 // This function converts a path so that it appears relative to the ../top/ directory, i.e. 191 // * Make install paths, which have the pattern "buildDir/../<path>" are converted into the top 192 // relative path "out/<path>" 193 // * Soong install paths and other writable paths, which have the pattern "buildDir/<path>" are 194 // converted into the top relative path "out/soong/<path>". 195 // * Source paths are already relative to the top. 196 // * Phony paths are not relative to anything. 197 // * toolDepPath have an absolute but known value in so don't need making relative to anything in 198 // order to test. 199 RelativeToTop() Path 200} 201 202const ( 203 OutDir = "out" 204 OutSoongDir = OutDir + "/soong" 205) 206 207// WritablePath is a type of path that can be used as an output for build rules. 208type WritablePath interface { 209 Path 210 211 // return the path to the build directory. 212 getBuildDir() string 213 214 // the writablePath method doesn't directly do anything, 215 // but it allows a struct to distinguish between whether or not it implements the WritablePath interface 216 writablePath() 217 218 ReplaceExtension(ctx PathContext, ext string) OutputPath 219} 220 221type genPathProvider interface { 222 genPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleGenPath 223} 224type objPathProvider interface { 225 objPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleObjPath 226} 227type resPathProvider interface { 228 resPathWithName(ctx ModuleOutPathContext, name string) ModuleResPath 229} 230 231// GenPathWithExt derives a new file path in ctx's generated sources directory 232// from the current path, but with the new extension. 233func GenPathWithExt(ctx ModuleOutPathContext, subdir string, p Path, ext string) ModuleGenPath { 234 if path, ok := p.(genPathProvider); ok { 235 return path.genPathWithExt(ctx, subdir, ext) 236 } 237 ReportPathErrorf(ctx, "Tried to create generated file from unsupported path: %s(%s)", reflect.TypeOf(p).Name(), p) 238 return PathForModuleGen(ctx) 239} 240 241// ObjPathWithExt derives a new file path in ctx's object directory from the 242// current path, but with the new extension. 243func ObjPathWithExt(ctx ModuleOutPathContext, subdir string, p Path, ext string) ModuleObjPath { 244 if path, ok := p.(objPathProvider); ok { 245 return path.objPathWithExt(ctx, subdir, ext) 246 } 247 ReportPathErrorf(ctx, "Tried to create object file from unsupported path: %s (%s)", reflect.TypeOf(p).Name(), p) 248 return PathForModuleObj(ctx) 249} 250 251// ResPathWithName derives a new path in ctx's output resource directory, using 252// the current path to create the directory name, and the `name` argument for 253// the filename. 254func ResPathWithName(ctx ModuleOutPathContext, p Path, name string) ModuleResPath { 255 if path, ok := p.(resPathProvider); ok { 256 return path.resPathWithName(ctx, name) 257 } 258 ReportPathErrorf(ctx, "Tried to create res file from unsupported path: %s (%s)", reflect.TypeOf(p).Name(), p) 259 return PathForModuleRes(ctx) 260} 261 262// OptionalPath is a container that may or may not contain a valid Path. 263type OptionalPath struct { 264 valid bool 265 path Path 266} 267 268// OptionalPathForPath returns an OptionalPath containing the path. 269func OptionalPathForPath(path Path) OptionalPath { 270 if path == nil { 271 return OptionalPath{} 272 } 273 return OptionalPath{valid: true, path: path} 274} 275 276// Valid returns whether there is a valid path 277func (p OptionalPath) Valid() bool { 278 return p.valid 279} 280 281// Path returns the Path embedded in this OptionalPath. You must be sure that 282// there is a valid path, since this method will panic if there is not. 283func (p OptionalPath) Path() Path { 284 if !p.valid { 285 panic("Requesting an invalid path") 286 } 287 return p.path 288} 289 290// AsPaths converts the OptionalPath into Paths. 291// 292// It returns nil if this is not valid, or a single length slice containing the Path embedded in 293// this OptionalPath. 294func (p OptionalPath) AsPaths() Paths { 295 if !p.valid { 296 return nil 297 } 298 return Paths{p.path} 299} 300 301// RelativeToTop returns an OptionalPath with the path that was embedded having been replaced by the 302// result of calling Path.RelativeToTop on it. 303func (p OptionalPath) RelativeToTop() OptionalPath { 304 if !p.valid { 305 return p 306 } 307 p.path = p.path.RelativeToTop() 308 return p 309} 310 311// String returns the string version of the Path, or "" if it isn't valid. 312func (p OptionalPath) String() string { 313 if p.valid { 314 return p.path.String() 315 } else { 316 return "" 317 } 318} 319 320// Paths is a slice of Path objects, with helpers to operate on the collection. 321type Paths []Path 322 323// RelativeToTop creates a new Paths containing the result of calling Path.RelativeToTop on each 324// item in this slice. 325func (p Paths) RelativeToTop() Paths { 326 ensureTestOnly() 327 if p == nil { 328 return p 329 } 330 ret := make(Paths, len(p)) 331 for i, path := range p { 332 ret[i] = path.RelativeToTop() 333 } 334 return ret 335} 336 337func (paths Paths) containsPath(path Path) bool { 338 for _, p := range paths { 339 if p == path { 340 return true 341 } 342 } 343 return false 344} 345 346// PathsForSource returns Paths rooted from SrcDir, *not* rooted from the module's local source 347// directory 348func PathsForSource(ctx PathContext, paths []string) Paths { 349 ret := make(Paths, len(paths)) 350 for i, path := range paths { 351 ret[i] = PathForSource(ctx, path) 352 } 353 return ret 354} 355 356// ExistentPathsForSources returns a list of Paths rooted from SrcDir, *not* rooted from the 357// module's local source directory, that are found in the tree. If any are not found, they are 358// omitted from the list, and dependencies are added so that we're re-run when they are added. 359func ExistentPathsForSources(ctx PathContext, paths []string) Paths { 360 ret := make(Paths, 0, len(paths)) 361 for _, path := range paths { 362 p := ExistentPathForSource(ctx, path) 363 if p.Valid() { 364 ret = append(ret, p.Path()) 365 } 366 } 367 return ret 368} 369 370// PathsForModuleSrc returns a Paths{} containing the resolved references in paths: 371// * filepath, relative to local module directory, resolves as a filepath relative to the local 372// source directory 373// * glob, relative to the local module directory, resolves as filepath(s), relative to the local 374// source directory. 375// * other modules using the ":name{.tag}" syntax. These modules must implement SourceFileProducer 376// or OutputFileProducer. These resolve as a filepath to an output filepath or generated source 377// filepath. 378// Properties passed as the paths argument must have been annotated with struct tag 379// `android:"path"` so that dependencies on SourceFileProducer modules will have already been handled by the 380// path_deps mutator. 381// If a requested module is not found as a dependency: 382// * if ctx.Config().AllowMissingDependencies() is true, this module to be marked as having 383// missing dependencies 384// * otherwise, a ModuleError is thrown. 385func PathsForModuleSrc(ctx ModuleMissingDepsPathContext, paths []string) Paths { 386 return PathsForModuleSrcExcludes(ctx, paths, nil) 387} 388 389// PathsForModuleSrcExcludes returns a Paths{} containing the resolved references in paths, minus 390// those listed in excludes. Elements of paths and excludes are resolved as: 391// * filepath, relative to local module directory, resolves as a filepath relative to the local 392// source directory 393// * glob, relative to the local module directory, resolves as filepath(s), relative to the local 394// source directory. Not valid in excludes. 395// * other modules using the ":name{.tag}" syntax. These modules must implement SourceFileProducer 396// or OutputFileProducer. These resolve as a filepath to an output filepath or generated source 397// filepath. 398// excluding the items (similarly resolved 399// Properties passed as the paths argument must have been annotated with struct tag 400// `android:"path"` so that dependencies on SourceFileProducer modules will have already been handled by the 401// path_deps mutator. 402// If a requested module is not found as a dependency: 403// * if ctx.Config().AllowMissingDependencies() is true, this module to be marked as having 404// missing dependencies 405// * otherwise, a ModuleError is thrown. 406func PathsForModuleSrcExcludes(ctx ModuleMissingDepsPathContext, paths, excludes []string) Paths { 407 ret, missingDeps := PathsAndMissingDepsForModuleSrcExcludes(ctx, paths, excludes) 408 if ctx.Config().AllowMissingDependencies() { 409 ctx.AddMissingDependencies(missingDeps) 410 } else { 411 for _, m := range missingDeps { 412 ctx.ModuleErrorf(`missing dependency on %q, is the property annotated with android:"path"?`, m) 413 } 414 } 415 return ret 416} 417 418// OutputPaths is a slice of OutputPath objects, with helpers to operate on the collection. 419type OutputPaths []OutputPath 420 421// Paths returns the OutputPaths as a Paths 422func (p OutputPaths) Paths() Paths { 423 if p == nil { 424 return nil 425 } 426 ret := make(Paths, len(p)) 427 for i, path := range p { 428 ret[i] = path 429 } 430 return ret 431} 432 433// Strings returns the string forms of the writable paths. 434func (p OutputPaths) Strings() []string { 435 if p == nil { 436 return nil 437 } 438 ret := make([]string, len(p)) 439 for i, path := range p { 440 ret[i] = path.String() 441 } 442 return ret 443} 444 445// Expands Paths to a SourceFileProducer or OutputFileProducer module dependency referenced via ":name" or ":name{.tag}" syntax. 446// If the dependency is not found, a missingErrorDependency is returned. 447// If the module dependency is not a SourceFileProducer or OutputFileProducer, appropriate errors will be returned. 448func getPathsFromModuleDep(ctx ModuleWithDepsPathContext, path, moduleName, tag string) (Paths, error) { 449 module := ctx.GetDirectDepWithTag(moduleName, sourceOrOutputDepTag(tag)) 450 if module == nil { 451 return nil, missingDependencyError{[]string{moduleName}} 452 } 453 if aModule, ok := module.(Module); ok && !aModule.Enabled() { 454 return nil, missingDependencyError{[]string{moduleName}} 455 } 456 if outProducer, ok := module.(OutputFileProducer); ok { 457 outputFiles, err := outProducer.OutputFiles(tag) 458 if err != nil { 459 return nil, fmt.Errorf("path dependency %q: %s", path, err) 460 } 461 return outputFiles, nil 462 } else if tag != "" { 463 return nil, fmt.Errorf("path dependency %q is not an output file producing module", path) 464 } else if goBinary, ok := module.(bootstrap.GoBinaryTool); ok { 465 if rel, err := filepath.Rel(PathForOutput(ctx).String(), goBinary.InstallPath()); err == nil { 466 return Paths{PathForOutput(ctx, rel).WithoutRel()}, nil 467 } else { 468 return nil, fmt.Errorf("cannot find output path for %q: %w", goBinary.InstallPath(), err) 469 } 470 } else if srcProducer, ok := module.(SourceFileProducer); ok { 471 return srcProducer.Srcs(), nil 472 } else { 473 return nil, fmt.Errorf("path dependency %q is not a source file producing module", path) 474 } 475} 476 477// PathsAndMissingDepsForModuleSrcExcludes returns a Paths{} containing the resolved references in 478// paths, minus those listed in excludes. Elements of paths and excludes are resolved as: 479// * filepath, relative to local module directory, resolves as a filepath relative to the local 480// source directory 481// * glob, relative to the local module directory, resolves as filepath(s), relative to the local 482// source directory. Not valid in excludes. 483// * other modules using the ":name{.tag}" syntax. These modules must implement SourceFileProducer 484// or OutputFileProducer. These resolve as a filepath to an output filepath or generated source 485// filepath. 486// and a list of the module names of missing module dependencies are returned as the second return. 487// Properties passed as the paths argument must have been annotated with struct tag 488// `android:"path"` so that dependencies on SourceFileProducer modules will have already been handled by the 489// path_deps mutator. 490func PathsAndMissingDepsForModuleSrcExcludes(ctx ModuleWithDepsPathContext, paths, excludes []string) (Paths, []string) { 491 prefix := pathForModuleSrc(ctx).String() 492 493 var expandedExcludes []string 494 if excludes != nil { 495 expandedExcludes = make([]string, 0, len(excludes)) 496 } 497 498 var missingExcludeDeps []string 499 500 for _, e := range excludes { 501 if m, t := SrcIsModuleWithTag(e); m != "" { 502 modulePaths, err := getPathsFromModuleDep(ctx, e, m, t) 503 if m, ok := err.(missingDependencyError); ok { 504 missingExcludeDeps = append(missingExcludeDeps, m.missingDeps...) 505 } else if err != nil { 506 reportPathError(ctx, err) 507 } else { 508 expandedExcludes = append(expandedExcludes, modulePaths.Strings()...) 509 } 510 } else { 511 expandedExcludes = append(expandedExcludes, filepath.Join(prefix, e)) 512 } 513 } 514 515 if paths == nil { 516 return nil, missingExcludeDeps 517 } 518 519 var missingDeps []string 520 521 expandedSrcFiles := make(Paths, 0, len(paths)) 522 for _, s := range paths { 523 srcFiles, err := expandOneSrcPath(ctx, s, expandedExcludes) 524 if depErr, ok := err.(missingDependencyError); ok { 525 missingDeps = append(missingDeps, depErr.missingDeps...) 526 } else if err != nil { 527 reportPathError(ctx, err) 528 } 529 expandedSrcFiles = append(expandedSrcFiles, srcFiles...) 530 } 531 532 return expandedSrcFiles, append(missingDeps, missingExcludeDeps...) 533} 534 535type missingDependencyError struct { 536 missingDeps []string 537} 538 539func (e missingDependencyError) Error() string { 540 return "missing dependencies: " + strings.Join(e.missingDeps, ", ") 541} 542 543// Expands one path string to Paths rooted from the module's local source 544// directory, excluding those listed in the expandedExcludes. 545// Expands globs, references to SourceFileProducer or OutputFileProducer modules using the ":name" and ":name{.tag}" syntax. 546func expandOneSrcPath(ctx ModuleWithDepsPathContext, sPath string, expandedExcludes []string) (Paths, error) { 547 excludePaths := func(paths Paths) Paths { 548 if len(expandedExcludes) == 0 { 549 return paths 550 } 551 remainder := make(Paths, 0, len(paths)) 552 for _, p := range paths { 553 if !InList(p.String(), expandedExcludes) { 554 remainder = append(remainder, p) 555 } 556 } 557 return remainder 558 } 559 if m, t := SrcIsModuleWithTag(sPath); m != "" { 560 modulePaths, err := getPathsFromModuleDep(ctx, sPath, m, t) 561 if err != nil { 562 return nil, err 563 } else { 564 return excludePaths(modulePaths), nil 565 } 566 } else if pathtools.IsGlob(sPath) { 567 paths := GlobFiles(ctx, pathForModuleSrc(ctx, sPath).String(), expandedExcludes) 568 return PathsWithModuleSrcSubDir(ctx, paths, ""), nil 569 } else { 570 p := pathForModuleSrc(ctx, sPath) 571 if exists, _, err := ctx.Config().fs.Exists(p.String()); err != nil { 572 ReportPathErrorf(ctx, "%s: %s", p, err.Error()) 573 } else if !exists && !ctx.Config().TestAllowNonExistentPaths { 574 ReportPathErrorf(ctx, "module source path %q does not exist", p) 575 } 576 577 if InList(p.String(), expandedExcludes) { 578 return nil, nil 579 } 580 return Paths{p}, nil 581 } 582} 583 584// pathsForModuleSrcFromFullPath returns Paths rooted from the module's local 585// source directory, but strip the local source directory from the beginning of 586// each string. If incDirs is false, strip paths with a trailing '/' from the list. 587// It intended for use in globs that only list files that exist, so it allows '$' in 588// filenames. 589func pathsForModuleSrcFromFullPath(ctx EarlyModulePathContext, paths []string, incDirs bool) Paths { 590 prefix := filepath.Join(ctx.Config().srcDir, ctx.ModuleDir()) + "/" 591 if prefix == "./" { 592 prefix = "" 593 } 594 ret := make(Paths, 0, len(paths)) 595 for _, p := range paths { 596 if !incDirs && strings.HasSuffix(p, "/") { 597 continue 598 } 599 path := filepath.Clean(p) 600 if !strings.HasPrefix(path, prefix) { 601 ReportPathErrorf(ctx, "Path %q is not in module source directory %q", p, prefix) 602 continue 603 } 604 605 srcPath, err := safePathForSource(ctx, ctx.ModuleDir(), path[len(prefix):]) 606 if err != nil { 607 reportPathError(ctx, err) 608 continue 609 } 610 611 srcPath.basePath.rel = srcPath.path 612 613 ret = append(ret, srcPath) 614 } 615 return ret 616} 617 618// PathsWithOptionalDefaultForModuleSrc returns Paths rooted from the module's local source 619// directory. If input is nil, use the default if it exists. If input is empty, returns nil. 620func PathsWithOptionalDefaultForModuleSrc(ctx ModuleMissingDepsPathContext, input []string, def string) Paths { 621 if input != nil { 622 return PathsForModuleSrc(ctx, input) 623 } 624 // Use Glob so that if the default doesn't exist, a dependency is added so that when it 625 // is created, we're run again. 626 path := filepath.Join(ctx.Config().srcDir, ctx.ModuleDir(), def) 627 return Glob(ctx, path, nil) 628} 629 630// Strings returns the Paths in string form 631func (p Paths) Strings() []string { 632 if p == nil { 633 return nil 634 } 635 ret := make([]string, len(p)) 636 for i, path := range p { 637 ret[i] = path.String() 638 } 639 return ret 640} 641 642func CopyOfPaths(paths Paths) Paths { 643 return append(Paths(nil), paths...) 644} 645 646// FirstUniquePaths returns all unique elements of a Paths, keeping the first copy of each. It 647// modifies the Paths slice contents in place, and returns a subslice of the original slice. 648func FirstUniquePaths(list Paths) Paths { 649 // 128 was chosen based on BenchmarkFirstUniquePaths results. 650 if len(list) > 128 { 651 return firstUniquePathsMap(list) 652 } 653 return firstUniquePathsList(list) 654} 655 656// SortedUniquePaths returns all unique elements of a Paths in sorted order. It modifies the 657// Paths slice contents in place, and returns a subslice of the original slice. 658func SortedUniquePaths(list Paths) Paths { 659 unique := FirstUniquePaths(list) 660 sort.Slice(unique, func(i, j int) bool { 661 return unique[i].String() < unique[j].String() 662 }) 663 return unique 664} 665 666func firstUniquePathsList(list Paths) Paths { 667 k := 0 668outer: 669 for i := 0; i < len(list); i++ { 670 for j := 0; j < k; j++ { 671 if list[i] == list[j] { 672 continue outer 673 } 674 } 675 list[k] = list[i] 676 k++ 677 } 678 return list[:k] 679} 680 681func firstUniquePathsMap(list Paths) Paths { 682 k := 0 683 seen := make(map[Path]bool, len(list)) 684 for i := 0; i < len(list); i++ { 685 if seen[list[i]] { 686 continue 687 } 688 seen[list[i]] = true 689 list[k] = list[i] 690 k++ 691 } 692 return list[:k] 693} 694 695// FirstUniqueInstallPaths returns all unique elements of an InstallPaths, keeping the first copy of each. It 696// modifies the InstallPaths slice contents in place, and returns a subslice of the original slice. 697func FirstUniqueInstallPaths(list InstallPaths) InstallPaths { 698 // 128 was chosen based on BenchmarkFirstUniquePaths results. 699 if len(list) > 128 { 700 return firstUniqueInstallPathsMap(list) 701 } 702 return firstUniqueInstallPathsList(list) 703} 704 705func firstUniqueInstallPathsList(list InstallPaths) InstallPaths { 706 k := 0 707outer: 708 for i := 0; i < len(list); i++ { 709 for j := 0; j < k; j++ { 710 if list[i] == list[j] { 711 continue outer 712 } 713 } 714 list[k] = list[i] 715 k++ 716 } 717 return list[:k] 718} 719 720func firstUniqueInstallPathsMap(list InstallPaths) InstallPaths { 721 k := 0 722 seen := make(map[InstallPath]bool, len(list)) 723 for i := 0; i < len(list); i++ { 724 if seen[list[i]] { 725 continue 726 } 727 seen[list[i]] = true 728 list[k] = list[i] 729 k++ 730 } 731 return list[:k] 732} 733 734// LastUniquePaths returns all unique elements of a Paths, keeping the last copy of each. It 735// modifies the Paths slice contents in place, and returns a subslice of the original slice. 736func LastUniquePaths(list Paths) Paths { 737 totalSkip := 0 738 for i := len(list) - 1; i >= totalSkip; i-- { 739 skip := 0 740 for j := i - 1; j >= totalSkip; j-- { 741 if list[i] == list[j] { 742 skip++ 743 } else { 744 list[j+skip] = list[j] 745 } 746 } 747 totalSkip += skip 748 } 749 return list[totalSkip:] 750} 751 752// ReversePaths returns a copy of a Paths in reverse order. 753func ReversePaths(list Paths) Paths { 754 if list == nil { 755 return nil 756 } 757 ret := make(Paths, len(list)) 758 for i := range list { 759 ret[i] = list[len(list)-1-i] 760 } 761 return ret 762} 763 764func indexPathList(s Path, list []Path) int { 765 for i, l := range list { 766 if l == s { 767 return i 768 } 769 } 770 771 return -1 772} 773 774func inPathList(p Path, list []Path) bool { 775 return indexPathList(p, list) != -1 776} 777 778func FilterPathList(list []Path, filter []Path) (remainder []Path, filtered []Path) { 779 return FilterPathListPredicate(list, func(p Path) bool { return inPathList(p, filter) }) 780} 781 782func FilterPathListPredicate(list []Path, predicate func(Path) bool) (remainder []Path, filtered []Path) { 783 for _, l := range list { 784 if predicate(l) { 785 filtered = append(filtered, l) 786 } else { 787 remainder = append(remainder, l) 788 } 789 } 790 791 return 792} 793 794// HasExt returns true of any of the paths have extension ext, otherwise false 795func (p Paths) HasExt(ext string) bool { 796 for _, path := range p { 797 if path.Ext() == ext { 798 return true 799 } 800 } 801 802 return false 803} 804 805// FilterByExt returns the subset of the paths that have extension ext 806func (p Paths) FilterByExt(ext string) Paths { 807 ret := make(Paths, 0, len(p)) 808 for _, path := range p { 809 if path.Ext() == ext { 810 ret = append(ret, path) 811 } 812 } 813 return ret 814} 815 816// FilterOutByExt returns the subset of the paths that do not have extension ext 817func (p Paths) FilterOutByExt(ext string) Paths { 818 ret := make(Paths, 0, len(p)) 819 for _, path := range p { 820 if path.Ext() != ext { 821 ret = append(ret, path) 822 } 823 } 824 return ret 825} 826 827// DirectorySortedPaths is a slice of paths that are sorted such that all files in a directory 828// (including subdirectories) are in a contiguous subslice of the list, and can be found in 829// O(log(N)) time using a binary search on the directory prefix. 830type DirectorySortedPaths Paths 831 832func PathsToDirectorySortedPaths(paths Paths) DirectorySortedPaths { 833 ret := append(DirectorySortedPaths(nil), paths...) 834 sort.Slice(ret, func(i, j int) bool { 835 return ret[i].String() < ret[j].String() 836 }) 837 return ret 838} 839 840// PathsInDirectory returns a subslice of the DirectorySortedPaths as a Paths that contains all entries 841// that are in the specified directory and its subdirectories. 842func (p DirectorySortedPaths) PathsInDirectory(dir string) Paths { 843 prefix := filepath.Clean(dir) + "/" 844 start := sort.Search(len(p), func(i int) bool { 845 return prefix < p[i].String() 846 }) 847 848 ret := p[start:] 849 850 end := sort.Search(len(ret), func(i int) bool { 851 return !strings.HasPrefix(ret[i].String(), prefix) 852 }) 853 854 ret = ret[:end] 855 856 return Paths(ret) 857} 858 859// WritablePaths is a slice of WritablePath, used for multiple outputs. 860type WritablePaths []WritablePath 861 862// RelativeToTop creates a new WritablePaths containing the result of calling Path.RelativeToTop on 863// each item in this slice. 864func (p WritablePaths) RelativeToTop() WritablePaths { 865 ensureTestOnly() 866 if p == nil { 867 return p 868 } 869 ret := make(WritablePaths, len(p)) 870 for i, path := range p { 871 ret[i] = path.RelativeToTop().(WritablePath) 872 } 873 return ret 874} 875 876// Strings returns the string forms of the writable paths. 877func (p WritablePaths) Strings() []string { 878 if p == nil { 879 return nil 880 } 881 ret := make([]string, len(p)) 882 for i, path := range p { 883 ret[i] = path.String() 884 } 885 return ret 886} 887 888// Paths returns the WritablePaths as a Paths 889func (p WritablePaths) Paths() Paths { 890 if p == nil { 891 return nil 892 } 893 ret := make(Paths, len(p)) 894 for i, path := range p { 895 ret[i] = path 896 } 897 return ret 898} 899 900type basePath struct { 901 path string 902 rel string 903} 904 905func (p basePath) Ext() string { 906 return filepath.Ext(p.path) 907} 908 909func (p basePath) Base() string { 910 return filepath.Base(p.path) 911} 912 913func (p basePath) Rel() string { 914 if p.rel != "" { 915 return p.rel 916 } 917 return p.path 918} 919 920func (p basePath) String() string { 921 return p.path 922} 923 924func (p basePath) withRel(rel string) basePath { 925 p.path = filepath.Join(p.path, rel) 926 p.rel = rel 927 return p 928} 929 930// SourcePath is a Path representing a file path rooted from SrcDir 931type SourcePath struct { 932 basePath 933 934 // The sources root, i.e. Config.SrcDir() 935 srcDir string 936} 937 938func (p SourcePath) RelativeToTop() Path { 939 ensureTestOnly() 940 return p 941} 942 943var _ Path = SourcePath{} 944 945func (p SourcePath) withRel(rel string) SourcePath { 946 p.basePath = p.basePath.withRel(rel) 947 return p 948} 949 950// safePathForSource is for paths that we expect are safe -- only for use by go 951// code that is embedding ninja variables in paths 952func safePathForSource(ctx PathContext, pathComponents ...string) (SourcePath, error) { 953 p, err := validateSafePath(pathComponents...) 954 ret := SourcePath{basePath{p, ""}, ctx.Config().srcDir} 955 if err != nil { 956 return ret, err 957 } 958 959 // absolute path already checked by validateSafePath 960 if strings.HasPrefix(ret.String(), ctx.Config().buildDir) { 961 return ret, fmt.Errorf("source path %q is in output", ret.String()) 962 } 963 964 return ret, err 965} 966 967// pathForSource creates a SourcePath from pathComponents, but does not check that it exists. 968func pathForSource(ctx PathContext, pathComponents ...string) (SourcePath, error) { 969 p, err := validatePath(pathComponents...) 970 ret := SourcePath{basePath{p, ""}, ctx.Config().srcDir} 971 if err != nil { 972 return ret, err 973 } 974 975 // absolute path already checked by validatePath 976 if strings.HasPrefix(ret.String(), ctx.Config().buildDir) { 977 return ret, fmt.Errorf("source path %q is in output", ret.String()) 978 } 979 980 return ret, nil 981} 982 983// existsWithDependencies returns true if the path exists, and adds appropriate dependencies to rerun if the 984// path does not exist. 985func existsWithDependencies(ctx PathContext, path SourcePath) (exists bool, err error) { 986 var files []string 987 988 if gctx, ok := ctx.(PathGlobContext); ok { 989 // Use glob to produce proper dependencies, even though we only want 990 // a single file. 991 files, err = gctx.GlobWithDeps(path.String(), nil) 992 } else { 993 var result pathtools.GlobResult 994 // We cannot add build statements in this context, so we fall back to 995 // AddNinjaFileDeps 996 result, err = ctx.Config().fs.Glob(path.String(), nil, pathtools.FollowSymlinks) 997 ctx.AddNinjaFileDeps(result.Deps...) 998 files = result.Matches 999 } 1000 1001 if err != nil { 1002 return false, fmt.Errorf("glob: %s", err.Error()) 1003 } 1004 1005 return len(files) > 0, nil 1006} 1007 1008// PathForSource joins the provided path components and validates that the result 1009// neither escapes the source dir nor is in the out dir. 1010// On error, it will return a usable, but invalid SourcePath, and report a ModuleError. 1011func PathForSource(ctx PathContext, pathComponents ...string) SourcePath { 1012 path, err := pathForSource(ctx, pathComponents...) 1013 if err != nil { 1014 reportPathError(ctx, err) 1015 } 1016 1017 if pathtools.IsGlob(path.String()) { 1018 ReportPathErrorf(ctx, "path may not contain a glob: %s", path.String()) 1019 } 1020 1021 if modCtx, ok := ctx.(ModuleMissingDepsPathContext); ok && ctx.Config().AllowMissingDependencies() { 1022 exists, err := existsWithDependencies(ctx, path) 1023 if err != nil { 1024 reportPathError(ctx, err) 1025 } 1026 if !exists { 1027 modCtx.AddMissingDependencies([]string{path.String()}) 1028 } 1029 } else if exists, _, err := ctx.Config().fs.Exists(path.String()); err != nil { 1030 ReportPathErrorf(ctx, "%s: %s", path, err.Error()) 1031 } else if !exists && !ctx.Config().TestAllowNonExistentPaths { 1032 ReportPathErrorf(ctx, "source path %q does not exist", path) 1033 } 1034 return path 1035} 1036 1037// ExistentPathForSource returns an OptionalPath with the SourcePath, rooted from SrcDir, *not* 1038// rooted from the module's local source directory, if the path exists, or an empty OptionalPath if 1039// it doesn't exist. Dependencies are added so that the ninja file will be regenerated if the state 1040// of the path changes. 1041func ExistentPathForSource(ctx PathContext, pathComponents ...string) OptionalPath { 1042 path, err := pathForSource(ctx, pathComponents...) 1043 if err != nil { 1044 reportPathError(ctx, err) 1045 return OptionalPath{} 1046 } 1047 1048 if pathtools.IsGlob(path.String()) { 1049 ReportPathErrorf(ctx, "path may not contain a glob: %s", path.String()) 1050 return OptionalPath{} 1051 } 1052 1053 exists, err := existsWithDependencies(ctx, path) 1054 if err != nil { 1055 reportPathError(ctx, err) 1056 return OptionalPath{} 1057 } 1058 if !exists { 1059 return OptionalPath{} 1060 } 1061 return OptionalPathForPath(path) 1062} 1063 1064func (p SourcePath) String() string { 1065 return filepath.Join(p.srcDir, p.path) 1066} 1067 1068// Join creates a new SourcePath with paths... joined with the current path. The 1069// provided paths... may not use '..' to escape from the current path. 1070func (p SourcePath) Join(ctx PathContext, paths ...string) SourcePath { 1071 path, err := validatePath(paths...) 1072 if err != nil { 1073 reportPathError(ctx, err) 1074 } 1075 return p.withRel(path) 1076} 1077 1078// join is like Join but does less path validation. 1079func (p SourcePath) join(ctx PathContext, paths ...string) SourcePath { 1080 path, err := validateSafePath(paths...) 1081 if err != nil { 1082 reportPathError(ctx, err) 1083 } 1084 return p.withRel(path) 1085} 1086 1087// OverlayPath returns the overlay for `path' if it exists. This assumes that the 1088// SourcePath is the path to a resource overlay directory. 1089func (p SourcePath) OverlayPath(ctx ModuleMissingDepsPathContext, path Path) OptionalPath { 1090 var relDir string 1091 if srcPath, ok := path.(SourcePath); ok { 1092 relDir = srcPath.path 1093 } else { 1094 ReportPathErrorf(ctx, "Cannot find relative path for %s(%s)", reflect.TypeOf(path).Name(), path) 1095 return OptionalPath{} 1096 } 1097 dir := filepath.Join(p.srcDir, p.path, relDir) 1098 // Use Glob so that we are run again if the directory is added. 1099 if pathtools.IsGlob(dir) { 1100 ReportPathErrorf(ctx, "Path may not contain a glob: %s", dir) 1101 } 1102 paths, err := ctx.GlobWithDeps(dir, nil) 1103 if err != nil { 1104 ReportPathErrorf(ctx, "glob: %s", err.Error()) 1105 return OptionalPath{} 1106 } 1107 if len(paths) == 0 { 1108 return OptionalPath{} 1109 } 1110 relPath := Rel(ctx, p.srcDir, paths[0]) 1111 return OptionalPathForPath(PathForSource(ctx, relPath)) 1112} 1113 1114// OutputPath is a Path representing an intermediates file path rooted from the build directory 1115type OutputPath struct { 1116 basePath 1117 1118 // The soong build directory, i.e. Config.BuildDir() 1119 buildDir string 1120 1121 fullPath string 1122} 1123 1124func (p OutputPath) withRel(rel string) OutputPath { 1125 p.basePath = p.basePath.withRel(rel) 1126 p.fullPath = filepath.Join(p.fullPath, rel) 1127 return p 1128} 1129 1130func (p OutputPath) WithoutRel() OutputPath { 1131 p.basePath.rel = filepath.Base(p.basePath.path) 1132 return p 1133} 1134 1135func (p OutputPath) getBuildDir() string { 1136 return p.buildDir 1137} 1138 1139func (p OutputPath) RelativeToTop() Path { 1140 return p.outputPathRelativeToTop() 1141} 1142 1143func (p OutputPath) outputPathRelativeToTop() OutputPath { 1144 p.fullPath = StringPathRelativeToTop(p.buildDir, p.fullPath) 1145 p.buildDir = OutSoongDir 1146 return p 1147} 1148 1149func (p OutputPath) objPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleObjPath { 1150 return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext)) 1151} 1152 1153var _ Path = OutputPath{} 1154var _ WritablePath = OutputPath{} 1155var _ objPathProvider = OutputPath{} 1156 1157// toolDepPath is a Path representing a dependency of the build tool. 1158type toolDepPath struct { 1159 basePath 1160} 1161 1162func (t toolDepPath) RelativeToTop() Path { 1163 ensureTestOnly() 1164 return t 1165} 1166 1167var _ Path = toolDepPath{} 1168 1169// pathForBuildToolDep returns a toolDepPath representing the given path string. 1170// There is no validation for the path, as it is "trusted": It may fail 1171// normal validation checks. For example, it may be an absolute path. 1172// Only use this function to construct paths for dependencies of the build 1173// tool invocation. 1174func pathForBuildToolDep(ctx PathContext, path string) toolDepPath { 1175 return toolDepPath{basePath{path, ""}} 1176} 1177 1178// PathForOutput joins the provided paths and returns an OutputPath that is 1179// validated to not escape the build dir. 1180// On error, it will return a usable, but invalid OutputPath, and report a ModuleError. 1181func PathForOutput(ctx PathContext, pathComponents ...string) OutputPath { 1182 path, err := validatePath(pathComponents...) 1183 if err != nil { 1184 reportPathError(ctx, err) 1185 } 1186 fullPath := filepath.Join(ctx.Config().buildDir, path) 1187 path = fullPath[len(fullPath)-len(path):] 1188 return OutputPath{basePath{path, ""}, ctx.Config().buildDir, fullPath} 1189} 1190 1191// PathsForOutput returns Paths rooted from buildDir 1192func PathsForOutput(ctx PathContext, paths []string) WritablePaths { 1193 ret := make(WritablePaths, len(paths)) 1194 for i, path := range paths { 1195 ret[i] = PathForOutput(ctx, path) 1196 } 1197 return ret 1198} 1199 1200func (p OutputPath) writablePath() {} 1201 1202func (p OutputPath) String() string { 1203 return p.fullPath 1204} 1205 1206// Join creates a new OutputPath with paths... joined with the current path. The 1207// provided paths... may not use '..' to escape from the current path. 1208func (p OutputPath) Join(ctx PathContext, paths ...string) OutputPath { 1209 path, err := validatePath(paths...) 1210 if err != nil { 1211 reportPathError(ctx, err) 1212 } 1213 return p.withRel(path) 1214} 1215 1216// ReplaceExtension creates a new OutputPath with the extension replaced with ext. 1217func (p OutputPath) ReplaceExtension(ctx PathContext, ext string) OutputPath { 1218 if strings.Contains(ext, "/") { 1219 ReportPathErrorf(ctx, "extension %q cannot contain /", ext) 1220 } 1221 ret := PathForOutput(ctx, pathtools.ReplaceExtension(p.path, ext)) 1222 ret.rel = pathtools.ReplaceExtension(p.rel, ext) 1223 return ret 1224} 1225 1226// InSameDir creates a new OutputPath from the directory of the current OutputPath joined with the elements in paths. 1227func (p OutputPath) InSameDir(ctx PathContext, paths ...string) OutputPath { 1228 path, err := validatePath(paths...) 1229 if err != nil { 1230 reportPathError(ctx, err) 1231 } 1232 1233 ret := PathForOutput(ctx, filepath.Dir(p.path), path) 1234 ret.rel = filepath.Join(filepath.Dir(p.rel), path) 1235 return ret 1236} 1237 1238// PathForIntermediates returns an OutputPath representing the top-level 1239// intermediates directory. 1240func PathForIntermediates(ctx PathContext, paths ...string) OutputPath { 1241 path, err := validatePath(paths...) 1242 if err != nil { 1243 reportPathError(ctx, err) 1244 } 1245 return PathForOutput(ctx, ".intermediates", path) 1246} 1247 1248var _ genPathProvider = SourcePath{} 1249var _ objPathProvider = SourcePath{} 1250var _ resPathProvider = SourcePath{} 1251 1252// PathForModuleSrc returns a Path representing the paths... under the 1253// module's local source directory. 1254func PathForModuleSrc(ctx ModuleMissingDepsPathContext, pathComponents ...string) Path { 1255 p, err := validatePath(pathComponents...) 1256 if err != nil { 1257 reportPathError(ctx, err) 1258 } 1259 paths, err := expandOneSrcPath(ctx, p, nil) 1260 if err != nil { 1261 if depErr, ok := err.(missingDependencyError); ok { 1262 if ctx.Config().AllowMissingDependencies() { 1263 ctx.AddMissingDependencies(depErr.missingDeps) 1264 } else { 1265 ctx.ModuleErrorf(`%s, is the property annotated with android:"path"?`, depErr.Error()) 1266 } 1267 } else { 1268 reportPathError(ctx, err) 1269 } 1270 return nil 1271 } else if len(paths) == 0 { 1272 ReportPathErrorf(ctx, "%q produced no files, expected exactly one", p) 1273 return nil 1274 } else if len(paths) > 1 { 1275 ReportPathErrorf(ctx, "%q produced %d files, expected exactly one", p, len(paths)) 1276 } 1277 return paths[0] 1278} 1279 1280func pathForModuleSrc(ctx EarlyModulePathContext, paths ...string) SourcePath { 1281 p, err := validatePath(paths...) 1282 if err != nil { 1283 reportPathError(ctx, err) 1284 } 1285 1286 path, err := pathForSource(ctx, ctx.ModuleDir(), p) 1287 if err != nil { 1288 reportPathError(ctx, err) 1289 } 1290 1291 path.basePath.rel = p 1292 1293 return path 1294} 1295 1296// PathsWithModuleSrcSubDir takes a list of Paths and returns a new list of Paths where Rel() on each path 1297// will return the path relative to subDir in the module's source directory. If any input paths are not located 1298// inside subDir then a path error will be reported. 1299func PathsWithModuleSrcSubDir(ctx EarlyModulePathContext, paths Paths, subDir string) Paths { 1300 paths = append(Paths(nil), paths...) 1301 subDirFullPath := pathForModuleSrc(ctx, subDir) 1302 for i, path := range paths { 1303 rel := Rel(ctx, subDirFullPath.String(), path.String()) 1304 paths[i] = subDirFullPath.join(ctx, rel) 1305 } 1306 return paths 1307} 1308 1309// PathWithModuleSrcSubDir takes a Path and returns a Path where Rel() will return the path relative to subDir in the 1310// module's source directory. If the input path is not located inside subDir then a path error will be reported. 1311func PathWithModuleSrcSubDir(ctx EarlyModulePathContext, path Path, subDir string) Path { 1312 subDirFullPath := pathForModuleSrc(ctx, subDir) 1313 rel := Rel(ctx, subDirFullPath.String(), path.String()) 1314 return subDirFullPath.Join(ctx, rel) 1315} 1316 1317// OptionalPathForModuleSrc returns an OptionalPath. The OptionalPath contains a 1318// valid path if p is non-nil. 1319func OptionalPathForModuleSrc(ctx ModuleMissingDepsPathContext, p *string) OptionalPath { 1320 if p == nil { 1321 return OptionalPath{} 1322 } 1323 return OptionalPathForPath(PathForModuleSrc(ctx, *p)) 1324} 1325 1326func (p SourcePath) genPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleGenPath { 1327 return PathForModuleGen(ctx, subdir, pathtools.ReplaceExtension(p.path, ext)) 1328} 1329 1330func (p SourcePath) objPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleObjPath { 1331 return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext)) 1332} 1333 1334func (p SourcePath) resPathWithName(ctx ModuleOutPathContext, name string) ModuleResPath { 1335 // TODO: Use full directory if the new ctx is not the current ctx? 1336 return PathForModuleRes(ctx, p.path, name) 1337} 1338 1339// ModuleOutPath is a Path representing a module's output directory. 1340type ModuleOutPath struct { 1341 OutputPath 1342} 1343 1344func (p ModuleOutPath) RelativeToTop() Path { 1345 p.OutputPath = p.outputPathRelativeToTop() 1346 return p 1347} 1348 1349var _ Path = ModuleOutPath{} 1350var _ WritablePath = ModuleOutPath{} 1351 1352func (p ModuleOutPath) objPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleObjPath { 1353 return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext)) 1354} 1355 1356// ModuleOutPathContext Subset of ModuleContext functions necessary for output path methods. 1357type ModuleOutPathContext interface { 1358 PathContext 1359 1360 ModuleName() string 1361 ModuleDir() string 1362 ModuleSubDir() string 1363} 1364 1365func pathForModuleOut(ctx ModuleOutPathContext) OutputPath { 1366 return PathForOutput(ctx, ".intermediates", ctx.ModuleDir(), ctx.ModuleName(), ctx.ModuleSubDir()) 1367} 1368 1369// PathForVndkRefAbiDump returns an OptionalPath representing the path of the 1370// reference abi dump for the given module. This is not guaranteed to be valid. 1371func PathForVndkRefAbiDump(ctx ModuleInstallPathContext, version, fileName string, 1372 isNdk, isLlndkOrVndk, isGzip bool) OptionalPath { 1373 1374 arches := ctx.DeviceConfig().Arches() 1375 if len(arches) == 0 { 1376 panic("device build with no primary arch") 1377 } 1378 currentArch := ctx.Arch() 1379 archNameAndVariant := currentArch.ArchType.String() 1380 if currentArch.ArchVariant != "" { 1381 archNameAndVariant += "_" + currentArch.ArchVariant 1382 } 1383 1384 var dirName string 1385 if isNdk { 1386 dirName = "ndk" 1387 } else if isLlndkOrVndk { 1388 dirName = "vndk" 1389 } else { 1390 dirName = "platform" // opt-in libs 1391 } 1392 1393 binderBitness := ctx.DeviceConfig().BinderBitness() 1394 1395 var ext string 1396 if isGzip { 1397 ext = ".lsdump.gz" 1398 } else { 1399 ext = ".lsdump" 1400 } 1401 1402 return ExistentPathForSource(ctx, "prebuilts", "abi-dumps", dirName, 1403 version, binderBitness, archNameAndVariant, "source-based", 1404 fileName+ext) 1405} 1406 1407// PathForModuleOut returns a Path representing the paths... under the module's 1408// output directory. 1409func PathForModuleOut(ctx ModuleOutPathContext, paths ...string) ModuleOutPath { 1410 p, err := validatePath(paths...) 1411 if err != nil { 1412 reportPathError(ctx, err) 1413 } 1414 return ModuleOutPath{ 1415 OutputPath: pathForModuleOut(ctx).withRel(p), 1416 } 1417} 1418 1419// ModuleGenPath is a Path representing the 'gen' directory in a module's output 1420// directory. Mainly used for generated sources. 1421type ModuleGenPath struct { 1422 ModuleOutPath 1423} 1424 1425func (p ModuleGenPath) RelativeToTop() Path { 1426 p.OutputPath = p.outputPathRelativeToTop() 1427 return p 1428} 1429 1430var _ Path = ModuleGenPath{} 1431var _ WritablePath = ModuleGenPath{} 1432var _ genPathProvider = ModuleGenPath{} 1433var _ objPathProvider = ModuleGenPath{} 1434 1435// PathForModuleGen returns a Path representing the paths... under the module's 1436// `gen' directory. 1437func PathForModuleGen(ctx ModuleOutPathContext, paths ...string) ModuleGenPath { 1438 p, err := validatePath(paths...) 1439 if err != nil { 1440 reportPathError(ctx, err) 1441 } 1442 return ModuleGenPath{ 1443 ModuleOutPath: ModuleOutPath{ 1444 OutputPath: pathForModuleOut(ctx).withRel("gen").withRel(p), 1445 }, 1446 } 1447} 1448 1449func (p ModuleGenPath) genPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleGenPath { 1450 // TODO: make a different path for local vs remote generated files? 1451 return PathForModuleGen(ctx, subdir, pathtools.ReplaceExtension(p.path, ext)) 1452} 1453 1454func (p ModuleGenPath) objPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleObjPath { 1455 return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext)) 1456} 1457 1458// ModuleObjPath is a Path representing the 'obj' directory in a module's output 1459// directory. Used for compiled objects. 1460type ModuleObjPath struct { 1461 ModuleOutPath 1462} 1463 1464func (p ModuleObjPath) RelativeToTop() Path { 1465 p.OutputPath = p.outputPathRelativeToTop() 1466 return p 1467} 1468 1469var _ Path = ModuleObjPath{} 1470var _ WritablePath = ModuleObjPath{} 1471 1472// PathForModuleObj returns a Path representing the paths... under the module's 1473// 'obj' directory. 1474func PathForModuleObj(ctx ModuleOutPathContext, pathComponents ...string) ModuleObjPath { 1475 p, err := validatePath(pathComponents...) 1476 if err != nil { 1477 reportPathError(ctx, err) 1478 } 1479 return ModuleObjPath{PathForModuleOut(ctx, "obj", p)} 1480} 1481 1482// ModuleResPath is a a Path representing the 'res' directory in a module's 1483// output directory. 1484type ModuleResPath struct { 1485 ModuleOutPath 1486} 1487 1488func (p ModuleResPath) RelativeToTop() Path { 1489 p.OutputPath = p.outputPathRelativeToTop() 1490 return p 1491} 1492 1493var _ Path = ModuleResPath{} 1494var _ WritablePath = ModuleResPath{} 1495 1496// PathForModuleRes returns a Path representing the paths... under the module's 1497// 'res' directory. 1498func PathForModuleRes(ctx ModuleOutPathContext, pathComponents ...string) ModuleResPath { 1499 p, err := validatePath(pathComponents...) 1500 if err != nil { 1501 reportPathError(ctx, err) 1502 } 1503 1504 return ModuleResPath{PathForModuleOut(ctx, "res", p)} 1505} 1506 1507// InstallPath is a Path representing a installed file path rooted from the build directory 1508type InstallPath struct { 1509 basePath 1510 1511 // The soong build directory, i.e. Config.BuildDir() 1512 buildDir string 1513 1514 // partitionDir is the part of the InstallPath that is automatically determined according to the context. 1515 // For example, it is host/<os>-<arch> for host modules, and target/product/<device>/<partition> for device modules. 1516 partitionDir string 1517 1518 // makePath indicates whether this path is for Soong (false) or Make (true). 1519 makePath bool 1520} 1521 1522// Will panic if called from outside a test environment. 1523func ensureTestOnly() { 1524 if PrefixInList(os.Args, "-test.") { 1525 return 1526 } 1527 panic(fmt.Errorf("Not in test. Command line:\n %s", strings.Join(os.Args, "\n "))) 1528} 1529 1530func (p InstallPath) RelativeToTop() Path { 1531 ensureTestOnly() 1532 p.buildDir = OutSoongDir 1533 return p 1534} 1535 1536func (p InstallPath) getBuildDir() string { 1537 return p.buildDir 1538} 1539 1540func (p InstallPath) ReplaceExtension(ctx PathContext, ext string) OutputPath { 1541 panic("Not implemented") 1542} 1543 1544var _ Path = InstallPath{} 1545var _ WritablePath = InstallPath{} 1546 1547func (p InstallPath) writablePath() {} 1548 1549func (p InstallPath) String() string { 1550 if p.makePath { 1551 // Make path starts with out/ instead of out/soong. 1552 return filepath.Join(p.buildDir, "../", p.path) 1553 } else { 1554 return filepath.Join(p.buildDir, p.path) 1555 } 1556} 1557 1558// PartitionDir returns the path to the partition where the install path is rooted at. It is 1559// out/soong/target/product/<device>/<partition> for device modules, and out/soong/host/<os>-<arch> for host modules. 1560// The ./soong is dropped if the install path is for Make. 1561func (p InstallPath) PartitionDir() string { 1562 if p.makePath { 1563 return filepath.Join(p.buildDir, "../", p.partitionDir) 1564 } else { 1565 return filepath.Join(p.buildDir, p.partitionDir) 1566 } 1567} 1568 1569// Join creates a new InstallPath with paths... joined with the current path. The 1570// provided paths... may not use '..' to escape from the current path. 1571func (p InstallPath) Join(ctx PathContext, paths ...string) InstallPath { 1572 path, err := validatePath(paths...) 1573 if err != nil { 1574 reportPathError(ctx, err) 1575 } 1576 return p.withRel(path) 1577} 1578 1579func (p InstallPath) withRel(rel string) InstallPath { 1580 p.basePath = p.basePath.withRel(rel) 1581 return p 1582} 1583 1584// ToMakePath returns a new InstallPath that points to Make's install directory instead of Soong's, 1585// i.e. out/ instead of out/soong/. 1586func (p InstallPath) ToMakePath() InstallPath { 1587 p.makePath = true 1588 return p 1589} 1590 1591// PathForModuleInstall returns a Path representing the install path for the 1592// module appended with paths... 1593func PathForModuleInstall(ctx ModuleInstallPathContext, pathComponents ...string) InstallPath { 1594 os := ctx.Os() 1595 arch := ctx.Arch().ArchType 1596 forceOS, forceArch := ctx.InstallForceOS() 1597 if forceOS != nil { 1598 os = *forceOS 1599 } 1600 if forceArch != nil { 1601 arch = *forceArch 1602 } 1603 partition := modulePartition(ctx, os) 1604 1605 ret := pathForInstall(ctx, os, arch, partition, ctx.Debug(), pathComponents...) 1606 1607 if ctx.InstallBypassMake() && ctx.Config().KatiEnabled() { 1608 ret = ret.ToMakePath() 1609 } 1610 1611 return ret 1612} 1613 1614func pathForInstall(ctx PathContext, os OsType, arch ArchType, partition string, debug bool, 1615 pathComponents ...string) InstallPath { 1616 1617 var partionPaths []string 1618 1619 if os.Class == Device { 1620 partionPaths = []string{"target", "product", ctx.Config().DeviceName(), partition} 1621 } else { 1622 osName := os.String() 1623 if os == Linux { 1624 // instead of linux_glibc 1625 osName = "linux" 1626 } 1627 // SOONG_HOST_OUT is set to out/host/$(HOST_OS)-$(HOST_PREBUILT_ARCH) 1628 // and HOST_PREBUILT_ARCH is forcibly set to x86 even on x86_64 hosts. We don't seem 1629 // to have a plan to fix it (see the comment in build/make/core/envsetup.mk). 1630 // Let's keep using x86 for the existing cases until we have a need to support 1631 // other architectures. 1632 archName := arch.String() 1633 if os.Class == Host && (arch == X86_64 || arch == Common) { 1634 archName = "x86" 1635 } 1636 partionPaths = []string{"host", osName + "-" + archName, partition} 1637 } 1638 if debug { 1639 partionPaths = append([]string{"debug"}, partionPaths...) 1640 } 1641 1642 partionPath, err := validatePath(partionPaths...) 1643 if err != nil { 1644 reportPathError(ctx, err) 1645 } 1646 1647 base := InstallPath{ 1648 basePath: basePath{partionPath, ""}, 1649 buildDir: ctx.Config().buildDir, 1650 partitionDir: partionPath, 1651 makePath: false, 1652 } 1653 1654 return base.Join(ctx, pathComponents...) 1655} 1656 1657func pathForNdkOrSdkInstall(ctx PathContext, prefix string, paths []string) InstallPath { 1658 base := InstallPath{ 1659 basePath: basePath{prefix, ""}, 1660 buildDir: ctx.Config().buildDir, 1661 partitionDir: prefix, 1662 makePath: false, 1663 } 1664 return base.Join(ctx, paths...) 1665} 1666 1667func PathForNdkInstall(ctx PathContext, paths ...string) InstallPath { 1668 return pathForNdkOrSdkInstall(ctx, "ndk", paths) 1669} 1670 1671func PathForMainlineSdksInstall(ctx PathContext, paths ...string) InstallPath { 1672 return pathForNdkOrSdkInstall(ctx, "mainline-sdks", paths) 1673} 1674 1675func InstallPathToOnDevicePath(ctx PathContext, path InstallPath) string { 1676 rel := Rel(ctx, PathForOutput(ctx, "target", "product", ctx.Config().DeviceName()).String(), path.String()) 1677 1678 return "/" + rel 1679} 1680 1681func modulePartition(ctx ModuleInstallPathContext, os OsType) string { 1682 var partition string 1683 if ctx.InstallInTestcases() { 1684 // "testcases" install directory can be used for host or device modules. 1685 partition = "testcases" 1686 } else if os.Class == Device { 1687 if ctx.InstallInData() { 1688 partition = "data" 1689 } else if ctx.InstallInRamdisk() { 1690 if ctx.DeviceConfig().BoardUsesRecoveryAsBoot() { 1691 partition = "recovery/root/first_stage_ramdisk" 1692 } else { 1693 partition = "ramdisk" 1694 } 1695 if !ctx.InstallInRoot() { 1696 partition += "/system" 1697 } 1698 } else if ctx.InstallInVendorRamdisk() { 1699 // The module is only available after switching root into 1700 // /first_stage_ramdisk. To expose the module before switching root 1701 // on a device without a dedicated recovery partition, install the 1702 // recovery variant. 1703 if ctx.DeviceConfig().BoardMoveRecoveryResourcesToVendorBoot() { 1704 partition = "vendor_ramdisk/first_stage_ramdisk" 1705 } else { 1706 partition = "vendor_ramdisk" 1707 } 1708 if !ctx.InstallInRoot() { 1709 partition += "/system" 1710 } 1711 } else if ctx.InstallInDebugRamdisk() { 1712 partition = "debug_ramdisk" 1713 } else if ctx.InstallInRecovery() { 1714 if ctx.InstallInRoot() { 1715 partition = "recovery/root" 1716 } else { 1717 // the layout of recovery partion is the same as that of system partition 1718 partition = "recovery/root/system" 1719 } 1720 } else if ctx.SocSpecific() { 1721 partition = ctx.DeviceConfig().VendorPath() 1722 } else if ctx.DeviceSpecific() { 1723 partition = ctx.DeviceConfig().OdmPath() 1724 } else if ctx.ProductSpecific() { 1725 partition = ctx.DeviceConfig().ProductPath() 1726 } else if ctx.SystemExtSpecific() { 1727 partition = ctx.DeviceConfig().SystemExtPath() 1728 } else if ctx.InstallInRoot() { 1729 partition = "root" 1730 } else { 1731 partition = "system" 1732 } 1733 if ctx.InstallInSanitizerDir() { 1734 partition = "data/asan/" + partition 1735 } 1736 } 1737 return partition 1738} 1739 1740type InstallPaths []InstallPath 1741 1742// Paths returns the InstallPaths as a Paths 1743func (p InstallPaths) Paths() Paths { 1744 if p == nil { 1745 return nil 1746 } 1747 ret := make(Paths, len(p)) 1748 for i, path := range p { 1749 ret[i] = path 1750 } 1751 return ret 1752} 1753 1754// Strings returns the string forms of the install paths. 1755func (p InstallPaths) Strings() []string { 1756 if p == nil { 1757 return nil 1758 } 1759 ret := make([]string, len(p)) 1760 for i, path := range p { 1761 ret[i] = path.String() 1762 } 1763 return ret 1764} 1765 1766// validateSafePath validates a path that we trust (may contain ninja variables). 1767// Ensures that each path component does not attempt to leave its component. 1768func validateSafePath(pathComponents ...string) (string, error) { 1769 for _, path := range pathComponents { 1770 path := filepath.Clean(path) 1771 if path == ".." || strings.HasPrefix(path, "../") || strings.HasPrefix(path, "/") { 1772 return "", fmt.Errorf("Path is outside directory: %s", path) 1773 } 1774 } 1775 // TODO: filepath.Join isn't necessarily correct with embedded ninja 1776 // variables. '..' may remove the entire ninja variable, even if it 1777 // will be expanded to multiple nested directories. 1778 return filepath.Join(pathComponents...), nil 1779} 1780 1781// validatePath validates that a path does not include ninja variables, and that 1782// each path component does not attempt to leave its component. Returns a joined 1783// version of each path component. 1784func validatePath(pathComponents ...string) (string, error) { 1785 for _, path := range pathComponents { 1786 if strings.Contains(path, "$") { 1787 return "", fmt.Errorf("Path contains invalid character($): %s", path) 1788 } 1789 } 1790 return validateSafePath(pathComponents...) 1791} 1792 1793func PathForPhony(ctx PathContext, phony string) WritablePath { 1794 if strings.ContainsAny(phony, "$/") { 1795 ReportPathErrorf(ctx, "Phony target contains invalid character ($ or /): %s", phony) 1796 } 1797 return PhonyPath{basePath{phony, ""}} 1798} 1799 1800type PhonyPath struct { 1801 basePath 1802} 1803 1804func (p PhonyPath) writablePath() {} 1805 1806func (p PhonyPath) getBuildDir() string { 1807 // A phone path cannot contain any / so cannot be relative to the build directory. 1808 return "" 1809} 1810 1811func (p PhonyPath) RelativeToTop() Path { 1812 ensureTestOnly() 1813 // A phony path cannot contain any / so does not have a build directory so switching to a new 1814 // build directory has no effect so just return this path. 1815 return p 1816} 1817 1818func (p PhonyPath) ReplaceExtension(ctx PathContext, ext string) OutputPath { 1819 panic("Not implemented") 1820} 1821 1822var _ Path = PhonyPath{} 1823var _ WritablePath = PhonyPath{} 1824 1825type testPath struct { 1826 basePath 1827} 1828 1829func (p testPath) RelativeToTop() Path { 1830 ensureTestOnly() 1831 return p 1832} 1833 1834func (p testPath) String() string { 1835 return p.path 1836} 1837 1838var _ Path = testPath{} 1839 1840// PathForTesting returns a Path constructed from joining the elements of paths with '/'. It should only be used from 1841// within tests. 1842func PathForTesting(paths ...string) Path { 1843 p, err := validateSafePath(paths...) 1844 if err != nil { 1845 panic(err) 1846 } 1847 return testPath{basePath{path: p, rel: p}} 1848} 1849 1850// PathsForTesting returns a Path constructed from each element in strs. It should only be used from within tests. 1851func PathsForTesting(strs ...string) Paths { 1852 p := make(Paths, len(strs)) 1853 for i, s := range strs { 1854 p[i] = PathForTesting(s) 1855 } 1856 1857 return p 1858} 1859 1860type testPathContext struct { 1861 config Config 1862} 1863 1864func (x *testPathContext) Config() Config { return x.config } 1865func (x *testPathContext) AddNinjaFileDeps(...string) {} 1866 1867// PathContextForTesting returns a PathContext that can be used in tests, for example to create an OutputPath with 1868// PathForOutput. 1869func PathContextForTesting(config Config) PathContext { 1870 return &testPathContext{ 1871 config: config, 1872 } 1873} 1874 1875type testModuleInstallPathContext struct { 1876 baseModuleContext 1877 1878 inData bool 1879 inTestcases bool 1880 inSanitizerDir bool 1881 inRamdisk bool 1882 inVendorRamdisk bool 1883 inDebugRamdisk bool 1884 inRecovery bool 1885 inRoot bool 1886 forceOS *OsType 1887 forceArch *ArchType 1888} 1889 1890func (m testModuleInstallPathContext) Config() Config { 1891 return m.baseModuleContext.config 1892} 1893 1894func (testModuleInstallPathContext) AddNinjaFileDeps(deps ...string) {} 1895 1896func (m testModuleInstallPathContext) InstallInData() bool { 1897 return m.inData 1898} 1899 1900func (m testModuleInstallPathContext) InstallInTestcases() bool { 1901 return m.inTestcases 1902} 1903 1904func (m testModuleInstallPathContext) InstallInSanitizerDir() bool { 1905 return m.inSanitizerDir 1906} 1907 1908func (m testModuleInstallPathContext) InstallInRamdisk() bool { 1909 return m.inRamdisk 1910} 1911 1912func (m testModuleInstallPathContext) InstallInVendorRamdisk() bool { 1913 return m.inVendorRamdisk 1914} 1915 1916func (m testModuleInstallPathContext) InstallInDebugRamdisk() bool { 1917 return m.inDebugRamdisk 1918} 1919 1920func (m testModuleInstallPathContext) InstallInRecovery() bool { 1921 return m.inRecovery 1922} 1923 1924func (m testModuleInstallPathContext) InstallInRoot() bool { 1925 return m.inRoot 1926} 1927 1928func (m testModuleInstallPathContext) InstallBypassMake() bool { 1929 return false 1930} 1931 1932func (m testModuleInstallPathContext) InstallForceOS() (*OsType, *ArchType) { 1933 return m.forceOS, m.forceArch 1934} 1935 1936// Construct a minimal ModuleInstallPathContext for testing. Note that baseModuleContext is 1937// default-initialized, which leaves blueprint.baseModuleContext set to nil, so methods that are 1938// delegated to it will panic. 1939func ModuleInstallPathContextForTesting(config Config) ModuleInstallPathContext { 1940 ctx := &testModuleInstallPathContext{} 1941 ctx.config = config 1942 ctx.os = Android 1943 return ctx 1944} 1945 1946// Rel performs the same function as filepath.Rel, but reports errors to a PathContext, and reports an error if 1947// targetPath is not inside basePath. 1948func Rel(ctx PathContext, basePath string, targetPath string) string { 1949 rel, isRel := MaybeRel(ctx, basePath, targetPath) 1950 if !isRel { 1951 ReportPathErrorf(ctx, "path %q is not under path %q", targetPath, basePath) 1952 return "" 1953 } 1954 return rel 1955} 1956 1957// MaybeRel performs the same function as filepath.Rel, but reports errors to a PathContext, and returns false if 1958// targetPath is not inside basePath. 1959func MaybeRel(ctx PathContext, basePath string, targetPath string) (string, bool) { 1960 rel, isRel, err := maybeRelErr(basePath, targetPath) 1961 if err != nil { 1962 reportPathError(ctx, err) 1963 } 1964 return rel, isRel 1965} 1966 1967func maybeRelErr(basePath string, targetPath string) (string, bool, error) { 1968 // filepath.Rel returns an error if one path is absolute and the other is not, handle that case first. 1969 if filepath.IsAbs(basePath) != filepath.IsAbs(targetPath) { 1970 return "", false, nil 1971 } 1972 rel, err := filepath.Rel(basePath, targetPath) 1973 if err != nil { 1974 return "", false, err 1975 } else if rel == ".." || strings.HasPrefix(rel, "../") || strings.HasPrefix(rel, "/") { 1976 return "", false, nil 1977 } 1978 return rel, true, nil 1979} 1980 1981// Writes a file to the output directory. Attempting to write directly to the output directory 1982// will fail due to the sandbox of the soong_build process. 1983func WriteFileToOutputDir(path WritablePath, data []byte, perm os.FileMode) error { 1984 return ioutil.WriteFile(absolutePath(path.String()), data, perm) 1985} 1986 1987func RemoveAllOutputDir(path WritablePath) error { 1988 return os.RemoveAll(absolutePath(path.String())) 1989} 1990 1991func CreateOutputDirIfNonexistent(path WritablePath, perm os.FileMode) error { 1992 dir := absolutePath(path.String()) 1993 if _, err := os.Stat(dir); os.IsNotExist(err) { 1994 return os.MkdirAll(dir, os.ModePerm) 1995 } else { 1996 return err 1997 } 1998} 1999 2000func absolutePath(path string) string { 2001 if filepath.IsAbs(path) { 2002 return path 2003 } 2004 return filepath.Join(absSrcDir, path) 2005} 2006 2007// A DataPath represents the path of a file to be used as data, for example 2008// a test library to be installed alongside a test. 2009// The data file should be installed (copied from `<SrcPath>`) to 2010// `<install_root>/<RelativeInstallPath>/<filename>`, or 2011// `<install_root>/<filename>` if RelativeInstallPath is empty. 2012type DataPath struct { 2013 // The path of the data file that should be copied into the data directory 2014 SrcPath Path 2015 // The install path of the data file, relative to the install root. 2016 RelativeInstallPath string 2017} 2018 2019// PathsIfNonNil returns a Paths containing only the non-nil input arguments. 2020func PathsIfNonNil(paths ...Path) Paths { 2021 if len(paths) == 0 { 2022 // Fast path for empty argument list 2023 return nil 2024 } else if len(paths) == 1 { 2025 // Fast path for a single argument 2026 if paths[0] != nil { 2027 return paths 2028 } else { 2029 return nil 2030 } 2031 } 2032 ret := make(Paths, 0, len(paths)) 2033 for _, path := range paths { 2034 if path != nil { 2035 ret = append(ret, path) 2036 } 2037 } 2038 if len(ret) == 0 { 2039 return nil 2040 } 2041 return ret 2042} 2043