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