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