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