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