Home | History | Annotate | Download | only in android
      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