blob: 8a085ea1769e467e2314d231ffa8e38ed70763d7 [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
24 "github.com/google/blueprint"
25 "github.com/google/blueprint/pathtools"
Colin Cross3f40fa42015-01-30 17:27:36 -080026)
27
Dan Willemsen34cc69e2015-09-23 15:26:20 -070028// PathContext is the subset of a (Module|Singleton)Context required by the
29// Path methods.
30type PathContext interface {
31 Config() interface{}
Colin Cross3f40fa42015-01-30 17:27:36 -080032}
33
Dan Willemsen34cc69e2015-09-23 15:26:20 -070034var _ PathContext = blueprint.SingletonContext(nil)
35var _ PathContext = blueprint.ModuleContext(nil)
36
37// errorfContext is the interface containing the Errorf method matching the
38// Errorf method in blueprint.SingletonContext.
39type errorfContext interface {
40 Errorf(format string, args ...interface{})
Colin Cross3f40fa42015-01-30 17:27:36 -080041}
42
Dan Willemsen34cc69e2015-09-23 15:26:20 -070043var _ errorfContext = blueprint.SingletonContext(nil)
44
45// moduleErrorf is the interface containing the ModuleErrorf method matching
46// the ModuleErrorf method in blueprint.ModuleContext.
47type moduleErrorf interface {
48 ModuleErrorf(format string, args ...interface{})
Colin Cross3f40fa42015-01-30 17:27:36 -080049}
50
Dan Willemsen34cc69e2015-09-23 15:26:20 -070051var _ moduleErrorf = blueprint.ModuleContext(nil)
52
53// pathConfig returns the android Config interface associated to the context.
54// Panics if the context isn't affiliated with an android build.
55func pathConfig(ctx PathContext) Config {
56 if ret, ok := ctx.Config().(Config); ok {
57 return ret
58 }
59 panic("Paths may only be used on Soong builds")
Colin Cross3f40fa42015-01-30 17:27:36 -080060}
61
Dan Willemsen34cc69e2015-09-23 15:26:20 -070062// reportPathError will register an error with the attached context. It
63// attempts ctx.ModuleErrorf for a better error message first, then falls
64// back to ctx.Errorf.
65func reportPathError(ctx PathContext, format string, args ...interface{}) {
66 if mctx, ok := ctx.(moduleErrorf); ok {
67 mctx.ModuleErrorf(format, args...)
68 } else if ectx, ok := ctx.(errorfContext); ok {
69 ectx.Errorf(format, args...)
70 } else {
71 panic(fmt.Sprintf(format, args...))
Colin Crossf2298272015-05-12 11:36:53 -070072 }
73}
74
Dan Willemsen34cc69e2015-09-23 15:26:20 -070075type Path interface {
76 // Returns the path in string form
77 String() string
78
79 // Returns the current file extension of the path
80 Ext() string
81}
82
83// WritablePath is a type of path that can be used as an output for build rules.
84type WritablePath interface {
85 Path
86
87 writablePath()
88}
89
90type genPathProvider interface {
91 genPathWithExt(ctx AndroidModuleContext, ext string) ModuleGenPath
92}
93type objPathProvider interface {
94 objPathWithExt(ctx AndroidModuleContext, subdir, ext string) ModuleObjPath
95}
96type resPathProvider interface {
97 resPathWithName(ctx AndroidModuleContext, name string) ModuleResPath
98}
99
100// GenPathWithExt derives a new file path in ctx's generated sources directory
101// from the current path, but with the new extension.
102func GenPathWithExt(ctx AndroidModuleContext, p Path, ext string) ModuleGenPath {
103 if path, ok := p.(genPathProvider); ok {
104 return path.genPathWithExt(ctx, ext)
105 }
106 reportPathError(ctx, "Tried to create generated file from unsupported path: %s(%s)", reflect.TypeOf(p).Name(), p)
107 return PathForModuleGen(ctx)
108}
109
110// ObjPathWithExt derives a new file path in ctx's object directory from the
111// current path, but with the new extension.
112func ObjPathWithExt(ctx AndroidModuleContext, p Path, subdir, ext string) ModuleObjPath {
113 if path, ok := p.(objPathProvider); ok {
114 return path.objPathWithExt(ctx, subdir, ext)
115 }
116 reportPathError(ctx, "Tried to create object file from unsupported path: %s (%s)", reflect.TypeOf(p).Name(), p)
117 return PathForModuleObj(ctx)
118}
119
120// ResPathWithName derives a new path in ctx's output resource directory, using
121// the current path to create the directory name, and the `name` argument for
122// the filename.
123func ResPathWithName(ctx AndroidModuleContext, p Path, name string) ModuleResPath {
124 if path, ok := p.(resPathProvider); ok {
125 return path.resPathWithName(ctx, name)
126 }
127 reportPathError(ctx, "Tried to create object file from unsupported path: %s (%s)", reflect.TypeOf(p).Name(), p)
128 return PathForModuleRes(ctx)
129}
130
131// OptionalPath is a container that may or may not contain a valid Path.
132type OptionalPath struct {
133 valid bool
134 path Path
135}
136
137// OptionalPathForPath returns an OptionalPath containing the path.
138func OptionalPathForPath(path Path) OptionalPath {
139 if path == nil {
140 return OptionalPath{}
141 }
142 return OptionalPath{valid: true, path: path}
143}
144
145// Valid returns whether there is a valid path
146func (p OptionalPath) Valid() bool {
147 return p.valid
148}
149
150// Path returns the Path embedded in this OptionalPath. You must be sure that
151// there is a valid path, since this method will panic if there is not.
152func (p OptionalPath) Path() Path {
153 if !p.valid {
154 panic("Requesting an invalid path")
155 }
156 return p.path
157}
158
159// String returns the string version of the Path, or "" if it isn't valid.
160func (p OptionalPath) String() string {
161 if p.valid {
162 return p.path.String()
163 } else {
164 return ""
Colin Crossf2298272015-05-12 11:36:53 -0700165 }
166}
Colin Cross6e18ca42015-07-14 18:55:36 -0700167
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700168// Paths is a slice of Path objects, with helpers to operate on the collection.
169type Paths []Path
170
171// PathsForSource returns Paths rooted from SrcDir
172func PathsForSource(ctx PathContext, paths []string) Paths {
173 ret := make(Paths, len(paths))
174 for i, path := range paths {
175 ret[i] = PathForSource(ctx, path)
176 }
177 return ret
178}
179
180// PathsForModuleSrc returns Paths rooted from the module's local source
181// directory
182func PathsForModuleSrc(ctx AndroidModuleContext, paths []string) Paths {
183 ret := make(Paths, len(paths))
184 for i, path := range paths {
185 ret[i] = PathForModuleSrc(ctx, path)
186 }
187 return ret
188}
189
190// pathsForModuleSrcFromFullPath returns Paths rooted from the module's local
191// source directory, but strip the local source directory from the beginning of
192// each string.
193func pathsForModuleSrcFromFullPath(ctx AndroidModuleContext, paths []string) Paths {
194 prefix := filepath.Join(ctx.AConfig().srcDir, ctx.ModuleDir()) + "/"
195 ret := make(Paths, 0, len(paths))
196 for _, p := range paths {
197 path := filepath.Clean(p)
198 if !strings.HasPrefix(path, prefix) {
199 reportPathError(ctx, "Path '%s' is not in module source directory '%s'", p, prefix)
200 continue
201 }
202 ret = append(ret, PathForModuleSrc(ctx, path[len(prefix):]))
203 }
204 return ret
205}
206
207// PathsWithOptionalDefaultForModuleSrc returns Paths rooted from the module's
208// local source directory. If none are provided, use the default if it exists.
209func PathsWithOptionalDefaultForModuleSrc(ctx AndroidModuleContext, input []string, def string) Paths {
210 if len(input) > 0 {
211 return PathsForModuleSrc(ctx, input)
212 }
213 // Use Glob so that if the default doesn't exist, a dependency is added so that when it
214 // is created, we're run again.
215 path := filepath.Join(ctx.AConfig().srcDir, ctx.ModuleDir(), def)
216 return ctx.Glob("default", path, []string{})
217}
218
219// Strings returns the Paths in string form
220func (p Paths) Strings() []string {
221 if p == nil {
222 return nil
223 }
224 ret := make([]string, len(p))
225 for i, path := range p {
226 ret[i] = path.String()
227 }
228 return ret
229}
230
231// WritablePaths is a slice of WritablePaths, used for multiple outputs.
232type WritablePaths []WritablePath
233
234// Strings returns the string forms of the writable paths.
235func (p WritablePaths) Strings() []string {
236 if p == nil {
237 return nil
238 }
239 ret := make([]string, len(p))
240 for i, path := range p {
241 ret[i] = path.String()
242 }
243 return ret
244}
245
246type basePath struct {
247 path string
248 config Config
249}
250
251func (p basePath) Ext() string {
252 return filepath.Ext(p.path)
253}
254
255// SourcePath is a Path representing a file path rooted from SrcDir
256type SourcePath struct {
257 basePath
258}
259
260var _ Path = SourcePath{}
261
262// safePathForSource is for paths that we expect are safe -- only for use by go
263// code that is embedding ninja variables in paths
264func safePathForSource(ctx PathContext, path string) SourcePath {
265 p := validateSafePath(ctx, path)
266 ret := SourcePath{basePath{p, pathConfig(ctx)}}
267
268 abs, err := filepath.Abs(ret.String())
Colin Cross6e18ca42015-07-14 18:55:36 -0700269 if err != nil {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700270 reportPathError(ctx, "%s", err.Error())
271 return ret
272 }
273 buildroot, err := filepath.Abs(pathConfig(ctx).buildDir)
274 if err != nil {
275 reportPathError(ctx, "%s", err.Error())
276 return ret
277 }
278 if strings.HasPrefix(abs, buildroot) {
279 reportPathError(ctx, "source path %s is in output", abs)
280 return ret
Colin Cross6e18ca42015-07-14 18:55:36 -0700281 }
282
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700283 return ret
284}
285
286// PathForSource returns a SourcePath for the provided paths... (which are
287// joined together with filepath.Join). This also validates that the path
288// doesn't escape the source dir, or is contained in the build dir. On error, it
289// will return a usable, but invalid SourcePath, and report a ModuleError.
290func PathForSource(ctx PathContext, paths ...string) SourcePath {
291 p := validatePath(ctx, paths...)
292 ret := SourcePath{basePath{p, pathConfig(ctx)}}
293
294 abs, err := filepath.Abs(ret.String())
295 if err != nil {
296 reportPathError(ctx, "%s", err.Error())
297 return ret
298 }
299 buildroot, err := filepath.Abs(pathConfig(ctx).buildDir)
300 if err != nil {
301 reportPathError(ctx, "%s", err.Error())
302 return ret
303 }
304 if strings.HasPrefix(abs, buildroot) {
305 reportPathError(ctx, "source path %s is in output", abs)
306 return ret
307 }
308
309 if _, err = os.Stat(ret.String()); err != nil {
310 if os.IsNotExist(err) {
311 reportPathError(ctx, "source path %s does not exist", ret)
312 } else {
313 reportPathError(ctx, "%s: %s", ret, err.Error())
314 }
315 }
316 return ret
317}
318
319// OptionalPathForSource returns an OptionalPath with the SourcePath if the
320// path exists, or an empty OptionalPath if it doesn't exist. Dependencies are added
321// so that the ninja file will be regenerated if the state of the path changes.
322func OptionalPathForSource(ctx blueprint.SingletonContext, intermediates string, paths ...string) OptionalPath {
323 p := validatePath(ctx, paths...)
324 path := SourcePath{basePath{p, pathConfig(ctx)}}
325
326 abs, err := filepath.Abs(path.String())
327 if err != nil {
328 reportPathError(ctx, "%s", err.Error())
329 return OptionalPath{}
330 }
331 buildroot, err := filepath.Abs(pathConfig(ctx).buildDir)
332 if err != nil {
333 reportPathError(ctx, "%s", err.Error())
334 return OptionalPath{}
335 }
336 if strings.HasPrefix(abs, buildroot) {
337 reportPathError(ctx, "source path %s is in output", abs)
338 return OptionalPath{}
339 }
340
341 // Use glob to produce proper dependencies, even though we only want
342 // a single file.
343 files, err := Glob(ctx, PathForIntermediates(ctx, intermediates).String(), path.String(), nil)
344 if err != nil {
345 reportPathError(ctx, "glob: %s", err.Error())
346 return OptionalPath{}
347 }
348
349 if len(files) == 0 {
350 return OptionalPath{}
351 }
352 return OptionalPathForPath(path)
353}
354
355func (p SourcePath) String() string {
356 return filepath.Join(p.config.srcDir, p.path)
357}
358
359// Join creates a new SourcePath with paths... joined with the current path. The
360// provided paths... may not use '..' to escape from the current path.
361func (p SourcePath) Join(ctx PathContext, paths ...string) SourcePath {
362 path := validatePath(ctx, paths...)
363 return PathForSource(ctx, p.path, path)
364}
365
366// OverlayPath returns the overlay for `path' if it exists. This assumes that the
367// SourcePath is the path to a resource overlay directory.
368func (p SourcePath) OverlayPath(ctx AndroidModuleContext, path Path) OptionalPath {
369 var relDir string
370 if moduleSrcPath, ok := path.(ModuleSrcPath); ok {
371 relDir = moduleSrcPath.sourcePath.path
372 } else if srcPath, ok := path.(SourcePath); ok {
373 relDir = srcPath.path
374 } else {
375 reportPathError(ctx, "Cannot find relative path for %s(%s)", reflect.TypeOf(path).Name(), path)
376 return OptionalPath{}
377 }
378 dir := filepath.Join(p.config.srcDir, p.path, relDir)
379 // Use Glob so that we are run again if the directory is added.
380 paths, err := Glob(ctx, PathForModuleOut(ctx, "overlay").String(), dir, []string{})
381 if err != nil {
382 reportPathError(ctx, "glob: %s", err.Error())
383 return OptionalPath{}
384 }
385 if len(paths) == 0 {
386 return OptionalPath{}
387 }
388 relPath, err := filepath.Rel(p.config.srcDir, paths[0])
389 if err != nil {
390 reportPathError(ctx, "%s", err.Error())
391 return OptionalPath{}
392 }
393 return OptionalPathForPath(PathForSource(ctx, relPath))
394}
395
396// OutputPath is a Path representing a file path rooted from the build directory
397type OutputPath struct {
398 basePath
399}
400
401var _ Path = OutputPath{}
402
403// PathForOutput returns an OutputPath for the provided paths... (which are
404// joined together with filepath.Join). This also validates that the path
405// does not escape the build dir. On error, it will return a usable, but invalid
406// OutputPath, and report a ModuleError.
407func PathForOutput(ctx PathContext, paths ...string) OutputPath {
408 path := validatePath(ctx, paths...)
409 return OutputPath{basePath{path, pathConfig(ctx)}}
410}
411
412func (p OutputPath) writablePath() {}
413
414func (p OutputPath) String() string {
415 return filepath.Join(p.config.buildDir, p.path)
416}
417
418// Join creates a new OutputPath with paths... joined with the current path. The
419// provided paths... may not use '..' to escape from the current path.
420func (p OutputPath) Join(ctx PathContext, paths ...string) OutputPath {
421 path := validatePath(ctx, paths...)
422 return PathForOutput(ctx, p.path, path)
423}
424
425// PathForIntermediates returns an OutputPath representing the top-level
426// intermediates directory.
427func PathForIntermediates(ctx PathContext, paths ...string) OutputPath {
428 path := validatePath(ctx, paths...)
429 return PathForOutput(ctx, ".intermediates", path)
430}
431
432// ModuleSrcPath is a Path representing a file rooted from a module's local source dir
433type ModuleSrcPath struct {
434 basePath
435 sourcePath SourcePath
436 moduleDir string
437}
438
439var _ Path = ModuleSrcPath{}
440var _ genPathProvider = ModuleSrcPath{}
441var _ objPathProvider = ModuleSrcPath{}
442var _ resPathProvider = ModuleSrcPath{}
443
444// PathForModuleSrc returns a ModuleSrcPath representing the paths... under the
445// module's local source directory.
446func PathForModuleSrc(ctx AndroidModuleContext, paths ...string) ModuleSrcPath {
447 path := validatePath(ctx, paths...)
448 return ModuleSrcPath{basePath{path, ctx.AConfig()}, PathForSource(ctx, ctx.ModuleDir(), path), ctx.ModuleDir()}
449}
450
451// OptionalPathForModuleSrc returns an OptionalPath. The OptionalPath contains a
452// valid path if p is non-nil.
453func OptionalPathForModuleSrc(ctx AndroidModuleContext, p *string) OptionalPath {
454 if p == nil {
455 return OptionalPath{}
456 }
457 return OptionalPathForPath(PathForModuleSrc(ctx, *p))
458}
459
460func (p ModuleSrcPath) String() string {
461 return p.sourcePath.String()
462}
463
464func (p ModuleSrcPath) genPathWithExt(ctx AndroidModuleContext, ext string) ModuleGenPath {
465 return PathForModuleGen(ctx, p.moduleDir, pathtools.ReplaceExtension(p.path, ext))
466}
467
468func (p ModuleSrcPath) objPathWithExt(ctx AndroidModuleContext, subdir, ext string) ModuleObjPath {
469 return PathForModuleObj(ctx, subdir, p.moduleDir, pathtools.ReplaceExtension(p.path, ext))
470}
471
472func (p ModuleSrcPath) resPathWithName(ctx AndroidModuleContext, name string) ModuleResPath {
473 // TODO: Use full directory if the new ctx is not the current ctx?
474 return PathForModuleRes(ctx, p.path, name)
475}
476
477// ModuleOutPath is a Path representing a module's output directory.
478type ModuleOutPath struct {
479 OutputPath
480}
481
482var _ Path = ModuleOutPath{}
483
484// PathForModuleOut returns a Path representing the paths... under the module's
485// output directory.
486func PathForModuleOut(ctx AndroidModuleContext, paths ...string) ModuleOutPath {
487 p := validatePath(ctx, paths...)
488 return ModuleOutPath{PathForOutput(ctx, ".intermediates", ctx.ModuleDir(), ctx.ModuleName(), ctx.ModuleSubDir(), p)}
489}
490
491// ModuleGenPath is a Path representing the 'gen' directory in a module's output
492// directory. Mainly used for generated sources.
493type ModuleGenPath struct {
494 ModuleOutPath
495 path string
496}
497
498var _ Path = ModuleGenPath{}
499var _ genPathProvider = ModuleGenPath{}
500var _ objPathProvider = ModuleGenPath{}
501
502// PathForModuleGen returns a Path representing the paths... under the module's
503// `gen' directory.
504func PathForModuleGen(ctx AndroidModuleContext, paths ...string) ModuleGenPath {
505 p := validatePath(ctx, paths...)
506 return ModuleGenPath{
507 PathForModuleOut(ctx, "gen", p),
508 p,
509 }
510}
511
512func (p ModuleGenPath) genPathWithExt(ctx AndroidModuleContext, ext string) ModuleGenPath {
513 // TODO: make a different path for local vs remote generated files?
514 return PathForModuleGen(ctx, pathtools.ReplaceExtension(p.path, ext))
515}
516
517func (p ModuleGenPath) objPathWithExt(ctx AndroidModuleContext, subdir, ext string) ModuleObjPath {
518 return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
519}
520
521// ModuleObjPath is a Path representing the 'obj' directory in a module's output
522// directory. Used for compiled objects.
523type ModuleObjPath struct {
524 ModuleOutPath
525}
526
527var _ Path = ModuleObjPath{}
528
529// PathForModuleObj returns a Path representing the paths... under the module's
530// 'obj' directory.
531func PathForModuleObj(ctx AndroidModuleContext, paths ...string) ModuleObjPath {
532 p := validatePath(ctx, paths...)
533 return ModuleObjPath{PathForModuleOut(ctx, "obj", p)}
534}
535
536// ModuleResPath is a a Path representing the 'res' directory in a module's
537// output directory.
538type ModuleResPath struct {
539 ModuleOutPath
540}
541
542var _ Path = ModuleResPath{}
543
544// PathForModuleRes returns a Path representing the paths... under the module's
545// 'res' directory.
546func PathForModuleRes(ctx AndroidModuleContext, paths ...string) ModuleResPath {
547 p := validatePath(ctx, paths...)
548 return ModuleResPath{PathForModuleOut(ctx, "res", p)}
549}
550
551// PathForModuleInstall returns a Path representing the install path for the
552// module appended with paths...
553func PathForModuleInstall(ctx AndroidModuleContext, paths ...string) OutputPath {
554 var outPaths []string
555 if ctx.Device() {
556 outPaths = []string{"target", "product", ctx.AConfig().DeviceName(), "system"}
557 } else {
558 outPaths = []string{"host", ctx.HostType().String() + "-x86"}
559 }
560 outPaths = append(outPaths, paths...)
561 return PathForOutput(ctx, outPaths...)
562}
563
564// validateSafePath validates a path that we trust (may contain ninja variables).
565// Ensures that it does not attempt to leave the containing directory.
566func validateSafePath(ctx PathContext, paths ...string) string {
567 // TODO: filepath.Join isn't necessarily correct with embedded ninja
568 // variables. '..' may remove the entire ninja variable, even if it
569 // will be expanded to multiple nested directories.
570 p := filepath.Join(paths...)
571 if p == ".." || strings.HasPrefix(p, "../") || strings.HasPrefix(p, "/") {
572 reportPathError(ctx, "Path is outside directory: %s", p)
573 return ""
574 }
575 return p
576}
577
578// validatePath validates that a path does not include ninja variables, and does
579// not attempt to leave the containing directory.
580func validatePath(ctx PathContext, paths ...string) string {
581 for _, path := range paths {
582 if strings.Contains(path, "$") {
583 reportPathError(ctx, "Path contains invalid character($): %s", path)
584 return ""
585 }
586 }
587 return validateSafePath(ctx, paths...)
Colin Cross6e18ca42015-07-14 18:55:36 -0700588}