1// Copyright 2015 Google Inc. All rights reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package android 16 17import ( 18 "fmt" 19 "io/ioutil" 20 "os" 21 "path/filepath" 22 "reflect" 23 "sort" 24 "strings" 25 26 "github.com/google/blueprint" 27 "github.com/google/blueprint/pathtools" 28) 29 30var absSrcDir string 31 32// PathContext is the subset of a (Module|Singleton)Context required by the 33// Path methods. 34type PathContext interface { 35 Config() Config 36 AddNinjaFileDeps(deps ...string) 37} 38 39type PathGlobContext interface { 40 GlobWithDeps(globPattern string, excludes []string) ([]string, error) 41} 42 43var _ PathContext = SingletonContext(nil) 44var _ PathContext = ModuleContext(nil) 45 46// "Null" path context is a minimal path context for a given config. 47type NullPathContext struct { 48 config Config 49} 50 51func (NullPathContext) AddNinjaFileDeps(...string) {} 52func (ctx NullPathContext) Config() Config { return ctx.config } 53 54type ModuleInstallPathContext interface { 55 BaseModuleContext 56 57 InstallInData() bool 58 InstallInTestcases() bool 59 InstallInSanitizerDir() bool 60 InstallInRamdisk() bool 61 InstallInRecovery() bool 62 InstallInRoot() bool 63 InstallBypassMake() bool 64 InstallForceOS() *OsType 65} 66 67var _ ModuleInstallPathContext = ModuleContext(nil) 68 69// errorfContext is the interface containing the Errorf method matching the 70// Errorf method in blueprint.SingletonContext. 71type errorfContext interface { 72 Errorf(format string, args ...interface{}) 73} 74 75var _ errorfContext = blueprint.SingletonContext(nil) 76 77// moduleErrorf is the interface containing the ModuleErrorf method matching 78// the ModuleErrorf method in blueprint.ModuleContext. 79type moduleErrorf interface { 80 ModuleErrorf(format string, args ...interface{}) 81} 82 83var _ moduleErrorf = blueprint.ModuleContext(nil) 84 85// reportPathError will register an error with the attached context. It 86// attempts ctx.ModuleErrorf for a better error message first, then falls 87// back to ctx.Errorf. 88func reportPathError(ctx PathContext, err error) { 89 reportPathErrorf(ctx, "%s", err.Error()) 90} 91 92// reportPathErrorf will register an error with the attached context. It 93// attempts ctx.ModuleErrorf for a better error message first, then falls 94// back to ctx.Errorf. 95func reportPathErrorf(ctx PathContext, format string, args ...interface{}) { 96 if mctx, ok := ctx.(moduleErrorf); ok { 97 mctx.ModuleErrorf(format, args...) 98 } else if ectx, ok := ctx.(errorfContext); ok { 99 ectx.Errorf(format, args...) 100 } else { 101 panic(fmt.Sprintf(format, args...)) 102 } 103} 104 105func pathContextName(ctx PathContext, module blueprint.Module) string { 106 if x, ok := ctx.(interface{ ModuleName(blueprint.Module) string }); ok { 107 return x.ModuleName(module) 108 } else if x, ok := ctx.(interface{ OtherModuleName(blueprint.Module) string }); ok { 109 return x.OtherModuleName(module) 110 } 111 return "unknown" 112} 113 114type Path interface { 115 // Returns the path in string form 116 String() string 117 118 // Ext returns the extension of the last element of the path 119 Ext() string 120 121 // Base returns the last element of the path 122 Base() string 123 124 // Rel returns the portion of the path relative to the directory it was created from. For 125 // example, Rel on a PathsForModuleSrc would return the path relative to the module source 126 // directory, and OutputPath.Join("foo").Rel() would return "foo". 127 Rel() string 128} 129 130// WritablePath is a type of path that can be used as an output for build rules. 131type WritablePath interface { 132 Path 133 134 // return the path to the build directory. 135 buildDir() string 136 137 // the writablePath method doesn't directly do anything, 138 // but it allows a struct to distinguish between whether or not it implements the WritablePath interface 139 writablePath() 140} 141 142type genPathProvider interface { 143 genPathWithExt(ctx ModuleContext, subdir, ext string) ModuleGenPath 144} 145type objPathProvider interface { 146 objPathWithExt(ctx ModuleContext, subdir, ext string) ModuleObjPath 147} 148type resPathProvider interface { 149 resPathWithName(ctx ModuleContext, name string) ModuleResPath 150} 151 152// GenPathWithExt derives a new file path in ctx's generated sources directory 153// from the current path, but with the new extension. 154func GenPathWithExt(ctx ModuleContext, subdir string, p Path, ext string) ModuleGenPath { 155 if path, ok := p.(genPathProvider); ok { 156 return path.genPathWithExt(ctx, subdir, ext) 157 } 158 reportPathErrorf(ctx, "Tried to create generated file from unsupported path: %s(%s)", reflect.TypeOf(p).Name(), p) 159 return PathForModuleGen(ctx) 160} 161 162// ObjPathWithExt derives a new file path in ctx's object directory from the 163// current path, but with the new extension. 164func ObjPathWithExt(ctx ModuleContext, subdir string, p Path, ext string) ModuleObjPath { 165 if path, ok := p.(objPathProvider); ok { 166 return path.objPathWithExt(ctx, subdir, ext) 167 } 168 reportPathErrorf(ctx, "Tried to create object file from unsupported path: %s (%s)", reflect.TypeOf(p).Name(), p) 169 return PathForModuleObj(ctx) 170} 171 172// ResPathWithName derives a new path in ctx's output resource directory, using 173// the current path to create the directory name, and the `name` argument for 174// the filename. 175func ResPathWithName(ctx ModuleContext, p Path, name string) ModuleResPath { 176 if path, ok := p.(resPathProvider); ok { 177 return path.resPathWithName(ctx, name) 178 } 179 reportPathErrorf(ctx, "Tried to create res file from unsupported path: %s (%s)", reflect.TypeOf(p).Name(), p) 180 return PathForModuleRes(ctx) 181} 182 183// OptionalPath is a container that may or may not contain a valid Path. 184type OptionalPath struct { 185 valid bool 186 path Path 187} 188 189// OptionalPathForPath returns an OptionalPath containing the path. 190func OptionalPathForPath(path Path) OptionalPath { 191 if path == nil { 192 return OptionalPath{} 193 } 194 return OptionalPath{valid: true, path: path} 195} 196 197// Valid returns whether there is a valid path 198func (p OptionalPath) Valid() bool { 199 return p.valid 200} 201 202// Path returns the Path embedded in this OptionalPath. You must be sure that 203// there is a valid path, since this method will panic if there is not. 204func (p OptionalPath) Path() Path { 205 if !p.valid { 206 panic("Requesting an invalid path") 207 } 208 return p.path 209} 210 211// String returns the string version of the Path, or "" if it isn't valid. 212func (p OptionalPath) String() string { 213 if p.valid { 214 return p.path.String() 215 } else { 216 return "" 217 } 218} 219 220// Paths is a slice of Path objects, with helpers to operate on the collection. 221type Paths []Path 222 223func (paths Paths) containsPath(path Path) bool { 224 for _, p := range paths { 225 if p == path { 226 return true 227 } 228 } 229 return false 230} 231 232// PathsForSource returns Paths rooted from SrcDir 233func PathsForSource(ctx PathContext, paths []string) Paths { 234 ret := make(Paths, len(paths)) 235 for i, path := range paths { 236 ret[i] = PathForSource(ctx, path) 237 } 238 return ret 239} 240 241// ExistentPathsForSources returns a list of Paths rooted from SrcDir that are 242// found in the tree. If any are not found, they are omitted from the list, 243// and dependencies are added so that we're re-run when they are added. 244func ExistentPathsForSources(ctx PathContext, paths []string) Paths { 245 ret := make(Paths, 0, len(paths)) 246 for _, path := range paths { 247 p := ExistentPathForSource(ctx, path) 248 if p.Valid() { 249 ret = append(ret, p.Path()) 250 } 251 } 252 return ret 253} 254 255// PathsForModuleSrc returns Paths rooted from the module's local source directory. It expands globs, references to 256// SourceFileProducer modules using the ":name" syntax, and references to OutputFileProducer modules using the 257// ":name{.tag}" syntax. Properties passed as the paths argument must have been annotated with struct tag 258// `android:"path"` so that dependencies on SourceFileProducer modules will have already been handled by the 259// path_properties mutator. If ctx.Config().AllowMissingDependencies() is true then any missing SourceFileProducer or 260// OutputFileProducer dependencies will cause the module to be marked as having missing dependencies. 261func PathsForModuleSrc(ctx ModuleContext, paths []string) Paths { 262 return PathsForModuleSrcExcludes(ctx, paths, nil) 263} 264 265// PathsForModuleSrcExcludes returns Paths rooted from the module's local source directory, excluding paths listed in 266// the excludes arguments. It expands globs, references to SourceFileProducer modules using the ":name" syntax, and 267// references to OutputFileProducer modules using the ":name{.tag}" syntax. Properties passed as the paths or excludes 268// argument must have been annotated with struct tag `android:"path"` so that dependencies on SourceFileProducer modules 269// will have already been handled by the path_properties mutator. If ctx.Config().AllowMissingDependencies() is 270// true then any missing SourceFileProducer or OutputFileProducer dependencies will cause the module to be marked as 271// having missing dependencies. 272func PathsForModuleSrcExcludes(ctx ModuleContext, paths, excludes []string) Paths { 273 ret, missingDeps := PathsAndMissingDepsForModuleSrcExcludes(ctx, paths, excludes) 274 if ctx.Config().AllowMissingDependencies() { 275 ctx.AddMissingDependencies(missingDeps) 276 } else { 277 for _, m := range missingDeps { 278 ctx.ModuleErrorf(`missing dependency on %q, is the property annotated with android:"path"?`, m) 279 } 280 } 281 return ret 282} 283 284// OutputPaths is a slice of OutputPath objects, with helpers to operate on the collection. 285type OutputPaths []OutputPath 286 287// Paths returns the OutputPaths as a Paths 288func (p OutputPaths) Paths() Paths { 289 if p == nil { 290 return nil 291 } 292 ret := make(Paths, len(p)) 293 for i, path := range p { 294 ret[i] = path 295 } 296 return ret 297} 298 299// Strings returns the string forms of the writable paths. 300func (p OutputPaths) Strings() []string { 301 if p == nil { 302 return nil 303 } 304 ret := make([]string, len(p)) 305 for i, path := range p { 306 ret[i] = path.String() 307 } 308 return ret 309} 310 311// PathsAndMissingDepsForModuleSrcExcludes returns Paths rooted from the module's local source directory, excluding 312// paths listed in the excludes arguments, and a list of missing dependencies. It expands globs, references to 313// SourceFileProducer modules using the ":name" syntax, and references to OutputFileProducer modules using the 314// ":name{.tag}" syntax. Properties passed as the paths or excludes argument must have been annotated with struct tag 315// `android:"path"` so that dependencies on SourceFileProducer modules will have already been handled by the 316// path_properties mutator. If ctx.Config().AllowMissingDependencies() is true then any missing SourceFileProducer or 317// OutputFileProducer dependencies will be returned, and they will NOT cause the module to be marked as having missing 318// dependencies. 319func PathsAndMissingDepsForModuleSrcExcludes(ctx ModuleContext, paths, excludes []string) (Paths, []string) { 320 prefix := pathForModuleSrc(ctx).String() 321 322 var expandedExcludes []string 323 if excludes != nil { 324 expandedExcludes = make([]string, 0, len(excludes)) 325 } 326 327 var missingExcludeDeps []string 328 329 for _, e := range excludes { 330 if m, t := SrcIsModuleWithTag(e); m != "" { 331 module := ctx.GetDirectDepWithTag(m, sourceOrOutputDepTag(t)) 332 if module == nil { 333 missingExcludeDeps = append(missingExcludeDeps, m) 334 continue 335 } 336 if outProducer, ok := module.(OutputFileProducer); ok { 337 outputFiles, err := outProducer.OutputFiles(t) 338 if err != nil { 339 ctx.ModuleErrorf("path dependency %q: %s", e, err) 340 } 341 expandedExcludes = append(expandedExcludes, outputFiles.Strings()...) 342 } else if t != "" { 343 ctx.ModuleErrorf("path dependency %q is not an output file producing module", e) 344 } else if srcProducer, ok := module.(SourceFileProducer); ok { 345 expandedExcludes = append(expandedExcludes, srcProducer.Srcs().Strings()...) 346 } else { 347 ctx.ModuleErrorf("path dependency %q is not a source file producing module", e) 348 } 349 } else { 350 expandedExcludes = append(expandedExcludes, filepath.Join(prefix, e)) 351 } 352 } 353 354 if paths == nil { 355 return nil, missingExcludeDeps 356 } 357 358 var missingDeps []string 359 360 expandedSrcFiles := make(Paths, 0, len(paths)) 361 for _, s := range paths { 362 srcFiles, err := expandOneSrcPath(ctx, s, expandedExcludes) 363 if depErr, ok := err.(missingDependencyError); ok { 364 missingDeps = append(missingDeps, depErr.missingDeps...) 365 } else if err != nil { 366 reportPathError(ctx, err) 367 } 368 expandedSrcFiles = append(expandedSrcFiles, srcFiles...) 369 } 370 371 return expandedSrcFiles, append(missingDeps, missingExcludeDeps...) 372} 373 374type missingDependencyError struct { 375 missingDeps []string 376} 377 378func (e missingDependencyError) Error() string { 379 return "missing dependencies: " + strings.Join(e.missingDeps, ", ") 380} 381 382func expandOneSrcPath(ctx ModuleContext, s string, expandedExcludes []string) (Paths, error) { 383 excludePaths := func(paths Paths) Paths { 384 if len(expandedExcludes) == 0 { 385 return paths 386 } 387 remainder := make(Paths, 0, len(paths)) 388 for _, p := range paths { 389 if !InList(p.String(), expandedExcludes) { 390 remainder = append(remainder, p) 391 } 392 } 393 return remainder 394 } 395 if m, t := SrcIsModuleWithTag(s); m != "" { 396 module := ctx.GetDirectDepWithTag(m, sourceOrOutputDepTag(t)) 397 if module == nil { 398 return nil, missingDependencyError{[]string{m}} 399 } 400 if outProducer, ok := module.(OutputFileProducer); ok { 401 outputFiles, err := outProducer.OutputFiles(t) 402 if err != nil { 403 return nil, fmt.Errorf("path dependency %q: %s", s, err) 404 } 405 return excludePaths(outputFiles), nil 406 } else if t != "" { 407 return nil, fmt.Errorf("path dependency %q is not an output file producing module", s) 408 } else if srcProducer, ok := module.(SourceFileProducer); ok { 409 return excludePaths(srcProducer.Srcs()), nil 410 } else { 411 return nil, fmt.Errorf("path dependency %q is not a source file producing module", s) 412 } 413 } else if pathtools.IsGlob(s) { 414 paths := ctx.GlobFiles(pathForModuleSrc(ctx, s).String(), expandedExcludes) 415 return PathsWithModuleSrcSubDir(ctx, paths, ""), nil 416 } else { 417 p := pathForModuleSrc(ctx, s) 418 if exists, _, err := ctx.Config().fs.Exists(p.String()); err != nil { 419 reportPathErrorf(ctx, "%s: %s", p, err.Error()) 420 } else if !exists && !ctx.Config().testAllowNonExistentPaths { 421 reportPathErrorf(ctx, "module source path %q does not exist", p) 422 } 423 424 if InList(p.String(), expandedExcludes) { 425 return nil, nil 426 } 427 return Paths{p}, nil 428 } 429} 430 431// pathsForModuleSrcFromFullPath returns Paths rooted from the module's local 432// source directory, but strip the local source directory from the beginning of 433// each string. If incDirs is false, strip paths with a trailing '/' from the list. 434// It intended for use in globs that only list files that exist, so it allows '$' in 435// filenames. 436func pathsForModuleSrcFromFullPath(ctx EarlyModuleContext, paths []string, incDirs bool) Paths { 437 prefix := filepath.Join(ctx.Config().srcDir, ctx.ModuleDir()) + "/" 438 if prefix == "./" { 439 prefix = "" 440 } 441 ret := make(Paths, 0, len(paths)) 442 for _, p := range paths { 443 if !incDirs && strings.HasSuffix(p, "/") { 444 continue 445 } 446 path := filepath.Clean(p) 447 if !strings.HasPrefix(path, prefix) { 448 reportPathErrorf(ctx, "Path %q is not in module source directory %q", p, prefix) 449 continue 450 } 451 452 srcPath, err := safePathForSource(ctx, ctx.ModuleDir(), path[len(prefix):]) 453 if err != nil { 454 reportPathError(ctx, err) 455 continue 456 } 457 458 srcPath.basePath.rel = srcPath.path 459 460 ret = append(ret, srcPath) 461 } 462 return ret 463} 464 465// PathsWithOptionalDefaultForModuleSrc returns Paths rooted from the module's 466// local source directory. If input is nil, use the default if it exists. If input is empty, returns nil. 467func PathsWithOptionalDefaultForModuleSrc(ctx ModuleContext, input []string, def string) Paths { 468 if input != nil { 469 return PathsForModuleSrc(ctx, input) 470 } 471 // Use Glob so that if the default doesn't exist, a dependency is added so that when it 472 // is created, we're run again. 473 path := filepath.Join(ctx.Config().srcDir, ctx.ModuleDir(), def) 474 return ctx.Glob(path, nil) 475} 476 477// Strings returns the Paths in string form 478func (p Paths) Strings() []string { 479 if p == nil { 480 return nil 481 } 482 ret := make([]string, len(p)) 483 for i, path := range p { 484 ret[i] = path.String() 485 } 486 return ret 487} 488 489func CopyOfPaths(paths Paths) Paths { 490 return append(Paths(nil), paths...) 491} 492 493// FirstUniquePaths returns all unique elements of a Paths, keeping the first copy of each. It 494// modifies the Paths slice contents in place, and returns a subslice of the original slice. 495func FirstUniquePaths(list Paths) Paths { 496 // 128 was chosen based on BenchmarkFirstUniquePaths results. 497 if len(list) > 128 { 498 return firstUniquePathsMap(list) 499 } 500 return firstUniquePathsList(list) 501} 502 503// SortedUniquePaths returns all unique elements of a Paths in sorted order. It modifies the 504// Paths slice contents in place, and returns a subslice of the original slice. 505func SortedUniquePaths(list Paths) Paths { 506 unique := FirstUniquePaths(list) 507 sort.Slice(unique, func(i, j int) bool { 508 return unique[i].String() < unique[j].String() 509 }) 510 return unique 511} 512 513func firstUniquePathsList(list Paths) Paths { 514 k := 0 515outer: 516 for i := 0; i < len(list); i++ { 517 for j := 0; j < k; j++ { 518 if list[i] == list[j] { 519 continue outer 520 } 521 } 522 list[k] = list[i] 523 k++ 524 } 525 return list[:k] 526} 527 528func firstUniquePathsMap(list Paths) Paths { 529 k := 0 530 seen := make(map[Path]bool, len(list)) 531 for i := 0; i < len(list); i++ { 532 if seen[list[i]] { 533 continue 534 } 535 seen[list[i]] = true 536 list[k] = list[i] 537 k++ 538 } 539 return list[:k] 540} 541 542// LastUniquePaths returns all unique elements of a Paths, keeping the last copy of each. It 543// modifies the Paths slice contents in place, and returns a subslice of the original slice. 544func LastUniquePaths(list Paths) Paths { 545 totalSkip := 0 546 for i := len(list) - 1; i >= totalSkip; i-- { 547 skip := 0 548 for j := i - 1; j >= totalSkip; j-- { 549 if list[i] == list[j] { 550 skip++ 551 } else { 552 list[j+skip] = list[j] 553 } 554 } 555 totalSkip += skip 556 } 557 return list[totalSkip:] 558} 559 560// ReversePaths returns a copy of a Paths in reverse order. 561func ReversePaths(list Paths) Paths { 562 if list == nil { 563 return nil 564 } 565 ret := make(Paths, len(list)) 566 for i := range list { 567 ret[i] = list[len(list)-1-i] 568 } 569 return ret 570} 571 572func indexPathList(s Path, list []Path) int { 573 for i, l := range list { 574 if l == s { 575 return i 576 } 577 } 578 579 return -1 580} 581 582func inPathList(p Path, list []Path) bool { 583 return indexPathList(p, list) != -1 584} 585 586func FilterPathList(list []Path, filter []Path) (remainder []Path, filtered []Path) { 587 return FilterPathListPredicate(list, func(p Path) bool { return inPathList(p, filter) }) 588} 589 590func FilterPathListPredicate(list []Path, predicate func(Path) bool) (remainder []Path, filtered []Path) { 591 for _, l := range list { 592 if predicate(l) { 593 filtered = append(filtered, l) 594 } else { 595 remainder = append(remainder, l) 596 } 597 } 598 599 return 600} 601 602// HasExt returns true of any of the paths have extension ext, otherwise false 603func (p Paths) HasExt(ext string) bool { 604 for _, path := range p { 605 if path.Ext() == ext { 606 return true 607 } 608 } 609 610 return false 611} 612 613// FilterByExt returns the subset of the paths that have extension ext 614func (p Paths) FilterByExt(ext string) Paths { 615 ret := make(Paths, 0, len(p)) 616 for _, path := range p { 617 if path.Ext() == ext { 618 ret = append(ret, path) 619 } 620 } 621 return ret 622} 623 624// FilterOutByExt returns the subset of the paths that do not have extension ext 625func (p Paths) FilterOutByExt(ext string) Paths { 626 ret := make(Paths, 0, len(p)) 627 for _, path := range p { 628 if path.Ext() != ext { 629 ret = append(ret, path) 630 } 631 } 632 return ret 633} 634 635// DirectorySortedPaths is a slice of paths that are sorted such that all files in a directory 636// (including subdirectories) are in a contiguous subslice of the list, and can be found in 637// O(log(N)) time using a binary search on the directory prefix. 638type DirectorySortedPaths Paths 639 640func PathsToDirectorySortedPaths(paths Paths) DirectorySortedPaths { 641 ret := append(DirectorySortedPaths(nil), paths...) 642 sort.Slice(ret, func(i, j int) bool { 643 return ret[i].String() < ret[j].String() 644 }) 645 return ret 646} 647 648// PathsInDirectory returns a subslice of the DirectorySortedPaths as a Paths that contains all entries 649// that are in the specified directory and its subdirectories. 650func (p DirectorySortedPaths) PathsInDirectory(dir string) Paths { 651 prefix := filepath.Clean(dir) + "/" 652 start := sort.Search(len(p), func(i int) bool { 653 return prefix < p[i].String() 654 }) 655 656 ret := p[start:] 657 658 end := sort.Search(len(ret), func(i int) bool { 659 return !strings.HasPrefix(ret[i].String(), prefix) 660 }) 661 662 ret = ret[:end] 663 664 return Paths(ret) 665} 666 667// WritablePaths is a slice of WritablePaths, used for multiple outputs. 668type WritablePaths []WritablePath 669 670// Strings returns the string forms of the writable paths. 671func (p WritablePaths) Strings() []string { 672 if p == nil { 673 return nil 674 } 675 ret := make([]string, len(p)) 676 for i, path := range p { 677 ret[i] = path.String() 678 } 679 return ret 680} 681 682// Paths returns the WritablePaths as a Paths 683func (p WritablePaths) Paths() Paths { 684 if p == nil { 685 return nil 686 } 687 ret := make(Paths, len(p)) 688 for i, path := range p { 689 ret[i] = path 690 } 691 return ret 692} 693 694type basePath struct { 695 path string 696 config Config 697 rel string 698} 699 700func (p basePath) Ext() string { 701 return filepath.Ext(p.path) 702} 703 704func (p basePath) Base() string { 705 return filepath.Base(p.path) 706} 707 708func (p basePath) Rel() string { 709 if p.rel != "" { 710 return p.rel 711 } 712 return p.path 713} 714 715func (p basePath) String() string { 716 return p.path 717} 718 719func (p basePath) withRel(rel string) basePath { 720 p.path = filepath.Join(p.path, rel) 721 p.rel = rel 722 return p 723} 724 725// SourcePath is a Path representing a file path rooted from SrcDir 726type SourcePath struct { 727 basePath 728} 729 730var _ Path = SourcePath{} 731 732func (p SourcePath) withRel(rel string) SourcePath { 733 p.basePath = p.basePath.withRel(rel) 734 return p 735} 736 737// safePathForSource is for paths that we expect are safe -- only for use by go 738// code that is embedding ninja variables in paths 739func safePathForSource(ctx PathContext, pathComponents ...string) (SourcePath, error) { 740 p, err := validateSafePath(pathComponents...) 741 ret := SourcePath{basePath{p, ctx.Config(), ""}} 742 if err != nil { 743 return ret, err 744 } 745 746 // absolute path already checked by validateSafePath 747 if strings.HasPrefix(ret.String(), ctx.Config().buildDir) { 748 return ret, fmt.Errorf("source path %q is in output", ret.String()) 749 } 750 751 return ret, err 752} 753 754// pathForSource creates a SourcePath from pathComponents, but does not check that it exists. 755func pathForSource(ctx PathContext, pathComponents ...string) (SourcePath, error) { 756 p, err := validatePath(pathComponents...) 757 ret := SourcePath{basePath{p, ctx.Config(), ""}} 758 if err != nil { 759 return ret, err 760 } 761 762 // absolute path already checked by validatePath 763 if strings.HasPrefix(ret.String(), ctx.Config().buildDir) { 764 return ret, fmt.Errorf("source path %q is in output", ret.String()) 765 } 766 767 return ret, nil 768} 769 770// existsWithDependencies returns true if the path exists, and adds appropriate dependencies to rerun if the 771// path does not exist. 772func existsWithDependencies(ctx PathContext, path SourcePath) (exists bool, err error) { 773 var files []string 774 775 if gctx, ok := ctx.(PathGlobContext); ok { 776 // Use glob to produce proper dependencies, even though we only want 777 // a single file. 778 files, err = gctx.GlobWithDeps(path.String(), nil) 779 } else { 780 var deps []string 781 // We cannot add build statements in this context, so we fall back to 782 // AddNinjaFileDeps 783 files, deps, err = ctx.Config().fs.Glob(path.String(), nil, pathtools.FollowSymlinks) 784 ctx.AddNinjaFileDeps(deps...) 785 } 786 787 if err != nil { 788 return false, fmt.Errorf("glob: %s", err.Error()) 789 } 790 791 return len(files) > 0, nil 792} 793 794// PathForSource joins the provided path components and validates that the result 795// neither escapes the source dir nor is in the out dir. 796// On error, it will return a usable, but invalid SourcePath, and report a ModuleError. 797func PathForSource(ctx PathContext, pathComponents ...string) SourcePath { 798 path, err := pathForSource(ctx, pathComponents...) 799 if err != nil { 800 reportPathError(ctx, err) 801 } 802 803 if pathtools.IsGlob(path.String()) { 804 reportPathErrorf(ctx, "path may not contain a glob: %s", path.String()) 805 } 806 807 if modCtx, ok := ctx.(ModuleContext); ok && ctx.Config().AllowMissingDependencies() { 808 exists, err := existsWithDependencies(ctx, path) 809 if err != nil { 810 reportPathError(ctx, err) 811 } 812 if !exists { 813 modCtx.AddMissingDependencies([]string{path.String()}) 814 } 815 } else if exists, _, err := ctx.Config().fs.Exists(path.String()); err != nil { 816 reportPathErrorf(ctx, "%s: %s", path, err.Error()) 817 } else if !exists && !ctx.Config().testAllowNonExistentPaths { 818 reportPathErrorf(ctx, "source path %q does not exist", path) 819 } 820 return path 821} 822 823// ExistentPathForSource returns an OptionalPath with the SourcePath if the 824// path exists, or an empty OptionalPath if it doesn't exist. Dependencies are added 825// so that the ninja file will be regenerated if the state of the path changes. 826func ExistentPathForSource(ctx PathContext, pathComponents ...string) OptionalPath { 827 path, err := pathForSource(ctx, pathComponents...) 828 if err != nil { 829 reportPathError(ctx, err) 830 return OptionalPath{} 831 } 832 833 if pathtools.IsGlob(path.String()) { 834 reportPathErrorf(ctx, "path may not contain a glob: %s", path.String()) 835 return OptionalPath{} 836 } 837 838 exists, err := existsWithDependencies(ctx, path) 839 if err != nil { 840 reportPathError(ctx, err) 841 return OptionalPath{} 842 } 843 if !exists { 844 return OptionalPath{} 845 } 846 return OptionalPathForPath(path) 847} 848 849func (p SourcePath) String() string { 850 return filepath.Join(p.config.srcDir, p.path) 851} 852 853// Join creates a new SourcePath with paths... joined with the current path. The 854// provided paths... may not use '..' to escape from the current path. 855func (p SourcePath) Join(ctx PathContext, paths ...string) SourcePath { 856 path, err := validatePath(paths...) 857 if err != nil { 858 reportPathError(ctx, err) 859 } 860 return p.withRel(path) 861} 862 863// join is like Join but does less path validation. 864func (p SourcePath) join(ctx PathContext, paths ...string) SourcePath { 865 path, err := validateSafePath(paths...) 866 if err != nil { 867 reportPathError(ctx, err) 868 } 869 return p.withRel(path) 870} 871 872// OverlayPath returns the overlay for `path' if it exists. This assumes that the 873// SourcePath is the path to a resource overlay directory. 874func (p SourcePath) OverlayPath(ctx ModuleContext, path Path) OptionalPath { 875 var relDir string 876 if srcPath, ok := path.(SourcePath); ok { 877 relDir = srcPath.path 878 } else { 879 reportPathErrorf(ctx, "Cannot find relative path for %s(%s)", reflect.TypeOf(path).Name(), path) 880 return OptionalPath{} 881 } 882 dir := filepath.Join(p.config.srcDir, p.path, relDir) 883 // Use Glob so that we are run again if the directory is added. 884 if pathtools.IsGlob(dir) { 885 reportPathErrorf(ctx, "Path may not contain a glob: %s", dir) 886 } 887 paths, err := ctx.GlobWithDeps(dir, nil) 888 if err != nil { 889 reportPathErrorf(ctx, "glob: %s", err.Error()) 890 return OptionalPath{} 891 } 892 if len(paths) == 0 { 893 return OptionalPath{} 894 } 895 relPath := Rel(ctx, p.config.srcDir, paths[0]) 896 return OptionalPathForPath(PathForSource(ctx, relPath)) 897} 898 899// OutputPath is a Path representing an intermediates file path rooted from the build directory 900type OutputPath struct { 901 basePath 902 fullPath string 903} 904 905func (p OutputPath) withRel(rel string) OutputPath { 906 p.basePath = p.basePath.withRel(rel) 907 p.fullPath = filepath.Join(p.fullPath, rel) 908 return p 909} 910 911func (p OutputPath) WithoutRel() OutputPath { 912 p.basePath.rel = filepath.Base(p.basePath.path) 913 return p 914} 915 916func (p OutputPath) buildDir() string { 917 return p.config.buildDir 918} 919 920var _ Path = OutputPath{} 921var _ WritablePath = OutputPath{} 922 923// toolDepPath is a Path representing a dependency of the build tool. 924type toolDepPath struct { 925 basePath 926} 927 928var _ Path = toolDepPath{} 929 930// pathForBuildToolDep returns a toolDepPath representing the given path string. 931// There is no validation for the path, as it is "trusted": It may fail 932// normal validation checks. For example, it may be an absolute path. 933// Only use this function to construct paths for dependencies of the build 934// tool invocation. 935func pathForBuildToolDep(ctx PathContext, path string) toolDepPath { 936 return toolDepPath{basePath{path, ctx.Config(), ""}} 937} 938 939// PathForOutput joins the provided paths and returns an OutputPath that is 940// validated to not escape the build dir. 941// On error, it will return a usable, but invalid OutputPath, and report a ModuleError. 942func PathForOutput(ctx PathContext, pathComponents ...string) OutputPath { 943 path, err := validatePath(pathComponents...) 944 if err != nil { 945 reportPathError(ctx, err) 946 } 947 fullPath := filepath.Join(ctx.Config().buildDir, path) 948 path = fullPath[len(fullPath)-len(path):] 949 return OutputPath{basePath{path, ctx.Config(), ""}, fullPath} 950} 951 952// PathsForOutput returns Paths rooted from buildDir 953func PathsForOutput(ctx PathContext, paths []string) WritablePaths { 954 ret := make(WritablePaths, len(paths)) 955 for i, path := range paths { 956 ret[i] = PathForOutput(ctx, path) 957 } 958 return ret 959} 960 961func (p OutputPath) writablePath() {} 962 963func (p OutputPath) String() string { 964 return p.fullPath 965} 966 967// Join creates a new OutputPath with paths... joined with the current path. The 968// provided paths... may not use '..' to escape from the current path. 969func (p OutputPath) Join(ctx PathContext, paths ...string) OutputPath { 970 path, err := validatePath(paths...) 971 if err != nil { 972 reportPathError(ctx, err) 973 } 974 return p.withRel(path) 975} 976 977// ReplaceExtension creates a new OutputPath with the extension replaced with ext. 978func (p OutputPath) ReplaceExtension(ctx PathContext, ext string) OutputPath { 979 if strings.Contains(ext, "/") { 980 reportPathErrorf(ctx, "extension %q cannot contain /", ext) 981 } 982 ret := PathForOutput(ctx, pathtools.ReplaceExtension(p.path, ext)) 983 ret.rel = pathtools.ReplaceExtension(p.rel, ext) 984 return ret 985} 986 987// InSameDir creates a new OutputPath from the directory of the current OutputPath joined with the elements in paths. 988func (p OutputPath) InSameDir(ctx PathContext, paths ...string) OutputPath { 989 path, err := validatePath(paths...) 990 if err != nil { 991 reportPathError(ctx, err) 992 } 993 994 ret := PathForOutput(ctx, filepath.Dir(p.path), path) 995 ret.rel = filepath.Join(filepath.Dir(p.rel), path) 996 return ret 997} 998 999// PathForIntermediates returns an OutputPath representing the top-level 1000// intermediates directory. 1001func PathForIntermediates(ctx PathContext, paths ...string) OutputPath { 1002 path, err := validatePath(paths...) 1003 if err != nil { 1004 reportPathError(ctx, err) 1005 } 1006 return PathForOutput(ctx, ".intermediates", path) 1007} 1008 1009var _ genPathProvider = SourcePath{} 1010var _ objPathProvider = SourcePath{} 1011var _ resPathProvider = SourcePath{} 1012 1013// PathForModuleSrc returns a Path representing the paths... under the 1014// module's local source directory. 1015func PathForModuleSrc(ctx ModuleContext, pathComponents ...string) Path { 1016 p, err := validatePath(pathComponents...) 1017 if err != nil { 1018 reportPathError(ctx, err) 1019 } 1020 paths, err := expandOneSrcPath(ctx, p, nil) 1021 if err != nil { 1022 if depErr, ok := err.(missingDependencyError); ok { 1023 if ctx.Config().AllowMissingDependencies() { 1024 ctx.AddMissingDependencies(depErr.missingDeps) 1025 } else { 1026 ctx.ModuleErrorf(`%s, is the property annotated with android:"path"?`, depErr.Error()) 1027 } 1028 } else { 1029 reportPathError(ctx, err) 1030 } 1031 return nil 1032 } else if len(paths) == 0 { 1033 reportPathErrorf(ctx, "%q produced no files, expected exactly one", p) 1034 return nil 1035 } else if len(paths) > 1 { 1036 reportPathErrorf(ctx, "%q produced %d files, expected exactly one", p, len(paths)) 1037 } 1038 return paths[0] 1039} 1040 1041func pathForModuleSrc(ctx ModuleContext, paths ...string) SourcePath { 1042 p, err := validatePath(paths...) 1043 if err != nil { 1044 reportPathError(ctx, err) 1045 } 1046 1047 path, err := pathForSource(ctx, ctx.ModuleDir(), p) 1048 if err != nil { 1049 reportPathError(ctx, err) 1050 } 1051 1052 path.basePath.rel = p 1053 1054 return path 1055} 1056 1057// PathsWithModuleSrcSubDir takes a list of Paths and returns a new list of Paths where Rel() on each path 1058// will return the path relative to subDir in the module's source directory. If any input paths are not located 1059// inside subDir then a path error will be reported. 1060func PathsWithModuleSrcSubDir(ctx ModuleContext, paths Paths, subDir string) Paths { 1061 paths = append(Paths(nil), paths...) 1062 subDirFullPath := pathForModuleSrc(ctx, subDir) 1063 for i, path := range paths { 1064 rel := Rel(ctx, subDirFullPath.String(), path.String()) 1065 paths[i] = subDirFullPath.join(ctx, rel) 1066 } 1067 return paths 1068} 1069 1070// PathWithModuleSrcSubDir takes a Path and returns a Path where Rel() will return the path relative to subDir in the 1071// module's source directory. If the input path is not located inside subDir then a path error will be reported. 1072func PathWithModuleSrcSubDir(ctx ModuleContext, path Path, subDir string) Path { 1073 subDirFullPath := pathForModuleSrc(ctx, subDir) 1074 rel := Rel(ctx, subDirFullPath.String(), path.String()) 1075 return subDirFullPath.Join(ctx, rel) 1076} 1077 1078// OptionalPathForModuleSrc returns an OptionalPath. The OptionalPath contains a 1079// valid path if p is non-nil. 1080func OptionalPathForModuleSrc(ctx ModuleContext, p *string) OptionalPath { 1081 if p == nil { 1082 return OptionalPath{} 1083 } 1084 return OptionalPathForPath(PathForModuleSrc(ctx, *p)) 1085} 1086 1087func (p SourcePath) genPathWithExt(ctx ModuleContext, subdir, ext string) ModuleGenPath { 1088 return PathForModuleGen(ctx, subdir, pathtools.ReplaceExtension(p.path, ext)) 1089} 1090 1091func (p SourcePath) objPathWithExt(ctx ModuleContext, subdir, ext string) ModuleObjPath { 1092 return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext)) 1093} 1094 1095func (p SourcePath) resPathWithName(ctx ModuleContext, name string) ModuleResPath { 1096 // TODO: Use full directory if the new ctx is not the current ctx? 1097 return PathForModuleRes(ctx, p.path, name) 1098} 1099 1100// ModuleOutPath is a Path representing a module's output directory. 1101type ModuleOutPath struct { 1102 OutputPath 1103} 1104 1105var _ Path = ModuleOutPath{} 1106 1107func (p ModuleOutPath) objPathWithExt(ctx ModuleContext, subdir, ext string) ModuleObjPath { 1108 return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext)) 1109} 1110 1111func pathForModule(ctx ModuleContext) OutputPath { 1112 return PathForOutput(ctx, ".intermediates", ctx.ModuleDir(), ctx.ModuleName(), ctx.ModuleSubDir()) 1113} 1114 1115// PathForVndkRefAbiDump returns an OptionalPath representing the path of the 1116// reference abi dump for the given module. This is not guaranteed to be valid. 1117func PathForVndkRefAbiDump(ctx ModuleContext, version, fileName string, 1118 isNdk, isLlndkOrVndk, isGzip bool) OptionalPath { 1119 1120 arches := ctx.DeviceConfig().Arches() 1121 if len(arches) == 0 { 1122 panic("device build with no primary arch") 1123 } 1124 currentArch := ctx.Arch() 1125 archNameAndVariant := currentArch.ArchType.String() 1126 if currentArch.ArchVariant != "" { 1127 archNameAndVariant += "_" + currentArch.ArchVariant 1128 } 1129 1130 var dirName string 1131 if isNdk { 1132 dirName = "ndk" 1133 } else if isLlndkOrVndk { 1134 dirName = "vndk" 1135 } else { 1136 dirName = "platform" // opt-in libs 1137 } 1138 1139 binderBitness := ctx.DeviceConfig().BinderBitness() 1140 1141 var ext string 1142 if isGzip { 1143 ext = ".lsdump.gz" 1144 } else { 1145 ext = ".lsdump" 1146 } 1147 1148 return ExistentPathForSource(ctx, "prebuilts", "abi-dumps", dirName, 1149 version, binderBitness, archNameAndVariant, "source-based", 1150 fileName+ext) 1151} 1152 1153// PathForModuleOut returns a Path representing the paths... under the module's 1154// output directory. 1155func PathForModuleOut(ctx ModuleContext, paths ...string) ModuleOutPath { 1156 p, err := validatePath(paths...) 1157 if err != nil { 1158 reportPathError(ctx, err) 1159 } 1160 return ModuleOutPath{ 1161 OutputPath: pathForModule(ctx).withRel(p), 1162 } 1163} 1164 1165// ModuleGenPath is a Path representing the 'gen' directory in a module's output 1166// directory. Mainly used for generated sources. 1167type ModuleGenPath struct { 1168 ModuleOutPath 1169} 1170 1171var _ Path = ModuleGenPath{} 1172var _ genPathProvider = ModuleGenPath{} 1173var _ objPathProvider = ModuleGenPath{} 1174 1175// PathForModuleGen returns a Path representing the paths... under the module's 1176// `gen' directory. 1177func PathForModuleGen(ctx ModuleContext, paths ...string) ModuleGenPath { 1178 p, err := validatePath(paths...) 1179 if err != nil { 1180 reportPathError(ctx, err) 1181 } 1182 return ModuleGenPath{ 1183 ModuleOutPath: ModuleOutPath{ 1184 OutputPath: pathForModule(ctx).withRel("gen").withRel(p), 1185 }, 1186 } 1187} 1188 1189func (p ModuleGenPath) genPathWithExt(ctx ModuleContext, subdir, ext string) ModuleGenPath { 1190 // TODO: make a different path for local vs remote generated files? 1191 return PathForModuleGen(ctx, subdir, pathtools.ReplaceExtension(p.path, ext)) 1192} 1193 1194func (p ModuleGenPath) objPathWithExt(ctx ModuleContext, subdir, ext string) ModuleObjPath { 1195 return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext)) 1196} 1197 1198// ModuleObjPath is a Path representing the 'obj' directory in a module's output 1199// directory. Used for compiled objects. 1200type ModuleObjPath struct { 1201 ModuleOutPath 1202} 1203 1204var _ Path = ModuleObjPath{} 1205 1206// PathForModuleObj returns a Path representing the paths... under the module's 1207// 'obj' directory. 1208func PathForModuleObj(ctx ModuleContext, pathComponents ...string) ModuleObjPath { 1209 p, err := validatePath(pathComponents...) 1210 if err != nil { 1211 reportPathError(ctx, err) 1212 } 1213 return ModuleObjPath{PathForModuleOut(ctx, "obj", p)} 1214} 1215 1216// ModuleResPath is a a Path representing the 'res' directory in a module's 1217// output directory. 1218type ModuleResPath struct { 1219 ModuleOutPath 1220} 1221 1222var _ Path = ModuleResPath{} 1223 1224// PathForModuleRes returns a Path representing the paths... under the module's 1225// 'res' directory. 1226func PathForModuleRes(ctx ModuleContext, pathComponents ...string) ModuleResPath { 1227 p, err := validatePath(pathComponents...) 1228 if err != nil { 1229 reportPathError(ctx, err) 1230 } 1231 1232 return ModuleResPath{PathForModuleOut(ctx, "res", p)} 1233} 1234 1235// InstallPath is a Path representing a installed file path rooted from the build directory 1236type InstallPath struct { 1237 basePath 1238 1239 baseDir string // "../" for Make paths to convert "out/soong" to "out", "" for Soong paths 1240} 1241 1242func (p InstallPath) buildDir() string { 1243 return p.config.buildDir 1244} 1245 1246var _ Path = InstallPath{} 1247var _ WritablePath = InstallPath{} 1248 1249func (p InstallPath) writablePath() {} 1250 1251func (p InstallPath) String() string { 1252 return filepath.Join(p.config.buildDir, p.baseDir, p.path) 1253} 1254 1255// Join creates a new InstallPath with paths... joined with the current path. The 1256// provided paths... may not use '..' to escape from the current path. 1257func (p InstallPath) Join(ctx PathContext, paths ...string) InstallPath { 1258 path, err := validatePath(paths...) 1259 if err != nil { 1260 reportPathError(ctx, err) 1261 } 1262 return p.withRel(path) 1263} 1264 1265func (p InstallPath) withRel(rel string) InstallPath { 1266 p.basePath = p.basePath.withRel(rel) 1267 return p 1268} 1269 1270// ToMakePath returns a new InstallPath that points to Make's install directory instead of Soong's, 1271// i.e. out/ instead of out/soong/. 1272func (p InstallPath) ToMakePath() InstallPath { 1273 p.baseDir = "../" 1274 return p 1275} 1276 1277// PathForModuleInstall returns a Path representing the install path for the 1278// module appended with paths... 1279func PathForModuleInstall(ctx ModuleInstallPathContext, pathComponents ...string) InstallPath { 1280 os := ctx.Os() 1281 if forceOS := ctx.InstallForceOS(); forceOS != nil { 1282 os = *forceOS 1283 } 1284 partition := modulePartition(ctx, os) 1285 1286 ret := pathForInstall(ctx, os, partition, ctx.Debug(), pathComponents...) 1287 1288 if ctx.InstallBypassMake() && ctx.Config().EmbeddedInMake() { 1289 ret = ret.ToMakePath() 1290 } 1291 1292 return ret 1293} 1294 1295func pathForInstall(ctx PathContext, os OsType, partition string, debug bool, 1296 pathComponents ...string) InstallPath { 1297 1298 var outPaths []string 1299 1300 if os.Class == Device { 1301 outPaths = []string{"target", "product", ctx.Config().DeviceName(), partition} 1302 } else { 1303 switch os { 1304 case Linux: 1305 outPaths = []string{"host", "linux-x86", partition} 1306 case LinuxBionic: 1307 // TODO: should this be a separate top level, or shared with linux-x86? 1308 outPaths = []string{"host", "linux_bionic-x86", partition} 1309 default: 1310 outPaths = []string{"host", os.String() + "-x86", partition} 1311 } 1312 } 1313 if debug { 1314 outPaths = append([]string{"debug"}, outPaths...) 1315 } 1316 outPaths = append(outPaths, pathComponents...) 1317 1318 path, err := validatePath(outPaths...) 1319 if err != nil { 1320 reportPathError(ctx, err) 1321 } 1322 1323 ret := InstallPath{basePath{path, ctx.Config(), ""}, ""} 1324 1325 return ret 1326} 1327 1328func pathForNdkOrSdkInstall(ctx PathContext, prefix string, paths []string) InstallPath { 1329 paths = append([]string{prefix}, paths...) 1330 path, err := validatePath(paths...) 1331 if err != nil { 1332 reportPathError(ctx, err) 1333 } 1334 return InstallPath{basePath{path, ctx.Config(), ""}, ""} 1335} 1336 1337func PathForNdkInstall(ctx PathContext, paths ...string) InstallPath { 1338 return pathForNdkOrSdkInstall(ctx, "ndk", paths) 1339} 1340 1341func PathForMainlineSdksInstall(ctx PathContext, paths ...string) InstallPath { 1342 return pathForNdkOrSdkInstall(ctx, "mainline-sdks", paths) 1343} 1344 1345func InstallPathToOnDevicePath(ctx PathContext, path InstallPath) string { 1346 rel := Rel(ctx, PathForOutput(ctx, "target", "product", ctx.Config().DeviceName()).String(), path.String()) 1347 1348 return "/" + rel 1349} 1350 1351func modulePartition(ctx ModuleInstallPathContext, os OsType) string { 1352 var partition string 1353 if ctx.InstallInTestcases() { 1354 // "testcases" install directory can be used for host or device modules. 1355 partition = "testcases" 1356 } else if os.Class == Device { 1357 if ctx.InstallInData() { 1358 partition = "data" 1359 } else if ctx.InstallInRamdisk() { 1360 if ctx.DeviceConfig().BoardUsesRecoveryAsBoot() { 1361 partition = "recovery/root/first_stage_ramdisk" 1362 } else { 1363 partition = "ramdisk" 1364 } 1365 if !ctx.InstallInRoot() { 1366 partition += "/system" 1367 } 1368 } else if ctx.InstallInRecovery() { 1369 if ctx.InstallInRoot() { 1370 partition = "recovery/root" 1371 } else { 1372 // the layout of recovery partion is the same as that of system partition 1373 partition = "recovery/root/system" 1374 } 1375 } else if ctx.SocSpecific() { 1376 partition = ctx.DeviceConfig().VendorPath() 1377 } else if ctx.DeviceSpecific() { 1378 partition = ctx.DeviceConfig().OdmPath() 1379 } else if ctx.ProductSpecific() { 1380 partition = ctx.DeviceConfig().ProductPath() 1381 } else if ctx.SystemExtSpecific() { 1382 partition = ctx.DeviceConfig().SystemExtPath() 1383 } else if ctx.InstallInRoot() { 1384 partition = "root" 1385 } else { 1386 partition = "system" 1387 } 1388 if ctx.InstallInSanitizerDir() { 1389 partition = "data/asan/" + partition 1390 } 1391 } 1392 return partition 1393} 1394 1395type InstallPaths []InstallPath 1396 1397// Paths returns the InstallPaths as a Paths 1398func (p InstallPaths) Paths() Paths { 1399 if p == nil { 1400 return nil 1401 } 1402 ret := make(Paths, len(p)) 1403 for i, path := range p { 1404 ret[i] = path 1405 } 1406 return ret 1407} 1408 1409// Strings returns the string forms of the install paths. 1410func (p InstallPaths) Strings() []string { 1411 if p == nil { 1412 return nil 1413 } 1414 ret := make([]string, len(p)) 1415 for i, path := range p { 1416 ret[i] = path.String() 1417 } 1418 return ret 1419} 1420 1421// validateSafePath validates a path that we trust (may contain ninja variables). 1422// Ensures that each path component does not attempt to leave its component. 1423func validateSafePath(pathComponents ...string) (string, error) { 1424 for _, path := range pathComponents { 1425 path := filepath.Clean(path) 1426 if path == ".." || strings.HasPrefix(path, "../") || strings.HasPrefix(path, "/") { 1427 return "", fmt.Errorf("Path is outside directory: %s", path) 1428 } 1429 } 1430 // TODO: filepath.Join isn't necessarily correct with embedded ninja 1431 // variables. '..' may remove the entire ninja variable, even if it 1432 // will be expanded to multiple nested directories. 1433 return filepath.Join(pathComponents...), nil 1434} 1435 1436// validatePath validates that a path does not include ninja variables, and that 1437// each path component does not attempt to leave its component. Returns a joined 1438// version of each path component. 1439func validatePath(pathComponents ...string) (string, error) { 1440 for _, path := range pathComponents { 1441 if strings.Contains(path, "$") { 1442 return "", fmt.Errorf("Path contains invalid character($): %s", path) 1443 } 1444 } 1445 return validateSafePath(pathComponents...) 1446} 1447 1448func PathForPhony(ctx PathContext, phony string) WritablePath { 1449 if strings.ContainsAny(phony, "$/") { 1450 reportPathErrorf(ctx, "Phony target contains invalid character ($ or /): %s", phony) 1451 } 1452 return PhonyPath{basePath{phony, ctx.Config(), ""}} 1453} 1454 1455type PhonyPath struct { 1456 basePath 1457} 1458 1459func (p PhonyPath) writablePath() {} 1460 1461func (p PhonyPath) buildDir() string { 1462 return p.config.buildDir 1463} 1464 1465var _ Path = PhonyPath{} 1466var _ WritablePath = PhonyPath{} 1467 1468type testPath struct { 1469 basePath 1470} 1471 1472func (p testPath) String() string { 1473 return p.path 1474} 1475 1476// PathForTesting returns a Path constructed from joining the elements of paths with '/'. It should only be used from 1477// within tests. 1478func PathForTesting(paths ...string) Path { 1479 p, err := validateSafePath(paths...) 1480 if err != nil { 1481 panic(err) 1482 } 1483 return testPath{basePath{path: p, rel: p}} 1484} 1485 1486// PathsForTesting returns a Path constructed from each element in strs. It should only be used from within tests. 1487func PathsForTesting(strs ...string) Paths { 1488 p := make(Paths, len(strs)) 1489 for i, s := range strs { 1490 p[i] = PathForTesting(s) 1491 } 1492 1493 return p 1494} 1495 1496type testPathContext struct { 1497 config Config 1498} 1499 1500func (x *testPathContext) Config() Config { return x.config } 1501func (x *testPathContext) AddNinjaFileDeps(...string) {} 1502 1503// PathContextForTesting returns a PathContext that can be used in tests, for example to create an OutputPath with 1504// PathForOutput. 1505func PathContextForTesting(config Config) PathContext { 1506 return &testPathContext{ 1507 config: config, 1508 } 1509} 1510 1511// Rel performs the same function as filepath.Rel, but reports errors to a PathContext, and reports an error if 1512// targetPath is not inside basePath. 1513func Rel(ctx PathContext, basePath string, targetPath string) string { 1514 rel, isRel := MaybeRel(ctx, basePath, targetPath) 1515 if !isRel { 1516 reportPathErrorf(ctx, "path %q is not under path %q", targetPath, basePath) 1517 return "" 1518 } 1519 return rel 1520} 1521 1522// MaybeRel performs the same function as filepath.Rel, but reports errors to a PathContext, and returns false if 1523// targetPath is not inside basePath. 1524func MaybeRel(ctx PathContext, basePath string, targetPath string) (string, bool) { 1525 rel, isRel, err := maybeRelErr(basePath, targetPath) 1526 if err != nil { 1527 reportPathError(ctx, err) 1528 } 1529 return rel, isRel 1530} 1531 1532func maybeRelErr(basePath string, targetPath string) (string, bool, error) { 1533 // filepath.Rel returns an error if one path is absolute and the other is not, handle that case first. 1534 if filepath.IsAbs(basePath) != filepath.IsAbs(targetPath) { 1535 return "", false, nil 1536 } 1537 rel, err := filepath.Rel(basePath, targetPath) 1538 if err != nil { 1539 return "", false, err 1540 } else if rel == ".." || strings.HasPrefix(rel, "../") || strings.HasPrefix(rel, "/") { 1541 return "", false, nil 1542 } 1543 return rel, true, nil 1544} 1545 1546// Writes a file to the output directory. Attempting to write directly to the output directory 1547// will fail due to the sandbox of the soong_build process. 1548func WriteFileToOutputDir(path WritablePath, data []byte, perm os.FileMode) error { 1549 return ioutil.WriteFile(absolutePath(path.String()), data, perm) 1550} 1551 1552func absolutePath(path string) string { 1553 if filepath.IsAbs(path) { 1554 return path 1555 } 1556 return filepath.Join(absSrcDir, path) 1557} 1558 1559// A DataPath represents the path of a file to be used as data, for example 1560// a test library to be installed alongside a test. 1561// The data file should be installed (copied from `<SrcPath>`) to 1562// `<install_root>/<RelativeInstallPath>/<filename>`, or 1563// `<install_root>/<filename>` if RelativeInstallPath is empty. 1564type DataPath struct { 1565 // The path of the data file that should be copied into the data directory 1566 SrcPath Path 1567 // The install path of the data file, relative to the install root. 1568 RelativeInstallPath string 1569} 1570