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 common 16 17import ( 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. 32type PathContext interface { 33 Config() interface{} 34 AddNinjaFileDeps(deps ...string) 35} 36 37var _ PathContext = blueprint.SingletonContext(nil) 38var _ PathContext = blueprint.ModuleContext(nil) 39 40// errorfContext is the interface containing the Errorf method matching the 41// Errorf method in blueprint.SingletonContext. 42type errorfContext interface { 43 Errorf(format string, args ...interface{}) 44} 45 46var _ errorfContext = blueprint.SingletonContext(nil) 47 48// moduleErrorf is the interface containing the ModuleErrorf method matching 49// the ModuleErrorf method in blueprint.ModuleContext. 50type moduleErrorf interface { 51 ModuleErrorf(format string, args ...interface{}) 52} 53 54var _ 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. 58func 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. 68func 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 78type 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. 87type WritablePath interface { 88 Path 89 90 writablePath() 91} 92 93type genPathProvider interface { 94 genPathWithExt(ctx AndroidModuleContext, ext string) ModuleGenPath 95} 96type objPathProvider interface { 97 objPathWithExt(ctx AndroidModuleContext, subdir, ext string) ModuleObjPath 98} 99type 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. 105func 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. 115func 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. 126func 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. 135type OptionalPath struct { 136 valid bool 137 path Path 138} 139 140// OptionalPathForPath returns an OptionalPath containing the path. 141func 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 149func (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. 155func (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. 163func (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. 172type Paths []Path 173 174// PathsForSource returns Paths rooted from SrcDir 175func 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. 201func 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 214func 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. 225func 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. 241func 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 252func (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. 264type WritablePaths []WritablePath 265 266// Strings returns the string forms of the writable paths. 267func (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 278type basePath struct { 279 path string 280 config Config 281} 282 283func (p basePath) Ext() string { 284 return filepath.Ext(p.path) 285} 286 287// SourcePath is a Path representing a file path rooted from SrcDir 288type SourcePath struct { 289 basePath 290} 291 292var _ 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 296func 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. 322func 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. 354func 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 415func (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. 421func (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. 428func (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 460type OutputPath struct { 461 basePath 462} 463 464var _ 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. 470func PathForOutput(ctx PathContext, paths ...string) OutputPath { 471 path := validatePath(ctx, paths...) 472 return OutputPath{basePath{path, pathConfig(ctx)}} 473} 474 475func (p OutputPath) writablePath() {} 476 477func (p OutputPath) String() string { 478 return filepath.Join(p.config.buildDir, p.path) 479} 480 481func (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. 487func (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. 494func 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 500type ModuleSrcPath struct { 501 basePath 502 sourcePath SourcePath 503 moduleDir string 504} 505 506var _ Path = ModuleSrcPath{} 507var _ genPathProvider = ModuleSrcPath{} 508var _ objPathProvider = ModuleSrcPath{} 509var _ resPathProvider = ModuleSrcPath{} 510 511// PathForModuleSrc returns a ModuleSrcPath representing the paths... under the 512// module's local source directory. 513func 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. 520func OptionalPathForModuleSrc(ctx AndroidModuleContext, p *string) OptionalPath { 521 if p == nil { 522 return OptionalPath{} 523 } 524 return OptionalPathForPath(PathForModuleSrc(ctx, *p)) 525} 526 527func (p ModuleSrcPath) String() string { 528 return p.sourcePath.String() 529} 530 531func (p ModuleSrcPath) genPathWithExt(ctx AndroidModuleContext, ext string) ModuleGenPath { 532 return PathForModuleGen(ctx, p.moduleDir, pathtools.ReplaceExtension(p.path, ext)) 533} 534 535func (p ModuleSrcPath) objPathWithExt(ctx AndroidModuleContext, subdir, ext string) ModuleObjPath { 536 return PathForModuleObj(ctx, subdir, p.moduleDir, pathtools.ReplaceExtension(p.path, ext)) 537} 538 539func (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. 545type ModuleOutPath struct { 546 OutputPath 547} 548 549var _ Path = ModuleOutPath{} 550 551// PathForModuleOut returns a Path representing the paths... under the module's 552// output directory. 553func 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. 560type ModuleGenPath struct { 561 ModuleOutPath 562 path string 563} 564 565var _ Path = ModuleGenPath{} 566var _ genPathProvider = ModuleGenPath{} 567var _ objPathProvider = ModuleGenPath{} 568 569// PathForModuleGen returns a Path representing the paths... under the module's 570// `gen' directory. 571func PathForModuleGen(ctx AndroidModuleContext, paths ...string) ModuleGenPath { 572 p := validatePath(ctx, paths...) 573 return ModuleGenPath{ 574 PathForModuleOut(ctx, "gen", p), 575 p, 576 } 577} 578 579func (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 584func (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. 590type ModuleObjPath struct { 591 ModuleOutPath 592} 593 594var _ Path = ModuleObjPath{} 595 596// PathForModuleObj returns a Path representing the paths... under the module's 597// 'obj' directory. 598func 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. 605type ModuleResPath struct { 606 ModuleOutPath 607} 608 609var _ Path = ModuleResPath{} 610 611// PathForModuleRes returns a Path representing the paths... under the module's 612// 'res' directory. 613func 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... 620func 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. 643func 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. 660func 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