blob: 1202d6d4760c8cbefa128b9aa56564f969e3b40c [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
Colin Cross635c3b02016-05-18 15:37:25 -070015package android
Colin Cross3f40fa42015-01-30 17:27:36 -080016
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
Colin Cross4f6fc9c2016-10-26 10:05:25 -070082 // Ext returns the extension of the last element of the path
Dan Willemsen34cc69e2015-09-23 15:26:20 -070083 Ext() string
Colin Cross4f6fc9c2016-10-26 10:05:25 -070084
85 // Base returns the last element of the path
86 Base() string
Dan Willemsen34cc69e2015-09-23 15:26:20 -070087}
88
89// WritablePath is a type of path that can be used as an output for build rules.
90type WritablePath interface {
91 Path
92
93 writablePath()
94}
95
96type genPathProvider interface {
Dan Willemsen21ec4902016-11-02 20:43:13 -070097 genPathWithExt(ctx ModuleContext, subdir, ext string) ModuleGenPath
Dan Willemsen34cc69e2015-09-23 15:26:20 -070098}
99type objPathProvider interface {
Colin Cross635c3b02016-05-18 15:37:25 -0700100 objPathWithExt(ctx ModuleContext, subdir, ext string) ModuleObjPath
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700101}
102type resPathProvider interface {
Colin Cross635c3b02016-05-18 15:37:25 -0700103 resPathWithName(ctx ModuleContext, name string) ModuleResPath
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700104}
105
106// GenPathWithExt derives a new file path in ctx's generated sources directory
107// from the current path, but with the new extension.
Dan Willemsen21ec4902016-11-02 20:43:13 -0700108func GenPathWithExt(ctx ModuleContext, subdir string, p Path, ext string) ModuleGenPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700109 if path, ok := p.(genPathProvider); ok {
Dan Willemsen21ec4902016-11-02 20:43:13 -0700110 return path.genPathWithExt(ctx, subdir, ext)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700111 }
112 reportPathError(ctx, "Tried to create generated file from unsupported path: %s(%s)", reflect.TypeOf(p).Name(), p)
113 return PathForModuleGen(ctx)
114}
115
116// ObjPathWithExt derives a new file path in ctx's object directory from the
117// current path, but with the new extension.
Dan Willemsen21ec4902016-11-02 20:43:13 -0700118func ObjPathWithExt(ctx ModuleContext, subdir string, p Path, ext string) ModuleObjPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700119 if path, ok := p.(objPathProvider); ok {
120 return path.objPathWithExt(ctx, subdir, ext)
121 }
122 reportPathError(ctx, "Tried to create object file from unsupported path: %s (%s)", reflect.TypeOf(p).Name(), p)
123 return PathForModuleObj(ctx)
124}
125
126// ResPathWithName derives a new path in ctx's output resource directory, using
127// the current path to create the directory name, and the `name` argument for
128// the filename.
Colin Cross635c3b02016-05-18 15:37:25 -0700129func ResPathWithName(ctx ModuleContext, p Path, name string) ModuleResPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700130 if path, ok := p.(resPathProvider); ok {
131 return path.resPathWithName(ctx, name)
132 }
133 reportPathError(ctx, "Tried to create object file from unsupported path: %s (%s)", reflect.TypeOf(p).Name(), p)
134 return PathForModuleRes(ctx)
135}
136
137// OptionalPath is a container that may or may not contain a valid Path.
138type OptionalPath struct {
139 valid bool
140 path Path
141}
142
143// OptionalPathForPath returns an OptionalPath containing the path.
144func OptionalPathForPath(path Path) OptionalPath {
145 if path == nil {
146 return OptionalPath{}
147 }
148 return OptionalPath{valid: true, path: path}
149}
150
151// Valid returns whether there is a valid path
152func (p OptionalPath) Valid() bool {
153 return p.valid
154}
155
156// Path returns the Path embedded in this OptionalPath. You must be sure that
157// there is a valid path, since this method will panic if there is not.
158func (p OptionalPath) Path() Path {
159 if !p.valid {
160 panic("Requesting an invalid path")
161 }
162 return p.path
163}
164
165// String returns the string version of the Path, or "" if it isn't valid.
166func (p OptionalPath) String() string {
167 if p.valid {
168 return p.path.String()
169 } else {
170 return ""
Colin Crossf2298272015-05-12 11:36:53 -0700171 }
172}
Colin Cross6e18ca42015-07-14 18:55:36 -0700173
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700174// Paths is a slice of Path objects, with helpers to operate on the collection.
175type Paths []Path
176
177// PathsForSource returns Paths rooted from SrcDir
178func PathsForSource(ctx PathContext, paths []string) Paths {
Dan Willemsene23dfb72016-03-11 15:02:46 -0800179 if pathConfig(ctx).AllowMissingDependencies() {
Colin Cross635c3b02016-05-18 15:37:25 -0700180 if modCtx, ok := ctx.(ModuleContext); ok {
Dan Willemsene23dfb72016-03-11 15:02:46 -0800181 ret := make(Paths, 0, len(paths))
Dan Willemsen0f6042e2016-03-11 17:01:03 -0800182 intermediates := filepath.Join(modCtx.ModuleDir(), modCtx.ModuleName(), modCtx.ModuleSubDir(), "missing")
Dan Willemsene23dfb72016-03-11 15:02:46 -0800183 for _, path := range paths {
184 p := OptionalPathForSource(ctx, intermediates, path)
185 if p.Valid() {
186 ret = append(ret, p.Path())
187 } else {
188 modCtx.AddMissingDependencies([]string{path})
189 }
190 }
191 return ret
192 }
193 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700194 ret := make(Paths, len(paths))
195 for i, path := range paths {
196 ret[i] = PathForSource(ctx, path)
197 }
198 return ret
199}
200
Dan Willemsen7b310ee2015-12-18 15:11:17 -0800201// PathsForOptionalSource returns a list of Paths rooted from SrcDir that are
202// found in the tree. If any are not found, they are omitted from the list,
203// and dependencies are added so that we're re-run when they are added.
204func PathsForOptionalSource(ctx PathContext, intermediates string, paths []string) Paths {
205 ret := make(Paths, 0, len(paths))
206 for _, path := range paths {
207 p := OptionalPathForSource(ctx, intermediates, path)
208 if p.Valid() {
209 ret = append(ret, p.Path())
210 }
211 }
212 return ret
213}
214
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700215// PathsForModuleSrc returns Paths rooted from the module's local source
216// directory
Colin Cross635c3b02016-05-18 15:37:25 -0700217func PathsForModuleSrc(ctx ModuleContext, paths []string) Paths {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700218 ret := make(Paths, len(paths))
219 for i, path := range paths {
220 ret[i] = PathForModuleSrc(ctx, path)
221 }
222 return ret
223}
224
225// pathsForModuleSrcFromFullPath returns Paths rooted from the module's local
226// source directory, but strip the local source directory from the beginning of
227// each string.
Colin Cross635c3b02016-05-18 15:37:25 -0700228func pathsForModuleSrcFromFullPath(ctx ModuleContext, paths []string) Paths {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700229 prefix := filepath.Join(ctx.AConfig().srcDir, ctx.ModuleDir()) + "/"
230 ret := make(Paths, 0, len(paths))
231 for _, p := range paths {
232 path := filepath.Clean(p)
233 if !strings.HasPrefix(path, prefix) {
234 reportPathError(ctx, "Path '%s' is not in module source directory '%s'", p, prefix)
235 continue
236 }
237 ret = append(ret, PathForModuleSrc(ctx, path[len(prefix):]))
238 }
239 return ret
240}
241
242// PathsWithOptionalDefaultForModuleSrc returns Paths rooted from the module's
243// local source directory. If none are provided, use the default if it exists.
Colin Cross635c3b02016-05-18 15:37:25 -0700244func PathsWithOptionalDefaultForModuleSrc(ctx ModuleContext, input []string, def string) Paths {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700245 if len(input) > 0 {
246 return PathsForModuleSrc(ctx, input)
247 }
248 // Use Glob so that if the default doesn't exist, a dependency is added so that when it
249 // is created, we're run again.
250 path := filepath.Join(ctx.AConfig().srcDir, ctx.ModuleDir(), def)
251 return ctx.Glob("default", path, []string{})
252}
253
254// Strings returns the Paths in string form
255func (p Paths) Strings() []string {
256 if p == nil {
257 return nil
258 }
259 ret := make([]string, len(p))
260 for i, path := range p {
261 ret[i] = path.String()
262 }
263 return ret
264}
265
266// WritablePaths is a slice of WritablePaths, used for multiple outputs.
267type WritablePaths []WritablePath
268
269// Strings returns the string forms of the writable paths.
270func (p WritablePaths) Strings() []string {
271 if p == nil {
272 return nil
273 }
274 ret := make([]string, len(p))
275 for i, path := range p {
276 ret[i] = path.String()
277 }
278 return ret
279}
280
281type basePath struct {
282 path string
283 config Config
284}
285
286func (p basePath) Ext() string {
287 return filepath.Ext(p.path)
288}
289
Colin Cross4f6fc9c2016-10-26 10:05:25 -0700290func (p basePath) Base() string {
291 return filepath.Base(p.path)
292}
293
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700294// SourcePath is a Path representing a file path rooted from SrcDir
295type SourcePath struct {
296 basePath
297}
298
299var _ Path = SourcePath{}
300
301// safePathForSource is for paths that we expect are safe -- only for use by go
302// code that is embedding ninja variables in paths
303func safePathForSource(ctx PathContext, path string) SourcePath {
304 p := validateSafePath(ctx, path)
305 ret := SourcePath{basePath{p, pathConfig(ctx)}}
306
307 abs, err := filepath.Abs(ret.String())
Colin Cross6e18ca42015-07-14 18:55:36 -0700308 if err != nil {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700309 reportPathError(ctx, "%s", err.Error())
310 return ret
311 }
312 buildroot, err := filepath.Abs(pathConfig(ctx).buildDir)
313 if err != nil {
314 reportPathError(ctx, "%s", err.Error())
315 return ret
316 }
317 if strings.HasPrefix(abs, buildroot) {
318 reportPathError(ctx, "source path %s is in output", abs)
319 return ret
Colin Cross6e18ca42015-07-14 18:55:36 -0700320 }
321
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700322 return ret
323}
324
325// PathForSource returns a SourcePath for the provided paths... (which are
326// joined together with filepath.Join). This also validates that the path
327// doesn't escape the source dir, or is contained in the build dir. On error, it
328// will return a usable, but invalid SourcePath, and report a ModuleError.
329func PathForSource(ctx PathContext, paths ...string) SourcePath {
330 p := validatePath(ctx, paths...)
331 ret := SourcePath{basePath{p, pathConfig(ctx)}}
332
333 abs, err := filepath.Abs(ret.String())
334 if err != nil {
335 reportPathError(ctx, "%s", err.Error())
336 return ret
337 }
338 buildroot, err := filepath.Abs(pathConfig(ctx).buildDir)
339 if err != nil {
340 reportPathError(ctx, "%s", err.Error())
341 return ret
342 }
343 if strings.HasPrefix(abs, buildroot) {
344 reportPathError(ctx, "source path %s is in output", abs)
345 return ret
346 }
347
348 if _, err = os.Stat(ret.String()); err != nil {
349 if os.IsNotExist(err) {
350 reportPathError(ctx, "source path %s does not exist", ret)
351 } else {
352 reportPathError(ctx, "%s: %s", ret, err.Error())
353 }
354 }
355 return ret
356}
357
358// OptionalPathForSource returns an OptionalPath with the SourcePath if the
359// path exists, or an empty OptionalPath if it doesn't exist. Dependencies are added
360// so that the ninja file will be regenerated if the state of the path changes.
Dan Willemsen7b310ee2015-12-18 15:11:17 -0800361func OptionalPathForSource(ctx PathContext, intermediates string, paths ...string) OptionalPath {
362 if len(paths) == 0 {
363 // For when someone forgets the 'intermediates' argument
364 panic("Missing path(s)")
365 }
366
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700367 p := validatePath(ctx, paths...)
368 path := SourcePath{basePath{p, pathConfig(ctx)}}
369
370 abs, err := filepath.Abs(path.String())
371 if err != nil {
372 reportPathError(ctx, "%s", err.Error())
373 return OptionalPath{}
374 }
375 buildroot, err := filepath.Abs(pathConfig(ctx).buildDir)
376 if err != nil {
377 reportPathError(ctx, "%s", err.Error())
378 return OptionalPath{}
379 }
380 if strings.HasPrefix(abs, buildroot) {
381 reportPathError(ctx, "source path %s is in output", abs)
382 return OptionalPath{}
383 }
384
Dan Willemsen7b310ee2015-12-18 15:11:17 -0800385 if glob.IsGlob(path.String()) {
386 reportPathError(ctx, "path may not contain a glob: %s", path.String())
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700387 return OptionalPath{}
388 }
389
Dan Willemsen7b310ee2015-12-18 15:11:17 -0800390 if gctx, ok := ctx.(globContext); ok {
391 // Use glob to produce proper dependencies, even though we only want
392 // a single file.
393 files, err := Glob(gctx, PathForIntermediates(ctx, intermediates).String(), path.String(), nil)
394 if err != nil {
395 reportPathError(ctx, "glob: %s", err.Error())
396 return OptionalPath{}
397 }
398
399 if len(files) == 0 {
400 return OptionalPath{}
401 }
402 } else {
403 // We cannot add build statements in this context, so we fall back to
404 // AddNinjaFileDeps
405 files, dirs, err := pathtools.Glob(path.String())
406 if err != nil {
407 reportPathError(ctx, "glob: %s", err.Error())
408 return OptionalPath{}
409 }
410
411 ctx.AddNinjaFileDeps(dirs...)
412
413 if len(files) == 0 {
414 return OptionalPath{}
415 }
416
417 ctx.AddNinjaFileDeps(path.String())
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700418 }
419 return OptionalPathForPath(path)
420}
421
422func (p SourcePath) String() string {
423 return filepath.Join(p.config.srcDir, p.path)
424}
425
426// Join creates a new SourcePath with paths... joined with the current path. The
427// provided paths... may not use '..' to escape from the current path.
428func (p SourcePath) Join(ctx PathContext, paths ...string) SourcePath {
429 path := validatePath(ctx, paths...)
430 return PathForSource(ctx, p.path, path)
431}
432
433// OverlayPath returns the overlay for `path' if it exists. This assumes that the
434// SourcePath is the path to a resource overlay directory.
Colin Cross635c3b02016-05-18 15:37:25 -0700435func (p SourcePath) OverlayPath(ctx ModuleContext, path Path) OptionalPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700436 var relDir string
437 if moduleSrcPath, ok := path.(ModuleSrcPath); ok {
438 relDir = moduleSrcPath.sourcePath.path
439 } else if srcPath, ok := path.(SourcePath); ok {
440 relDir = srcPath.path
441 } else {
442 reportPathError(ctx, "Cannot find relative path for %s(%s)", reflect.TypeOf(path).Name(), path)
443 return OptionalPath{}
444 }
445 dir := filepath.Join(p.config.srcDir, p.path, relDir)
446 // Use Glob so that we are run again if the directory is added.
Dan Willemsen7b310ee2015-12-18 15:11:17 -0800447 if glob.IsGlob(dir) {
448 reportPathError(ctx, "Path may not contain a glob: %s", dir)
449 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700450 paths, err := Glob(ctx, PathForModuleOut(ctx, "overlay").String(), dir, []string{})
451 if err != nil {
452 reportPathError(ctx, "glob: %s", err.Error())
453 return OptionalPath{}
454 }
455 if len(paths) == 0 {
456 return OptionalPath{}
457 }
458 relPath, err := filepath.Rel(p.config.srcDir, paths[0])
459 if err != nil {
460 reportPathError(ctx, "%s", err.Error())
461 return OptionalPath{}
462 }
463 return OptionalPathForPath(PathForSource(ctx, relPath))
464}
465
466// OutputPath is a Path representing a file path rooted from the build directory
467type OutputPath struct {
468 basePath
469}
470
471var _ Path = OutputPath{}
472
473// PathForOutput returns an OutputPath for the provided paths... (which are
474// joined together with filepath.Join). This also validates that the path
475// does not escape the build dir. On error, it will return a usable, but invalid
476// OutputPath, and report a ModuleError.
477func PathForOutput(ctx PathContext, paths ...string) OutputPath {
478 path := validatePath(ctx, paths...)
479 return OutputPath{basePath{path, pathConfig(ctx)}}
480}
481
482func (p OutputPath) writablePath() {}
483
484func (p OutputPath) String() string {
485 return filepath.Join(p.config.buildDir, p.path)
486}
487
Colin Crossa2344662016-03-24 13:14:12 -0700488func (p OutputPath) RelPathString() string {
489 return p.path
490}
491
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700492// Join creates a new OutputPath with paths... joined with the current path. The
493// provided paths... may not use '..' to escape from the current path.
494func (p OutputPath) Join(ctx PathContext, paths ...string) OutputPath {
495 path := validatePath(ctx, paths...)
496 return PathForOutput(ctx, p.path, path)
497}
498
499// PathForIntermediates returns an OutputPath representing the top-level
500// intermediates directory.
501func PathForIntermediates(ctx PathContext, paths ...string) OutputPath {
502 path := validatePath(ctx, paths...)
503 return PathForOutput(ctx, ".intermediates", path)
504}
505
506// ModuleSrcPath is a Path representing a file rooted from a module's local source dir
507type ModuleSrcPath struct {
508 basePath
509 sourcePath SourcePath
510 moduleDir string
511}
512
513var _ Path = ModuleSrcPath{}
514var _ genPathProvider = ModuleSrcPath{}
515var _ objPathProvider = ModuleSrcPath{}
516var _ resPathProvider = ModuleSrcPath{}
517
518// PathForModuleSrc returns a ModuleSrcPath representing the paths... under the
519// module's local source directory.
Colin Cross635c3b02016-05-18 15:37:25 -0700520func PathForModuleSrc(ctx ModuleContext, paths ...string) ModuleSrcPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700521 path := validatePath(ctx, paths...)
522 return ModuleSrcPath{basePath{path, ctx.AConfig()}, PathForSource(ctx, ctx.ModuleDir(), path), ctx.ModuleDir()}
523}
524
525// OptionalPathForModuleSrc returns an OptionalPath. The OptionalPath contains a
526// valid path if p is non-nil.
Colin Cross635c3b02016-05-18 15:37:25 -0700527func OptionalPathForModuleSrc(ctx ModuleContext, p *string) OptionalPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700528 if p == nil {
529 return OptionalPath{}
530 }
531 return OptionalPathForPath(PathForModuleSrc(ctx, *p))
532}
533
534func (p ModuleSrcPath) String() string {
535 return p.sourcePath.String()
536}
537
Dan Willemsen21ec4902016-11-02 20:43:13 -0700538func (p ModuleSrcPath) genPathWithExt(ctx ModuleContext, subdir, ext string) ModuleGenPath {
539 return PathForModuleGen(ctx, subdir, p.moduleDir, pathtools.ReplaceExtension(p.path, ext))
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700540}
541
Colin Cross635c3b02016-05-18 15:37:25 -0700542func (p ModuleSrcPath) objPathWithExt(ctx ModuleContext, subdir, ext string) ModuleObjPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700543 return PathForModuleObj(ctx, subdir, p.moduleDir, pathtools.ReplaceExtension(p.path, ext))
544}
545
Colin Cross635c3b02016-05-18 15:37:25 -0700546func (p ModuleSrcPath) resPathWithName(ctx ModuleContext, name string) ModuleResPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700547 // TODO: Use full directory if the new ctx is not the current ctx?
548 return PathForModuleRes(ctx, p.path, name)
549}
550
551// ModuleOutPath is a Path representing a module's output directory.
552type ModuleOutPath struct {
553 OutputPath
554}
555
556var _ Path = ModuleOutPath{}
557
558// PathForModuleOut returns a Path representing the paths... under the module's
559// output directory.
Colin Cross635c3b02016-05-18 15:37:25 -0700560func PathForModuleOut(ctx ModuleContext, paths ...string) ModuleOutPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700561 p := validatePath(ctx, paths...)
562 return ModuleOutPath{PathForOutput(ctx, ".intermediates", ctx.ModuleDir(), ctx.ModuleName(), ctx.ModuleSubDir(), p)}
563}
564
565// ModuleGenPath is a Path representing the 'gen' directory in a module's output
566// directory. Mainly used for generated sources.
567type ModuleGenPath struct {
568 ModuleOutPath
569 path string
570}
571
572var _ Path = ModuleGenPath{}
573var _ genPathProvider = ModuleGenPath{}
574var _ objPathProvider = ModuleGenPath{}
575
576// PathForModuleGen returns a Path representing the paths... under the module's
577// `gen' directory.
Colin Cross635c3b02016-05-18 15:37:25 -0700578func PathForModuleGen(ctx ModuleContext, paths ...string) ModuleGenPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700579 p := validatePath(ctx, paths...)
580 return ModuleGenPath{
581 PathForModuleOut(ctx, "gen", p),
582 p,
583 }
584}
585
Dan Willemsen21ec4902016-11-02 20:43:13 -0700586func (p ModuleGenPath) genPathWithExt(ctx ModuleContext, subdir, ext string) ModuleGenPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700587 // TODO: make a different path for local vs remote generated files?
Dan Willemsen21ec4902016-11-02 20:43:13 -0700588 return PathForModuleGen(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700589}
590
Colin Cross635c3b02016-05-18 15:37:25 -0700591func (p ModuleGenPath) objPathWithExt(ctx ModuleContext, subdir, ext string) ModuleObjPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700592 return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
593}
594
595// ModuleObjPath is a Path representing the 'obj' directory in a module's output
596// directory. Used for compiled objects.
597type ModuleObjPath struct {
598 ModuleOutPath
599}
600
601var _ Path = ModuleObjPath{}
602
603// PathForModuleObj returns a Path representing the paths... under the module's
604// 'obj' directory.
Colin Cross635c3b02016-05-18 15:37:25 -0700605func PathForModuleObj(ctx ModuleContext, paths ...string) ModuleObjPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700606 p := validatePath(ctx, paths...)
607 return ModuleObjPath{PathForModuleOut(ctx, "obj", p)}
608}
609
610// ModuleResPath is a a Path representing the 'res' directory in a module's
611// output directory.
612type ModuleResPath struct {
613 ModuleOutPath
614}
615
616var _ Path = ModuleResPath{}
617
618// PathForModuleRes returns a Path representing the paths... under the module's
619// 'res' directory.
Colin Cross635c3b02016-05-18 15:37:25 -0700620func PathForModuleRes(ctx ModuleContext, paths ...string) ModuleResPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700621 p := validatePath(ctx, paths...)
622 return ModuleResPath{PathForModuleOut(ctx, "res", p)}
623}
624
625// PathForModuleInstall returns a Path representing the install path for the
626// module appended with paths...
Colin Cross635c3b02016-05-18 15:37:25 -0700627func PathForModuleInstall(ctx ModuleContext, paths ...string) OutputPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700628 var outPaths []string
629 if ctx.Device() {
Dan Willemsen782a2d12015-12-21 14:55:28 -0800630 partition := "system"
631 if ctx.Proprietary() {
632 partition = "vendor"
633 }
634 if ctx.InstallInData() {
635 partition = "data"
636 }
637 outPaths = []string{"target", "product", ctx.AConfig().DeviceName(), partition}
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700638 } else {
Colin Crossa1ad8d12016-06-01 17:09:44 -0700639 outPaths = []string{"host", ctx.Os().String() + "-x86"}
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700640 }
Dan Willemsen782a2d12015-12-21 14:55:28 -0800641 if ctx.Debug() {
642 outPaths = append([]string{"debug"}, outPaths...)
643 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700644 outPaths = append(outPaths, paths...)
645 return PathForOutput(ctx, outPaths...)
646}
647
648// validateSafePath validates a path that we trust (may contain ninja variables).
Dan Willemsen80a7c2a2015-12-21 14:57:11 -0800649// Ensures that each path component does not attempt to leave its component.
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700650func validateSafePath(ctx PathContext, paths ...string) string {
Dan Willemsen80a7c2a2015-12-21 14:57:11 -0800651 for _, path := range paths {
652 path := filepath.Clean(path)
653 if path == ".." || strings.HasPrefix(path, "../") || strings.HasPrefix(path, "/") {
654 reportPathError(ctx, "Path is outside directory: %s", path)
655 return ""
656 }
657 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700658 // TODO: filepath.Join isn't necessarily correct with embedded ninja
659 // variables. '..' may remove the entire ninja variable, even if it
660 // will be expanded to multiple nested directories.
Dan Willemsen80a7c2a2015-12-21 14:57:11 -0800661 return filepath.Join(paths...)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700662}
663
Dan Willemsen80a7c2a2015-12-21 14:57:11 -0800664// validatePath validates that a path does not include ninja variables, and that
665// each path component does not attempt to leave its component. Returns a joined
666// version of each path component.
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700667func validatePath(ctx PathContext, paths ...string) string {
668 for _, path := range paths {
669 if strings.Contains(path, "$") {
670 reportPathError(ctx, "Path contains invalid character($): %s", path)
671 return ""
672 }
673 }
674 return validateSafePath(ctx, paths...)
Colin Cross6e18ca42015-07-14 18:55:36 -0700675}