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