blob: 3ea9ef8077367dd2c71f59dd2dae735a50d020da [file] [log] [blame]
Colin Cross3f40fa42015-01-30 17:27:36 -08001// 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 (
Colin Cross6e18ca42015-07-14 18:55:36 -070018 "fmt"
Colin Crossf2298272015-05-12 11:36:53 -070019 "os"
Colin Cross6a745c62015-06-16 16:38:10 -070020 "path/filepath"
Dan Willemsen34cc69e2015-09-23 15:26:20 -070021 "reflect"
22 "strings"
23
Dan Willemsen7b310ee2015-12-18 15:11:17 -080024 "android/soong/glob"
25
Dan Willemsen34cc69e2015-09-23 15:26:20 -070026 "github.com/google/blueprint"
27 "github.com/google/blueprint/pathtools"
Colin Cross3f40fa42015-01-30 17:27:36 -080028)
29
Dan Willemsen34cc69e2015-09-23 15:26:20 -070030// PathContext is the subset of a (Module|Singleton)Context required by the
31// Path methods.
32type PathContext interface {
33 Config() interface{}
Dan Willemsen7b310ee2015-12-18 15:11:17 -080034 AddNinjaFileDeps(deps ...string)
Colin Cross3f40fa42015-01-30 17:27:36 -080035}
36
Dan Willemsen34cc69e2015-09-23 15:26:20 -070037var _ 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{})
Colin Cross3f40fa42015-01-30 17:27:36 -080044}
45
Dan Willemsen34cc69e2015-09-23 15:26:20 -070046var _ 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{})
Colin Cross3f40fa42015-01-30 17:27:36 -080052}
53
Dan Willemsen34cc69e2015-09-23 15:26:20 -070054var _ 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")
Colin Cross3f40fa42015-01-30 17:27:36 -080063}
64
Dan Willemsen34cc69e2015-09-23 15:26:20 -070065// 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...))
Colin Crossf2298272015-05-12 11:36:53 -070075 }
76}
77
Dan Willemsen34cc69e2015-09-23 15:26:20 -070078type 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 ""
Colin Crossf2298272015-05-12 11:36:53 -0700168 }
169}
Colin Cross6e18ca42015-07-14 18:55:36 -0700170
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700171// 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 ret := make(Paths, len(paths))
177 for i, path := range paths {
178 ret[i] = PathForSource(ctx, path)
179 }
180 return ret
181}
182
Dan Willemsen7b310ee2015-12-18 15:11:17 -0800183// PathsForOptionalSource returns a list of Paths rooted from SrcDir that are
184// found in the tree. If any are not found, they are omitted from the list,
185// and dependencies are added so that we're re-run when they are added.
186func PathsForOptionalSource(ctx PathContext, intermediates string, paths []string) Paths {
187 ret := make(Paths, 0, len(paths))
188 for _, path := range paths {
189 p := OptionalPathForSource(ctx, intermediates, path)
190 if p.Valid() {
191 ret = append(ret, p.Path())
192 }
193 }
194 return ret
195}
196
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700197// PathsForModuleSrc returns Paths rooted from the module's local source
198// directory
199func PathsForModuleSrc(ctx AndroidModuleContext, paths []string) Paths {
200 ret := make(Paths, len(paths))
201 for i, path := range paths {
202 ret[i] = PathForModuleSrc(ctx, path)
203 }
204 return ret
205}
206
207// pathsForModuleSrcFromFullPath returns Paths rooted from the module's local
208// source directory, but strip the local source directory from the beginning of
209// each string.
210func pathsForModuleSrcFromFullPath(ctx AndroidModuleContext, paths []string) Paths {
211 prefix := filepath.Join(ctx.AConfig().srcDir, ctx.ModuleDir()) + "/"
212 ret := make(Paths, 0, len(paths))
213 for _, p := range paths {
214 path := filepath.Clean(p)
215 if !strings.HasPrefix(path, prefix) {
216 reportPathError(ctx, "Path '%s' is not in module source directory '%s'", p, prefix)
217 continue
218 }
219 ret = append(ret, PathForModuleSrc(ctx, path[len(prefix):]))
220 }
221 return ret
222}
223
224// PathsWithOptionalDefaultForModuleSrc returns Paths rooted from the module's
225// local source directory. If none are provided, use the default if it exists.
226func PathsWithOptionalDefaultForModuleSrc(ctx AndroidModuleContext, input []string, def string) Paths {
227 if len(input) > 0 {
228 return PathsForModuleSrc(ctx, input)
229 }
230 // Use Glob so that if the default doesn't exist, a dependency is added so that when it
231 // is created, we're run again.
232 path := filepath.Join(ctx.AConfig().srcDir, ctx.ModuleDir(), def)
233 return ctx.Glob("default", path, []string{})
234}
235
236// Strings returns the Paths in string form
237func (p Paths) Strings() []string {
238 if p == nil {
239 return nil
240 }
241 ret := make([]string, len(p))
242 for i, path := range p {
243 ret[i] = path.String()
244 }
245 return ret
246}
247
248// WritablePaths is a slice of WritablePaths, used for multiple outputs.
249type WritablePaths []WritablePath
250
251// Strings returns the string forms of the writable paths.
252func (p WritablePaths) 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
263type basePath struct {
264 path string
265 config Config
266}
267
268func (p basePath) Ext() string {
269 return filepath.Ext(p.path)
270}
271
272// SourcePath is a Path representing a file path rooted from SrcDir
273type SourcePath struct {
274 basePath
275}
276
277var _ Path = SourcePath{}
278
279// safePathForSource is for paths that we expect are safe -- only for use by go
280// code that is embedding ninja variables in paths
281func safePathForSource(ctx PathContext, path string) SourcePath {
282 p := validateSafePath(ctx, path)
283 ret := SourcePath{basePath{p, pathConfig(ctx)}}
284
285 abs, err := filepath.Abs(ret.String())
Colin Cross6e18ca42015-07-14 18:55:36 -0700286 if err != nil {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700287 reportPathError(ctx, "%s", err.Error())
288 return ret
289 }
290 buildroot, err := filepath.Abs(pathConfig(ctx).buildDir)
291 if err != nil {
292 reportPathError(ctx, "%s", err.Error())
293 return ret
294 }
295 if strings.HasPrefix(abs, buildroot) {
296 reportPathError(ctx, "source path %s is in output", abs)
297 return ret
Colin Cross6e18ca42015-07-14 18:55:36 -0700298 }
299
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700300 return ret
301}
302
303// PathForSource returns a SourcePath for the provided paths... (which are
304// joined together with filepath.Join). This also validates that the path
305// doesn't escape the source dir, or is contained in the build dir. On error, it
306// will return a usable, but invalid SourcePath, and report a ModuleError.
307func PathForSource(ctx PathContext, paths ...string) SourcePath {
308 p := validatePath(ctx, paths...)
309 ret := SourcePath{basePath{p, pathConfig(ctx)}}
310
311 abs, err := filepath.Abs(ret.String())
312 if err != nil {
313 reportPathError(ctx, "%s", err.Error())
314 return ret
315 }
316 buildroot, err := filepath.Abs(pathConfig(ctx).buildDir)
317 if err != nil {
318 reportPathError(ctx, "%s", err.Error())
319 return ret
320 }
321 if strings.HasPrefix(abs, buildroot) {
322 reportPathError(ctx, "source path %s is in output", abs)
323 return ret
324 }
325
326 if _, err = os.Stat(ret.String()); err != nil {
327 if os.IsNotExist(err) {
328 reportPathError(ctx, "source path %s does not exist", ret)
329 } else {
330 reportPathError(ctx, "%s: %s", ret, err.Error())
331 }
332 }
333 return ret
334}
335
336// OptionalPathForSource returns an OptionalPath with the SourcePath if the
337// path exists, or an empty OptionalPath if it doesn't exist. Dependencies are added
338// so that the ninja file will be regenerated if the state of the path changes.
Dan Willemsen7b310ee2015-12-18 15:11:17 -0800339func OptionalPathForSource(ctx PathContext, intermediates string, paths ...string) OptionalPath {
340 if len(paths) == 0 {
341 // For when someone forgets the 'intermediates' argument
342 panic("Missing path(s)")
343 }
344
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700345 p := validatePath(ctx, paths...)
346 path := SourcePath{basePath{p, pathConfig(ctx)}}
347
348 abs, err := filepath.Abs(path.String())
349 if err != nil {
350 reportPathError(ctx, "%s", err.Error())
351 return OptionalPath{}
352 }
353 buildroot, err := filepath.Abs(pathConfig(ctx).buildDir)
354 if err != nil {
355 reportPathError(ctx, "%s", err.Error())
356 return OptionalPath{}
357 }
358 if strings.HasPrefix(abs, buildroot) {
359 reportPathError(ctx, "source path %s is in output", abs)
360 return OptionalPath{}
361 }
362
Dan Willemsen7b310ee2015-12-18 15:11:17 -0800363 if glob.IsGlob(path.String()) {
364 reportPathError(ctx, "path may not contain a glob: %s", path.String())
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700365 return OptionalPath{}
366 }
367
Dan Willemsen7b310ee2015-12-18 15:11:17 -0800368 if gctx, ok := ctx.(globContext); ok {
369 // Use glob to produce proper dependencies, even though we only want
370 // a single file.
371 files, err := Glob(gctx, PathForIntermediates(ctx, intermediates).String(), path.String(), nil)
372 if err != nil {
373 reportPathError(ctx, "glob: %s", err.Error())
374 return OptionalPath{}
375 }
376
377 if len(files) == 0 {
378 return OptionalPath{}
379 }
380 } else {
381 // We cannot add build statements in this context, so we fall back to
382 // AddNinjaFileDeps
383 files, dirs, err := pathtools.Glob(path.String())
384 if err != nil {
385 reportPathError(ctx, "glob: %s", err.Error())
386 return OptionalPath{}
387 }
388
389 ctx.AddNinjaFileDeps(dirs...)
390
391 if len(files) == 0 {
392 return OptionalPath{}
393 }
394
395 ctx.AddNinjaFileDeps(path.String())
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700396 }
397 return OptionalPathForPath(path)
398}
399
400func (p SourcePath) String() string {
401 return filepath.Join(p.config.srcDir, p.path)
402}
403
404// Join creates a new SourcePath with paths... joined with the current path. The
405// provided paths... may not use '..' to escape from the current path.
406func (p SourcePath) Join(ctx PathContext, paths ...string) SourcePath {
407 path := validatePath(ctx, paths...)
408 return PathForSource(ctx, p.path, path)
409}
410
411// OverlayPath returns the overlay for `path' if it exists. This assumes that the
412// SourcePath is the path to a resource overlay directory.
413func (p SourcePath) OverlayPath(ctx AndroidModuleContext, path Path) OptionalPath {
414 var relDir string
415 if moduleSrcPath, ok := path.(ModuleSrcPath); ok {
416 relDir = moduleSrcPath.sourcePath.path
417 } else if srcPath, ok := path.(SourcePath); ok {
418 relDir = srcPath.path
419 } else {
420 reportPathError(ctx, "Cannot find relative path for %s(%s)", reflect.TypeOf(path).Name(), path)
421 return OptionalPath{}
422 }
423 dir := filepath.Join(p.config.srcDir, p.path, relDir)
424 // Use Glob so that we are run again if the directory is added.
Dan Willemsen7b310ee2015-12-18 15:11:17 -0800425 if glob.IsGlob(dir) {
426 reportPathError(ctx, "Path may not contain a glob: %s", dir)
427 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700428 paths, err := Glob(ctx, PathForModuleOut(ctx, "overlay").String(), dir, []string{})
429 if err != nil {
430 reportPathError(ctx, "glob: %s", err.Error())
431 return OptionalPath{}
432 }
433 if len(paths) == 0 {
434 return OptionalPath{}
435 }
436 relPath, err := filepath.Rel(p.config.srcDir, paths[0])
437 if err != nil {
438 reportPathError(ctx, "%s", err.Error())
439 return OptionalPath{}
440 }
441 return OptionalPathForPath(PathForSource(ctx, relPath))
442}
443
444// OutputPath is a Path representing a file path rooted from the build directory
445type OutputPath struct {
446 basePath
447}
448
449var _ Path = OutputPath{}
450
451// PathForOutput returns an OutputPath for the provided paths... (which are
452// joined together with filepath.Join). This also validates that the path
453// does not escape the build dir. On error, it will return a usable, but invalid
454// OutputPath, and report a ModuleError.
455func PathForOutput(ctx PathContext, paths ...string) OutputPath {
456 path := validatePath(ctx, paths...)
457 return OutputPath{basePath{path, pathConfig(ctx)}}
458}
459
460func (p OutputPath) writablePath() {}
461
462func (p OutputPath) String() string {
463 return filepath.Join(p.config.buildDir, p.path)
464}
465
466// Join creates a new OutputPath with paths... joined with the current path. The
467// provided paths... may not use '..' to escape from the current path.
468func (p OutputPath) Join(ctx PathContext, paths ...string) OutputPath {
469 path := validatePath(ctx, paths...)
470 return PathForOutput(ctx, p.path, path)
471}
472
473// PathForIntermediates returns an OutputPath representing the top-level
474// intermediates directory.
475func PathForIntermediates(ctx PathContext, paths ...string) OutputPath {
476 path := validatePath(ctx, paths...)
477 return PathForOutput(ctx, ".intermediates", path)
478}
479
480// ModuleSrcPath is a Path representing a file rooted from a module's local source dir
481type ModuleSrcPath struct {
482 basePath
483 sourcePath SourcePath
484 moduleDir string
485}
486
487var _ Path = ModuleSrcPath{}
488var _ genPathProvider = ModuleSrcPath{}
489var _ objPathProvider = ModuleSrcPath{}
490var _ resPathProvider = ModuleSrcPath{}
491
492// PathForModuleSrc returns a ModuleSrcPath representing the paths... under the
493// module's local source directory.
494func PathForModuleSrc(ctx AndroidModuleContext, paths ...string) ModuleSrcPath {
495 path := validatePath(ctx, paths...)
496 return ModuleSrcPath{basePath{path, ctx.AConfig()}, PathForSource(ctx, ctx.ModuleDir(), path), ctx.ModuleDir()}
497}
498
499// OptionalPathForModuleSrc returns an OptionalPath. The OptionalPath contains a
500// valid path if p is non-nil.
501func OptionalPathForModuleSrc(ctx AndroidModuleContext, p *string) OptionalPath {
502 if p == nil {
503 return OptionalPath{}
504 }
505 return OptionalPathForPath(PathForModuleSrc(ctx, *p))
506}
507
508func (p ModuleSrcPath) String() string {
509 return p.sourcePath.String()
510}
511
512func (p ModuleSrcPath) genPathWithExt(ctx AndroidModuleContext, ext string) ModuleGenPath {
513 return PathForModuleGen(ctx, p.moduleDir, pathtools.ReplaceExtension(p.path, ext))
514}
515
516func (p ModuleSrcPath) objPathWithExt(ctx AndroidModuleContext, subdir, ext string) ModuleObjPath {
517 return PathForModuleObj(ctx, subdir, p.moduleDir, pathtools.ReplaceExtension(p.path, ext))
518}
519
520func (p ModuleSrcPath) resPathWithName(ctx AndroidModuleContext, name string) ModuleResPath {
521 // TODO: Use full directory if the new ctx is not the current ctx?
522 return PathForModuleRes(ctx, p.path, name)
523}
524
525// ModuleOutPath is a Path representing a module's output directory.
526type ModuleOutPath struct {
527 OutputPath
528}
529
530var _ Path = ModuleOutPath{}
531
532// PathForModuleOut returns a Path representing the paths... under the module's
533// output directory.
534func PathForModuleOut(ctx AndroidModuleContext, paths ...string) ModuleOutPath {
535 p := validatePath(ctx, paths...)
536 return ModuleOutPath{PathForOutput(ctx, ".intermediates", ctx.ModuleDir(), ctx.ModuleName(), ctx.ModuleSubDir(), p)}
537}
538
539// ModuleGenPath is a Path representing the 'gen' directory in a module's output
540// directory. Mainly used for generated sources.
541type ModuleGenPath struct {
542 ModuleOutPath
543 path string
544}
545
546var _ Path = ModuleGenPath{}
547var _ genPathProvider = ModuleGenPath{}
548var _ objPathProvider = ModuleGenPath{}
549
550// PathForModuleGen returns a Path representing the paths... under the module's
551// `gen' directory.
552func PathForModuleGen(ctx AndroidModuleContext, paths ...string) ModuleGenPath {
553 p := validatePath(ctx, paths...)
554 return ModuleGenPath{
555 PathForModuleOut(ctx, "gen", p),
556 p,
557 }
558}
559
560func (p ModuleGenPath) genPathWithExt(ctx AndroidModuleContext, ext string) ModuleGenPath {
561 // TODO: make a different path for local vs remote generated files?
562 return PathForModuleGen(ctx, pathtools.ReplaceExtension(p.path, ext))
563}
564
565func (p ModuleGenPath) objPathWithExt(ctx AndroidModuleContext, subdir, ext string) ModuleObjPath {
566 return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
567}
568
569// ModuleObjPath is a Path representing the 'obj' directory in a module's output
570// directory. Used for compiled objects.
571type ModuleObjPath struct {
572 ModuleOutPath
573}
574
575var _ Path = ModuleObjPath{}
576
577// PathForModuleObj returns a Path representing the paths... under the module's
578// 'obj' directory.
579func PathForModuleObj(ctx AndroidModuleContext, paths ...string) ModuleObjPath {
580 p := validatePath(ctx, paths...)
581 return ModuleObjPath{PathForModuleOut(ctx, "obj", p)}
582}
583
584// ModuleResPath is a a Path representing the 'res' directory in a module's
585// output directory.
586type ModuleResPath struct {
587 ModuleOutPath
588}
589
590var _ Path = ModuleResPath{}
591
592// PathForModuleRes returns a Path representing the paths... under the module's
593// 'res' directory.
594func PathForModuleRes(ctx AndroidModuleContext, paths ...string) ModuleResPath {
595 p := validatePath(ctx, paths...)
596 return ModuleResPath{PathForModuleOut(ctx, "res", p)}
597}
598
599// PathForModuleInstall returns a Path representing the install path for the
600// module appended with paths...
601func PathForModuleInstall(ctx AndroidModuleContext, paths ...string) OutputPath {
602 var outPaths []string
603 if ctx.Device() {
Dan Willemsen782a2d12015-12-21 14:55:28 -0800604 partition := "system"
605 if ctx.Proprietary() {
606 partition = "vendor"
607 }
608 if ctx.InstallInData() {
609 partition = "data"
610 }
611 outPaths = []string{"target", "product", ctx.AConfig().DeviceName(), partition}
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700612 } else {
613 outPaths = []string{"host", ctx.HostType().String() + "-x86"}
614 }
Dan Willemsen782a2d12015-12-21 14:55:28 -0800615 if ctx.Debug() {
616 outPaths = append([]string{"debug"}, outPaths...)
617 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700618 outPaths = append(outPaths, paths...)
619 return PathForOutput(ctx, outPaths...)
620}
621
622// validateSafePath validates a path that we trust (may contain ninja variables).
Dan Willemsen80a7c2a2015-12-21 14:57:11 -0800623// Ensures that each path component does not attempt to leave its component.
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700624func validateSafePath(ctx PathContext, paths ...string) string {
Dan Willemsen80a7c2a2015-12-21 14:57:11 -0800625 for _, path := range paths {
626 path := filepath.Clean(path)
627 if path == ".." || strings.HasPrefix(path, "../") || strings.HasPrefix(path, "/") {
628 reportPathError(ctx, "Path is outside directory: %s", path)
629 return ""
630 }
631 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700632 // TODO: filepath.Join isn't necessarily correct with embedded ninja
633 // variables. '..' may remove the entire ninja variable, even if it
634 // will be expanded to multiple nested directories.
Dan Willemsen80a7c2a2015-12-21 14:57:11 -0800635 return filepath.Join(paths...)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700636}
637
Dan Willemsen80a7c2a2015-12-21 14:57:11 -0800638// validatePath validates that a path does not include ninja variables, and that
639// each path component does not attempt to leave its component. Returns a joined
640// version of each path component.
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700641func validatePath(ctx PathContext, paths ...string) string {
642 for _, path := range paths {
643 if strings.Contains(path, "$") {
644 reportPathError(ctx, "Path contains invalid character($): %s", path)
645 return ""
646 }
647 }
648 return validateSafePath(ctx, paths...)
Colin Cross6e18ca42015-07-14 18:55:36 -0700649}