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