blob: e0cbd21b2e510163b85fa0f884671b36ac9a3391 [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
95 // directory.
96 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
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700459// SourcePath is a Path representing a file path rooted from SrcDir
460type SourcePath struct {
461 basePath
462}
463
464var _ Path = SourcePath{}
465
466// safePathForSource is for paths that we expect are safe -- only for use by go
467// code that is embedding ninja variables in paths
468func safePathForSource(ctx PathContext, path string) SourcePath {
469 p := validateSafePath(ctx, path)
Colin Crossaabf6792017-11-29 00:27:14 -0800470 ret := SourcePath{basePath{p, ctx.Config(), ""}}
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700471
472 abs, err := filepath.Abs(ret.String())
Colin Cross6e18ca42015-07-14 18:55:36 -0700473 if err != nil {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700474 reportPathError(ctx, "%s", err.Error())
475 return ret
476 }
Colin Crossaabf6792017-11-29 00:27:14 -0800477 buildroot, err := filepath.Abs(ctx.Config().buildDir)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700478 if err != nil {
479 reportPathError(ctx, "%s", err.Error())
480 return ret
481 }
482 if strings.HasPrefix(abs, buildroot) {
483 reportPathError(ctx, "source path %s is in output", abs)
484 return ret
Colin Cross6e18ca42015-07-14 18:55:36 -0700485 }
486
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700487 return ret
488}
489
Jeff Gaston734e3802017-04-10 15:47:24 -0700490// PathForSource joins the provided path components and validates that the result
491// neither escapes the source dir nor is in the out dir.
492// On error, it will return a usable, but invalid SourcePath, and report a ModuleError.
493func PathForSource(ctx PathContext, pathComponents ...string) SourcePath {
494 p := validatePath(ctx, pathComponents...)
Colin Crossaabf6792017-11-29 00:27:14 -0800495 ret := SourcePath{basePath{p, ctx.Config(), ""}}
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700496
497 abs, err := filepath.Abs(ret.String())
498 if err != nil {
499 reportPathError(ctx, "%s", err.Error())
500 return ret
501 }
Colin Crossaabf6792017-11-29 00:27:14 -0800502 buildroot, err := filepath.Abs(ctx.Config().buildDir)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700503 if err != nil {
504 reportPathError(ctx, "%s", err.Error())
505 return ret
506 }
507 if strings.HasPrefix(abs, buildroot) {
508 reportPathError(ctx, "source path %s is in output", abs)
509 return ret
510 }
511
Colin Cross294941b2017-02-01 14:10:36 -0800512 if exists, _, err := ctx.Fs().Exists(ret.String()); err != nil {
513 reportPathError(ctx, "%s: %s", ret, err.Error())
514 } else if !exists {
515 reportPathError(ctx, "source path %s does not exist", ret)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700516 }
517 return ret
518}
519
Jeff Gaston734e3802017-04-10 15:47:24 -0700520// ExistentPathForSource returns an OptionalPath with the SourcePath if the
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700521// path exists, or an empty OptionalPath if it doesn't exist. Dependencies are added
522// so that the ninja file will be regenerated if the state of the path changes.
Jeff Gaston734e3802017-04-10 15:47:24 -0700523func ExistentPathForSource(ctx PathContext, intermediates string, pathComponents ...string) OptionalPath {
524 if len(pathComponents) == 0 {
Dan Willemsen7b310ee2015-12-18 15:11:17 -0800525 // For when someone forgets the 'intermediates' argument
526 panic("Missing path(s)")
527 }
528
Jeff Gaston734e3802017-04-10 15:47:24 -0700529 p := validatePath(ctx, pathComponents...)
Colin Crossaabf6792017-11-29 00:27:14 -0800530 path := SourcePath{basePath{p, ctx.Config(), ""}}
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700531
532 abs, err := filepath.Abs(path.String())
533 if err != nil {
534 reportPathError(ctx, "%s", err.Error())
535 return OptionalPath{}
536 }
Colin Crossaabf6792017-11-29 00:27:14 -0800537 buildroot, err := filepath.Abs(ctx.Config().buildDir)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700538 if err != nil {
539 reportPathError(ctx, "%s", err.Error())
540 return OptionalPath{}
541 }
542 if strings.HasPrefix(abs, buildroot) {
543 reportPathError(ctx, "source path %s is in output", abs)
544 return OptionalPath{}
545 }
546
Colin Cross7f19f372016-11-01 11:10:25 -0700547 if pathtools.IsGlob(path.String()) {
Dan Willemsen7b310ee2015-12-18 15:11:17 -0800548 reportPathError(ctx, "path may not contain a glob: %s", path.String())
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700549 return OptionalPath{}
550 }
551
Colin Cross7f19f372016-11-01 11:10:25 -0700552 if gctx, ok := ctx.(PathGlobContext); ok {
Dan Willemsen7b310ee2015-12-18 15:11:17 -0800553 // Use glob to produce proper dependencies, even though we only want
554 // a single file.
Colin Cross7f19f372016-11-01 11:10:25 -0700555 files, err := gctx.GlobWithDeps(path.String(), nil)
Dan Willemsen7b310ee2015-12-18 15:11:17 -0800556 if err != nil {
557 reportPathError(ctx, "glob: %s", err.Error())
558 return OptionalPath{}
559 }
560
561 if len(files) == 0 {
562 return OptionalPath{}
563 }
564 } else {
565 // We cannot add build statements in this context, so we fall back to
566 // AddNinjaFileDeps
Colin Cross294941b2017-02-01 14:10:36 -0800567 files, dirs, err := pathtools.Glob(path.String(), nil)
Dan Willemsen7b310ee2015-12-18 15:11:17 -0800568 if err != nil {
569 reportPathError(ctx, "glob: %s", err.Error())
570 return OptionalPath{}
571 }
572
573 ctx.AddNinjaFileDeps(dirs...)
574
575 if len(files) == 0 {
576 return OptionalPath{}
577 }
578
579 ctx.AddNinjaFileDeps(path.String())
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700580 }
581 return OptionalPathForPath(path)
582}
583
584func (p SourcePath) String() string {
585 return filepath.Join(p.config.srcDir, p.path)
586}
587
588// Join creates a new SourcePath with paths... joined with the current path. The
589// provided paths... may not use '..' to escape from the current path.
590func (p SourcePath) Join(ctx PathContext, paths ...string) SourcePath {
591 path := validatePath(ctx, paths...)
592 return PathForSource(ctx, p.path, path)
593}
594
595// OverlayPath returns the overlay for `path' if it exists. This assumes that the
596// SourcePath is the path to a resource overlay directory.
Colin Cross635c3b02016-05-18 15:37:25 -0700597func (p SourcePath) OverlayPath(ctx ModuleContext, path Path) OptionalPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700598 var relDir string
599 if moduleSrcPath, ok := path.(ModuleSrcPath); ok {
Colin Cross7fc17db2017-02-01 14:07:55 -0800600 relDir = moduleSrcPath.path
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700601 } else if srcPath, ok := path.(SourcePath); ok {
602 relDir = srcPath.path
603 } else {
604 reportPathError(ctx, "Cannot find relative path for %s(%s)", reflect.TypeOf(path).Name(), path)
605 return OptionalPath{}
606 }
607 dir := filepath.Join(p.config.srcDir, p.path, relDir)
608 // Use Glob so that we are run again if the directory is added.
Colin Cross7f19f372016-11-01 11:10:25 -0700609 if pathtools.IsGlob(dir) {
Dan Willemsen7b310ee2015-12-18 15:11:17 -0800610 reportPathError(ctx, "Path may not contain a glob: %s", dir)
611 }
Colin Cross7f19f372016-11-01 11:10:25 -0700612 paths, err := ctx.GlobWithDeps(dir, []string{})
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700613 if err != nil {
614 reportPathError(ctx, "glob: %s", err.Error())
615 return OptionalPath{}
616 }
617 if len(paths) == 0 {
618 return OptionalPath{}
619 }
620 relPath, err := filepath.Rel(p.config.srcDir, paths[0])
621 if err != nil {
622 reportPathError(ctx, "%s", err.Error())
623 return OptionalPath{}
624 }
625 return OptionalPathForPath(PathForSource(ctx, relPath))
626}
627
628// OutputPath is a Path representing a file path rooted from the build directory
629type OutputPath struct {
630 basePath
631}
632
Colin Cross702e0f82017-10-18 17:27:54 -0700633func (p OutputPath) withRel(rel string) OutputPath {
634 p.basePath.path = filepath.Join(p.basePath.path, rel)
635 p.basePath.rel = rel
636 return p
637}
638
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700639var _ Path = OutputPath{}
640
Jeff Gaston734e3802017-04-10 15:47:24 -0700641// PathForOutput joins the provided paths and returns an OutputPath that is
642// validated to not escape the build dir.
643// On error, it will return a usable, but invalid OutputPath, and report a ModuleError.
644func PathForOutput(ctx PathContext, pathComponents ...string) OutputPath {
645 path := validatePath(ctx, pathComponents...)
Colin Crossaabf6792017-11-29 00:27:14 -0800646 return OutputPath{basePath{path, ctx.Config(), ""}}
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700647}
648
649func (p OutputPath) writablePath() {}
650
651func (p OutputPath) String() string {
652 return filepath.Join(p.config.buildDir, p.path)
653}
654
Colin Crossa2344662016-03-24 13:14:12 -0700655func (p OutputPath) RelPathString() string {
656 return p.path
657}
658
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700659// Join creates a new OutputPath with paths... joined with the current path. The
660// provided paths... may not use '..' to escape from the current path.
661func (p OutputPath) Join(ctx PathContext, paths ...string) OutputPath {
662 path := validatePath(ctx, paths...)
663 return PathForOutput(ctx, p.path, path)
664}
665
666// PathForIntermediates returns an OutputPath representing the top-level
667// intermediates directory.
668func PathForIntermediates(ctx PathContext, paths ...string) OutputPath {
669 path := validatePath(ctx, paths...)
670 return PathForOutput(ctx, ".intermediates", path)
671}
672
673// ModuleSrcPath is a Path representing a file rooted from a module's local source dir
674type ModuleSrcPath struct {
Colin Cross7fc17db2017-02-01 14:07:55 -0800675 SourcePath
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700676}
677
678var _ Path = ModuleSrcPath{}
679var _ genPathProvider = ModuleSrcPath{}
680var _ objPathProvider = ModuleSrcPath{}
681var _ resPathProvider = ModuleSrcPath{}
682
683// PathForModuleSrc returns a ModuleSrcPath representing the paths... under the
684// module's local source directory.
Colin Cross635c3b02016-05-18 15:37:25 -0700685func PathForModuleSrc(ctx ModuleContext, paths ...string) ModuleSrcPath {
Colin Crossfaeb7aa2017-02-01 14:12:44 -0800686 p := validatePath(ctx, paths...)
687 path := ModuleSrcPath{PathForSource(ctx, ctx.ModuleDir(), p)}
688 path.basePath.rel = p
689 return path
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700690}
691
692// OptionalPathForModuleSrc returns an OptionalPath. The OptionalPath contains a
693// valid path if p is non-nil.
Colin Cross635c3b02016-05-18 15:37:25 -0700694func OptionalPathForModuleSrc(ctx ModuleContext, p *string) OptionalPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700695 if p == nil {
696 return OptionalPath{}
697 }
698 return OptionalPathForPath(PathForModuleSrc(ctx, *p))
699}
700
Dan Willemsen21ec4902016-11-02 20:43:13 -0700701func (p ModuleSrcPath) genPathWithExt(ctx ModuleContext, subdir, ext string) ModuleGenPath {
Colin Cross7fc17db2017-02-01 14:07:55 -0800702 return PathForModuleGen(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700703}
704
Colin Cross635c3b02016-05-18 15:37:25 -0700705func (p ModuleSrcPath) objPathWithExt(ctx ModuleContext, subdir, ext string) ModuleObjPath {
Colin Cross7fc17db2017-02-01 14:07:55 -0800706 return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700707}
708
Colin Cross635c3b02016-05-18 15:37:25 -0700709func (p ModuleSrcPath) resPathWithName(ctx ModuleContext, name string) ModuleResPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700710 // TODO: Use full directory if the new ctx is not the current ctx?
711 return PathForModuleRes(ctx, p.path, name)
712}
713
Colin Crossfaeb7aa2017-02-01 14:12:44 -0800714func (p ModuleSrcPath) WithSubDir(ctx ModuleContext, subdir string) ModuleSrcPath {
715 subdir = PathForModuleSrc(ctx, subdir).String()
716 var err error
717 rel, err := filepath.Rel(subdir, p.path)
718 if err != nil {
719 ctx.ModuleErrorf("source file %q is not under path %q", p.path, subdir)
720 return p
721 }
722 p.rel = rel
723 return p
724}
725
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700726// ModuleOutPath is a Path representing a module's output directory.
727type ModuleOutPath struct {
728 OutputPath
729}
730
731var _ Path = ModuleOutPath{}
732
Colin Cross702e0f82017-10-18 17:27:54 -0700733func pathForModule(ctx ModuleContext) OutputPath {
734 return PathForOutput(ctx, ".intermediates", ctx.ModuleDir(), ctx.ModuleName(), ctx.ModuleSubDir())
735}
736
Jayant Chowdhary3e231fd2017-02-08 13:45:53 -0800737// PathForVndkRefDump returns an OptionalPath representing the path of the reference
738// abi dump for the given module. This is not guaranteed to be valid.
739func PathForVndkRefAbiDump(ctx ModuleContext, version, fileName string, vndkOrNdk, isSourceDump bool) OptionalPath {
740 archName := ctx.Arch().ArchType.Name
741 var sourceOrBinaryDir string
742 var vndkOrNdkDir string
743 var ext string
744 if isSourceDump {
Jayant Chowdhary715cac32017-04-20 06:53:59 -0700745 ext = ".lsdump.gz"
Jayant Chowdhary3e231fd2017-02-08 13:45:53 -0800746 sourceOrBinaryDir = "source-based"
747 } else {
Jayant Chowdhary715cac32017-04-20 06:53:59 -0700748 ext = ".bdump.gz"
Jayant Chowdhary3e231fd2017-02-08 13:45:53 -0800749 sourceOrBinaryDir = "binary-based"
750 }
751 if vndkOrNdk {
752 vndkOrNdkDir = "vndk"
753 } else {
754 vndkOrNdkDir = "ndk"
755 }
756 refDumpFileStr := "prebuilts/abi-dumps/" + vndkOrNdkDir + "/" + version + "/" +
757 archName + "/" + sourceOrBinaryDir + "/" + fileName + ext
Jeff Gaston734e3802017-04-10 15:47:24 -0700758 return ExistentPathForSource(ctx, "", refDumpFileStr)
Jayant Chowdhary3e231fd2017-02-08 13:45:53 -0800759}
760
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700761// PathForModuleOut returns a Path representing the paths... under the module's
762// output directory.
Colin Cross635c3b02016-05-18 15:37:25 -0700763func PathForModuleOut(ctx ModuleContext, paths ...string) ModuleOutPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700764 p := validatePath(ctx, paths...)
Colin Cross702e0f82017-10-18 17:27:54 -0700765 return ModuleOutPath{
766 OutputPath: pathForModule(ctx).withRel(p),
767 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700768}
769
770// ModuleGenPath is a Path representing the 'gen' directory in a module's output
771// directory. Mainly used for generated sources.
772type ModuleGenPath struct {
773 ModuleOutPath
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700774}
775
776var _ Path = ModuleGenPath{}
777var _ genPathProvider = ModuleGenPath{}
778var _ objPathProvider = ModuleGenPath{}
779
780// PathForModuleGen returns a Path representing the paths... under the module's
781// `gen' directory.
Colin Cross635c3b02016-05-18 15:37:25 -0700782func PathForModuleGen(ctx ModuleContext, paths ...string) ModuleGenPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700783 p := validatePath(ctx, paths...)
784 return ModuleGenPath{
Colin Cross702e0f82017-10-18 17:27:54 -0700785 ModuleOutPath: ModuleOutPath{
786 OutputPath: pathForModule(ctx).withRel("gen").withRel(p),
787 },
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700788 }
789}
790
Dan Willemsen21ec4902016-11-02 20:43:13 -0700791func (p ModuleGenPath) genPathWithExt(ctx ModuleContext, subdir, ext string) ModuleGenPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700792 // TODO: make a different path for local vs remote generated files?
Dan Willemsen21ec4902016-11-02 20:43:13 -0700793 return PathForModuleGen(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700794}
795
Colin Cross635c3b02016-05-18 15:37:25 -0700796func (p ModuleGenPath) objPathWithExt(ctx ModuleContext, subdir, ext string) ModuleObjPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700797 return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
798}
799
800// ModuleObjPath is a Path representing the 'obj' directory in a module's output
801// directory. Used for compiled objects.
802type ModuleObjPath struct {
803 ModuleOutPath
804}
805
806var _ Path = ModuleObjPath{}
807
808// PathForModuleObj returns a Path representing the paths... under the module's
809// 'obj' directory.
Jeff Gaston734e3802017-04-10 15:47:24 -0700810func PathForModuleObj(ctx ModuleContext, pathComponents ...string) ModuleObjPath {
811 p := validatePath(ctx, pathComponents...)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700812 return ModuleObjPath{PathForModuleOut(ctx, "obj", p)}
813}
814
815// ModuleResPath is a a Path representing the 'res' directory in a module's
816// output directory.
817type ModuleResPath struct {
818 ModuleOutPath
819}
820
821var _ Path = ModuleResPath{}
822
823// PathForModuleRes returns a Path representing the paths... under the module's
824// 'res' directory.
Jeff Gaston734e3802017-04-10 15:47:24 -0700825func PathForModuleRes(ctx ModuleContext, pathComponents ...string) ModuleResPath {
826 p := validatePath(ctx, pathComponents...)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700827 return ModuleResPath{PathForModuleOut(ctx, "res", p)}
828}
829
830// PathForModuleInstall returns a Path representing the install path for the
831// module appended with paths...
Dan Willemsen00269f22017-07-06 16:59:48 -0700832func PathForModuleInstall(ctx ModuleInstallPathContext, pathComponents ...string) OutputPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700833 var outPaths []string
834 if ctx.Device() {
Vishwath Mohan87f3b242017-06-07 12:31:57 -0700835 var partition string
Dan Willemsen00269f22017-07-06 16:59:48 -0700836 if ctx.InstallInData() {
Vishwath Mohan87f3b242017-06-07 12:31:57 -0700837 partition = "data"
Jeff Gastonaf3cc2d2017-09-27 17:01:44 -0700838 } else if ctx.InstallOnVendorPartition() {
Dan Willemsen00269f22017-07-06 16:59:48 -0700839 partition = ctx.DeviceConfig().VendorPath()
Vishwath Mohan87f3b242017-06-07 12:31:57 -0700840 } else {
841 partition = "system"
Dan Willemsen782a2d12015-12-21 14:55:28 -0800842 }
Vishwath Mohan1dd88392017-03-29 22:00:18 -0700843
844 if ctx.InstallInSanitizerDir() {
845 partition = "data/asan/" + partition
Dan Willemsen782a2d12015-12-21 14:55:28 -0800846 }
Colin Cross6510f912017-11-29 00:27:14 -0800847 outPaths = []string{"target", "product", ctx.Config().DeviceName(), partition}
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700848 } else {
Dan Willemsen866b5632017-09-22 12:28:24 -0700849 switch ctx.Os() {
850 case Linux:
851 outPaths = []string{"host", "linux-x86"}
852 case LinuxBionic:
853 // TODO: should this be a separate top level, or shared with linux-x86?
854 outPaths = []string{"host", "linux_bionic-x86"}
855 default:
856 outPaths = []string{"host", ctx.Os().String() + "-x86"}
857 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700858 }
Dan Willemsen782a2d12015-12-21 14:55:28 -0800859 if ctx.Debug() {
860 outPaths = append([]string{"debug"}, outPaths...)
861 }
Jeff Gaston734e3802017-04-10 15:47:24 -0700862 outPaths = append(outPaths, pathComponents...)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700863 return PathForOutput(ctx, outPaths...)
864}
865
866// validateSafePath validates a path that we trust (may contain ninja variables).
Dan Willemsen80a7c2a2015-12-21 14:57:11 -0800867// Ensures that each path component does not attempt to leave its component.
Jeff Gaston734e3802017-04-10 15:47:24 -0700868func validateSafePath(ctx PathContext, pathComponents ...string) string {
869 for _, path := range pathComponents {
Dan Willemsen80a7c2a2015-12-21 14:57:11 -0800870 path := filepath.Clean(path)
871 if path == ".." || strings.HasPrefix(path, "../") || strings.HasPrefix(path, "/") {
872 reportPathError(ctx, "Path is outside directory: %s", path)
873 return ""
874 }
875 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700876 // TODO: filepath.Join isn't necessarily correct with embedded ninja
877 // variables. '..' may remove the entire ninja variable, even if it
878 // will be expanded to multiple nested directories.
Jeff Gaston734e3802017-04-10 15:47:24 -0700879 return filepath.Join(pathComponents...)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700880}
881
Dan Willemsen80a7c2a2015-12-21 14:57:11 -0800882// validatePath validates that a path does not include ninja variables, and that
883// each path component does not attempt to leave its component. Returns a joined
884// version of each path component.
Jeff Gaston734e3802017-04-10 15:47:24 -0700885func validatePath(ctx PathContext, pathComponents ...string) string {
886 for _, path := range pathComponents {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700887 if strings.Contains(path, "$") {
888 reportPathError(ctx, "Path contains invalid character($): %s", path)
889 return ""
890 }
891 }
Jeff Gaston734e3802017-04-10 15:47:24 -0700892 return validateSafePath(ctx, pathComponents...)
Colin Cross6e18ca42015-07-14 18:55:36 -0700893}
Colin Cross5b529592017-05-09 13:34:34 -0700894
Colin Cross0875c522017-11-28 17:34:01 -0800895func PathForPhony(ctx PathContext, phony string) WritablePath {
896 if strings.ContainsAny(phony, "$/") {
897 reportPathError(ctx, "Phony target contains invalid character ($ or /): %s", phony)
898 }
Colin Crossaabf6792017-11-29 00:27:14 -0800899 return OutputPath{basePath{phony, ctx.Config(), ""}}
Colin Cross0875c522017-11-28 17:34:01 -0800900}
901
Colin Cross5b529592017-05-09 13:34:34 -0700902type testPath struct {
903 basePath
904}
905
906func (p testPath) String() string {
907 return p.path
908}
909
910func PathForTesting(paths ...string) Path {
911 p := validateSafePath(nil, paths...)
912 return testPath{basePath{path: p, rel: p}}
913}
914
915func PathsForTesting(strs []string) Paths {
916 p := make(Paths, len(strs))
917 for i, s := range strs {
918 p[i] = PathForTesting(s)
919 }
920
921 return p
922}