blob: 10a9dc6359f5c55e6b9a3a644b4659f0603b9abc [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))
Dan Willemsene23dfb72016-03-11 15:02:46 -0800194 for _, path := range paths {
Colin Cross32f38982018-02-22 11:47:25 -0800195 p := ExistentPathForSource(ctx, path)
Dan Willemsene23dfb72016-03-11 15:02:46 -0800196 if p.Valid() {
197 ret = append(ret, p.Path())
198 } else {
199 modCtx.AddMissingDependencies([]string{path})
200 }
201 }
202 return ret
203 }
204 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700205 ret := make(Paths, len(paths))
206 for i, path := range paths {
207 ret[i] = PathForSource(ctx, path)
208 }
209 return ret
210}
211
Jeff Gaston734e3802017-04-10 15:47:24 -0700212// ExistentPathsForSources returns a list of Paths rooted from SrcDir that are
Dan Willemsen7b310ee2015-12-18 15:11:17 -0800213// found in the tree. If any are not found, they are omitted from the list,
214// and dependencies are added so that we're re-run when they are added.
Colin Cross32f38982018-02-22 11:47:25 -0800215func ExistentPathsForSources(ctx PathContext, paths []string) Paths {
Dan Willemsen7b310ee2015-12-18 15:11:17 -0800216 ret := make(Paths, 0, len(paths))
217 for _, path := range paths {
Colin Cross32f38982018-02-22 11:47:25 -0800218 p := ExistentPathForSource(ctx, path)
Dan Willemsen7b310ee2015-12-18 15:11:17 -0800219 if p.Valid() {
220 ret = append(ret, p.Path())
221 }
222 }
223 return ret
224}
225
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700226// PathsForModuleSrc returns Paths rooted from the module's local source
227// directory
Colin Cross635c3b02016-05-18 15:37:25 -0700228func PathsForModuleSrc(ctx ModuleContext, paths []string) Paths {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700229 ret := make(Paths, len(paths))
230 for i, path := range paths {
231 ret[i] = PathForModuleSrc(ctx, path)
232 }
233 return ret
234}
235
236// pathsForModuleSrcFromFullPath returns Paths rooted from the module's local
237// source directory, but strip the local source directory from the beginning of
238// each string.
Colin Cross635c3b02016-05-18 15:37:25 -0700239func pathsForModuleSrcFromFullPath(ctx ModuleContext, paths []string) Paths {
Colin Cross6510f912017-11-29 00:27:14 -0800240 prefix := filepath.Join(ctx.Config().srcDir, ctx.ModuleDir()) + "/"
Colin Cross0f37af02017-09-27 17:42:05 -0700241 if prefix == "./" {
242 prefix = ""
243 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700244 ret := make(Paths, 0, len(paths))
245 for _, p := range paths {
246 path := filepath.Clean(p)
247 if !strings.HasPrefix(path, prefix) {
248 reportPathError(ctx, "Path '%s' is not in module source directory '%s'", p, prefix)
249 continue
250 }
251 ret = append(ret, PathForModuleSrc(ctx, path[len(prefix):]))
252 }
253 return ret
254}
255
256// PathsWithOptionalDefaultForModuleSrc returns Paths rooted from the module's
257// local source directory. If none are provided, use the default if it exists.
Colin Cross635c3b02016-05-18 15:37:25 -0700258func PathsWithOptionalDefaultForModuleSrc(ctx ModuleContext, input []string, def string) Paths {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700259 if len(input) > 0 {
260 return PathsForModuleSrc(ctx, input)
261 }
262 // Use Glob so that if the default doesn't exist, a dependency is added so that when it
263 // is created, we're run again.
Colin Cross6510f912017-11-29 00:27:14 -0800264 path := filepath.Join(ctx.Config().srcDir, ctx.ModuleDir(), def)
Colin Cross7f19f372016-11-01 11:10:25 -0700265 return ctx.Glob(path, []string{})
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700266}
267
268// Strings returns the Paths in string form
269func (p Paths) Strings() []string {
270 if p == nil {
271 return nil
272 }
273 ret := make([]string, len(p))
274 for i, path := range p {
275 ret[i] = path.String()
276 }
277 return ret
278}
279
Colin Crossb6715442017-10-24 11:13:31 -0700280// FirstUniquePaths returns all unique elements of a Paths, keeping the first copy of each. It
281// modifies the Paths slice contents in place, and returns a subslice of the original slice.
Dan Willemsenfe92c962017-08-29 12:28:37 -0700282func FirstUniquePaths(list Paths) Paths {
283 k := 0
284outer:
285 for i := 0; i < len(list); i++ {
286 for j := 0; j < k; j++ {
287 if list[i] == list[j] {
288 continue outer
289 }
290 }
291 list[k] = list[i]
292 k++
293 }
294 return list[:k]
295}
296
Colin Crossb6715442017-10-24 11:13:31 -0700297// LastUniquePaths returns all unique elements of a Paths, keeping the last copy of each. It
298// modifies the Paths slice contents in place, and returns a subslice of the original slice.
299func LastUniquePaths(list Paths) Paths {
300 totalSkip := 0
301 for i := len(list) - 1; i >= totalSkip; i-- {
302 skip := 0
303 for j := i - 1; j >= totalSkip; j-- {
304 if list[i] == list[j] {
305 skip++
306 } else {
307 list[j+skip] = list[j]
308 }
309 }
310 totalSkip += skip
311 }
312 return list[totalSkip:]
313}
314
Jeff Gaston294356f2017-09-27 17:05:30 -0700315func indexPathList(s Path, list []Path) int {
316 for i, l := range list {
317 if l == s {
318 return i
319 }
320 }
321
322 return -1
323}
324
325func inPathList(p Path, list []Path) bool {
326 return indexPathList(p, list) != -1
327}
328
329func FilterPathList(list []Path, filter []Path) (remainder []Path, filtered []Path) {
330 for _, l := range list {
331 if inPathList(l, filter) {
332 filtered = append(filtered, l)
333 } else {
334 remainder = append(remainder, l)
335 }
336 }
337
338 return
339}
340
Colin Cross93e85952017-08-15 13:34:18 -0700341// HasExt returns true of any of the paths have extension ext, otherwise false
342func (p Paths) HasExt(ext string) bool {
343 for _, path := range p {
344 if path.Ext() == ext {
345 return true
346 }
347 }
348
349 return false
350}
351
352// FilterByExt returns the subset of the paths that have extension ext
353func (p Paths) FilterByExt(ext string) Paths {
354 ret := make(Paths, 0, len(p))
355 for _, path := range p {
356 if path.Ext() == ext {
357 ret = append(ret, path)
358 }
359 }
360 return ret
361}
362
363// FilterOutByExt returns the subset of the paths that do not have extension ext
364func (p Paths) FilterOutByExt(ext string) Paths {
365 ret := make(Paths, 0, len(p))
366 for _, path := range p {
367 if path.Ext() != ext {
368 ret = append(ret, path)
369 }
370 }
371 return ret
372}
373
Colin Cross5e6cfbe2017-11-03 15:20:35 -0700374// DirectorySortedPaths is a slice of paths that are sorted such that all files in a directory
375// (including subdirectories) are in a contiguous subslice of the list, and can be found in
376// O(log(N)) time using a binary search on the directory prefix.
377type DirectorySortedPaths Paths
378
379func PathsToDirectorySortedPaths(paths Paths) DirectorySortedPaths {
380 ret := append(DirectorySortedPaths(nil), paths...)
381 sort.Slice(ret, func(i, j int) bool {
382 return ret[i].String() < ret[j].String()
383 })
384 return ret
385}
386
387// PathsInDirectory returns a subslice of the DirectorySortedPaths as a Paths that contains all entries
388// that are in the specified directory and its subdirectories.
389func (p DirectorySortedPaths) PathsInDirectory(dir string) Paths {
390 prefix := filepath.Clean(dir) + "/"
391 start := sort.Search(len(p), func(i int) bool {
392 return prefix < p[i].String()
393 })
394
395 ret := p[start:]
396
397 end := sort.Search(len(ret), func(i int) bool {
398 return !strings.HasPrefix(ret[i].String(), prefix)
399 })
400
401 ret = ret[:end]
402
403 return Paths(ret)
404}
405
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700406// WritablePaths is a slice of WritablePaths, used for multiple outputs.
407type WritablePaths []WritablePath
408
409// Strings returns the string forms of the writable paths.
410func (p WritablePaths) Strings() []string {
411 if p == nil {
412 return nil
413 }
414 ret := make([]string, len(p))
415 for i, path := range p {
416 ret[i] = path.String()
417 }
418 return ret
419}
420
Colin Cross3bc7ffa2017-11-22 16:19:37 -0800421// Paths returns the WritablePaths as a Paths
422func (p WritablePaths) Paths() Paths {
423 if p == nil {
424 return nil
425 }
426 ret := make(Paths, len(p))
427 for i, path := range p {
428 ret[i] = path
429 }
430 return ret
431}
432
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700433type basePath struct {
434 path string
435 config Config
Colin Crossfaeb7aa2017-02-01 14:12:44 -0800436 rel string
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700437}
438
439func (p basePath) Ext() string {
440 return filepath.Ext(p.path)
441}
442
Colin Cross4f6fc9c2016-10-26 10:05:25 -0700443func (p basePath) Base() string {
444 return filepath.Base(p.path)
445}
446
Colin Crossfaeb7aa2017-02-01 14:12:44 -0800447func (p basePath) Rel() string {
448 if p.rel != "" {
449 return p.rel
450 }
451 return p.path
452}
453
Colin Cross0875c522017-11-28 17:34:01 -0800454func (p basePath) String() string {
455 return p.path
456}
457
Colin Cross0db55682017-12-05 15:36:55 -0800458func (p basePath) withRel(rel string) basePath {
459 p.path = filepath.Join(p.path, rel)
460 p.rel = rel
461 return p
462}
463
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700464// SourcePath is a Path representing a file path rooted from SrcDir
465type SourcePath struct {
466 basePath
467}
468
469var _ Path = SourcePath{}
470
Colin Cross0db55682017-12-05 15:36:55 -0800471func (p SourcePath) withRel(rel string) SourcePath {
472 p.basePath = p.basePath.withRel(rel)
473 return p
474}
475
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700476// safePathForSource is for paths that we expect are safe -- only for use by go
477// code that is embedding ninja variables in paths
478func safePathForSource(ctx PathContext, path string) SourcePath {
479 p := validateSafePath(ctx, path)
Colin Crossaabf6792017-11-29 00:27:14 -0800480 ret := SourcePath{basePath{p, ctx.Config(), ""}}
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700481
482 abs, err := filepath.Abs(ret.String())
Colin Cross6e18ca42015-07-14 18:55:36 -0700483 if err != nil {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700484 reportPathError(ctx, "%s", err.Error())
485 return ret
486 }
Colin Crossaabf6792017-11-29 00:27:14 -0800487 buildroot, err := filepath.Abs(ctx.Config().buildDir)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700488 if err != nil {
489 reportPathError(ctx, "%s", err.Error())
490 return ret
491 }
492 if strings.HasPrefix(abs, buildroot) {
493 reportPathError(ctx, "source path %s is in output", abs)
494 return ret
Colin Cross6e18ca42015-07-14 18:55:36 -0700495 }
496
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700497 return ret
498}
499
Jeff Gaston734e3802017-04-10 15:47:24 -0700500// PathForSource joins the provided path components and validates that the result
501// neither escapes the source dir nor is in the out dir.
502// On error, it will return a usable, but invalid SourcePath, and report a ModuleError.
503func PathForSource(ctx PathContext, pathComponents ...string) SourcePath {
504 p := validatePath(ctx, pathComponents...)
Colin Crossaabf6792017-11-29 00:27:14 -0800505 ret := SourcePath{basePath{p, ctx.Config(), ""}}
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700506
507 abs, err := filepath.Abs(ret.String())
508 if err != nil {
509 reportPathError(ctx, "%s", err.Error())
510 return ret
511 }
Colin Crossaabf6792017-11-29 00:27:14 -0800512 buildroot, err := filepath.Abs(ctx.Config().buildDir)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700513 if err != nil {
514 reportPathError(ctx, "%s", err.Error())
515 return ret
516 }
517 if strings.HasPrefix(abs, buildroot) {
518 reportPathError(ctx, "source path %s is in output", abs)
519 return ret
520 }
521
Colin Cross294941b2017-02-01 14:10:36 -0800522 if exists, _, err := ctx.Fs().Exists(ret.String()); err != nil {
523 reportPathError(ctx, "%s: %s", ret, err.Error())
524 } else if !exists {
525 reportPathError(ctx, "source path %s does not exist", ret)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700526 }
527 return ret
528}
529
Jeff Gaston734e3802017-04-10 15:47:24 -0700530// ExistentPathForSource returns an OptionalPath with the SourcePath if the
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700531// path exists, or an empty OptionalPath if it doesn't exist. Dependencies are added
532// so that the ninja file will be regenerated if the state of the path changes.
Colin Cross32f38982018-02-22 11:47:25 -0800533func ExistentPathForSource(ctx PathContext, pathComponents ...string) OptionalPath {
Jeff Gaston734e3802017-04-10 15:47:24 -0700534 p := validatePath(ctx, pathComponents...)
Colin Crossaabf6792017-11-29 00:27:14 -0800535 path := SourcePath{basePath{p, ctx.Config(), ""}}
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700536
537 abs, err := filepath.Abs(path.String())
538 if err != nil {
539 reportPathError(ctx, "%s", err.Error())
540 return OptionalPath{}
541 }
Colin Crossaabf6792017-11-29 00:27:14 -0800542 buildroot, err := filepath.Abs(ctx.Config().buildDir)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700543 if err != nil {
544 reportPathError(ctx, "%s", err.Error())
545 return OptionalPath{}
546 }
547 if strings.HasPrefix(abs, buildroot) {
548 reportPathError(ctx, "source path %s is in output", abs)
549 return OptionalPath{}
550 }
551
Colin Cross7f19f372016-11-01 11:10:25 -0700552 if pathtools.IsGlob(path.String()) {
Dan Willemsen7b310ee2015-12-18 15:11:17 -0800553 reportPathError(ctx, "path may not contain a glob: %s", path.String())
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700554 return OptionalPath{}
555 }
556
Colin Cross7f19f372016-11-01 11:10:25 -0700557 if gctx, ok := ctx.(PathGlobContext); ok {
Dan Willemsen7b310ee2015-12-18 15:11:17 -0800558 // Use glob to produce proper dependencies, even though we only want
559 // a single file.
Colin Cross7f19f372016-11-01 11:10:25 -0700560 files, err := gctx.GlobWithDeps(path.String(), nil)
Dan Willemsen7b310ee2015-12-18 15:11:17 -0800561 if err != nil {
562 reportPathError(ctx, "glob: %s", err.Error())
563 return OptionalPath{}
564 }
565
566 if len(files) == 0 {
567 return OptionalPath{}
568 }
569 } else {
570 // We cannot add build statements in this context, so we fall back to
571 // AddNinjaFileDeps
Colin Cross294941b2017-02-01 14:10:36 -0800572 files, dirs, err := pathtools.Glob(path.String(), nil)
Dan Willemsen7b310ee2015-12-18 15:11:17 -0800573 if err != nil {
574 reportPathError(ctx, "glob: %s", err.Error())
575 return OptionalPath{}
576 }
577
578 ctx.AddNinjaFileDeps(dirs...)
579
580 if len(files) == 0 {
581 return OptionalPath{}
582 }
583
584 ctx.AddNinjaFileDeps(path.String())
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700585 }
586 return OptionalPathForPath(path)
587}
588
589func (p SourcePath) String() string {
590 return filepath.Join(p.config.srcDir, p.path)
591}
592
593// Join creates a new SourcePath with paths... joined with the current path. The
594// provided paths... may not use '..' to escape from the current path.
595func (p SourcePath) Join(ctx PathContext, paths ...string) SourcePath {
596 path := validatePath(ctx, paths...)
Colin Cross0db55682017-12-05 15:36:55 -0800597 return p.withRel(path)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700598}
599
600// OverlayPath returns the overlay for `path' if it exists. This assumes that the
601// SourcePath is the path to a resource overlay directory.
Colin Cross635c3b02016-05-18 15:37:25 -0700602func (p SourcePath) OverlayPath(ctx ModuleContext, path Path) OptionalPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700603 var relDir string
604 if moduleSrcPath, ok := path.(ModuleSrcPath); ok {
Colin Cross7fc17db2017-02-01 14:07:55 -0800605 relDir = moduleSrcPath.path
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700606 } else if srcPath, ok := path.(SourcePath); ok {
607 relDir = srcPath.path
608 } else {
609 reportPathError(ctx, "Cannot find relative path for %s(%s)", reflect.TypeOf(path).Name(), path)
610 return OptionalPath{}
611 }
612 dir := filepath.Join(p.config.srcDir, p.path, relDir)
613 // Use Glob so that we are run again if the directory is added.
Colin Cross7f19f372016-11-01 11:10:25 -0700614 if pathtools.IsGlob(dir) {
Dan Willemsen7b310ee2015-12-18 15:11:17 -0800615 reportPathError(ctx, "Path may not contain a glob: %s", dir)
616 }
Colin Cross7f19f372016-11-01 11:10:25 -0700617 paths, err := ctx.GlobWithDeps(dir, []string{})
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700618 if err != nil {
619 reportPathError(ctx, "glob: %s", err.Error())
620 return OptionalPath{}
621 }
622 if len(paths) == 0 {
623 return OptionalPath{}
624 }
625 relPath, err := filepath.Rel(p.config.srcDir, paths[0])
626 if err != nil {
627 reportPathError(ctx, "%s", err.Error())
628 return OptionalPath{}
629 }
630 return OptionalPathForPath(PathForSource(ctx, relPath))
631}
632
633// OutputPath is a Path representing a file path rooted from the build directory
634type OutputPath struct {
635 basePath
636}
637
Colin Cross702e0f82017-10-18 17:27:54 -0700638func (p OutputPath) withRel(rel string) OutputPath {
Colin Cross0db55682017-12-05 15:36:55 -0800639 p.basePath = p.basePath.withRel(rel)
Colin Cross702e0f82017-10-18 17:27:54 -0700640 return p
641}
642
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700643var _ Path = OutputPath{}
644
Jeff Gaston734e3802017-04-10 15:47:24 -0700645// PathForOutput joins the provided paths and returns an OutputPath that is
646// validated to not escape the build dir.
647// On error, it will return a usable, but invalid OutputPath, and report a ModuleError.
648func PathForOutput(ctx PathContext, pathComponents ...string) OutputPath {
649 path := validatePath(ctx, pathComponents...)
Colin Crossaabf6792017-11-29 00:27:14 -0800650 return OutputPath{basePath{path, ctx.Config(), ""}}
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700651}
652
653func (p OutputPath) writablePath() {}
654
655func (p OutputPath) String() string {
656 return filepath.Join(p.config.buildDir, p.path)
657}
658
Colin Crossa2344662016-03-24 13:14:12 -0700659func (p OutputPath) RelPathString() string {
660 return p.path
661}
662
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700663// Join creates a new OutputPath with paths... joined with the current path. The
664// provided paths... may not use '..' to escape from the current path.
665func (p OutputPath) Join(ctx PathContext, paths ...string) OutputPath {
666 path := validatePath(ctx, paths...)
Colin Cross0db55682017-12-05 15:36:55 -0800667 return p.withRel(path)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700668}
669
670// PathForIntermediates returns an OutputPath representing the top-level
671// intermediates directory.
672func PathForIntermediates(ctx PathContext, paths ...string) OutputPath {
673 path := validatePath(ctx, paths...)
674 return PathForOutput(ctx, ".intermediates", path)
675}
676
677// ModuleSrcPath is a Path representing a file rooted from a module's local source dir
678type ModuleSrcPath struct {
Colin Cross7fc17db2017-02-01 14:07:55 -0800679 SourcePath
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700680}
681
682var _ Path = ModuleSrcPath{}
683var _ genPathProvider = ModuleSrcPath{}
684var _ objPathProvider = ModuleSrcPath{}
685var _ resPathProvider = ModuleSrcPath{}
686
687// PathForModuleSrc returns a ModuleSrcPath representing the paths... under the
688// module's local source directory.
Colin Cross635c3b02016-05-18 15:37:25 -0700689func PathForModuleSrc(ctx ModuleContext, paths ...string) ModuleSrcPath {
Colin Crossfaeb7aa2017-02-01 14:12:44 -0800690 p := validatePath(ctx, paths...)
691 path := ModuleSrcPath{PathForSource(ctx, ctx.ModuleDir(), p)}
692 path.basePath.rel = p
693 return path
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700694}
695
696// OptionalPathForModuleSrc returns an OptionalPath. The OptionalPath contains a
697// valid path if p is non-nil.
Colin Cross635c3b02016-05-18 15:37:25 -0700698func OptionalPathForModuleSrc(ctx ModuleContext, p *string) OptionalPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700699 if p == nil {
700 return OptionalPath{}
701 }
702 return OptionalPathForPath(PathForModuleSrc(ctx, *p))
703}
704
Dan Willemsen21ec4902016-11-02 20:43:13 -0700705func (p ModuleSrcPath) genPathWithExt(ctx ModuleContext, subdir, ext string) ModuleGenPath {
Colin Cross7fc17db2017-02-01 14:07:55 -0800706 return PathForModuleGen(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) objPathWithExt(ctx ModuleContext, subdir, ext string) ModuleObjPath {
Colin Cross7fc17db2017-02-01 14:07:55 -0800710 return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700711}
712
Colin Cross635c3b02016-05-18 15:37:25 -0700713func (p ModuleSrcPath) resPathWithName(ctx ModuleContext, name string) ModuleResPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700714 // TODO: Use full directory if the new ctx is not the current ctx?
715 return PathForModuleRes(ctx, p.path, name)
716}
717
Colin Crossfaeb7aa2017-02-01 14:12:44 -0800718func (p ModuleSrcPath) WithSubDir(ctx ModuleContext, subdir string) ModuleSrcPath {
719 subdir = PathForModuleSrc(ctx, subdir).String()
720 var err error
721 rel, err := filepath.Rel(subdir, p.path)
722 if err != nil {
723 ctx.ModuleErrorf("source file %q is not under path %q", p.path, subdir)
724 return p
725 }
726 p.rel = rel
727 return p
728}
729
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700730// ModuleOutPath is a Path representing a module's output directory.
731type ModuleOutPath struct {
732 OutputPath
733}
734
735var _ Path = ModuleOutPath{}
736
Colin Cross702e0f82017-10-18 17:27:54 -0700737func pathForModule(ctx ModuleContext) OutputPath {
738 return PathForOutput(ctx, ".intermediates", ctx.ModuleDir(), ctx.ModuleName(), ctx.ModuleSubDir())
739}
740
Jayant Chowdhary3e231fd2017-02-08 13:45:53 -0800741// PathForVndkRefDump returns an OptionalPath representing the path of the reference
742// abi dump for the given module. This is not guaranteed to be valid.
743func PathForVndkRefAbiDump(ctx ModuleContext, version, fileName string, vndkOrNdk, isSourceDump bool) OptionalPath {
744 archName := ctx.Arch().ArchType.Name
745 var sourceOrBinaryDir string
746 var vndkOrNdkDir string
747 var ext string
748 if isSourceDump {
Jayant Chowdhary715cac32017-04-20 06:53:59 -0700749 ext = ".lsdump.gz"
Jayant Chowdhary3e231fd2017-02-08 13:45:53 -0800750 sourceOrBinaryDir = "source-based"
751 } else {
Jayant Chowdhary715cac32017-04-20 06:53:59 -0700752 ext = ".bdump.gz"
Jayant Chowdhary3e231fd2017-02-08 13:45:53 -0800753 sourceOrBinaryDir = "binary-based"
754 }
755 if vndkOrNdk {
756 vndkOrNdkDir = "vndk"
757 } else {
758 vndkOrNdkDir = "ndk"
759 }
760 refDumpFileStr := "prebuilts/abi-dumps/" + vndkOrNdkDir + "/" + version + "/" +
761 archName + "/" + sourceOrBinaryDir + "/" + fileName + ext
Colin Cross32f38982018-02-22 11:47:25 -0800762 return ExistentPathForSource(ctx, refDumpFileStr)
Jayant Chowdhary3e231fd2017-02-08 13:45:53 -0800763}
764
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700765// PathForModuleOut returns a Path representing the paths... under the module's
766// output directory.
Colin Cross635c3b02016-05-18 15:37:25 -0700767func PathForModuleOut(ctx ModuleContext, paths ...string) ModuleOutPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700768 p := validatePath(ctx, paths...)
Colin Cross702e0f82017-10-18 17:27:54 -0700769 return ModuleOutPath{
770 OutputPath: pathForModule(ctx).withRel(p),
771 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700772}
773
774// ModuleGenPath is a Path representing the 'gen' directory in a module's output
775// directory. Mainly used for generated sources.
776type ModuleGenPath struct {
777 ModuleOutPath
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700778}
779
780var _ Path = ModuleGenPath{}
781var _ genPathProvider = ModuleGenPath{}
782var _ objPathProvider = ModuleGenPath{}
783
784// PathForModuleGen returns a Path representing the paths... under the module's
785// `gen' directory.
Colin Cross635c3b02016-05-18 15:37:25 -0700786func PathForModuleGen(ctx ModuleContext, paths ...string) ModuleGenPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700787 p := validatePath(ctx, paths...)
788 return ModuleGenPath{
Colin Cross702e0f82017-10-18 17:27:54 -0700789 ModuleOutPath: ModuleOutPath{
790 OutputPath: pathForModule(ctx).withRel("gen").withRel(p),
791 },
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700792 }
793}
794
Dan Willemsen21ec4902016-11-02 20:43:13 -0700795func (p ModuleGenPath) genPathWithExt(ctx ModuleContext, subdir, ext string) ModuleGenPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700796 // TODO: make a different path for local vs remote generated files?
Dan Willemsen21ec4902016-11-02 20:43:13 -0700797 return PathForModuleGen(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700798}
799
Colin Cross635c3b02016-05-18 15:37:25 -0700800func (p ModuleGenPath) objPathWithExt(ctx ModuleContext, subdir, ext string) ModuleObjPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700801 return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
802}
803
804// ModuleObjPath is a Path representing the 'obj' directory in a module's output
805// directory. Used for compiled objects.
806type ModuleObjPath struct {
807 ModuleOutPath
808}
809
810var _ Path = ModuleObjPath{}
811
812// PathForModuleObj returns a Path representing the paths... under the module's
813// 'obj' directory.
Jeff Gaston734e3802017-04-10 15:47:24 -0700814func PathForModuleObj(ctx ModuleContext, pathComponents ...string) ModuleObjPath {
815 p := validatePath(ctx, pathComponents...)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700816 return ModuleObjPath{PathForModuleOut(ctx, "obj", p)}
817}
818
819// ModuleResPath is a a Path representing the 'res' directory in a module's
820// output directory.
821type ModuleResPath struct {
822 ModuleOutPath
823}
824
825var _ Path = ModuleResPath{}
826
827// PathForModuleRes returns a Path representing the paths... under the module's
828// 'res' directory.
Jeff Gaston734e3802017-04-10 15:47:24 -0700829func PathForModuleRes(ctx ModuleContext, pathComponents ...string) ModuleResPath {
830 p := validatePath(ctx, pathComponents...)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700831 return ModuleResPath{PathForModuleOut(ctx, "res", p)}
832}
833
834// PathForModuleInstall returns a Path representing the install path for the
835// module appended with paths...
Dan Willemsen00269f22017-07-06 16:59:48 -0700836func PathForModuleInstall(ctx ModuleInstallPathContext, pathComponents ...string) OutputPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700837 var outPaths []string
838 if ctx.Device() {
Vishwath Mohan87f3b242017-06-07 12:31:57 -0700839 var partition string
Dan Willemsen00269f22017-07-06 16:59:48 -0700840 if ctx.InstallInData() {
Vishwath Mohan87f3b242017-06-07 12:31:57 -0700841 partition = "data"
Jiyong Park2db76922017-11-08 16:03:48 +0900842 } else if ctx.SocSpecific() {
Dan Willemsen00269f22017-07-06 16:59:48 -0700843 partition = ctx.DeviceConfig().VendorPath()
Jiyong Park2db76922017-11-08 16:03:48 +0900844 } else if ctx.DeviceSpecific() {
845 partition = ctx.DeviceConfig().OdmPath()
846 } else if ctx.ProductSpecific() {
Jaekyun Seok5cfbfbb2018-01-10 19:00:15 +0900847 partition = ctx.DeviceConfig().ProductPath()
Vishwath Mohan87f3b242017-06-07 12:31:57 -0700848 } else {
849 partition = "system"
Dan Willemsen782a2d12015-12-21 14:55:28 -0800850 }
Vishwath Mohan1dd88392017-03-29 22:00:18 -0700851
852 if ctx.InstallInSanitizerDir() {
853 partition = "data/asan/" + partition
Dan Willemsen782a2d12015-12-21 14:55:28 -0800854 }
Colin Cross6510f912017-11-29 00:27:14 -0800855 outPaths = []string{"target", "product", ctx.Config().DeviceName(), partition}
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700856 } else {
Dan Willemsen866b5632017-09-22 12:28:24 -0700857 switch ctx.Os() {
858 case Linux:
859 outPaths = []string{"host", "linux-x86"}
860 case LinuxBionic:
861 // TODO: should this be a separate top level, or shared with linux-x86?
862 outPaths = []string{"host", "linux_bionic-x86"}
863 default:
864 outPaths = []string{"host", ctx.Os().String() + "-x86"}
865 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700866 }
Dan Willemsen782a2d12015-12-21 14:55:28 -0800867 if ctx.Debug() {
868 outPaths = append([]string{"debug"}, outPaths...)
869 }
Jeff Gaston734e3802017-04-10 15:47:24 -0700870 outPaths = append(outPaths, pathComponents...)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700871 return PathForOutput(ctx, outPaths...)
872}
873
874// validateSafePath validates a path that we trust (may contain ninja variables).
Dan Willemsen80a7c2a2015-12-21 14:57:11 -0800875// Ensures that each path component does not attempt to leave its component.
Jeff Gaston734e3802017-04-10 15:47:24 -0700876func validateSafePath(ctx PathContext, pathComponents ...string) string {
877 for _, path := range pathComponents {
Dan Willemsen80a7c2a2015-12-21 14:57:11 -0800878 path := filepath.Clean(path)
879 if path == ".." || strings.HasPrefix(path, "../") || strings.HasPrefix(path, "/") {
880 reportPathError(ctx, "Path is outside directory: %s", path)
881 return ""
882 }
883 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700884 // TODO: filepath.Join isn't necessarily correct with embedded ninja
885 // variables. '..' may remove the entire ninja variable, even if it
886 // will be expanded to multiple nested directories.
Jeff Gaston734e3802017-04-10 15:47:24 -0700887 return filepath.Join(pathComponents...)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700888}
889
Dan Willemsen80a7c2a2015-12-21 14:57:11 -0800890// validatePath validates that a path does not include ninja variables, and that
891// each path component does not attempt to leave its component. Returns a joined
892// version of each path component.
Jeff Gaston734e3802017-04-10 15:47:24 -0700893func validatePath(ctx PathContext, pathComponents ...string) string {
894 for _, path := range pathComponents {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700895 if strings.Contains(path, "$") {
896 reportPathError(ctx, "Path contains invalid character($): %s", path)
897 return ""
898 }
899 }
Jeff Gaston734e3802017-04-10 15:47:24 -0700900 return validateSafePath(ctx, pathComponents...)
Colin Cross6e18ca42015-07-14 18:55:36 -0700901}
Colin Cross5b529592017-05-09 13:34:34 -0700902
Colin Cross0875c522017-11-28 17:34:01 -0800903func PathForPhony(ctx PathContext, phony string) WritablePath {
904 if strings.ContainsAny(phony, "$/") {
905 reportPathError(ctx, "Phony target contains invalid character ($ or /): %s", phony)
906 }
Colin Cross74e3fe42017-12-11 15:51:44 -0800907 return PhonyPath{basePath{phony, ctx.Config(), ""}}
Colin Cross0875c522017-11-28 17:34:01 -0800908}
909
Colin Cross74e3fe42017-12-11 15:51:44 -0800910type PhonyPath struct {
911 basePath
912}
913
914func (p PhonyPath) writablePath() {}
915
916var _ Path = PhonyPath{}
917var _ WritablePath = PhonyPath{}
918
Colin Cross5b529592017-05-09 13:34:34 -0700919type testPath struct {
920 basePath
921}
922
923func (p testPath) String() string {
924 return p.path
925}
926
927func PathForTesting(paths ...string) Path {
928 p := validateSafePath(nil, paths...)
929 return testPath{basePath{path: p, rel: p}}
930}
931
932func PathsForTesting(strs []string) Paths {
933 p := make(Paths, len(strs))
934 for i, s := range strs {
935 p[i] = PathForTesting(s)
936 }
937
938 return p
939}