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