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 "strings" 22 23 "github.com/google/blueprint" 24 "github.com/google/blueprint/pathtools" 25) 26 27// PathContext is the subset of a (Module|Singleton)Context required by the 28// Path methods. 29type PathContext interface { 30 Fs() pathtools.FileSystem 31 Config() interface{} 32 AddNinjaFileDeps(deps ...string) 33} 34 35type PathGlobContext interface { 36 GlobWithDeps(globPattern string, excludes []string) ([]string, error) 37} 38 39var _ PathContext = blueprint.SingletonContext(nil) 40var _ PathContext = blueprint.ModuleContext(nil) 41 42type ModuleInstallPathContext interface { 43 PathContext 44 45 androidBaseContext 46 47 InstallInData() bool 48 InstallInSanitizerDir() bool 49} 50 51var _ ModuleInstallPathContext = ModuleContext(nil) 52 53// errorfContext is the interface containing the Errorf method matching the 54// Errorf method in blueprint.SingletonContext. 55type errorfContext interface { 56 Errorf(format string, args ...interface{}) 57} 58 59var _ errorfContext = blueprint.SingletonContext(nil) 60 61// moduleErrorf is the interface containing the ModuleErrorf method matching 62// the ModuleErrorf method in blueprint.ModuleContext. 63type moduleErrorf interface { 64 ModuleErrorf(format string, args ...interface{}) 65} 66 67var _ moduleErrorf = blueprint.ModuleContext(nil) 68 69// pathConfig returns the android Config interface associated to the context. 70// Panics if the context isn't affiliated with an android build. 71func pathConfig(ctx PathContext) Config { 72 if ret, ok := ctx.Config().(Config); ok { 73 return ret 74 } 75 panic("Paths may only be used on Soong builds") 76} 77 78// reportPathError 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 reportPathError(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. 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 reportPathError(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 reportPathError(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 reportPathError(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 if pathConfig(ctx).AllowMissingDependencies() { 200 if modCtx, ok := ctx.(ModuleContext); ok { 201 ret := make(Paths, 0, len(paths)) 202 intermediates := filepath.Join(modCtx.ModuleDir(), modCtx.ModuleName(), modCtx.ModuleSubDir(), "missing") 203 for _, path := range paths { 204 p := ExistentPathForSource(ctx, intermediates, path) 205 if p.Valid() { 206 ret = append(ret, p.Path()) 207 } else { 208 modCtx.AddMissingDependencies([]string{path}) 209 } 210 } 211 return ret 212 } 213 } 214 ret := make(Paths, len(paths)) 215 for i, path := range paths { 216 ret[i] = PathForSource(ctx, path) 217 } 218 return ret 219} 220 221// ExistentPathsForSources returns a list of Paths rooted from SrcDir that are 222// found in the tree. If any are not found, they are omitted from the list, 223// and dependencies are added so that we're re-run when they are added. 224func ExistentPathsForSources(ctx PathContext, intermediates string, paths []string) Paths { 225 ret := make(Paths, 0, len(paths)) 226 for _, path := range paths { 227 p := ExistentPathForSource(ctx, intermediates, path) 228 if p.Valid() { 229 ret = append(ret, p.Path()) 230 } 231 } 232 return ret 233} 234 235// PathsForModuleSrc returns Paths rooted from the module's local source 236// directory 237func PathsForModuleSrc(ctx ModuleContext, paths []string) Paths { 238 ret := make(Paths, len(paths)) 239 for i, path := range paths { 240 ret[i] = PathForModuleSrc(ctx, path) 241 } 242 return ret 243} 244 245// pathsForModuleSrcFromFullPath returns Paths rooted from the module's local 246// source directory, but strip the local source directory from the beginning of 247// each string. 248func pathsForModuleSrcFromFullPath(ctx ModuleContext, paths []string) Paths { 249 prefix := filepath.Join(ctx.AConfig().srcDir, ctx.ModuleDir()) + "/" 250 ret := make(Paths, 0, len(paths)) 251 for _, p := range paths { 252 path := filepath.Clean(p) 253 if !strings.HasPrefix(path, prefix) { 254 reportPathError(ctx, "Path '%s' is not in module source directory '%s'", p, prefix) 255 continue 256 } 257 ret = append(ret, PathForModuleSrc(ctx, path[len(prefix):])) 258 } 259 return ret 260} 261 262// PathsWithOptionalDefaultForModuleSrc returns Paths rooted from the module's 263// local source directory. If none are provided, use the default if it exists. 264func PathsWithOptionalDefaultForModuleSrc(ctx ModuleContext, input []string, def string) Paths { 265 if len(input) > 0 { 266 return PathsForModuleSrc(ctx, input) 267 } 268 // Use Glob so that if the default doesn't exist, a dependency is added so that when it 269 // is created, we're run again. 270 path := filepath.Join(ctx.AConfig().srcDir, ctx.ModuleDir(), def) 271 return ctx.Glob(path, []string{}) 272} 273 274// Strings returns the Paths in string form 275func (p Paths) Strings() []string { 276 if p == nil { 277 return nil 278 } 279 ret := make([]string, len(p)) 280 for i, path := range p { 281 ret[i] = path.String() 282 } 283 return ret 284} 285 286// WritablePaths is a slice of WritablePaths, used for multiple outputs. 287type WritablePaths []WritablePath 288 289// Strings returns the string forms of the writable paths. 290func (p WritablePaths) Strings() []string { 291 if p == nil { 292 return nil 293 } 294 ret := make([]string, len(p)) 295 for i, path := range p { 296 ret[i] = path.String() 297 } 298 return ret 299} 300 301type basePath struct { 302 path string 303 config Config 304 rel string 305} 306 307func (p basePath) Ext() string { 308 return filepath.Ext(p.path) 309} 310 311func (p basePath) Base() string { 312 return filepath.Base(p.path) 313} 314 315func (p basePath) Rel() string { 316 if p.rel != "" { 317 return p.rel 318 } 319 return p.path 320} 321 322// SourcePath is a Path representing a file path rooted from SrcDir 323type SourcePath struct { 324 basePath 325} 326 327var _ Path = SourcePath{} 328 329// safePathForSource is for paths that we expect are safe -- only for use by go 330// code that is embedding ninja variables in paths 331func safePathForSource(ctx PathContext, path string) SourcePath { 332 p := validateSafePath(ctx, path) 333 ret := SourcePath{basePath{p, pathConfig(ctx), ""}} 334 335 abs, err := filepath.Abs(ret.String()) 336 if err != nil { 337 reportPathError(ctx, "%s", err.Error()) 338 return ret 339 } 340 buildroot, err := filepath.Abs(pathConfig(ctx).buildDir) 341 if err != nil { 342 reportPathError(ctx, "%s", err.Error()) 343 return ret 344 } 345 if strings.HasPrefix(abs, buildroot) { 346 reportPathError(ctx, "source path %s is in output", abs) 347 return ret 348 } 349 350 return ret 351} 352 353// PathForSource joins the provided path components and validates that the result 354// neither escapes the source dir nor is in the out dir. 355// On error, it will return a usable, but invalid SourcePath, and report a ModuleError. 356func PathForSource(ctx PathContext, pathComponents ...string) SourcePath { 357 p := validatePath(ctx, pathComponents...) 358 ret := SourcePath{basePath{p, pathConfig(ctx), ""}} 359 360 abs, err := filepath.Abs(ret.String()) 361 if err != nil { 362 reportPathError(ctx, "%s", err.Error()) 363 return ret 364 } 365 buildroot, err := filepath.Abs(pathConfig(ctx).buildDir) 366 if err != nil { 367 reportPathError(ctx, "%s", err.Error()) 368 return ret 369 } 370 if strings.HasPrefix(abs, buildroot) { 371 reportPathError(ctx, "source path %s is in output", abs) 372 return ret 373 } 374 375 if exists, _, err := ctx.Fs().Exists(ret.String()); err != nil { 376 reportPathError(ctx, "%s: %s", ret, err.Error()) 377 } else if !exists { 378 reportPathError(ctx, "source path %s does not exist", ret) 379 } 380 return ret 381} 382 383// ExistentPathForSource returns an OptionalPath with the SourcePath if the 384// path exists, or an empty OptionalPath if it doesn't exist. Dependencies are added 385// so that the ninja file will be regenerated if the state of the path changes. 386func ExistentPathForSource(ctx PathContext, intermediates string, pathComponents ...string) OptionalPath { 387 if len(pathComponents) == 0 { 388 // For when someone forgets the 'intermediates' argument 389 panic("Missing path(s)") 390 } 391 392 p := validatePath(ctx, pathComponents...) 393 path := SourcePath{basePath{p, pathConfig(ctx), ""}} 394 395 abs, err := filepath.Abs(path.String()) 396 if err != nil { 397 reportPathError(ctx, "%s", err.Error()) 398 return OptionalPath{} 399 } 400 buildroot, err := filepath.Abs(pathConfig(ctx).buildDir) 401 if err != nil { 402 reportPathError(ctx, "%s", err.Error()) 403 return OptionalPath{} 404 } 405 if strings.HasPrefix(abs, buildroot) { 406 reportPathError(ctx, "source path %s is in output", abs) 407 return OptionalPath{} 408 } 409 410 if pathtools.IsGlob(path.String()) { 411 reportPathError(ctx, "path may not contain a glob: %s", path.String()) 412 return OptionalPath{} 413 } 414 415 if gctx, ok := ctx.(PathGlobContext); ok { 416 // Use glob to produce proper dependencies, even though we only want 417 // a single file. 418 files, err := gctx.GlobWithDeps(path.String(), nil) 419 if err != nil { 420 reportPathError(ctx, "glob: %s", err.Error()) 421 return OptionalPath{} 422 } 423 424 if len(files) == 0 { 425 return OptionalPath{} 426 } 427 } else { 428 // We cannot add build statements in this context, so we fall back to 429 // AddNinjaFileDeps 430 files, dirs, err := pathtools.Glob(path.String(), nil) 431 if err != nil { 432 reportPathError(ctx, "glob: %s", err.Error()) 433 return OptionalPath{} 434 } 435 436 ctx.AddNinjaFileDeps(dirs...) 437 438 if len(files) == 0 { 439 return OptionalPath{} 440 } 441 442 ctx.AddNinjaFileDeps(path.String()) 443 } 444 return OptionalPathForPath(path) 445} 446 447func (p SourcePath) String() string { 448 return filepath.Join(p.config.srcDir, p.path) 449} 450 451// Join creates a new SourcePath with paths... joined with the current path. The 452// provided paths... may not use '..' to escape from the current path. 453func (p SourcePath) Join(ctx PathContext, paths ...string) SourcePath { 454 path := validatePath(ctx, paths...) 455 return PathForSource(ctx, p.path, path) 456} 457 458// OverlayPath returns the overlay for `path' if it exists. This assumes that the 459// SourcePath is the path to a resource overlay directory. 460func (p SourcePath) OverlayPath(ctx ModuleContext, path Path) OptionalPath { 461 var relDir string 462 if moduleSrcPath, ok := path.(ModuleSrcPath); ok { 463 relDir = moduleSrcPath.path 464 } else if srcPath, ok := path.(SourcePath); ok { 465 relDir = srcPath.path 466 } else { 467 reportPathError(ctx, "Cannot find relative path for %s(%s)", reflect.TypeOf(path).Name(), path) 468 return OptionalPath{} 469 } 470 dir := filepath.Join(p.config.srcDir, p.path, relDir) 471 // Use Glob so that we are run again if the directory is added. 472 if pathtools.IsGlob(dir) { 473 reportPathError(ctx, "Path may not contain a glob: %s", dir) 474 } 475 paths, err := ctx.GlobWithDeps(dir, []string{}) 476 if err != nil { 477 reportPathError(ctx, "glob: %s", err.Error()) 478 return OptionalPath{} 479 } 480 if len(paths) == 0 { 481 return OptionalPath{} 482 } 483 relPath, err := filepath.Rel(p.config.srcDir, paths[0]) 484 if err != nil { 485 reportPathError(ctx, "%s", err.Error()) 486 return OptionalPath{} 487 } 488 return OptionalPathForPath(PathForSource(ctx, relPath)) 489} 490 491// OutputPath is a Path representing a file path rooted from the build directory 492type OutputPath struct { 493 basePath 494} 495 496var _ Path = OutputPath{} 497 498// PathForOutput joins the provided paths and returns an OutputPath that is 499// validated to not escape the build dir. 500// On error, it will return a usable, but invalid OutputPath, and report a ModuleError. 501func PathForOutput(ctx PathContext, pathComponents ...string) OutputPath { 502 path := validatePath(ctx, pathComponents...) 503 return OutputPath{basePath{path, pathConfig(ctx), ""}} 504} 505 506func (p OutputPath) writablePath() {} 507 508func (p OutputPath) String() string { 509 return filepath.Join(p.config.buildDir, p.path) 510} 511 512func (p OutputPath) RelPathString() string { 513 return p.path 514} 515 516// Join creates a new OutputPath with paths... joined with the current path. The 517// provided paths... may not use '..' to escape from the current path. 518func (p OutputPath) Join(ctx PathContext, paths ...string) OutputPath { 519 path := validatePath(ctx, paths...) 520 return PathForOutput(ctx, p.path, path) 521} 522 523// PathForIntermediates returns an OutputPath representing the top-level 524// intermediates directory. 525func PathForIntermediates(ctx PathContext, paths ...string) OutputPath { 526 path := validatePath(ctx, paths...) 527 return PathForOutput(ctx, ".intermediates", path) 528} 529 530// ModuleSrcPath is a Path representing a file rooted from a module's local source dir 531type ModuleSrcPath struct { 532 SourcePath 533} 534 535var _ Path = ModuleSrcPath{} 536var _ genPathProvider = ModuleSrcPath{} 537var _ objPathProvider = ModuleSrcPath{} 538var _ resPathProvider = ModuleSrcPath{} 539 540// PathForModuleSrc returns a ModuleSrcPath representing the paths... under the 541// module's local source directory. 542func PathForModuleSrc(ctx ModuleContext, paths ...string) ModuleSrcPath { 543 p := validatePath(ctx, paths...) 544 path := ModuleSrcPath{PathForSource(ctx, ctx.ModuleDir(), p)} 545 path.basePath.rel = p 546 return path 547} 548 549// OptionalPathForModuleSrc returns an OptionalPath. The OptionalPath contains a 550// valid path if p is non-nil. 551func OptionalPathForModuleSrc(ctx ModuleContext, p *string) OptionalPath { 552 if p == nil { 553 return OptionalPath{} 554 } 555 return OptionalPathForPath(PathForModuleSrc(ctx, *p)) 556} 557 558func (p ModuleSrcPath) genPathWithExt(ctx ModuleContext, subdir, ext string) ModuleGenPath { 559 return PathForModuleGen(ctx, subdir, pathtools.ReplaceExtension(p.path, ext)) 560} 561 562func (p ModuleSrcPath) objPathWithExt(ctx ModuleContext, subdir, ext string) ModuleObjPath { 563 return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext)) 564} 565 566func (p ModuleSrcPath) resPathWithName(ctx ModuleContext, name string) ModuleResPath { 567 // TODO: Use full directory if the new ctx is not the current ctx? 568 return PathForModuleRes(ctx, p.path, name) 569} 570 571func (p ModuleSrcPath) WithSubDir(ctx ModuleContext, subdir string) ModuleSrcPath { 572 subdir = PathForModuleSrc(ctx, subdir).String() 573 var err error 574 rel, err := filepath.Rel(subdir, p.path) 575 if err != nil { 576 ctx.ModuleErrorf("source file %q is not under path %q", p.path, subdir) 577 return p 578 } 579 p.rel = rel 580 return p 581} 582 583// ModuleOutPath is a Path representing a module's output directory. 584type ModuleOutPath struct { 585 OutputPath 586} 587 588var _ Path = ModuleOutPath{} 589 590// PathForVndkRefDump returns an OptionalPath representing the path of the reference 591// abi dump for the given module. This is not guaranteed to be valid. 592func PathForVndkRefAbiDump(ctx ModuleContext, version, fileName string, vndkOrNdk, isSourceDump bool) OptionalPath { 593 archName := ctx.Arch().ArchType.Name 594 var sourceOrBinaryDir string 595 var vndkOrNdkDir string 596 var ext string 597 if isSourceDump { 598 ext = ".lsdump.gz" 599 sourceOrBinaryDir = "source-based" 600 } else { 601 ext = ".bdump.gz" 602 sourceOrBinaryDir = "binary-based" 603 } 604 if vndkOrNdk { 605 vndkOrNdkDir = "vndk" 606 } else { 607 vndkOrNdkDir = "ndk" 608 } 609 refDumpFileStr := "prebuilts/abi-dumps/" + vndkOrNdkDir + "/" + version + "/" + 610 archName + "/" + sourceOrBinaryDir + "/" + fileName + ext 611 return ExistentPathForSource(ctx, "", refDumpFileStr) 612} 613 614// PathForModuleOut returns a Path representing the paths... under the module's 615// output directory. 616func PathForModuleOut(ctx ModuleContext, paths ...string) ModuleOutPath { 617 p := validatePath(ctx, paths...) 618 return ModuleOutPath{PathForOutput(ctx, ".intermediates", ctx.ModuleDir(), ctx.ModuleName(), ctx.ModuleSubDir(), p)} 619} 620 621// ModuleGenPath is a Path representing the 'gen' directory in a module's output 622// directory. Mainly used for generated sources. 623type ModuleGenPath struct { 624 ModuleOutPath 625 path string 626} 627 628var _ Path = ModuleGenPath{} 629var _ genPathProvider = ModuleGenPath{} 630var _ objPathProvider = ModuleGenPath{} 631 632// PathForModuleGen returns a Path representing the paths... under the module's 633// `gen' directory. 634func PathForModuleGen(ctx ModuleContext, paths ...string) ModuleGenPath { 635 p := validatePath(ctx, paths...) 636 return ModuleGenPath{ 637 PathForModuleOut(ctx, "gen", p), 638 p, 639 } 640} 641 642func (p ModuleGenPath) genPathWithExt(ctx ModuleContext, subdir, ext string) ModuleGenPath { 643 // TODO: make a different path for local vs remote generated files? 644 return PathForModuleGen(ctx, subdir, pathtools.ReplaceExtension(p.path, ext)) 645} 646 647func (p ModuleGenPath) objPathWithExt(ctx ModuleContext, subdir, ext string) ModuleObjPath { 648 return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext)) 649} 650 651// ModuleObjPath is a Path representing the 'obj' directory in a module's output 652// directory. Used for compiled objects. 653type ModuleObjPath struct { 654 ModuleOutPath 655} 656 657var _ Path = ModuleObjPath{} 658 659// PathForModuleObj returns a Path representing the paths... under the module's 660// 'obj' directory. 661func PathForModuleObj(ctx ModuleContext, pathComponents ...string) ModuleObjPath { 662 p := validatePath(ctx, pathComponents...) 663 return ModuleObjPath{PathForModuleOut(ctx, "obj", p)} 664} 665 666// ModuleResPath is a a Path representing the 'res' directory in a module's 667// output directory. 668type ModuleResPath struct { 669 ModuleOutPath 670} 671 672var _ Path = ModuleResPath{} 673 674// PathForModuleRes returns a Path representing the paths... under the module's 675// 'res' directory. 676func PathForModuleRes(ctx ModuleContext, pathComponents ...string) ModuleResPath { 677 p := validatePath(ctx, pathComponents...) 678 return ModuleResPath{PathForModuleOut(ctx, "res", p)} 679} 680 681// PathForModuleInstall returns a Path representing the install path for the 682// module appended with paths... 683func PathForModuleInstall(ctx ModuleInstallPathContext, pathComponents ...string) OutputPath { 684 var outPaths []string 685 if ctx.Device() { 686 var partition string 687 if ctx.InstallInData() { 688 partition = "data" 689 } else if ctx.Vendor() { 690 partition = ctx.DeviceConfig().VendorPath() 691 } else { 692 partition = "system" 693 } 694 695 if ctx.InstallInSanitizerDir() { 696 partition = "data/asan/" + partition 697 } 698 outPaths = []string{"target", "product", ctx.AConfig().DeviceName(), partition} 699 } else { 700 outPaths = []string{"host", ctx.Os().String() + "-x86"} 701 } 702 if ctx.Debug() { 703 outPaths = append([]string{"debug"}, outPaths...) 704 } 705 outPaths = append(outPaths, pathComponents...) 706 return PathForOutput(ctx, outPaths...) 707} 708 709// validateSafePath validates a path that we trust (may contain ninja variables). 710// Ensures that each path component does not attempt to leave its component. 711func validateSafePath(ctx PathContext, pathComponents ...string) string { 712 for _, path := range pathComponents { 713 path := filepath.Clean(path) 714 if path == ".." || strings.HasPrefix(path, "../") || strings.HasPrefix(path, "/") { 715 reportPathError(ctx, "Path is outside directory: %s", path) 716 return "" 717 } 718 } 719 // TODO: filepath.Join isn't necessarily correct with embedded ninja 720 // variables. '..' may remove the entire ninja variable, even if it 721 // will be expanded to multiple nested directories. 722 return filepath.Join(pathComponents...) 723} 724 725// validatePath validates that a path does not include ninja variables, and that 726// each path component does not attempt to leave its component. Returns a joined 727// version of each path component. 728func validatePath(ctx PathContext, pathComponents ...string) string { 729 for _, path := range pathComponents { 730 if strings.Contains(path, "$") { 731 reportPathError(ctx, "Path contains invalid character($): %s", path) 732 return "" 733 } 734 } 735 return validateSafePath(ctx, pathComponents...) 736} 737 738type testPath struct { 739 basePath 740} 741 742func (p testPath) String() string { 743 return p.path 744} 745 746func PathForTesting(paths ...string) Path { 747 p := validateSafePath(nil, paths...) 748 return testPath{basePath{path: p, rel: p}} 749} 750 751func PathsForTesting(strs []string) Paths { 752 p := make(Paths, len(strs)) 753 for i, s := range strs { 754 p[i] = PathForTesting(s) 755 } 756 757 return p 758} 759