blob: 4d9c85860b5c1f61ea3f8cf7ceffb3352e5d83a1 [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 Cross6a745c62015-06-16 16:38:10 -070019 "path/filepath"
Dan Willemsen34cc69e2015-09-23 15:26:20 -070020 "reflect"
Colin Cross5e6cfbe2017-11-03 15:20:35 -070021 "sort"
Dan Willemsen34cc69e2015-09-23 15:26:20 -070022 "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 {
Colin Cross294941b2017-02-01 14:10:36 -080031 Fs() pathtools.FileSystem
Colin Crossaabf6792017-11-29 00:27:14 -080032 Config() Config
Dan Willemsen7b310ee2015-12-18 15:11:17 -080033 AddNinjaFileDeps(deps ...string)
Colin Cross3f40fa42015-01-30 17:27:36 -080034}
35
Colin Cross7f19f372016-11-01 11:10:25 -070036type PathGlobContext interface {
37 GlobWithDeps(globPattern string, excludes []string) ([]string, error)
38}
39
Colin Crossaabf6792017-11-29 00:27:14 -080040var _ PathContext = SingletonContext(nil)
41var _ PathContext = ModuleContext(nil)
Dan Willemsen34cc69e2015-09-23 15:26:20 -070042
Dan Willemsen00269f22017-07-06 16:59:48 -070043type ModuleInstallPathContext interface {
44 PathContext
45
46 androidBaseContext
47
48 InstallInData() bool
49 InstallInSanitizerDir() bool
50}
51
52var _ ModuleInstallPathContext = ModuleContext(nil)
53
Dan Willemsen34cc69e2015-09-23 15:26:20 -070054// errorfContext is the interface containing the Errorf method matching the
55// Errorf method in blueprint.SingletonContext.
56type errorfContext interface {
57 Errorf(format string, args ...interface{})
Colin Cross3f40fa42015-01-30 17:27:36 -080058}
59
Dan Willemsen34cc69e2015-09-23 15:26:20 -070060var _ errorfContext = blueprint.SingletonContext(nil)
61
62// moduleErrorf is the interface containing the ModuleErrorf method matching
63// the ModuleErrorf method in blueprint.ModuleContext.
64type moduleErrorf interface {
65 ModuleErrorf(format string, args ...interface{})
Colin Cross3f40fa42015-01-30 17:27:36 -080066}
67
Dan Willemsen34cc69e2015-09-23 15:26:20 -070068var _ moduleErrorf = blueprint.ModuleContext(nil)
69
Dan Willemsen34cc69e2015-09-23 15:26:20 -070070// reportPathError will register an error with the attached context. It
71// attempts ctx.ModuleErrorf for a better error message first, then falls
72// back to ctx.Errorf.
73func reportPathError(ctx PathContext, format string, args ...interface{}) {
74 if mctx, ok := ctx.(moduleErrorf); ok {
75 mctx.ModuleErrorf(format, args...)
76 } else if ectx, ok := ctx.(errorfContext); ok {
77 ectx.Errorf(format, args...)
78 } else {
79 panic(fmt.Sprintf(format, args...))
Colin Crossf2298272015-05-12 11:36:53 -070080 }
81}
82
Dan Willemsen34cc69e2015-09-23 15:26:20 -070083type Path interface {
84 // Returns the path in string form
85 String() string
86
Colin Cross4f6fc9c2016-10-26 10:05:25 -070087 // Ext returns the extension of the last element of the path
Dan Willemsen34cc69e2015-09-23 15:26:20 -070088 Ext() string
Colin Cross4f6fc9c2016-10-26 10:05:25 -070089
90 // Base returns the last element of the path
91 Base() string
Colin Crossfaeb7aa2017-02-01 14:12:44 -080092
93 // Rel returns the portion of the path relative to the directory it was created from. For
94 // example, Rel on a PathsForModuleSrc would return the path relative to the module source
Colin Cross0db55682017-12-05 15:36:55 -080095 // directory, and OutputPath.Join("foo").Rel() would return "foo".
Colin Crossfaeb7aa2017-02-01 14:12:44 -080096 Rel() string
Dan Willemsen34cc69e2015-09-23 15:26:20 -070097}
98
99// WritablePath is a type of path that can be used as an output for build rules.
100type WritablePath interface {
101 Path
102
Jeff Gaston734e3802017-04-10 15:47:24 -0700103 // the writablePath method doesn't directly do anything,
104 // but it allows a struct to distinguish between whether or not it implements the WritablePath interface
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700105 writablePath()
106}
107
108type genPathProvider interface {
Dan Willemsen21ec4902016-11-02 20:43:13 -0700109 genPathWithExt(ctx ModuleContext, subdir, ext string) ModuleGenPath
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700110}
111type objPathProvider interface {
Colin Cross635c3b02016-05-18 15:37:25 -0700112 objPathWithExt(ctx ModuleContext, subdir, ext string) ModuleObjPath
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700113}
114type resPathProvider interface {
Colin Cross635c3b02016-05-18 15:37:25 -0700115 resPathWithName(ctx ModuleContext, name string) ModuleResPath
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700116}
117
118// GenPathWithExt derives a new file path in ctx's generated sources directory
119// from the current path, but with the new extension.
Dan Willemsen21ec4902016-11-02 20:43:13 -0700120func GenPathWithExt(ctx ModuleContext, subdir string, p Path, ext string) ModuleGenPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700121 if path, ok := p.(genPathProvider); ok {
Dan Willemsen21ec4902016-11-02 20:43:13 -0700122 return path.genPathWithExt(ctx, subdir, ext)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700123 }
124 reportPathError(ctx, "Tried to create generated file from unsupported path: %s(%s)", reflect.TypeOf(p).Name(), p)
125 return PathForModuleGen(ctx)
126}
127
128// ObjPathWithExt derives a new file path in ctx's object directory from the
129// current path, but with the new extension.
Dan Willemsen21ec4902016-11-02 20:43:13 -0700130func ObjPathWithExt(ctx ModuleContext, subdir string, p Path, ext string) ModuleObjPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700131 if path, ok := p.(objPathProvider); ok {
132 return path.objPathWithExt(ctx, subdir, ext)
133 }
134 reportPathError(ctx, "Tried to create object file from unsupported path: %s (%s)", reflect.TypeOf(p).Name(), p)
135 return PathForModuleObj(ctx)
136}
137
138// ResPathWithName derives a new path in ctx's output resource directory, using
139// the current path to create the directory name, and the `name` argument for
140// the filename.
Colin Cross635c3b02016-05-18 15:37:25 -0700141func ResPathWithName(ctx ModuleContext, p Path, name string) ModuleResPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700142 if path, ok := p.(resPathProvider); ok {
143 return path.resPathWithName(ctx, name)
144 }
Jeff Gaston734e3802017-04-10 15:47:24 -0700145 reportPathError(ctx, "Tried to create res file from unsupported path: %s (%s)", reflect.TypeOf(p).Name(), p)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700146 return PathForModuleRes(ctx)
147}
148
149// OptionalPath is a container that may or may not contain a valid Path.
150type OptionalPath struct {
151 valid bool
152 path Path
153}
154
155// OptionalPathForPath returns an OptionalPath containing the path.
156func OptionalPathForPath(path Path) OptionalPath {
157 if path == nil {
158 return OptionalPath{}
159 }
160 return OptionalPath{valid: true, path: path}
161}
162
163// Valid returns whether there is a valid path
164func (p OptionalPath) Valid() bool {
165 return p.valid
166}
167
168// Path returns the Path embedded in this OptionalPath. You must be sure that
169// there is a valid path, since this method will panic if there is not.
170func (p OptionalPath) Path() Path {
171 if !p.valid {
172 panic("Requesting an invalid path")
173 }
174 return p.path
175}
176
177// String returns the string version of the Path, or "" if it isn't valid.
178func (p OptionalPath) String() string {
179 if p.valid {
180 return p.path.String()
181 } else {
182 return ""
Colin Crossf2298272015-05-12 11:36:53 -0700183 }
184}
Colin Cross6e18ca42015-07-14 18:55:36 -0700185
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700186// Paths is a slice of Path objects, with helpers to operate on the collection.
187type Paths []Path
188
189// PathsForSource returns Paths rooted from SrcDir
190func PathsForSource(ctx PathContext, paths []string) Paths {
Colin Crossaabf6792017-11-29 00:27:14 -0800191 if ctx.Config().AllowMissingDependencies() {
Colin Cross635c3b02016-05-18 15:37:25 -0700192 if modCtx, ok := ctx.(ModuleContext); ok {
Dan Willemsene23dfb72016-03-11 15:02:46 -0800193 ret := make(Paths, 0, len(paths))
Colin Cross702e0f82017-10-18 17:27:54 -0700194 intermediates := pathForModule(modCtx).withRel("missing")
Dan Willemsene23dfb72016-03-11 15:02:46 -0800195 for _, path := range paths {
Colin Cross702e0f82017-10-18 17:27:54 -0700196 p := ExistentPathForSource(ctx, intermediates.String(), path)
Dan Willemsene23dfb72016-03-11 15:02:46 -0800197 if p.Valid() {
198 ret = append(ret, p.Path())
199 } else {
200 modCtx.AddMissingDependencies([]string{path})
201 }
202 }
203 return ret
204 }
205 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700206 ret := make(Paths, len(paths))
207 for i, path := range paths {
208 ret[i] = PathForSource(ctx, path)
209 }
210 return ret
211}
212
Jeff Gaston734e3802017-04-10 15:47:24 -0700213// ExistentPathsForSources returns a list of Paths rooted from SrcDir that are
Dan Willemsen7b310ee2015-12-18 15:11:17 -0800214// found in the tree. If any are not found, they are omitted from the list,
215// and dependencies are added so that we're re-run when they are added.
Jeff Gaston734e3802017-04-10 15:47:24 -0700216func ExistentPathsForSources(ctx PathContext, intermediates string, paths []string) Paths {
Dan Willemsen7b310ee2015-12-18 15:11:17 -0800217 ret := make(Paths, 0, len(paths))
218 for _, path := range paths {
Jeff Gaston734e3802017-04-10 15:47:24 -0700219 p := ExistentPathForSource(ctx, intermediates, path)
Dan Willemsen7b310ee2015-12-18 15:11:17 -0800220 if p.Valid() {
221 ret = append(ret, p.Path())
222 }
223 }
224 return ret
225}
226
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700227// PathsForModuleSrc returns Paths rooted from the module's local source
228// directory
Colin Cross635c3b02016-05-18 15:37:25 -0700229func PathsForModuleSrc(ctx ModuleContext, paths []string) Paths {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700230 ret := make(Paths, len(paths))
231 for i, path := range paths {
232 ret[i] = PathForModuleSrc(ctx, path)
233 }
234 return ret
235}
236
237// pathsForModuleSrcFromFullPath returns Paths rooted from the module's local
238// source directory, but strip the local source directory from the beginning of
239// each string.
Colin Cross635c3b02016-05-18 15:37:25 -0700240func pathsForModuleSrcFromFullPath(ctx ModuleContext, paths []string) Paths {
Colin Cross6510f912017-11-29 00:27:14 -0800241 prefix := filepath.Join(ctx.Config().srcDir, ctx.ModuleDir()) + "/"
Colin Cross0f37af02017-09-27 17:42:05 -0700242 if prefix == "./" {
243 prefix = ""
244 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700245 ret := make(Paths, 0, len(paths))
246 for _, p := range paths {
247 path := filepath.Clean(p)
248 if !strings.HasPrefix(path, prefix) {
249 reportPathError(ctx, "Path '%s' is not in module source directory '%s'", p, prefix)
250 continue
251 }
252 ret = append(ret, PathForModuleSrc(ctx, path[len(prefix):]))
253 }
254 return ret
255}
256
257// PathsWithOptionalDefaultForModuleSrc returns Paths rooted from the module's
258// local source directory. If none are provided, use the default if it exists.
Colin Cross635c3b02016-05-18 15:37:25 -0700259func PathsWithOptionalDefaultForModuleSrc(ctx ModuleContext, input []string, def string) Paths {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700260 if len(input) > 0 {
261 return PathsForModuleSrc(ctx, input)
262 }
263 // Use Glob so that if the default doesn't exist, a dependency is added so that when it
264 // is created, we're run again.
Colin Cross6510f912017-11-29 00:27:14 -0800265 path := filepath.Join(ctx.Config().srcDir, ctx.ModuleDir(), def)
Colin Cross7f19f372016-11-01 11:10:25 -0700266 return ctx.Glob(path, []string{})
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700267}
268
269// Strings returns the Paths in string form
270func (p Paths) 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
Colin Crossb6715442017-10-24 11:13:31 -0700281// FirstUniquePaths returns all unique elements of a Paths, keeping the first copy of each. It
282// modifies the Paths slice contents in place, and returns a subslice of the original slice.
Dan Willemsenfe92c962017-08-29 12:28:37 -0700283func FirstUniquePaths(list Paths) Paths {
284 k := 0
285outer:
286 for i := 0; i < len(list); i++ {
287 for j := 0; j < k; j++ {
288 if list[i] == list[j] {
289 continue outer
290 }
291 }
292 list[k] = list[i]
293 k++
294 }
295 return list[:k]
296}
297
Colin Crossb6715442017-10-24 11:13:31 -0700298// LastUniquePaths returns all unique elements of a Paths, keeping the last copy of each. It
299// modifies the Paths slice contents in place, and returns a subslice of the original slice.
300func LastUniquePaths(list Paths) Paths {
301 totalSkip := 0
302 for i := len(list) - 1; i >= totalSkip; i-- {
303 skip := 0
304 for j := i - 1; j >= totalSkip; j-- {
305 if list[i] == list[j] {
306 skip++
307 } else {
308 list[j+skip] = list[j]
309 }
310 }
311 totalSkip += skip
312 }
313 return list[totalSkip:]
314}
315
Jeff Gaston294356f2017-09-27 17:05:30 -0700316func indexPathList(s Path, list []Path) int {
317 for i, l := range list {
318 if l == s {
319 return i
320 }
321 }
322
323 return -1
324}
325
326func inPathList(p Path, list []Path) bool {
327 return indexPathList(p, list) != -1
328}
329
330func FilterPathList(list []Path, filter []Path) (remainder []Path, filtered []Path) {
331 for _, l := range list {
332 if inPathList(l, filter) {
333 filtered = append(filtered, l)
334 } else {
335 remainder = append(remainder, l)
336 }
337 }
338
339 return
340}
341
Colin Cross93e85952017-08-15 13:34:18 -0700342// HasExt returns true of any of the paths have extension ext, otherwise false
343func (p Paths) HasExt(ext string) bool {
344 for _, path := range p {
345 if path.Ext() == ext {
346 return true
347 }
348 }
349
350 return false
351}
352
353// FilterByExt returns the subset of the paths that have extension ext
354func (p Paths) FilterByExt(ext string) Paths {
355 ret := make(Paths, 0, len(p))
356 for _, path := range p {
357 if path.Ext() == ext {
358 ret = append(ret, path)
359 }
360 }
361 return ret
362}
363
364// FilterOutByExt returns the subset of the paths that do not have extension ext
365func (p Paths) FilterOutByExt(ext string) Paths {
366 ret := make(Paths, 0, len(p))
367 for _, path := range p {
368 if path.Ext() != ext {
369 ret = append(ret, path)
370 }
371 }
372 return ret
373}
374
Colin Cross5e6cfbe2017-11-03 15:20:35 -0700375// DirectorySortedPaths is a slice of paths that are sorted such that all files in a directory
376// (including subdirectories) are in a contiguous subslice of the list, and can be found in
377// O(log(N)) time using a binary search on the directory prefix.
378type DirectorySortedPaths Paths
379
380func PathsToDirectorySortedPaths(paths Paths) DirectorySortedPaths {
381 ret := append(DirectorySortedPaths(nil), paths...)
382 sort.Slice(ret, func(i, j int) bool {
383 return ret[i].String() < ret[j].String()
384 })
385 return ret
386}
387
388// PathsInDirectory returns a subslice of the DirectorySortedPaths as a Paths that contains all entries
389// that are in the specified directory and its subdirectories.
390func (p DirectorySortedPaths) PathsInDirectory(dir string) Paths {
391 prefix := filepath.Clean(dir) + "/"
392 start := sort.Search(len(p), func(i int) bool {
393 return prefix < p[i].String()
394 })
395
396 ret := p[start:]
397
398 end := sort.Search(len(ret), func(i int) bool {
399 return !strings.HasPrefix(ret[i].String(), prefix)
400 })
401
402 ret = ret[:end]
403
404 return Paths(ret)
405}
406
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700407// WritablePaths is a slice of WritablePaths, used for multiple outputs.
408type WritablePaths []WritablePath
409
410// Strings returns the string forms of the writable paths.
411func (p WritablePaths) Strings() []string {
412 if p == nil {
413 return nil
414 }
415 ret := make([]string, len(p))
416 for i, path := range p {
417 ret[i] = path.String()
418 }
419 return ret
420}
421
Colin Cross3bc7ffa2017-11-22 16:19:37 -0800422// Paths returns the WritablePaths as a Paths
423func (p WritablePaths) Paths() Paths {
424 if p == nil {
425 return nil
426 }
427 ret := make(Paths, len(p))
428 for i, path := range p {
429 ret[i] = path
430 }
431 return ret
432}
433
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700434type basePath struct {
435 path string
436 config Config
Colin Crossfaeb7aa2017-02-01 14:12:44 -0800437 rel string
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700438}
439
440func (p basePath) Ext() string {
441 return filepath.Ext(p.path)
442}
443
Colin Cross4f6fc9c2016-10-26 10:05:25 -0700444func (p basePath) Base() string {
445 return filepath.Base(p.path)
446}
447
Colin Crossfaeb7aa2017-02-01 14:12:44 -0800448func (p basePath) Rel() string {
449 if p.rel != "" {
450 return p.rel
451 }
452 return p.path
453}
454
Colin Cross0875c522017-11-28 17:34:01 -0800455func (p basePath) String() string {
456 return p.path
457}
458
Colin Cross0db55682017-12-05 15:36:55 -0800459func (p basePath) withRel(rel string) basePath {
460 p.path = filepath.Join(p.path, rel)
461 p.rel = rel
462 return p
463}
464
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700465// SourcePath is a Path representing a file path rooted from SrcDir
466type SourcePath struct {
467 basePath
468}
469
470var _ Path = SourcePath{}
471
Colin Cross0db55682017-12-05 15:36:55 -0800472func (p SourcePath) withRel(rel string) SourcePath {
473 p.basePath = p.basePath.withRel(rel)
474 return p
475}
476
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700477// safePathForSource is for paths that we expect are safe -- only for use by go
478// code that is embedding ninja variables in paths
479func safePathForSource(ctx PathContext, path string) SourcePath {
480 p := validateSafePath(ctx, path)
Colin Crossaabf6792017-11-29 00:27:14 -0800481 ret := SourcePath{basePath{p, ctx.Config(), ""}}
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700482
483 abs, err := filepath.Abs(ret.String())
Colin Cross6e18ca42015-07-14 18:55:36 -0700484 if err != nil {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700485 reportPathError(ctx, "%s", err.Error())
486 return ret
487 }
Colin Crossaabf6792017-11-29 00:27:14 -0800488 buildroot, err := filepath.Abs(ctx.Config().buildDir)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700489 if err != nil {
490 reportPathError(ctx, "%s", err.Error())
491 return ret
492 }
493 if strings.HasPrefix(abs, buildroot) {
494 reportPathError(ctx, "source path %s is in output", abs)
495 return ret
Colin Cross6e18ca42015-07-14 18:55:36 -0700496 }
497
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700498 return ret
499}
500
Jeff Gaston734e3802017-04-10 15:47:24 -0700501// PathForSource joins the provided path components and validates that the result
502// neither escapes the source dir nor is in the out dir.
503// On error, it will return a usable, but invalid SourcePath, and report a ModuleError.
504func PathForSource(ctx PathContext, pathComponents ...string) SourcePath {
505 p := validatePath(ctx, pathComponents...)
Colin Crossaabf6792017-11-29 00:27:14 -0800506 ret := SourcePath{basePath{p, ctx.Config(), ""}}
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700507
508 abs, err := filepath.Abs(ret.String())
509 if err != nil {
510 reportPathError(ctx, "%s", err.Error())
511 return ret
512 }
Colin Crossaabf6792017-11-29 00:27:14 -0800513 buildroot, err := filepath.Abs(ctx.Config().buildDir)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700514 if err != nil {
515 reportPathError(ctx, "%s", err.Error())
516 return ret
517 }
518 if strings.HasPrefix(abs, buildroot) {
519 reportPathError(ctx, "source path %s is in output", abs)
520 return ret
521 }
522
Colin Cross294941b2017-02-01 14:10:36 -0800523 if exists, _, err := ctx.Fs().Exists(ret.String()); err != nil {
524 reportPathError(ctx, "%s: %s", ret, err.Error())
525 } else if !exists {
526 reportPathError(ctx, "source path %s does not exist", ret)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700527 }
528 return ret
529}
530
Jeff Gaston734e3802017-04-10 15:47:24 -0700531// ExistentPathForSource returns an OptionalPath with the SourcePath if the
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700532// path exists, or an empty OptionalPath if it doesn't exist. Dependencies are added
533// so that the ninja file will be regenerated if the state of the path changes.
Jeff Gaston734e3802017-04-10 15:47:24 -0700534func ExistentPathForSource(ctx PathContext, intermediates string, pathComponents ...string) OptionalPath {
535 if len(pathComponents) == 0 {
Dan Willemsen7b310ee2015-12-18 15:11:17 -0800536 // For when someone forgets the 'intermediates' argument
537 panic("Missing path(s)")
538 }
539
Jeff Gaston734e3802017-04-10 15:47:24 -0700540 p := validatePath(ctx, pathComponents...)
Colin Crossaabf6792017-11-29 00:27:14 -0800541 path := SourcePath{basePath{p, ctx.Config(), ""}}
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700542
543 abs, err := filepath.Abs(path.String())
544 if err != nil {
545 reportPathError(ctx, "%s", err.Error())
546 return OptionalPath{}
547 }
Colin Crossaabf6792017-11-29 00:27:14 -0800548 buildroot, err := filepath.Abs(ctx.Config().buildDir)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700549 if err != nil {
550 reportPathError(ctx, "%s", err.Error())
551 return OptionalPath{}
552 }
553 if strings.HasPrefix(abs, buildroot) {
554 reportPathError(ctx, "source path %s is in output", abs)
555 return OptionalPath{}
556 }
557
Colin Cross7f19f372016-11-01 11:10:25 -0700558 if pathtools.IsGlob(path.String()) {
Dan Willemsen7b310ee2015-12-18 15:11:17 -0800559 reportPathError(ctx, "path may not contain a glob: %s", path.String())
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700560 return OptionalPath{}
561 }
562
Colin Cross7f19f372016-11-01 11:10:25 -0700563 if gctx, ok := ctx.(PathGlobContext); ok {
Dan Willemsen7b310ee2015-12-18 15:11:17 -0800564 // Use glob to produce proper dependencies, even though we only want
565 // a single file.
Colin Cross7f19f372016-11-01 11:10:25 -0700566 files, err := gctx.GlobWithDeps(path.String(), nil)
Dan Willemsen7b310ee2015-12-18 15:11:17 -0800567 if err != nil {
568 reportPathError(ctx, "glob: %s", err.Error())
569 return OptionalPath{}
570 }
571
572 if len(files) == 0 {
573 return OptionalPath{}
574 }
575 } else {
576 // We cannot add build statements in this context, so we fall back to
577 // AddNinjaFileDeps
Colin Cross294941b2017-02-01 14:10:36 -0800578 files, dirs, err := pathtools.Glob(path.String(), nil)
Dan Willemsen7b310ee2015-12-18 15:11:17 -0800579 if err != nil {
580 reportPathError(ctx, "glob: %s", err.Error())
581 return OptionalPath{}
582 }
583
584 ctx.AddNinjaFileDeps(dirs...)
585
586 if len(files) == 0 {
587 return OptionalPath{}
588 }
589
590 ctx.AddNinjaFileDeps(path.String())
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700591 }
592 return OptionalPathForPath(path)
593}
594
595func (p SourcePath) String() string {
596 return filepath.Join(p.config.srcDir, p.path)
597}
598
599// Join creates a new SourcePath with paths... joined with the current path. The
600// provided paths... may not use '..' to escape from the current path.
601func (p SourcePath) Join(ctx PathContext, paths ...string) SourcePath {
602 path := validatePath(ctx, paths...)
Colin Cross0db55682017-12-05 15:36:55 -0800603 return p.withRel(path)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700604}
605
606// OverlayPath returns the overlay for `path' if it exists. This assumes that the
607// SourcePath is the path to a resource overlay directory.
Colin Cross635c3b02016-05-18 15:37:25 -0700608func (p SourcePath) OverlayPath(ctx ModuleContext, path Path) OptionalPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700609 var relDir string
610 if moduleSrcPath, ok := path.(ModuleSrcPath); ok {
Colin Cross7fc17db2017-02-01 14:07:55 -0800611 relDir = moduleSrcPath.path
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700612 } else if srcPath, ok := path.(SourcePath); ok {
613 relDir = srcPath.path
614 } else {
615 reportPathError(ctx, "Cannot find relative path for %s(%s)", reflect.TypeOf(path).Name(), path)
616 return OptionalPath{}
617 }
618 dir := filepath.Join(p.config.srcDir, p.path, relDir)
619 // Use Glob so that we are run again if the directory is added.
Colin Cross7f19f372016-11-01 11:10:25 -0700620 if pathtools.IsGlob(dir) {
Dan Willemsen7b310ee2015-12-18 15:11:17 -0800621 reportPathError(ctx, "Path may not contain a glob: %s", dir)
622 }
Colin Cross7f19f372016-11-01 11:10:25 -0700623 paths, err := ctx.GlobWithDeps(dir, []string{})
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700624 if err != nil {
625 reportPathError(ctx, "glob: %s", err.Error())
626 return OptionalPath{}
627 }
628 if len(paths) == 0 {
629 return OptionalPath{}
630 }
631 relPath, err := filepath.Rel(p.config.srcDir, paths[0])
632 if err != nil {
633 reportPathError(ctx, "%s", err.Error())
634 return OptionalPath{}
635 }
636 return OptionalPathForPath(PathForSource(ctx, relPath))
637}
638
639// OutputPath is a Path representing a file path rooted from the build directory
640type OutputPath struct {
641 basePath
642}
643
Colin Cross702e0f82017-10-18 17:27:54 -0700644func (p OutputPath) withRel(rel string) OutputPath {
Colin Cross0db55682017-12-05 15:36:55 -0800645 p.basePath = p.basePath.withRel(rel)
Colin Cross702e0f82017-10-18 17:27:54 -0700646 return p
647}
648
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700649var _ Path = OutputPath{}
650
Jeff Gaston734e3802017-04-10 15:47:24 -0700651// PathForOutput joins the provided paths and returns an OutputPath that is
652// validated to not escape the build dir.
653// On error, it will return a usable, but invalid OutputPath, and report a ModuleError.
654func PathForOutput(ctx PathContext, pathComponents ...string) OutputPath {
655 path := validatePath(ctx, pathComponents...)
Colin Crossaabf6792017-11-29 00:27:14 -0800656 return OutputPath{basePath{path, ctx.Config(), ""}}
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700657}
658
659func (p OutputPath) writablePath() {}
660
661func (p OutputPath) String() string {
662 return filepath.Join(p.config.buildDir, p.path)
663}
664
Colin Crossa2344662016-03-24 13:14:12 -0700665func (p OutputPath) RelPathString() string {
666 return p.path
667}
668
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700669// Join creates a new OutputPath with paths... joined with the current path. The
670// provided paths... may not use '..' to escape from the current path.
671func (p OutputPath) Join(ctx PathContext, paths ...string) OutputPath {
672 path := validatePath(ctx, paths...)
Colin Cross0db55682017-12-05 15:36:55 -0800673 return p.withRel(path)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700674}
675
676// PathForIntermediates returns an OutputPath representing the top-level
677// intermediates directory.
678func PathForIntermediates(ctx PathContext, paths ...string) OutputPath {
679 path := validatePath(ctx, paths...)
680 return PathForOutput(ctx, ".intermediates", path)
681}
682
683// ModuleSrcPath is a Path representing a file rooted from a module's local source dir
684type ModuleSrcPath struct {
Colin Cross7fc17db2017-02-01 14:07:55 -0800685 SourcePath
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700686}
687
688var _ Path = ModuleSrcPath{}
689var _ genPathProvider = ModuleSrcPath{}
690var _ objPathProvider = ModuleSrcPath{}
691var _ resPathProvider = ModuleSrcPath{}
692
693// PathForModuleSrc returns a ModuleSrcPath representing the paths... under the
694// module's local source directory.
Colin Cross635c3b02016-05-18 15:37:25 -0700695func PathForModuleSrc(ctx ModuleContext, paths ...string) ModuleSrcPath {
Colin Crossfaeb7aa2017-02-01 14:12:44 -0800696 p := validatePath(ctx, paths...)
697 path := ModuleSrcPath{PathForSource(ctx, ctx.ModuleDir(), p)}
698 path.basePath.rel = p
699 return path
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700700}
701
702// OptionalPathForModuleSrc returns an OptionalPath. The OptionalPath contains a
703// valid path if p is non-nil.
Colin Cross635c3b02016-05-18 15:37:25 -0700704func OptionalPathForModuleSrc(ctx ModuleContext, p *string) OptionalPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700705 if p == nil {
706 return OptionalPath{}
707 }
708 return OptionalPathForPath(PathForModuleSrc(ctx, *p))
709}
710
Dan Willemsen21ec4902016-11-02 20:43:13 -0700711func (p ModuleSrcPath) genPathWithExt(ctx ModuleContext, subdir, ext string) ModuleGenPath {
Colin Cross7fc17db2017-02-01 14:07:55 -0800712 return PathForModuleGen(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700713}
714
Colin Cross635c3b02016-05-18 15:37:25 -0700715func (p ModuleSrcPath) objPathWithExt(ctx ModuleContext, subdir, ext string) ModuleObjPath {
Colin Cross7fc17db2017-02-01 14:07:55 -0800716 return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700717}
718
Colin Cross635c3b02016-05-18 15:37:25 -0700719func (p ModuleSrcPath) resPathWithName(ctx ModuleContext, name string) ModuleResPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700720 // TODO: Use full directory if the new ctx is not the current ctx?
721 return PathForModuleRes(ctx, p.path, name)
722}
723
Colin Crossfaeb7aa2017-02-01 14:12:44 -0800724func (p ModuleSrcPath) WithSubDir(ctx ModuleContext, subdir string) ModuleSrcPath {
725 subdir = PathForModuleSrc(ctx, subdir).String()
726 var err error
727 rel, err := filepath.Rel(subdir, p.path)
728 if err != nil {
729 ctx.ModuleErrorf("source file %q is not under path %q", p.path, subdir)
730 return p
731 }
732 p.rel = rel
733 return p
734}
735
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700736// ModuleOutPath is a Path representing a module's output directory.
737type ModuleOutPath struct {
738 OutputPath
739}
740
741var _ Path = ModuleOutPath{}
742
Colin Cross702e0f82017-10-18 17:27:54 -0700743func pathForModule(ctx ModuleContext) OutputPath {
744 return PathForOutput(ctx, ".intermediates", ctx.ModuleDir(), ctx.ModuleName(), ctx.ModuleSubDir())
745}
746
Jayant Chowdhary3e231fd2017-02-08 13:45:53 -0800747// PathForVndkRefDump returns an OptionalPath representing the path of the reference
748// abi dump for the given module. This is not guaranteed to be valid.
749func PathForVndkRefAbiDump(ctx ModuleContext, version, fileName string, vndkOrNdk, isSourceDump bool) OptionalPath {
750 archName := ctx.Arch().ArchType.Name
751 var sourceOrBinaryDir string
752 var vndkOrNdkDir string
753 var ext string
754 if isSourceDump {
Jayant Chowdhary715cac32017-04-20 06:53:59 -0700755 ext = ".lsdump.gz"
Jayant Chowdhary3e231fd2017-02-08 13:45:53 -0800756 sourceOrBinaryDir = "source-based"
757 } else {
Jayant Chowdhary715cac32017-04-20 06:53:59 -0700758 ext = ".bdump.gz"
Jayant Chowdhary3e231fd2017-02-08 13:45:53 -0800759 sourceOrBinaryDir = "binary-based"
760 }
761 if vndkOrNdk {
762 vndkOrNdkDir = "vndk"
763 } else {
764 vndkOrNdkDir = "ndk"
765 }
766 refDumpFileStr := "prebuilts/abi-dumps/" + vndkOrNdkDir + "/" + version + "/" +
767 archName + "/" + sourceOrBinaryDir + "/" + fileName + ext
Jeff Gaston734e3802017-04-10 15:47:24 -0700768 return ExistentPathForSource(ctx, "", refDumpFileStr)
Jayant Chowdhary3e231fd2017-02-08 13:45:53 -0800769}
770
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700771// PathForModuleOut returns a Path representing the paths... under the module's
772// output directory.
Colin Cross635c3b02016-05-18 15:37:25 -0700773func PathForModuleOut(ctx ModuleContext, paths ...string) ModuleOutPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700774 p := validatePath(ctx, paths...)
Colin Cross702e0f82017-10-18 17:27:54 -0700775 return ModuleOutPath{
776 OutputPath: pathForModule(ctx).withRel(p),
777 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700778}
779
780// ModuleGenPath is a Path representing the 'gen' directory in a module's output
781// directory. Mainly used for generated sources.
782type ModuleGenPath struct {
783 ModuleOutPath
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700784}
785
786var _ Path = ModuleGenPath{}
787var _ genPathProvider = ModuleGenPath{}
788var _ objPathProvider = ModuleGenPath{}
789
790// PathForModuleGen returns a Path representing the paths... under the module's
791// `gen' directory.
Colin Cross635c3b02016-05-18 15:37:25 -0700792func PathForModuleGen(ctx ModuleContext, paths ...string) ModuleGenPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700793 p := validatePath(ctx, paths...)
794 return ModuleGenPath{
Colin Cross702e0f82017-10-18 17:27:54 -0700795 ModuleOutPath: ModuleOutPath{
796 OutputPath: pathForModule(ctx).withRel("gen").withRel(p),
797 },
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700798 }
799}
800
Dan Willemsen21ec4902016-11-02 20:43:13 -0700801func (p ModuleGenPath) genPathWithExt(ctx ModuleContext, subdir, ext string) ModuleGenPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700802 // TODO: make a different path for local vs remote generated files?
Dan Willemsen21ec4902016-11-02 20:43:13 -0700803 return PathForModuleGen(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700804}
805
Colin Cross635c3b02016-05-18 15:37:25 -0700806func (p ModuleGenPath) objPathWithExt(ctx ModuleContext, subdir, ext string) ModuleObjPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700807 return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
808}
809
810// ModuleObjPath is a Path representing the 'obj' directory in a module's output
811// directory. Used for compiled objects.
812type ModuleObjPath struct {
813 ModuleOutPath
814}
815
816var _ Path = ModuleObjPath{}
817
818// PathForModuleObj returns a Path representing the paths... under the module's
819// 'obj' directory.
Jeff Gaston734e3802017-04-10 15:47:24 -0700820func PathForModuleObj(ctx ModuleContext, pathComponents ...string) ModuleObjPath {
821 p := validatePath(ctx, pathComponents...)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700822 return ModuleObjPath{PathForModuleOut(ctx, "obj", p)}
823}
824
825// ModuleResPath is a a Path representing the 'res' directory in a module's
826// output directory.
827type ModuleResPath struct {
828 ModuleOutPath
829}
830
831var _ Path = ModuleResPath{}
832
833// PathForModuleRes returns a Path representing the paths... under the module's
834// 'res' directory.
Jeff Gaston734e3802017-04-10 15:47:24 -0700835func PathForModuleRes(ctx ModuleContext, pathComponents ...string) ModuleResPath {
836 p := validatePath(ctx, pathComponents...)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700837 return ModuleResPath{PathForModuleOut(ctx, "res", p)}
838}
839
840// PathForModuleInstall returns a Path representing the install path for the
841// module appended with paths...
Dan Willemsen00269f22017-07-06 16:59:48 -0700842func PathForModuleInstall(ctx ModuleInstallPathContext, pathComponents ...string) OutputPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700843 var outPaths []string
844 if ctx.Device() {
Vishwath Mohan87f3b242017-06-07 12:31:57 -0700845 var partition string
Dan Willemsen00269f22017-07-06 16:59:48 -0700846 if ctx.InstallInData() {
Vishwath Mohan87f3b242017-06-07 12:31:57 -0700847 partition = "data"
Jiyong Park2db76922017-11-08 16:03:48 +0900848 } else if ctx.SocSpecific() {
Dan Willemsen00269f22017-07-06 16:59:48 -0700849 partition = ctx.DeviceConfig().VendorPath()
Jiyong Park2db76922017-11-08 16:03:48 +0900850 } else if ctx.DeviceSpecific() {
851 partition = ctx.DeviceConfig().OdmPath()
852 } else if ctx.ProductSpecific() {
853 partition = ctx.DeviceConfig().OemPath()
Vishwath Mohan87f3b242017-06-07 12:31:57 -0700854 } else {
855 partition = "system"
Dan Willemsen782a2d12015-12-21 14:55:28 -0800856 }
Vishwath Mohan1dd88392017-03-29 22:00:18 -0700857
858 if ctx.InstallInSanitizerDir() {
859 partition = "data/asan/" + partition
Dan Willemsen782a2d12015-12-21 14:55:28 -0800860 }
Colin Cross6510f912017-11-29 00:27:14 -0800861 outPaths = []string{"target", "product", ctx.Config().DeviceName(), partition}
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700862 } else {
Dan Willemsen866b5632017-09-22 12:28:24 -0700863 switch ctx.Os() {
864 case Linux:
865 outPaths = []string{"host", "linux-x86"}
866 case LinuxBionic:
867 // TODO: should this be a separate top level, or shared with linux-x86?
868 outPaths = []string{"host", "linux_bionic-x86"}
869 default:
870 outPaths = []string{"host", ctx.Os().String() + "-x86"}
871 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700872 }
Dan Willemsen782a2d12015-12-21 14:55:28 -0800873 if ctx.Debug() {
874 outPaths = append([]string{"debug"}, outPaths...)
875 }
Jeff Gaston734e3802017-04-10 15:47:24 -0700876 outPaths = append(outPaths, pathComponents...)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700877 return PathForOutput(ctx, outPaths...)
878}
879
880// validateSafePath validates a path that we trust (may contain ninja variables).
Dan Willemsen80a7c2a2015-12-21 14:57:11 -0800881// Ensures that each path component does not attempt to leave its component.
Jeff Gaston734e3802017-04-10 15:47:24 -0700882func validateSafePath(ctx PathContext, pathComponents ...string) string {
883 for _, path := range pathComponents {
Dan Willemsen80a7c2a2015-12-21 14:57:11 -0800884 path := filepath.Clean(path)
885 if path == ".." || strings.HasPrefix(path, "../") || strings.HasPrefix(path, "/") {
886 reportPathError(ctx, "Path is outside directory: %s", path)
887 return ""
888 }
889 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700890 // TODO: filepath.Join isn't necessarily correct with embedded ninja
891 // variables. '..' may remove the entire ninja variable, even if it
892 // will be expanded to multiple nested directories.
Jeff Gaston734e3802017-04-10 15:47:24 -0700893 return filepath.Join(pathComponents...)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700894}
895
Dan Willemsen80a7c2a2015-12-21 14:57:11 -0800896// validatePath validates that a path does not include ninja variables, and that
897// each path component does not attempt to leave its component. Returns a joined
898// version of each path component.
Jeff Gaston734e3802017-04-10 15:47:24 -0700899func validatePath(ctx PathContext, pathComponents ...string) string {
900 for _, path := range pathComponents {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700901 if strings.Contains(path, "$") {
902 reportPathError(ctx, "Path contains invalid character($): %s", path)
903 return ""
904 }
905 }
Jeff Gaston734e3802017-04-10 15:47:24 -0700906 return validateSafePath(ctx, pathComponents...)
Colin Cross6e18ca42015-07-14 18:55:36 -0700907}
Colin Cross5b529592017-05-09 13:34:34 -0700908
Colin Cross0875c522017-11-28 17:34:01 -0800909func PathForPhony(ctx PathContext, phony string) WritablePath {
910 if strings.ContainsAny(phony, "$/") {
911 reportPathError(ctx, "Phony target contains invalid character ($ or /): %s", phony)
912 }
Colin Cross74e3fe42017-12-11 15:51:44 -0800913 return PhonyPath{basePath{phony, ctx.Config(), ""}}
Colin Cross0875c522017-11-28 17:34:01 -0800914}
915
Colin Cross74e3fe42017-12-11 15:51:44 -0800916type PhonyPath struct {
917 basePath
918}
919
920func (p PhonyPath) writablePath() {}
921
922var _ Path = PhonyPath{}
923var _ WritablePath = PhonyPath{}
924
Colin Cross5b529592017-05-09 13:34:34 -0700925type testPath struct {
926 basePath
927}
928
929func (p testPath) String() string {
930 return p.path
931}
932
933func PathForTesting(paths ...string) Path {
934 p := validateSafePath(nil, paths...)
935 return testPath{basePath{path: p, rel: p}}
936}
937
938func PathsForTesting(strs []string) Paths {
939 p := make(Paths, len(strs))
940 for i, s := range strs {
941 p[i] = PathForTesting(s)
942 }
943
944 return p
945}