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 15 package android 16 17 import ( 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. 29 type PathContext interface { 30 Fs() pathtools.FileSystem 31 Config() interface{} 32 AddNinjaFileDeps(deps ...string) 33 } 34 35 type PathGlobContext interface { 36 GlobWithDeps(globPattern string, excludes []string) ([]string, error) 37 } 38 39 var _ PathContext = blueprint.SingletonContext(nil) 40 var _ PathContext = blueprint.ModuleContext(nil) 41 42 // errorfContext is the interface containing the Errorf method matching the 43 // Errorf method in blueprint.SingletonContext. 44 type errorfContext interface { 45 Errorf(format string, args ...interface{}) 46 } 47 48 var _ errorfContext = blueprint.SingletonContext(nil) 49 50 // moduleErrorf is the interface containing the ModuleErrorf method matching 51 // the ModuleErrorf method in blueprint.ModuleContext. 52 type moduleErrorf interface { 53 ModuleErrorf(format string, args ...interface{}) 54 } 55 56 var _ moduleErrorf = blueprint.ModuleContext(nil) 57 58 // pathConfig returns the android Config interface associated to the context. 59 // Panics if the context isn't affiliated with an android build. 60 func pathConfig(ctx PathContext) Config { 61 if ret, ok := ctx.Config().(Config); ok { 62 return ret 63 } 64 panic("Paths may only be used on Soong builds") 65 } 66 67 // reportPathError will register an error with the attached context. It 68 // attempts ctx.ModuleErrorf for a better error message first, then falls 69 // back to ctx.Errorf. 70 func reportPathError(ctx PathContext, format string, args ...interface{}) { 71 if mctx, ok := ctx.(moduleErrorf); ok { 72 mctx.ModuleErrorf(format, args...) 73 } else if ectx, ok := ctx.(errorfContext); ok { 74 ectx.Errorf(format, args...) 75 } else { 76 panic(fmt.Sprintf(format, args...)) 77 } 78 } 79 80 type Path interface { 81 // Returns the path in string form 82 String() string 83 84 // Ext returns the extension of the last element of the path 85 Ext() string 86 87 // Base returns the last element of the path 88 Base() string 89 90 // Rel returns the portion of the path relative to the directory it was created from. For 91 // example, Rel on a PathsForModuleSrc would return the path relative to the module source 92 // directory. 93 Rel() string 94 } 95 96 // WritablePath is a type of path that can be used as an output for build rules. 97 type WritablePath interface { 98 Path 99 100 writablePath() 101 } 102 103 type genPathProvider interface { 104 genPathWithExt(ctx ModuleContext, subdir, ext string) ModuleGenPath 105 } 106 type objPathProvider interface { 107 objPathWithExt(ctx ModuleContext, subdir, ext string) ModuleObjPath 108 } 109 type resPathProvider interface { 110 resPathWithName(ctx ModuleContext, name string) ModuleResPath 111 } 112 113 // GenPathWithExt derives a new file path in ctx's generated sources directory 114 // from the current path, but with the new extension. 115 func GenPathWithExt(ctx ModuleContext, subdir string, p Path, ext string) ModuleGenPath { 116 if path, ok := p.(genPathProvider); ok { 117 return path.genPathWithExt(ctx, subdir, ext) 118 } 119 reportPathError(ctx, "Tried to create generated file from unsupported path: %s(%s)", reflect.TypeOf(p).Name(), p) 120 return PathForModuleGen(ctx) 121 } 122 123 // ObjPathWithExt derives a new file path in ctx's object directory from the 124 // current path, but with the new extension. 125 func ObjPathWithExt(ctx ModuleContext, subdir string, p Path, ext string) ModuleObjPath { 126 if path, ok := p.(objPathProvider); ok { 127 return path.objPathWithExt(ctx, subdir, ext) 128 } 129 reportPathError(ctx, "Tried to create object file from unsupported path: %s (%s)", reflect.TypeOf(p).Name(), p) 130 return PathForModuleObj(ctx) 131 } 132 133 // ResPathWithName derives a new path in ctx's output resource directory, using 134 // the current path to create the directory name, and the `name` argument for 135 // the filename. 136 func ResPathWithName(ctx ModuleContext, p Path, name string) ModuleResPath { 137 if path, ok := p.(resPathProvider); ok { 138 return path.resPathWithName(ctx, name) 139 } 140 reportPathError(ctx, "Tried to create object file from unsupported path: %s (%s)", reflect.TypeOf(p).Name(), p) 141 return PathForModuleRes(ctx) 142 } 143 144 // OptionalPath is a container that may or may not contain a valid Path. 145 type OptionalPath struct { 146 valid bool 147 path Path 148 } 149 150 // OptionalPathForPath returns an OptionalPath containing the path. 151 func OptionalPathForPath(path Path) OptionalPath { 152 if path == nil { 153 return OptionalPath{} 154 } 155 return OptionalPath{valid: true, path: path} 156 } 157 158 // Valid returns whether there is a valid path 159 func (p OptionalPath) Valid() bool { 160 return p.valid 161 } 162 163 // Path returns the Path embedded in this OptionalPath. You must be sure that 164 // there is a valid path, since this method will panic if there is not. 165 func (p OptionalPath) Path() Path { 166 if !p.valid { 167 panic("Requesting an invalid path") 168 } 169 return p.path 170 } 171 172 // String returns the string version of the Path, or "" if it isn't valid. 173 func (p OptionalPath) String() string { 174 if p.valid { 175 return p.path.String() 176 } else { 177 return "" 178 } 179 } 180 181 // Paths is a slice of Path objects, with helpers to operate on the collection. 182 type Paths []Path 183 184 // PathsForSource returns Paths rooted from SrcDir 185 func PathsForSource(ctx PathContext, paths []string) Paths { 186 if pathConfig(ctx).AllowMissingDependencies() { 187 if modCtx, ok := ctx.(ModuleContext); ok { 188 ret := make(Paths, 0, len(paths)) 189 intermediates := filepath.Join(modCtx.ModuleDir(), modCtx.ModuleName(), modCtx.ModuleSubDir(), "missing") 190 for _, path := range paths { 191 p := OptionalPathForSource(ctx, intermediates, path) 192 if p.Valid() { 193 ret = append(ret, p.Path()) 194 } else { 195 modCtx.AddMissingDependencies([]string{path}) 196 } 197 } 198 return ret 199 } 200 } 201 ret := make(Paths, len(paths)) 202 for i, path := range paths { 203 ret[i] = PathForSource(ctx, path) 204 } 205 return ret 206 } 207 208 // PathsForOptionalSource returns a list of Paths rooted from SrcDir that are 209 // found in the tree. If any are not found, they are omitted from the list, 210 // and dependencies are added so that we're re-run when they are added. 211 func PathsForOptionalSource(ctx PathContext, intermediates string, paths []string) Paths { 212 ret := make(Paths, 0, len(paths)) 213 for _, path := range paths { 214 p := OptionalPathForSource(ctx, intermediates, path) 215 if p.Valid() { 216 ret = append(ret, p.Path()) 217 } 218 } 219 return ret 220 } 221 222 // PathsForModuleSrc returns Paths rooted from the module's local source 223 // directory 224 func PathsForModuleSrc(ctx ModuleContext, paths []string) Paths { 225 ret := make(Paths, len(paths)) 226 for i, path := range paths { 227 ret[i] = PathForModuleSrc(ctx, path) 228 } 229 return ret 230 } 231 232 // pathsForModuleSrcFromFullPath returns Paths rooted from the module's local 233 // source directory, but strip the local source directory from the beginning of 234 // each string. 235 func pathsForModuleSrcFromFullPath(ctx ModuleContext, paths []string) Paths { 236 prefix := filepath.Join(ctx.AConfig().srcDir, ctx.ModuleDir()) + "/" 237 ret := make(Paths, 0, len(paths)) 238 for _, p := range paths { 239 path := filepath.Clean(p) 240 if !strings.HasPrefix(path, prefix) { 241 reportPathError(ctx, "Path '%s' is not in module source directory '%s'", p, prefix) 242 continue 243 } 244 ret = append(ret, PathForModuleSrc(ctx, path[len(prefix):])) 245 } 246 return ret 247 } 248 249 // PathsWithOptionalDefaultForModuleSrc returns Paths rooted from the module's 250 // local source directory. If none are provided, use the default if it exists. 251 func PathsWithOptionalDefaultForModuleSrc(ctx ModuleContext, input []string, def string) Paths { 252 if len(input) > 0 { 253 return PathsForModuleSrc(ctx, input) 254 } 255 // Use Glob so that if the default doesn't exist, a dependency is added so that when it 256 // is created, we're run again. 257 path := filepath.Join(ctx.AConfig().srcDir, ctx.ModuleDir(), def) 258 return ctx.Glob(path, []string{}) 259 } 260 261 // Strings returns the Paths in string form 262 func (p Paths) Strings() []string { 263 if p == nil { 264 return nil 265 } 266 ret := make([]string, len(p)) 267 for i, path := range p { 268 ret[i] = path.String() 269 } 270 return ret 271 } 272 273 // WritablePaths is a slice of WritablePaths, used for multiple outputs. 274 type WritablePaths []WritablePath 275 276 // Strings returns the string forms of the writable paths. 277 func (p WritablePaths) Strings() []string { 278 if p == nil { 279 return nil 280 } 281 ret := make([]string, len(p)) 282 for i, path := range p { 283 ret[i] = path.String() 284 } 285 return ret 286 } 287 288 type basePath struct { 289 path string 290 config Config 291 rel string 292 } 293 294 func (p basePath) Ext() string { 295 return filepath.Ext(p.path) 296 } 297 298 func (p basePath) Base() string { 299 return filepath.Base(p.path) 300 } 301 302 func (p basePath) Rel() string { 303 if p.rel != "" { 304 return p.rel 305 } 306 return p.path 307 } 308 309 // SourcePath is a Path representing a file path rooted from SrcDir 310 type SourcePath struct { 311 basePath 312 } 313 314 var _ Path = SourcePath{} 315 316 // safePathForSource is for paths that we expect are safe -- only for use by go 317 // code that is embedding ninja variables in paths 318 func safePathForSource(ctx PathContext, path string) SourcePath { 319 p := validateSafePath(ctx, path) 320 ret := SourcePath{basePath{p, pathConfig(ctx), ""}} 321 322 abs, err := filepath.Abs(ret.String()) 323 if err != nil { 324 reportPathError(ctx, "%s", err.Error()) 325 return ret 326 } 327 buildroot, err := filepath.Abs(pathConfig(ctx).buildDir) 328 if err != nil { 329 reportPathError(ctx, "%s", err.Error()) 330 return ret 331 } 332 if strings.HasPrefix(abs, buildroot) { 333 reportPathError(ctx, "source path %s is in output", abs) 334 return ret 335 } 336 337 return ret 338 } 339 340 // PathForSource returns a SourcePath for the provided paths... (which are 341 // joined together with filepath.Join). This also validates that the path 342 // doesn't escape the source dir, or is contained in the build dir. On error, it 343 // will return a usable, but invalid SourcePath, and report a ModuleError. 344 func PathForSource(ctx PathContext, paths ...string) SourcePath { 345 p := validatePath(ctx, paths...) 346 ret := SourcePath{basePath{p, pathConfig(ctx), ""}} 347 348 abs, err := filepath.Abs(ret.String()) 349 if err != nil { 350 reportPathError(ctx, "%s", err.Error()) 351 return ret 352 } 353 buildroot, err := filepath.Abs(pathConfig(ctx).buildDir) 354 if err != nil { 355 reportPathError(ctx, "%s", err.Error()) 356 return ret 357 } 358 if strings.HasPrefix(abs, buildroot) { 359 reportPathError(ctx, "source path %s is in output", abs) 360 return ret 361 } 362 363 if exists, _, err := ctx.Fs().Exists(ret.String()); err != nil { 364 reportPathError(ctx, "%s: %s", ret, err.Error()) 365 } else if !exists { 366 reportPathError(ctx, "source path %s does not exist", ret) 367 } 368 return ret 369 } 370 371 // OptionalPathForSource returns an OptionalPath with the SourcePath if the 372 // path exists, or an empty OptionalPath if it doesn't exist. Dependencies are added 373 // so that the ninja file will be regenerated if the state of the path changes. 374 func OptionalPathForSource(ctx PathContext, intermediates string, paths ...string) OptionalPath { 375 if len(paths) == 0 { 376 // For when someone forgets the 'intermediates' argument 377 panic("Missing path(s)") 378 } 379 380 p := validatePath(ctx, paths...) 381 path := SourcePath{basePath{p, pathConfig(ctx), ""}} 382 383 abs, err := filepath.Abs(path.String()) 384 if err != nil { 385 reportPathError(ctx, "%s", err.Error()) 386 return OptionalPath{} 387 } 388 buildroot, err := filepath.Abs(pathConfig(ctx).buildDir) 389 if err != nil { 390 reportPathError(ctx, "%s", err.Error()) 391 return OptionalPath{} 392 } 393 if strings.HasPrefix(abs, buildroot) { 394 reportPathError(ctx, "source path %s is in output", abs) 395 return OptionalPath{} 396 } 397 398 if pathtools.IsGlob(path.String()) { 399 reportPathError(ctx, "path may not contain a glob: %s", path.String()) 400 return OptionalPath{} 401 } 402 403 if gctx, ok := ctx.(PathGlobContext); ok { 404 // Use glob to produce proper dependencies, even though we only want 405 // a single file. 406 files, err := gctx.GlobWithDeps(path.String(), nil) 407 if err != nil { 408 reportPathError(ctx, "glob: %s", err.Error()) 409 return OptionalPath{} 410 } 411 412 if len(files) == 0 { 413 return OptionalPath{} 414 } 415 } else { 416 // We cannot add build statements in this context, so we fall back to 417 // AddNinjaFileDeps 418 files, dirs, err := pathtools.Glob(path.String(), nil) 419 if err != nil { 420 reportPathError(ctx, "glob: %s", err.Error()) 421 return OptionalPath{} 422 } 423 424 ctx.AddNinjaFileDeps(dirs...) 425 426 if len(files) == 0 { 427 return OptionalPath{} 428 } 429 430 ctx.AddNinjaFileDeps(path.String()) 431 } 432 return OptionalPathForPath(path) 433 } 434 435 func (p SourcePath) String() string { 436 return filepath.Join(p.config.srcDir, p.path) 437 } 438 439 // Join creates a new SourcePath with paths... joined with the current path. The 440 // provided paths... may not use '..' to escape from the current path. 441 func (p SourcePath) Join(ctx PathContext, paths ...string) SourcePath { 442 path := validatePath(ctx, paths...) 443 return PathForSource(ctx, p.path, path) 444 } 445 446 // OverlayPath returns the overlay for `path' if it exists. This assumes that the 447 // SourcePath is the path to a resource overlay directory. 448 func (p SourcePath) OverlayPath(ctx ModuleContext, path Path) OptionalPath { 449 var relDir string 450 if moduleSrcPath, ok := path.(ModuleSrcPath); ok { 451 relDir = moduleSrcPath.path 452 } else if srcPath, ok := path.(SourcePath); ok { 453 relDir = srcPath.path 454 } else { 455 reportPathError(ctx, "Cannot find relative path for %s(%s)", reflect.TypeOf(path).Name(), path) 456 return OptionalPath{} 457 } 458 dir := filepath.Join(p.config.srcDir, p.path, relDir) 459 // Use Glob so that we are run again if the directory is added. 460 if pathtools.IsGlob(dir) { 461 reportPathError(ctx, "Path may not contain a glob: %s", dir) 462 } 463 paths, err := ctx.GlobWithDeps(dir, []string{}) 464 if err != nil { 465 reportPathError(ctx, "glob: %s", err.Error()) 466 return OptionalPath{} 467 } 468 if len(paths) == 0 { 469 return OptionalPath{} 470 } 471 relPath, err := filepath.Rel(p.config.srcDir, paths[0]) 472 if err != nil { 473 reportPathError(ctx, "%s", err.Error()) 474 return OptionalPath{} 475 } 476 return OptionalPathForPath(PathForSource(ctx, relPath)) 477 } 478 479 // OutputPath is a Path representing a file path rooted from the build directory 480 type OutputPath struct { 481 basePath 482 } 483 484 var _ Path = OutputPath{} 485 486 // PathForOutput returns an OutputPath for the provided paths... (which are 487 // joined together with filepath.Join). This also validates that the path 488 // does not escape the build dir. On error, it will return a usable, but invalid 489 // OutputPath, and report a ModuleError. 490 func PathForOutput(ctx PathContext, paths ...string) OutputPath { 491 path := validatePath(ctx, paths...) 492 return OutputPath{basePath{path, pathConfig(ctx), ""}} 493 } 494 495 func (p OutputPath) writablePath() {} 496 497 func (p OutputPath) String() string { 498 return filepath.Join(p.config.buildDir, p.path) 499 } 500 501 func (p OutputPath) RelPathString() string { 502 return p.path 503 } 504 505 // Join creates a new OutputPath with paths... joined with the current path. The 506 // provided paths... may not use '..' to escape from the current path. 507 func (p OutputPath) Join(ctx PathContext, paths ...string) OutputPath { 508 path := validatePath(ctx, paths...) 509 return PathForOutput(ctx, p.path, path) 510 } 511 512 // PathForIntermediates returns an OutputPath representing the top-level 513 // intermediates directory. 514 func PathForIntermediates(ctx PathContext, paths ...string) OutputPath { 515 path := validatePath(ctx, paths...) 516 return PathForOutput(ctx, ".intermediates", path) 517 } 518 519 // ModuleSrcPath is a Path representing a file rooted from a module's local source dir 520 type ModuleSrcPath struct { 521 SourcePath 522 } 523 524 var _ Path = ModuleSrcPath{} 525 var _ genPathProvider = ModuleSrcPath{} 526 var _ objPathProvider = ModuleSrcPath{} 527 var _ resPathProvider = ModuleSrcPath{} 528 529 // PathForModuleSrc returns a ModuleSrcPath representing the paths... under the 530 // module's local source directory. 531 func PathForModuleSrc(ctx ModuleContext, paths ...string) ModuleSrcPath { 532 p := validatePath(ctx, paths...) 533 path := ModuleSrcPath{PathForSource(ctx, ctx.ModuleDir(), p)} 534 path.basePath.rel = p 535 return path 536 } 537 538 // OptionalPathForModuleSrc returns an OptionalPath. The OptionalPath contains a 539 // valid path if p is non-nil. 540 func OptionalPathForModuleSrc(ctx ModuleContext, p *string) OptionalPath { 541 if p == nil { 542 return OptionalPath{} 543 } 544 return OptionalPathForPath(PathForModuleSrc(ctx, *p)) 545 } 546 547 func (p ModuleSrcPath) genPathWithExt(ctx ModuleContext, subdir, ext string) ModuleGenPath { 548 return PathForModuleGen(ctx, subdir, pathtools.ReplaceExtension(p.path, ext)) 549 } 550 551 func (p ModuleSrcPath) objPathWithExt(ctx ModuleContext, subdir, ext string) ModuleObjPath { 552 return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext)) 553 } 554 555 func (p ModuleSrcPath) resPathWithName(ctx ModuleContext, name string) ModuleResPath { 556 // TODO: Use full directory if the new ctx is not the current ctx? 557 return PathForModuleRes(ctx, p.path, name) 558 } 559 560 func (p ModuleSrcPath) WithSubDir(ctx ModuleContext, subdir string) ModuleSrcPath { 561 subdir = PathForModuleSrc(ctx, subdir).String() 562 var err error 563 rel, err := filepath.Rel(subdir, p.path) 564 if err != nil { 565 ctx.ModuleErrorf("source file %q is not under path %q", p.path, subdir) 566 return p 567 } 568 p.rel = rel 569 return p 570 } 571 572 // ModuleOutPath is a Path representing a module's output directory. 573 type ModuleOutPath struct { 574 OutputPath 575 } 576 577 var _ Path = ModuleOutPath{} 578 579 // PathForVndkRefDump returns an OptionalPath representing the path of the reference 580 // abi dump for the given module. This is not guaranteed to be valid. 581 func PathForVndkRefAbiDump(ctx ModuleContext, version, fileName string, vndkOrNdk, isSourceDump bool) OptionalPath { 582 archName := ctx.Arch().ArchType.Name 583 var sourceOrBinaryDir string 584 var vndkOrNdkDir string 585 var ext string 586 if isSourceDump { 587 ext = ".lsdump.gz" 588 sourceOrBinaryDir = "source-based" 589 } else { 590 ext = ".bdump.gz" 591 sourceOrBinaryDir = "binary-based" 592 } 593 if vndkOrNdk { 594 vndkOrNdkDir = "vndk" 595 } else { 596 vndkOrNdkDir = "ndk" 597 } 598 refDumpFileStr := "prebuilts/abi-dumps/" + vndkOrNdkDir + "/" + version + "/" + 599 archName + "/" + sourceOrBinaryDir + "/" + fileName + ext 600 return OptionalPathForSource(ctx, "", refDumpFileStr) 601 } 602 603 // PathForModuleOut returns a Path representing the paths... under the module's 604 // output directory. 605 func PathForModuleOut(ctx ModuleContext, paths ...string) ModuleOutPath { 606 p := validatePath(ctx, paths...) 607 return ModuleOutPath{PathForOutput(ctx, ".intermediates", ctx.ModuleDir(), ctx.ModuleName(), ctx.ModuleSubDir(), p)} 608 } 609 610 // ModuleGenPath is a Path representing the 'gen' directory in a module's output 611 // directory. Mainly used for generated sources. 612 type ModuleGenPath struct { 613 ModuleOutPath 614 path string 615 } 616 617 var _ Path = ModuleGenPath{} 618 var _ genPathProvider = ModuleGenPath{} 619 var _ objPathProvider = ModuleGenPath{} 620 621 // PathForModuleGen returns a Path representing the paths... under the module's 622 // `gen' directory. 623 func PathForModuleGen(ctx ModuleContext, paths ...string) ModuleGenPath { 624 p := validatePath(ctx, paths...) 625 return ModuleGenPath{ 626 PathForModuleOut(ctx, "gen", p), 627 p, 628 } 629 } 630 631 func (p ModuleGenPath) genPathWithExt(ctx ModuleContext, subdir, ext string) ModuleGenPath { 632 // TODO: make a different path for local vs remote generated files? 633 return PathForModuleGen(ctx, subdir, pathtools.ReplaceExtension(p.path, ext)) 634 } 635 636 func (p ModuleGenPath) objPathWithExt(ctx ModuleContext, subdir, ext string) ModuleObjPath { 637 return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext)) 638 } 639 640 // ModuleObjPath is a Path representing the 'obj' directory in a module's output 641 // directory. Used for compiled objects. 642 type ModuleObjPath struct { 643 ModuleOutPath 644 } 645 646 var _ Path = ModuleObjPath{} 647 648 // PathForModuleObj returns a Path representing the paths... under the module's 649 // 'obj' directory. 650 func PathForModuleObj(ctx ModuleContext, paths ...string) ModuleObjPath { 651 p := validatePath(ctx, paths...) 652 return ModuleObjPath{PathForModuleOut(ctx, "obj", p)} 653 } 654 655 // ModuleResPath is a a Path representing the 'res' directory in a module's 656 // output directory. 657 type ModuleResPath struct { 658 ModuleOutPath 659 } 660 661 var _ Path = ModuleResPath{} 662 663 // PathForModuleRes returns a Path representing the paths... under the module's 664 // 'res' directory. 665 func PathForModuleRes(ctx ModuleContext, paths ...string) ModuleResPath { 666 p := validatePath(ctx, paths...) 667 return ModuleResPath{PathForModuleOut(ctx, "res", p)} 668 } 669 670 // PathForModuleInstall returns a Path representing the install path for the 671 // module appended with paths... 672 func PathForModuleInstall(ctx ModuleContext, paths ...string) OutputPath { 673 var outPaths []string 674 if ctx.Device() { 675 partition := "system" 676 if ctx.Vendor() { 677 partition = ctx.DeviceConfig().VendorPath() 678 } 679 680 if ctx.InstallInSanitizerDir() { 681 partition = "data/asan/" + partition 682 } else if ctx.InstallInData() { 683 partition = "data" 684 } 685 outPaths = []string{"target", "product", ctx.AConfig().DeviceName(), partition} 686 } else { 687 outPaths = []string{"host", ctx.Os().String() + "-x86"} 688 } 689 if ctx.Debug() { 690 outPaths = append([]string{"debug"}, outPaths...) 691 } 692 outPaths = append(outPaths, paths...) 693 return PathForOutput(ctx, outPaths...) 694 } 695 696 // validateSafePath validates a path that we trust (may contain ninja variables). 697 // Ensures that each path component does not attempt to leave its component. 698 func validateSafePath(ctx PathContext, paths ...string) string { 699 for _, path := range paths { 700 path := filepath.Clean(path) 701 if path == ".." || strings.HasPrefix(path, "../") || strings.HasPrefix(path, "/") { 702 reportPathError(ctx, "Path is outside directory: %s", path) 703 return "" 704 } 705 } 706 // TODO: filepath.Join isn't necessarily correct with embedded ninja 707 // variables. '..' may remove the entire ninja variable, even if it 708 // will be expanded to multiple nested directories. 709 return filepath.Join(paths...) 710 } 711 712 // validatePath validates that a path does not include ninja variables, and that 713 // each path component does not attempt to leave its component. Returns a joined 714 // version of each path component. 715 func validatePath(ctx PathContext, paths ...string) string { 716 for _, path := range paths { 717 if strings.Contains(path, "$") { 718 reportPathError(ctx, "Path contains invalid character($): %s", path) 719 return "" 720 } 721 } 722 return validateSafePath(ctx, paths...) 723 } 724