blob: 02f56d0957afb22354bb44cc90c5536eb34797ba [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 Cross988414c2020-01-11 01:11:46 +000019 "io/ioutil"
20 "os"
Colin Cross6a745c62015-06-16 16:38:10 -070021 "path/filepath"
Dan Willemsen34cc69e2015-09-23 15:26:20 -070022 "reflect"
Colin Cross5e6cfbe2017-11-03 15:20:35 -070023 "sort"
Dan Willemsen34cc69e2015-09-23 15:26:20 -070024 "strings"
25
26 "github.com/google/blueprint"
27 "github.com/google/blueprint/pathtools"
Colin Cross3f40fa42015-01-30 17:27:36 -080028)
29
Colin Cross988414c2020-01-11 01:11:46 +000030var absSrcDir string
31
Dan Willemsen34cc69e2015-09-23 15:26:20 -070032// PathContext is the subset of a (Module|Singleton)Context required by the
33// Path methods.
34type PathContext interface {
Colin Crossaabf6792017-11-29 00:27:14 -080035 Config() Config
Dan Willemsen7b310ee2015-12-18 15:11:17 -080036 AddNinjaFileDeps(deps ...string)
Colin Cross3f40fa42015-01-30 17:27:36 -080037}
38
Colin Cross7f19f372016-11-01 11:10:25 -070039type PathGlobContext interface {
40 GlobWithDeps(globPattern string, excludes []string) ([]string, error)
41}
42
Colin Crossaabf6792017-11-29 00:27:14 -080043var _ PathContext = SingletonContext(nil)
44var _ PathContext = ModuleContext(nil)
Dan Willemsen34cc69e2015-09-23 15:26:20 -070045
Dan Willemsen00269f22017-07-06 16:59:48 -070046type ModuleInstallPathContext interface {
Colin Cross0ea8ba82019-06-06 14:33:29 -070047 BaseModuleContext
Dan Willemsen00269f22017-07-06 16:59:48 -070048
49 InstallInData() bool
Jaewoong Jung0949f312019-09-11 10:25:18 -070050 InstallInTestcases() bool
Dan Willemsen00269f22017-07-06 16:59:48 -070051 InstallInSanitizerDir() bool
Jiyong Parkf9332f12018-02-01 00:54:12 +090052 InstallInRecovery() bool
Colin Cross90ba5f42019-10-02 11:10:58 -070053 InstallInRoot() bool
Colin Cross607d8582019-07-29 16:44:46 -070054 InstallBypassMake() bool
Dan Willemsen00269f22017-07-06 16:59:48 -070055}
56
57var _ ModuleInstallPathContext = ModuleContext(nil)
58
Dan Willemsen34cc69e2015-09-23 15:26:20 -070059// errorfContext is the interface containing the Errorf method matching the
60// Errorf method in blueprint.SingletonContext.
61type errorfContext interface {
62 Errorf(format string, args ...interface{})
Colin Cross3f40fa42015-01-30 17:27:36 -080063}
64
Dan Willemsen34cc69e2015-09-23 15:26:20 -070065var _ errorfContext = blueprint.SingletonContext(nil)
66
67// moduleErrorf is the interface containing the ModuleErrorf method matching
68// the ModuleErrorf method in blueprint.ModuleContext.
69type moduleErrorf interface {
70 ModuleErrorf(format string, args ...interface{})
Colin Cross3f40fa42015-01-30 17:27:36 -080071}
72
Dan Willemsen34cc69e2015-09-23 15:26:20 -070073var _ moduleErrorf = blueprint.ModuleContext(nil)
74
Dan Willemsen34cc69e2015-09-23 15:26:20 -070075// reportPathError will register an error with the attached context. It
76// attempts ctx.ModuleErrorf for a better error message first, then falls
77// back to ctx.Errorf.
Colin Cross1ccfcc32018-02-22 13:54:26 -080078func reportPathError(ctx PathContext, err error) {
79 reportPathErrorf(ctx, "%s", err.Error())
80}
81
82// reportPathErrorf will register an error with the attached context. It
83// attempts ctx.ModuleErrorf for a better error message first, then falls
84// back to ctx.Errorf.
85func reportPathErrorf(ctx PathContext, format string, args ...interface{}) {
Dan Willemsen34cc69e2015-09-23 15:26:20 -070086 if mctx, ok := ctx.(moduleErrorf); ok {
87 mctx.ModuleErrorf(format, args...)
88 } else if ectx, ok := ctx.(errorfContext); ok {
89 ectx.Errorf(format, args...)
90 } else {
91 panic(fmt.Sprintf(format, args...))
Colin Crossf2298272015-05-12 11:36:53 -070092 }
93}
94
Colin Cross5e708052019-08-06 13:59:50 -070095func pathContextName(ctx PathContext, module blueprint.Module) string {
96 if x, ok := ctx.(interface{ ModuleName(blueprint.Module) string }); ok {
97 return x.ModuleName(module)
98 } else if x, ok := ctx.(interface{ OtherModuleName(blueprint.Module) string }); ok {
99 return x.OtherModuleName(module)
100 }
101 return "unknown"
102}
103
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700104type Path interface {
105 // Returns the path in string form
106 String() string
107
Colin Cross4f6fc9c2016-10-26 10:05:25 -0700108 // Ext returns the extension of the last element of the path
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700109 Ext() string
Colin Cross4f6fc9c2016-10-26 10:05:25 -0700110
111 // Base returns the last element of the path
112 Base() string
Colin Crossfaeb7aa2017-02-01 14:12:44 -0800113
114 // Rel returns the portion of the path relative to the directory it was created from. For
115 // example, Rel on a PathsForModuleSrc would return the path relative to the module source
Colin Cross0db55682017-12-05 15:36:55 -0800116 // directory, and OutputPath.Join("foo").Rel() would return "foo".
Colin Crossfaeb7aa2017-02-01 14:12:44 -0800117 Rel() string
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700118}
119
120// WritablePath is a type of path that can be used as an output for build rules.
121type WritablePath interface {
122 Path
123
Paul Duffin9b478b02019-12-10 13:41:51 +0000124 // return the path to the build directory.
125 buildDir() string
126
Jeff Gaston734e3802017-04-10 15:47:24 -0700127 // the writablePath method doesn't directly do anything,
128 // but it allows a struct to distinguish between whether or not it implements the WritablePath interface
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700129 writablePath()
130}
131
132type genPathProvider interface {
Dan Willemsen21ec4902016-11-02 20:43:13 -0700133 genPathWithExt(ctx ModuleContext, subdir, ext string) ModuleGenPath
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700134}
135type objPathProvider interface {
Colin Cross635c3b02016-05-18 15:37:25 -0700136 objPathWithExt(ctx ModuleContext, subdir, ext string) ModuleObjPath
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700137}
138type resPathProvider interface {
Colin Cross635c3b02016-05-18 15:37:25 -0700139 resPathWithName(ctx ModuleContext, name string) ModuleResPath
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700140}
141
142// GenPathWithExt derives a new file path in ctx's generated sources directory
143// from the current path, but with the new extension.
Dan Willemsen21ec4902016-11-02 20:43:13 -0700144func GenPathWithExt(ctx ModuleContext, subdir string, p Path, ext string) ModuleGenPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700145 if path, ok := p.(genPathProvider); ok {
Dan Willemsen21ec4902016-11-02 20:43:13 -0700146 return path.genPathWithExt(ctx, subdir, ext)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700147 }
Colin Cross1ccfcc32018-02-22 13:54:26 -0800148 reportPathErrorf(ctx, "Tried to create generated file from unsupported path: %s(%s)", reflect.TypeOf(p).Name(), p)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700149 return PathForModuleGen(ctx)
150}
151
152// ObjPathWithExt derives a new file path in ctx's object directory from the
153// current path, but with the new extension.
Dan Willemsen21ec4902016-11-02 20:43:13 -0700154func ObjPathWithExt(ctx ModuleContext, subdir string, p Path, ext string) ModuleObjPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700155 if path, ok := p.(objPathProvider); ok {
156 return path.objPathWithExt(ctx, subdir, ext)
157 }
Colin Cross1ccfcc32018-02-22 13:54:26 -0800158 reportPathErrorf(ctx, "Tried to create object file from unsupported path: %s (%s)", reflect.TypeOf(p).Name(), p)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700159 return PathForModuleObj(ctx)
160}
161
162// ResPathWithName derives a new path in ctx's output resource directory, using
163// the current path to create the directory name, and the `name` argument for
164// the filename.
Colin Cross635c3b02016-05-18 15:37:25 -0700165func ResPathWithName(ctx ModuleContext, p Path, name string) ModuleResPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700166 if path, ok := p.(resPathProvider); ok {
167 return path.resPathWithName(ctx, name)
168 }
Colin Cross1ccfcc32018-02-22 13:54:26 -0800169 reportPathErrorf(ctx, "Tried to create res file from unsupported path: %s (%s)", reflect.TypeOf(p).Name(), p)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700170 return PathForModuleRes(ctx)
171}
172
173// OptionalPath is a container that may or may not contain a valid Path.
174type OptionalPath struct {
175 valid bool
176 path Path
177}
178
179// OptionalPathForPath returns an OptionalPath containing the path.
180func OptionalPathForPath(path Path) OptionalPath {
181 if path == nil {
182 return OptionalPath{}
183 }
184 return OptionalPath{valid: true, path: path}
185}
186
187// Valid returns whether there is a valid path
188func (p OptionalPath) Valid() bool {
189 return p.valid
190}
191
192// Path returns the Path embedded in this OptionalPath. You must be sure that
193// there is a valid path, since this method will panic if there is not.
194func (p OptionalPath) Path() Path {
195 if !p.valid {
196 panic("Requesting an invalid path")
197 }
198 return p.path
199}
200
201// String returns the string version of the Path, or "" if it isn't valid.
202func (p OptionalPath) String() string {
203 if p.valid {
204 return p.path.String()
205 } else {
206 return ""
Colin Crossf2298272015-05-12 11:36:53 -0700207 }
208}
Colin Cross6e18ca42015-07-14 18:55:36 -0700209
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700210// Paths is a slice of Path objects, with helpers to operate on the collection.
211type Paths []Path
212
213// PathsForSource returns Paths rooted from SrcDir
214func PathsForSource(ctx PathContext, paths []string) Paths {
215 ret := make(Paths, len(paths))
216 for i, path := range paths {
217 ret[i] = PathForSource(ctx, path)
218 }
219 return ret
220}
221
Jeff Gaston734e3802017-04-10 15:47:24 -0700222// ExistentPathsForSources returns a list of Paths rooted from SrcDir that are
Dan Willemsen7b310ee2015-12-18 15:11:17 -0800223// found in the tree. If any are not found, they are omitted from the list,
224// and dependencies are added so that we're re-run when they are added.
Colin Cross32f38982018-02-22 11:47:25 -0800225func ExistentPathsForSources(ctx PathContext, paths []string) Paths {
Dan Willemsen7b310ee2015-12-18 15:11:17 -0800226 ret := make(Paths, 0, len(paths))
227 for _, path := range paths {
Colin Cross32f38982018-02-22 11:47:25 -0800228 p := ExistentPathForSource(ctx, path)
Dan Willemsen7b310ee2015-12-18 15:11:17 -0800229 if p.Valid() {
230 ret = append(ret, p.Path())
231 }
232 }
233 return ret
234}
235
Colin Cross41955e82019-05-29 14:40:35 -0700236// PathsForModuleSrc returns Paths rooted from the module's local source directory. It expands globs, references to
237// SourceFileProducer modules using the ":name" syntax, and references to OutputFileProducer modules using the
238// ":name{.tag}" syntax. Properties passed as the paths argument must have been annotated with struct tag
239// `android:"path"` so that dependencies on SourceFileProducer modules will have already been handled by the
240// path_properties mutator. If ctx.Config().AllowMissingDependencies() is true then any missing SourceFileProducer or
241// OutputFileProducer dependencies will cause the module to be marked as having missing dependencies.
Colin Cross635c3b02016-05-18 15:37:25 -0700242func PathsForModuleSrc(ctx ModuleContext, paths []string) Paths {
Colin Cross8a497952019-03-05 22:25:09 -0800243 return PathsForModuleSrcExcludes(ctx, paths, nil)
244}
245
Colin Crossba71a3f2019-03-18 12:12:48 -0700246// PathsForModuleSrcExcludes returns Paths rooted from the module's local source directory, excluding paths listed in
Colin Cross41955e82019-05-29 14:40:35 -0700247// the excludes arguments. It expands globs, references to SourceFileProducer modules using the ":name" syntax, and
248// references to OutputFileProducer modules using the ":name{.tag}" syntax. Properties passed as the paths or excludes
249// argument must have been annotated with struct tag `android:"path"` so that dependencies on SourceFileProducer modules
250// will have already been handled by the path_properties mutator. If ctx.Config().AllowMissingDependencies() is
Paul Duffin036cace2019-07-25 14:44:56 +0100251// true then any missing SourceFileProducer or OutputFileProducer dependencies will cause the module to be marked as
Colin Cross41955e82019-05-29 14:40:35 -0700252// having missing dependencies.
Colin Cross8a497952019-03-05 22:25:09 -0800253func PathsForModuleSrcExcludes(ctx ModuleContext, paths, excludes []string) Paths {
Colin Crossba71a3f2019-03-18 12:12:48 -0700254 ret, missingDeps := PathsAndMissingDepsForModuleSrcExcludes(ctx, paths, excludes)
255 if ctx.Config().AllowMissingDependencies() {
256 ctx.AddMissingDependencies(missingDeps)
257 } else {
258 for _, m := range missingDeps {
259 ctx.ModuleErrorf(`missing dependency on %q, is the property annotated with android:"path"?`, m)
260 }
261 }
262 return ret
263}
264
Ulya Trafimovich4d2eeed2019-11-08 10:54:21 +0000265// OutputPaths is a slice of OutputPath objects, with helpers to operate on the collection.
266type OutputPaths []OutputPath
267
268// Paths returns the OutputPaths as a Paths
269func (p OutputPaths) Paths() Paths {
270 if p == nil {
271 return nil
272 }
273 ret := make(Paths, len(p))
274 for i, path := range p {
275 ret[i] = path
276 }
277 return ret
278}
279
280// Strings returns the string forms of the writable paths.
281func (p OutputPaths) Strings() []string {
282 if p == nil {
283 return nil
284 }
285 ret := make([]string, len(p))
286 for i, path := range p {
287 ret[i] = path.String()
288 }
289 return ret
290}
291
Colin Crossba71a3f2019-03-18 12:12:48 -0700292// PathsAndMissingDepsForModuleSrcExcludes returns Paths rooted from the module's local source directory, excluding
Colin Cross41955e82019-05-29 14:40:35 -0700293// paths listed in the excludes arguments, and a list of missing dependencies. It expands globs, references to
294// SourceFileProducer modules using the ":name" syntax, and references to OutputFileProducer modules using the
295// ":name{.tag}" syntax. Properties passed as the paths or excludes argument must have been annotated with struct tag
296// `android:"path"` so that dependencies on SourceFileProducer modules will have already been handled by the
297// path_properties mutator. If ctx.Config().AllowMissingDependencies() is true then any missing SourceFileProducer or
298// OutputFileProducer dependencies will be returned, and they will NOT cause the module to be marked as having missing
299// dependencies.
Colin Crossba71a3f2019-03-18 12:12:48 -0700300func PathsAndMissingDepsForModuleSrcExcludes(ctx ModuleContext, paths, excludes []string) (Paths, []string) {
Colin Cross8a497952019-03-05 22:25:09 -0800301 prefix := pathForModuleSrc(ctx).String()
302
303 var expandedExcludes []string
304 if excludes != nil {
305 expandedExcludes = make([]string, 0, len(excludes))
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700306 }
Colin Cross8a497952019-03-05 22:25:09 -0800307
Colin Crossba71a3f2019-03-18 12:12:48 -0700308 var missingExcludeDeps []string
309
Colin Cross8a497952019-03-05 22:25:09 -0800310 for _, e := range excludes {
Colin Cross41955e82019-05-29 14:40:35 -0700311 if m, t := SrcIsModuleWithTag(e); m != "" {
312 module := ctx.GetDirectDepWithTag(m, sourceOrOutputDepTag(t))
Colin Cross8a497952019-03-05 22:25:09 -0800313 if module == nil {
Colin Crossba71a3f2019-03-18 12:12:48 -0700314 missingExcludeDeps = append(missingExcludeDeps, m)
Colin Cross8a497952019-03-05 22:25:09 -0800315 continue
316 }
Colin Cross41955e82019-05-29 14:40:35 -0700317 if outProducer, ok := module.(OutputFileProducer); ok {
318 outputFiles, err := outProducer.OutputFiles(t)
319 if err != nil {
320 ctx.ModuleErrorf("path dependency %q: %s", e, err)
321 }
322 expandedExcludes = append(expandedExcludes, outputFiles.Strings()...)
323 } else if t != "" {
324 ctx.ModuleErrorf("path dependency %q is not an output file producing module", e)
325 } else if srcProducer, ok := module.(SourceFileProducer); ok {
Colin Cross8a497952019-03-05 22:25:09 -0800326 expandedExcludes = append(expandedExcludes, srcProducer.Srcs().Strings()...)
327 } else {
Colin Cross41955e82019-05-29 14:40:35 -0700328 ctx.ModuleErrorf("path dependency %q is not a source file producing module", e)
Colin Cross8a497952019-03-05 22:25:09 -0800329 }
330 } else {
331 expandedExcludes = append(expandedExcludes, filepath.Join(prefix, e))
332 }
333 }
334
335 if paths == nil {
Colin Crossba71a3f2019-03-18 12:12:48 -0700336 return nil, missingExcludeDeps
Colin Cross8a497952019-03-05 22:25:09 -0800337 }
338
Colin Crossba71a3f2019-03-18 12:12:48 -0700339 var missingDeps []string
340
Colin Cross8a497952019-03-05 22:25:09 -0800341 expandedSrcFiles := make(Paths, 0, len(paths))
342 for _, s := range paths {
343 srcFiles, err := expandOneSrcPath(ctx, s, expandedExcludes)
344 if depErr, ok := err.(missingDependencyError); ok {
Colin Crossba71a3f2019-03-18 12:12:48 -0700345 missingDeps = append(missingDeps, depErr.missingDeps...)
Colin Cross8a497952019-03-05 22:25:09 -0800346 } else if err != nil {
347 reportPathError(ctx, err)
348 }
349 expandedSrcFiles = append(expandedSrcFiles, srcFiles...)
350 }
Colin Crossba71a3f2019-03-18 12:12:48 -0700351
352 return expandedSrcFiles, append(missingDeps, missingExcludeDeps...)
Colin Cross8a497952019-03-05 22:25:09 -0800353}
354
355type missingDependencyError struct {
356 missingDeps []string
357}
358
359func (e missingDependencyError) Error() string {
360 return "missing dependencies: " + strings.Join(e.missingDeps, ", ")
361}
362
363func expandOneSrcPath(ctx ModuleContext, s string, expandedExcludes []string) (Paths, error) {
Colin Cross41955e82019-05-29 14:40:35 -0700364 if m, t := SrcIsModuleWithTag(s); m != "" {
365 module := ctx.GetDirectDepWithTag(m, sourceOrOutputDepTag(t))
Colin Cross8a497952019-03-05 22:25:09 -0800366 if module == nil {
367 return nil, missingDependencyError{[]string{m}}
368 }
Colin Cross41955e82019-05-29 14:40:35 -0700369 if outProducer, ok := module.(OutputFileProducer); ok {
370 outputFiles, err := outProducer.OutputFiles(t)
371 if err != nil {
372 return nil, fmt.Errorf("path dependency %q: %s", s, err)
373 }
374 return outputFiles, nil
375 } else if t != "" {
376 return nil, fmt.Errorf("path dependency %q is not an output file producing module", s)
377 } else if srcProducer, ok := module.(SourceFileProducer); ok {
Colin Cross8a497952019-03-05 22:25:09 -0800378 moduleSrcs := srcProducer.Srcs()
379 for _, e := range expandedExcludes {
380 for j := 0; j < len(moduleSrcs); j++ {
381 if moduleSrcs[j].String() == e {
382 moduleSrcs = append(moduleSrcs[:j], moduleSrcs[j+1:]...)
383 j--
384 }
385 }
386 }
387 return moduleSrcs, nil
388 } else {
Colin Cross41955e82019-05-29 14:40:35 -0700389 return nil, fmt.Errorf("path dependency %q is not a source file producing module", s)
Colin Cross8a497952019-03-05 22:25:09 -0800390 }
391 } else if pathtools.IsGlob(s) {
392 paths := ctx.GlobFiles(pathForModuleSrc(ctx, s).String(), expandedExcludes)
393 return PathsWithModuleSrcSubDir(ctx, paths, ""), nil
394 } else {
395 p := pathForModuleSrc(ctx, s)
Colin Cross988414c2020-01-11 01:11:46 +0000396 if exists, _, err := ctx.Config().fs.Exists(p.String()); err != nil {
Colin Cross8a497952019-03-05 22:25:09 -0800397 reportPathErrorf(ctx, "%s: %s", p, err.Error())
398 } else if !exists {
399 reportPathErrorf(ctx, "module source path %q does not exist", p)
400 }
401
402 j := findStringInSlice(p.String(), expandedExcludes)
403 if j >= 0 {
404 return nil, nil
405 }
406 return Paths{p}, nil
407 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700408}
409
410// pathsForModuleSrcFromFullPath returns Paths rooted from the module's local
411// source directory, but strip the local source directory from the beginning of
Dan Willemsen540a78c2018-02-26 21:50:08 -0800412// each string. If incDirs is false, strip paths with a trailing '/' from the list.
Colin Crossfe4bc362018-09-12 10:02:13 -0700413// It intended for use in globs that only list files that exist, so it allows '$' in
414// filenames.
Colin Cross1184b642019-12-30 18:43:07 -0800415func pathsForModuleSrcFromFullPath(ctx EarlyModuleContext, paths []string, incDirs bool) Paths {
Colin Cross6510f912017-11-29 00:27:14 -0800416 prefix := filepath.Join(ctx.Config().srcDir, ctx.ModuleDir()) + "/"
Colin Cross0f37af02017-09-27 17:42:05 -0700417 if prefix == "./" {
418 prefix = ""
419 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700420 ret := make(Paths, 0, len(paths))
421 for _, p := range paths {
Dan Willemsen540a78c2018-02-26 21:50:08 -0800422 if !incDirs && strings.HasSuffix(p, "/") {
423 continue
424 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700425 path := filepath.Clean(p)
426 if !strings.HasPrefix(path, prefix) {
Mikhail Naganovab1f5182019-02-08 13:17:55 -0800427 reportPathErrorf(ctx, "Path %q is not in module source directory %q", p, prefix)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700428 continue
429 }
Colin Crosse3924e12018-08-15 20:18:53 -0700430
Colin Crossfe4bc362018-09-12 10:02:13 -0700431 srcPath, err := safePathForSource(ctx, ctx.ModuleDir(), path[len(prefix):])
Colin Crosse3924e12018-08-15 20:18:53 -0700432 if err != nil {
433 reportPathError(ctx, err)
434 continue
435 }
436
Colin Cross07e51612019-03-05 12:46:40 -0800437 srcPath.basePath.rel = srcPath.path
Colin Crosse3924e12018-08-15 20:18:53 -0700438
Colin Cross07e51612019-03-05 12:46:40 -0800439 ret = append(ret, srcPath)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700440 }
441 return ret
442}
443
444// PathsWithOptionalDefaultForModuleSrc returns Paths rooted from the module's
Colin Cross0ddae7f2019-02-07 15:30:01 -0800445// local source directory. If input is nil, use the default if it exists. If input is empty, returns nil.
Colin Cross635c3b02016-05-18 15:37:25 -0700446func PathsWithOptionalDefaultForModuleSrc(ctx ModuleContext, input []string, def string) Paths {
Colin Cross0ddae7f2019-02-07 15:30:01 -0800447 if input != nil {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700448 return PathsForModuleSrc(ctx, input)
449 }
450 // Use Glob so that if the default doesn't exist, a dependency is added so that when it
451 // is created, we're run again.
Colin Cross6510f912017-11-29 00:27:14 -0800452 path := filepath.Join(ctx.Config().srcDir, ctx.ModuleDir(), def)
Colin Cross461b4452018-02-23 09:22:42 -0800453 return ctx.Glob(path, nil)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700454}
455
456// Strings returns the Paths in string form
457func (p Paths) Strings() []string {
458 if p == nil {
459 return nil
460 }
461 ret := make([]string, len(p))
462 for i, path := range p {
463 ret[i] = path.String()
464 }
465 return ret
466}
467
Colin Crossb6715442017-10-24 11:13:31 -0700468// FirstUniquePaths returns all unique elements of a Paths, keeping the first copy of each. It
469// modifies the Paths slice contents in place, and returns a subslice of the original slice.
Dan Willemsenfe92c962017-08-29 12:28:37 -0700470func FirstUniquePaths(list Paths) Paths {
471 k := 0
472outer:
473 for i := 0; i < len(list); i++ {
474 for j := 0; j < k; j++ {
475 if list[i] == list[j] {
476 continue outer
477 }
478 }
479 list[k] = list[i]
480 k++
481 }
482 return list[:k]
483}
484
Colin Crossb6715442017-10-24 11:13:31 -0700485// LastUniquePaths returns all unique elements of a Paths, keeping the last copy of each. It
486// modifies the Paths slice contents in place, and returns a subslice of the original slice.
487func LastUniquePaths(list Paths) Paths {
488 totalSkip := 0
489 for i := len(list) - 1; i >= totalSkip; i-- {
490 skip := 0
491 for j := i - 1; j >= totalSkip; j-- {
492 if list[i] == list[j] {
493 skip++
494 } else {
495 list[j+skip] = list[j]
496 }
497 }
498 totalSkip += skip
499 }
500 return list[totalSkip:]
501}
502
Colin Crossa140bb02018-04-17 10:52:26 -0700503// ReversePaths returns a copy of a Paths in reverse order.
504func ReversePaths(list Paths) Paths {
505 if list == nil {
506 return nil
507 }
508 ret := make(Paths, len(list))
509 for i := range list {
510 ret[i] = list[len(list)-1-i]
511 }
512 return ret
513}
514
Jeff Gaston294356f2017-09-27 17:05:30 -0700515func indexPathList(s Path, list []Path) int {
516 for i, l := range list {
517 if l == s {
518 return i
519 }
520 }
521
522 return -1
523}
524
525func inPathList(p Path, list []Path) bool {
526 return indexPathList(p, list) != -1
527}
528
529func FilterPathList(list []Path, filter []Path) (remainder []Path, filtered []Path) {
Paul Duffin57b9e1d2019-12-13 00:03:35 +0000530 return FilterPathListPredicate(list, func(p Path) bool { return inPathList(p, filter) })
531}
532
533func FilterPathListPredicate(list []Path, predicate func(Path) bool) (remainder []Path, filtered []Path) {
Jeff Gaston294356f2017-09-27 17:05:30 -0700534 for _, l := range list {
Paul Duffin57b9e1d2019-12-13 00:03:35 +0000535 if predicate(l) {
Jeff Gaston294356f2017-09-27 17:05:30 -0700536 filtered = append(filtered, l)
537 } else {
538 remainder = append(remainder, l)
539 }
540 }
541
542 return
543}
544
Colin Cross93e85952017-08-15 13:34:18 -0700545// HasExt returns true of any of the paths have extension ext, otherwise false
546func (p Paths) HasExt(ext string) bool {
547 for _, path := range p {
548 if path.Ext() == ext {
549 return true
550 }
551 }
552
553 return false
554}
555
556// FilterByExt returns the subset of the paths that have extension ext
557func (p Paths) FilterByExt(ext string) Paths {
558 ret := make(Paths, 0, len(p))
559 for _, path := range p {
560 if path.Ext() == ext {
561 ret = append(ret, path)
562 }
563 }
564 return ret
565}
566
567// FilterOutByExt returns the subset of the paths that do not have extension ext
568func (p Paths) FilterOutByExt(ext string) Paths {
569 ret := make(Paths, 0, len(p))
570 for _, path := range p {
571 if path.Ext() != ext {
572 ret = append(ret, path)
573 }
574 }
575 return ret
576}
577
Colin Cross5e6cfbe2017-11-03 15:20:35 -0700578// DirectorySortedPaths is a slice of paths that are sorted such that all files in a directory
579// (including subdirectories) are in a contiguous subslice of the list, and can be found in
580// O(log(N)) time using a binary search on the directory prefix.
581type DirectorySortedPaths Paths
582
583func PathsToDirectorySortedPaths(paths Paths) DirectorySortedPaths {
584 ret := append(DirectorySortedPaths(nil), paths...)
585 sort.Slice(ret, func(i, j int) bool {
586 return ret[i].String() < ret[j].String()
587 })
588 return ret
589}
590
591// PathsInDirectory returns a subslice of the DirectorySortedPaths as a Paths that contains all entries
592// that are in the specified directory and its subdirectories.
593func (p DirectorySortedPaths) PathsInDirectory(dir string) Paths {
594 prefix := filepath.Clean(dir) + "/"
595 start := sort.Search(len(p), func(i int) bool {
596 return prefix < p[i].String()
597 })
598
599 ret := p[start:]
600
601 end := sort.Search(len(ret), func(i int) bool {
602 return !strings.HasPrefix(ret[i].String(), prefix)
603 })
604
605 ret = ret[:end]
606
607 return Paths(ret)
608}
609
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700610// WritablePaths is a slice of WritablePaths, used for multiple outputs.
611type WritablePaths []WritablePath
612
613// Strings returns the string forms of the writable paths.
614func (p WritablePaths) Strings() []string {
615 if p == nil {
616 return nil
617 }
618 ret := make([]string, len(p))
619 for i, path := range p {
620 ret[i] = path.String()
621 }
622 return ret
623}
624
Colin Cross3bc7ffa2017-11-22 16:19:37 -0800625// Paths returns the WritablePaths as a Paths
626func (p WritablePaths) Paths() Paths {
627 if p == nil {
628 return nil
629 }
630 ret := make(Paths, len(p))
631 for i, path := range p {
632 ret[i] = path
633 }
634 return ret
635}
636
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700637type basePath struct {
638 path string
639 config Config
Colin Crossfaeb7aa2017-02-01 14:12:44 -0800640 rel string
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700641}
642
643func (p basePath) Ext() string {
644 return filepath.Ext(p.path)
645}
646
Colin Cross4f6fc9c2016-10-26 10:05:25 -0700647func (p basePath) Base() string {
648 return filepath.Base(p.path)
649}
650
Colin Crossfaeb7aa2017-02-01 14:12:44 -0800651func (p basePath) Rel() string {
652 if p.rel != "" {
653 return p.rel
654 }
655 return p.path
656}
657
Colin Cross0875c522017-11-28 17:34:01 -0800658func (p basePath) String() string {
659 return p.path
660}
661
Colin Cross0db55682017-12-05 15:36:55 -0800662func (p basePath) withRel(rel string) basePath {
663 p.path = filepath.Join(p.path, rel)
664 p.rel = rel
665 return p
666}
667
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700668// SourcePath is a Path representing a file path rooted from SrcDir
669type SourcePath struct {
670 basePath
671}
672
673var _ Path = SourcePath{}
674
Colin Cross0db55682017-12-05 15:36:55 -0800675func (p SourcePath) withRel(rel string) SourcePath {
676 p.basePath = p.basePath.withRel(rel)
677 return p
678}
679
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700680// safePathForSource is for paths that we expect are safe -- only for use by go
681// code that is embedding ninja variables in paths
Colin Crossfe4bc362018-09-12 10:02:13 -0700682func safePathForSource(ctx PathContext, pathComponents ...string) (SourcePath, error) {
683 p, err := validateSafePath(pathComponents...)
Colin Crossaabf6792017-11-29 00:27:14 -0800684 ret := SourcePath{basePath{p, ctx.Config(), ""}}
Colin Crossfe4bc362018-09-12 10:02:13 -0700685 if err != nil {
686 return ret, err
687 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700688
Colin Cross7b3dcc32019-01-24 13:14:39 -0800689 // absolute path already checked by validateSafePath
690 if strings.HasPrefix(ret.String(), ctx.Config().buildDir) {
Mikhail Naganovab1f5182019-02-08 13:17:55 -0800691 return ret, fmt.Errorf("source path %q is in output", ret.String())
Colin Cross6e18ca42015-07-14 18:55:36 -0700692 }
693
Colin Crossfe4bc362018-09-12 10:02:13 -0700694 return ret, err
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700695}
696
Colin Cross192e97a2018-02-22 14:21:02 -0800697// pathForSource creates a SourcePath from pathComponents, but does not check that it exists.
698func pathForSource(ctx PathContext, pathComponents ...string) (SourcePath, error) {
Colin Crossc48c1432018-02-23 07:09:01 +0000699 p, err := validatePath(pathComponents...)
700 ret := SourcePath{basePath{p, ctx.Config(), ""}}
Colin Cross94a32102018-02-22 14:21:02 -0800701 if err != nil {
Colin Cross192e97a2018-02-22 14:21:02 -0800702 return ret, err
Colin Cross94a32102018-02-22 14:21:02 -0800703 }
704
Colin Cross7b3dcc32019-01-24 13:14:39 -0800705 // absolute path already checked by validatePath
706 if strings.HasPrefix(ret.String(), ctx.Config().buildDir) {
Mikhail Naganovab1f5182019-02-08 13:17:55 -0800707 return ret, fmt.Errorf("source path %q is in output", ret.String())
Colin Crossc48c1432018-02-23 07:09:01 +0000708 }
709
Colin Cross192e97a2018-02-22 14:21:02 -0800710 return ret, nil
711}
712
713// existsWithDependencies returns true if the path exists, and adds appropriate dependencies to rerun if the
714// path does not exist.
715func existsWithDependencies(ctx PathContext, path SourcePath) (exists bool, err error) {
716 var files []string
717
718 if gctx, ok := ctx.(PathGlobContext); ok {
719 // Use glob to produce proper dependencies, even though we only want
720 // a single file.
721 files, err = gctx.GlobWithDeps(path.String(), nil)
722 } else {
723 var deps []string
724 // We cannot add build statements in this context, so we fall back to
725 // AddNinjaFileDeps
Colin Cross988414c2020-01-11 01:11:46 +0000726 files, deps, err = ctx.Config().fs.Glob(path.String(), nil, pathtools.FollowSymlinks)
Colin Cross192e97a2018-02-22 14:21:02 -0800727 ctx.AddNinjaFileDeps(deps...)
728 }
729
730 if err != nil {
731 return false, fmt.Errorf("glob: %s", err.Error())
732 }
733
734 return len(files) > 0, nil
735}
736
737// PathForSource joins the provided path components and validates that the result
738// neither escapes the source dir nor is in the out dir.
739// On error, it will return a usable, but invalid SourcePath, and report a ModuleError.
740func PathForSource(ctx PathContext, pathComponents ...string) SourcePath {
741 path, err := pathForSource(ctx, pathComponents...)
742 if err != nil {
743 reportPathError(ctx, err)
744 }
745
Colin Crosse3924e12018-08-15 20:18:53 -0700746 if pathtools.IsGlob(path.String()) {
747 reportPathErrorf(ctx, "path may not contain a glob: %s", path.String())
748 }
749
Colin Cross192e97a2018-02-22 14:21:02 -0800750 if modCtx, ok := ctx.(ModuleContext); ok && ctx.Config().AllowMissingDependencies() {
751 exists, err := existsWithDependencies(ctx, path)
752 if err != nil {
753 reportPathError(ctx, err)
754 }
755 if !exists {
756 modCtx.AddMissingDependencies([]string{path.String()})
757 }
Colin Cross988414c2020-01-11 01:11:46 +0000758 } else if exists, _, err := ctx.Config().fs.Exists(path.String()); err != nil {
Colin Cross192e97a2018-02-22 14:21:02 -0800759 reportPathErrorf(ctx, "%s: %s", path, err.Error())
760 } else if !exists {
Mikhail Naganovab1f5182019-02-08 13:17:55 -0800761 reportPathErrorf(ctx, "source path %q does not exist", path)
Colin Cross192e97a2018-02-22 14:21:02 -0800762 }
763 return path
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700764}
765
Jeff Gaston734e3802017-04-10 15:47:24 -0700766// ExistentPathForSource returns an OptionalPath with the SourcePath if the
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700767// path exists, or an empty OptionalPath if it doesn't exist. Dependencies are added
768// so that the ninja file will be regenerated if the state of the path changes.
Colin Cross32f38982018-02-22 11:47:25 -0800769func ExistentPathForSource(ctx PathContext, pathComponents ...string) OptionalPath {
Colin Cross192e97a2018-02-22 14:21:02 -0800770 path, err := pathForSource(ctx, pathComponents...)
Colin Cross1ccfcc32018-02-22 13:54:26 -0800771 if err != nil {
772 reportPathError(ctx, err)
773 return OptionalPath{}
774 }
Colin Crossc48c1432018-02-23 07:09:01 +0000775
Colin Crosse3924e12018-08-15 20:18:53 -0700776 if pathtools.IsGlob(path.String()) {
777 reportPathErrorf(ctx, "path may not contain a glob: %s", path.String())
778 return OptionalPath{}
779 }
780
Colin Cross192e97a2018-02-22 14:21:02 -0800781 exists, err := existsWithDependencies(ctx, path)
Colin Crossc48c1432018-02-23 07:09:01 +0000782 if err != nil {
783 reportPathError(ctx, err)
784 return OptionalPath{}
785 }
Colin Cross192e97a2018-02-22 14:21:02 -0800786 if !exists {
Colin Crossc48c1432018-02-23 07:09:01 +0000787 return OptionalPath{}
788 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700789 return OptionalPathForPath(path)
790}
791
792func (p SourcePath) String() string {
793 return filepath.Join(p.config.srcDir, p.path)
794}
795
796// Join creates a new SourcePath with paths... joined with the current path. The
797// provided paths... may not use '..' to escape from the current path.
798func (p SourcePath) Join(ctx PathContext, paths ...string) SourcePath {
Colin Cross1ccfcc32018-02-22 13:54:26 -0800799 path, err := validatePath(paths...)
800 if err != nil {
801 reportPathError(ctx, err)
802 }
Colin Cross0db55682017-12-05 15:36:55 -0800803 return p.withRel(path)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700804}
805
Colin Cross2fafa3e2019-03-05 12:39:51 -0800806// join is like Join but does less path validation.
807func (p SourcePath) join(ctx PathContext, paths ...string) SourcePath {
808 path, err := validateSafePath(paths...)
809 if err != nil {
810 reportPathError(ctx, err)
811 }
812 return p.withRel(path)
813}
814
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700815// OverlayPath returns the overlay for `path' if it exists. This assumes that the
816// SourcePath is the path to a resource overlay directory.
Colin Cross635c3b02016-05-18 15:37:25 -0700817func (p SourcePath) OverlayPath(ctx ModuleContext, path Path) OptionalPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700818 var relDir string
Colin Cross07e51612019-03-05 12:46:40 -0800819 if srcPath, ok := path.(SourcePath); ok {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700820 relDir = srcPath.path
821 } else {
Colin Cross1ccfcc32018-02-22 13:54:26 -0800822 reportPathErrorf(ctx, "Cannot find relative path for %s(%s)", reflect.TypeOf(path).Name(), path)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700823 return OptionalPath{}
824 }
825 dir := filepath.Join(p.config.srcDir, p.path, relDir)
826 // Use Glob so that we are run again if the directory is added.
Colin Cross7f19f372016-11-01 11:10:25 -0700827 if pathtools.IsGlob(dir) {
Colin Cross1ccfcc32018-02-22 13:54:26 -0800828 reportPathErrorf(ctx, "Path may not contain a glob: %s", dir)
Dan Willemsen7b310ee2015-12-18 15:11:17 -0800829 }
Colin Cross461b4452018-02-23 09:22:42 -0800830 paths, err := ctx.GlobWithDeps(dir, nil)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700831 if err != nil {
Colin Cross1ccfcc32018-02-22 13:54:26 -0800832 reportPathErrorf(ctx, "glob: %s", err.Error())
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700833 return OptionalPath{}
834 }
835 if len(paths) == 0 {
836 return OptionalPath{}
837 }
Colin Cross43f08db2018-11-12 10:13:39 -0800838 relPath := Rel(ctx, p.config.srcDir, paths[0])
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700839 return OptionalPathForPath(PathForSource(ctx, relPath))
840}
841
Colin Cross70dda7e2019-10-01 22:05:35 -0700842// OutputPath is a Path representing an intermediates file path rooted from the build directory
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700843type OutputPath struct {
844 basePath
845}
846
Colin Cross702e0f82017-10-18 17:27:54 -0700847func (p OutputPath) withRel(rel string) OutputPath {
Colin Cross0db55682017-12-05 15:36:55 -0800848 p.basePath = p.basePath.withRel(rel)
Colin Cross702e0f82017-10-18 17:27:54 -0700849 return p
850}
851
Colin Cross3063b782018-08-15 11:19:12 -0700852func (p OutputPath) WithoutRel() OutputPath {
853 p.basePath.rel = filepath.Base(p.basePath.path)
854 return p
855}
856
Paul Duffin9b478b02019-12-10 13:41:51 +0000857func (p OutputPath) buildDir() string {
858 return p.config.buildDir
859}
860
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700861var _ Path = OutputPath{}
Paul Duffin9b478b02019-12-10 13:41:51 +0000862var _ WritablePath = OutputPath{}
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700863
Jeff Gaston734e3802017-04-10 15:47:24 -0700864// PathForOutput joins the provided paths and returns an OutputPath that is
865// validated to not escape the build dir.
866// On error, it will return a usable, but invalid OutputPath, and report a ModuleError.
867func PathForOutput(ctx PathContext, pathComponents ...string) OutputPath {
Colin Cross1ccfcc32018-02-22 13:54:26 -0800868 path, err := validatePath(pathComponents...)
869 if err != nil {
870 reportPathError(ctx, err)
871 }
Colin Crossaabf6792017-11-29 00:27:14 -0800872 return OutputPath{basePath{path, ctx.Config(), ""}}
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700873}
874
Colin Cross40e33732019-02-15 11:08:35 -0800875// PathsForOutput returns Paths rooted from buildDir
876func PathsForOutput(ctx PathContext, paths []string) WritablePaths {
877 ret := make(WritablePaths, len(paths))
878 for i, path := range paths {
879 ret[i] = PathForOutput(ctx, path)
880 }
881 return ret
882}
883
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700884func (p OutputPath) writablePath() {}
885
886func (p OutputPath) String() string {
887 return filepath.Join(p.config.buildDir, p.path)
888}
889
890// Join creates a new OutputPath with paths... joined with the current path. The
891// provided paths... may not use '..' to escape from the current path.
892func (p OutputPath) Join(ctx PathContext, paths ...string) OutputPath {
Colin Cross1ccfcc32018-02-22 13:54:26 -0800893 path, err := validatePath(paths...)
894 if err != nil {
895 reportPathError(ctx, err)
896 }
Colin Cross0db55682017-12-05 15:36:55 -0800897 return p.withRel(path)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700898}
899
Colin Cross8854a5a2019-02-11 14:14:16 -0800900// ReplaceExtension creates a new OutputPath with the extension replaced with ext.
901func (p OutputPath) ReplaceExtension(ctx PathContext, ext string) OutputPath {
902 if strings.Contains(ext, "/") {
903 reportPathErrorf(ctx, "extension %q cannot contain /", ext)
904 }
905 ret := PathForOutput(ctx, pathtools.ReplaceExtension(p.path, ext))
Colin Cross2cdd5df2019-02-25 10:25:24 -0800906 ret.rel = pathtools.ReplaceExtension(p.rel, ext)
Colin Cross8854a5a2019-02-11 14:14:16 -0800907 return ret
908}
909
Colin Cross40e33732019-02-15 11:08:35 -0800910// InSameDir creates a new OutputPath from the directory of the current OutputPath joined with the elements in paths.
911func (p OutputPath) InSameDir(ctx PathContext, paths ...string) OutputPath {
912 path, err := validatePath(paths...)
913 if err != nil {
914 reportPathError(ctx, err)
915 }
916
917 ret := PathForOutput(ctx, filepath.Dir(p.path), path)
Colin Cross2cdd5df2019-02-25 10:25:24 -0800918 ret.rel = filepath.Join(filepath.Dir(p.rel), path)
Colin Cross40e33732019-02-15 11:08:35 -0800919 return ret
920}
921
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700922// PathForIntermediates returns an OutputPath representing the top-level
923// intermediates directory.
924func PathForIntermediates(ctx PathContext, paths ...string) OutputPath {
Colin Cross1ccfcc32018-02-22 13:54:26 -0800925 path, err := validatePath(paths...)
926 if err != nil {
927 reportPathError(ctx, err)
928 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700929 return PathForOutput(ctx, ".intermediates", path)
930}
931
Colin Cross07e51612019-03-05 12:46:40 -0800932var _ genPathProvider = SourcePath{}
933var _ objPathProvider = SourcePath{}
934var _ resPathProvider = SourcePath{}
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700935
Colin Cross07e51612019-03-05 12:46:40 -0800936// PathForModuleSrc returns a Path representing the paths... under the
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700937// module's local source directory.
Colin Cross8a497952019-03-05 22:25:09 -0800938func PathForModuleSrc(ctx ModuleContext, pathComponents ...string) Path {
939 p, err := validatePath(pathComponents...)
940 if err != nil {
941 reportPathError(ctx, err)
Colin Cross192e97a2018-02-22 14:21:02 -0800942 }
Colin Cross8a497952019-03-05 22:25:09 -0800943 paths, err := expandOneSrcPath(ctx, p, nil)
944 if err != nil {
945 if depErr, ok := err.(missingDependencyError); ok {
946 if ctx.Config().AllowMissingDependencies() {
947 ctx.AddMissingDependencies(depErr.missingDeps)
948 } else {
949 ctx.ModuleErrorf(`%s, is the property annotated with android:"path"?`, depErr.Error())
950 }
951 } else {
952 reportPathError(ctx, err)
953 }
954 return nil
955 } else if len(paths) == 0 {
956 reportPathErrorf(ctx, "%q produced no files, expected exactly one", p)
957 return nil
958 } else if len(paths) > 1 {
959 reportPathErrorf(ctx, "%q produced %d files, expected exactly one", p, len(paths))
960 }
961 return paths[0]
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700962}
963
Colin Cross07e51612019-03-05 12:46:40 -0800964func pathForModuleSrc(ctx ModuleContext, paths ...string) SourcePath {
965 p, err := validatePath(paths...)
966 if err != nil {
967 reportPathError(ctx, err)
968 }
969
970 path, err := pathForSource(ctx, ctx.ModuleDir(), p)
971 if err != nil {
972 reportPathError(ctx, err)
973 }
974
975 path.basePath.rel = p
976
977 return path
978}
979
Colin Cross2fafa3e2019-03-05 12:39:51 -0800980// PathsWithModuleSrcSubDir takes a list of Paths and returns a new list of Paths where Rel() on each path
981// will return the path relative to subDir in the module's source directory. If any input paths are not located
982// inside subDir then a path error will be reported.
983func PathsWithModuleSrcSubDir(ctx ModuleContext, paths Paths, subDir string) Paths {
984 paths = append(Paths(nil), paths...)
Colin Cross07e51612019-03-05 12:46:40 -0800985 subDirFullPath := pathForModuleSrc(ctx, subDir)
Colin Cross2fafa3e2019-03-05 12:39:51 -0800986 for i, path := range paths {
987 rel := Rel(ctx, subDirFullPath.String(), path.String())
988 paths[i] = subDirFullPath.join(ctx, rel)
989 }
990 return paths
991}
992
993// PathWithModuleSrcSubDir takes a Path and returns a Path where Rel() will return the path relative to subDir in the
994// module's source directory. If the input path is not located inside subDir then a path error will be reported.
995func PathWithModuleSrcSubDir(ctx ModuleContext, path Path, subDir string) Path {
Colin Cross07e51612019-03-05 12:46:40 -0800996 subDirFullPath := pathForModuleSrc(ctx, subDir)
Colin Cross2fafa3e2019-03-05 12:39:51 -0800997 rel := Rel(ctx, subDirFullPath.String(), path.String())
998 return subDirFullPath.Join(ctx, rel)
999}
1000
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001001// OptionalPathForModuleSrc returns an OptionalPath. The OptionalPath contains a
1002// valid path if p is non-nil.
Colin Cross635c3b02016-05-18 15:37:25 -07001003func OptionalPathForModuleSrc(ctx ModuleContext, p *string) OptionalPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001004 if p == nil {
1005 return OptionalPath{}
1006 }
1007 return OptionalPathForPath(PathForModuleSrc(ctx, *p))
1008}
1009
Colin Cross07e51612019-03-05 12:46:40 -08001010func (p SourcePath) genPathWithExt(ctx ModuleContext, subdir, ext string) ModuleGenPath {
Colin Cross7fc17db2017-02-01 14:07:55 -08001011 return PathForModuleGen(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001012}
1013
Colin Cross07e51612019-03-05 12:46:40 -08001014func (p SourcePath) objPathWithExt(ctx ModuleContext, subdir, ext string) ModuleObjPath {
Colin Cross7fc17db2017-02-01 14:07:55 -08001015 return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001016}
1017
Colin Cross07e51612019-03-05 12:46:40 -08001018func (p SourcePath) resPathWithName(ctx ModuleContext, name string) ModuleResPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001019 // TODO: Use full directory if the new ctx is not the current ctx?
1020 return PathForModuleRes(ctx, p.path, name)
1021}
1022
1023// ModuleOutPath is a Path representing a module's output directory.
1024type ModuleOutPath struct {
1025 OutputPath
1026}
1027
1028var _ Path = ModuleOutPath{}
1029
Pete Bentleyfcf55bf2019-08-16 20:14:32 +01001030func (p ModuleOutPath) objPathWithExt(ctx ModuleContext, subdir, ext string) ModuleObjPath {
1031 return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
1032}
1033
Colin Cross702e0f82017-10-18 17:27:54 -07001034func pathForModule(ctx ModuleContext) OutputPath {
1035 return PathForOutput(ctx, ".intermediates", ctx.ModuleDir(), ctx.ModuleName(), ctx.ModuleSubDir())
1036}
1037
Logan Chien7eefdc42018-07-11 18:10:41 +08001038// PathForVndkRefAbiDump returns an OptionalPath representing the path of the
1039// reference abi dump for the given module. This is not guaranteed to be valid.
1040func PathForVndkRefAbiDump(ctx ModuleContext, version, fileName string,
Hsin-Yi Chen53489642019-07-31 17:10:45 +08001041 isNdk, isLlndkOrVndk, isGzip bool) OptionalPath {
Logan Chien7eefdc42018-07-11 18:10:41 +08001042
Jayant Chowdharyac066c62018-02-20 10:53:31 -08001043 arches := ctx.DeviceConfig().Arches()
Logan Chien7eefdc42018-07-11 18:10:41 +08001044 if len(arches) == 0 {
1045 panic("device build with no primary arch")
1046 }
Jayant Chowdharyac066c62018-02-20 10:53:31 -08001047 currentArch := ctx.Arch()
1048 archNameAndVariant := currentArch.ArchType.String()
1049 if currentArch.ArchVariant != "" {
1050 archNameAndVariant += "_" + currentArch.ArchVariant
1051 }
Logan Chien5237bed2018-07-11 17:15:57 +08001052
1053 var dirName string
Hsin-Yi Chen53489642019-07-31 17:10:45 +08001054 if isNdk {
Logan Chien5237bed2018-07-11 17:15:57 +08001055 dirName = "ndk"
Hsin-Yi Chen53489642019-07-31 17:10:45 +08001056 } else if isLlndkOrVndk {
Logan Chien5237bed2018-07-11 17:15:57 +08001057 dirName = "vndk"
Logan Chien41eabe62019-04-10 13:33:58 +08001058 } else {
1059 dirName = "platform" // opt-in libs
Jayant Chowdhary3e231fd2017-02-08 13:45:53 -08001060 }
Logan Chien5237bed2018-07-11 17:15:57 +08001061
Jayant Chowdhary34ce67d2018-03-08 11:00:50 -08001062 binderBitness := ctx.DeviceConfig().BinderBitness()
Logan Chien7eefdc42018-07-11 18:10:41 +08001063
1064 var ext string
1065 if isGzip {
1066 ext = ".lsdump.gz"
1067 } else {
1068 ext = ".lsdump"
1069 }
1070
1071 return ExistentPathForSource(ctx, "prebuilts", "abi-dumps", dirName,
1072 version, binderBitness, archNameAndVariant, "source-based",
1073 fileName+ext)
Jayant Chowdhary3e231fd2017-02-08 13:45:53 -08001074}
1075
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001076// PathForModuleOut returns a Path representing the paths... under the module's
1077// output directory.
Colin Cross635c3b02016-05-18 15:37:25 -07001078func PathForModuleOut(ctx ModuleContext, paths ...string) ModuleOutPath {
Colin Cross1ccfcc32018-02-22 13:54:26 -08001079 p, err := validatePath(paths...)
1080 if err != nil {
1081 reportPathError(ctx, err)
1082 }
Colin Cross702e0f82017-10-18 17:27:54 -07001083 return ModuleOutPath{
1084 OutputPath: pathForModule(ctx).withRel(p),
1085 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001086}
1087
1088// ModuleGenPath is a Path representing the 'gen' directory in a module's output
1089// directory. Mainly used for generated sources.
1090type ModuleGenPath struct {
1091 ModuleOutPath
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001092}
1093
1094var _ Path = ModuleGenPath{}
1095var _ genPathProvider = ModuleGenPath{}
1096var _ objPathProvider = ModuleGenPath{}
1097
1098// PathForModuleGen returns a Path representing the paths... under the module's
1099// `gen' directory.
Colin Cross635c3b02016-05-18 15:37:25 -07001100func PathForModuleGen(ctx ModuleContext, paths ...string) ModuleGenPath {
Colin Cross1ccfcc32018-02-22 13:54:26 -08001101 p, err := validatePath(paths...)
1102 if err != nil {
1103 reportPathError(ctx, err)
1104 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001105 return ModuleGenPath{
Colin Cross702e0f82017-10-18 17:27:54 -07001106 ModuleOutPath: ModuleOutPath{
1107 OutputPath: pathForModule(ctx).withRel("gen").withRel(p),
1108 },
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001109 }
1110}
1111
Dan Willemsen21ec4902016-11-02 20:43:13 -07001112func (p ModuleGenPath) genPathWithExt(ctx ModuleContext, subdir, ext string) ModuleGenPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001113 // TODO: make a different path for local vs remote generated files?
Dan Willemsen21ec4902016-11-02 20:43:13 -07001114 return PathForModuleGen(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001115}
1116
Colin Cross635c3b02016-05-18 15:37:25 -07001117func (p ModuleGenPath) objPathWithExt(ctx ModuleContext, subdir, ext string) ModuleObjPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001118 return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
1119}
1120
1121// ModuleObjPath is a Path representing the 'obj' directory in a module's output
1122// directory. Used for compiled objects.
1123type ModuleObjPath struct {
1124 ModuleOutPath
1125}
1126
1127var _ Path = ModuleObjPath{}
1128
1129// PathForModuleObj returns a Path representing the paths... under the module's
1130// 'obj' directory.
Jeff Gaston734e3802017-04-10 15:47:24 -07001131func PathForModuleObj(ctx ModuleContext, pathComponents ...string) ModuleObjPath {
Colin Cross1ccfcc32018-02-22 13:54:26 -08001132 p, err := validatePath(pathComponents...)
1133 if err != nil {
1134 reportPathError(ctx, err)
1135 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001136 return ModuleObjPath{PathForModuleOut(ctx, "obj", p)}
1137}
1138
1139// ModuleResPath is a a Path representing the 'res' directory in a module's
1140// output directory.
1141type ModuleResPath struct {
1142 ModuleOutPath
1143}
1144
1145var _ Path = ModuleResPath{}
1146
1147// PathForModuleRes returns a Path representing the paths... under the module's
1148// 'res' directory.
Jeff Gaston734e3802017-04-10 15:47:24 -07001149func PathForModuleRes(ctx ModuleContext, pathComponents ...string) ModuleResPath {
Colin Cross1ccfcc32018-02-22 13:54:26 -08001150 p, err := validatePath(pathComponents...)
1151 if err != nil {
1152 reportPathError(ctx, err)
1153 }
1154
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001155 return ModuleResPath{PathForModuleOut(ctx, "res", p)}
1156}
1157
Colin Cross70dda7e2019-10-01 22:05:35 -07001158// InstallPath is a Path representing a installed file path rooted from the build directory
1159type InstallPath struct {
1160 basePath
Colin Crossff6c33d2019-10-02 16:01:35 -07001161
1162 baseDir string // "../" for Make paths to convert "out/soong" to "out", "" for Soong paths
Colin Cross70dda7e2019-10-01 22:05:35 -07001163}
1164
Paul Duffin9b478b02019-12-10 13:41:51 +00001165func (p InstallPath) buildDir() string {
1166 return p.config.buildDir
1167}
1168
1169var _ Path = InstallPath{}
1170var _ WritablePath = InstallPath{}
1171
Colin Cross70dda7e2019-10-01 22:05:35 -07001172func (p InstallPath) writablePath() {}
1173
1174func (p InstallPath) String() string {
Colin Crossff6c33d2019-10-02 16:01:35 -07001175 return filepath.Join(p.config.buildDir, p.baseDir, p.path)
Colin Cross70dda7e2019-10-01 22:05:35 -07001176}
1177
1178// Join creates a new InstallPath with paths... joined with the current path. The
1179// provided paths... may not use '..' to escape from the current path.
1180func (p InstallPath) Join(ctx PathContext, paths ...string) InstallPath {
1181 path, err := validatePath(paths...)
1182 if err != nil {
1183 reportPathError(ctx, err)
1184 }
1185 return p.withRel(path)
1186}
1187
1188func (p InstallPath) withRel(rel string) InstallPath {
1189 p.basePath = p.basePath.withRel(rel)
1190 return p
1191}
1192
Colin Crossff6c33d2019-10-02 16:01:35 -07001193// ToMakePath returns a new InstallPath that points to Make's install directory instead of Soong's,
1194// i.e. out/ instead of out/soong/.
1195func (p InstallPath) ToMakePath() InstallPath {
1196 p.baseDir = "../"
1197 return p
Colin Cross70dda7e2019-10-01 22:05:35 -07001198}
1199
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001200// PathForModuleInstall returns a Path representing the install path for the
1201// module appended with paths...
Colin Cross70dda7e2019-10-01 22:05:35 -07001202func PathForModuleInstall(ctx ModuleInstallPathContext, pathComponents ...string) InstallPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001203 var outPaths []string
1204 if ctx.Device() {
Colin Cross43f08db2018-11-12 10:13:39 -08001205 partition := modulePartition(ctx)
Colin Cross6510f912017-11-29 00:27:14 -08001206 outPaths = []string{"target", "product", ctx.Config().DeviceName(), partition}
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001207 } else {
Dan Willemsen866b5632017-09-22 12:28:24 -07001208 switch ctx.Os() {
1209 case Linux:
1210 outPaths = []string{"host", "linux-x86"}
1211 case LinuxBionic:
1212 // TODO: should this be a separate top level, or shared with linux-x86?
1213 outPaths = []string{"host", "linux_bionic-x86"}
1214 default:
1215 outPaths = []string{"host", ctx.Os().String() + "-x86"}
1216 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001217 }
Dan Willemsen782a2d12015-12-21 14:55:28 -08001218 if ctx.Debug() {
1219 outPaths = append([]string{"debug"}, outPaths...)
1220 }
Jeff Gaston734e3802017-04-10 15:47:24 -07001221 outPaths = append(outPaths, pathComponents...)
Colin Cross70dda7e2019-10-01 22:05:35 -07001222
1223 path, err := validatePath(outPaths...)
1224 if err != nil {
1225 reportPathError(ctx, err)
1226 }
Colin Crossff6c33d2019-10-02 16:01:35 -07001227
1228 ret := InstallPath{basePath{path, ctx.Config(), ""}, ""}
1229 if ctx.InstallBypassMake() && ctx.Config().EmbeddedInMake() {
1230 ret = ret.ToMakePath()
1231 }
1232
1233 return ret
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001234}
1235
Colin Cross70dda7e2019-10-01 22:05:35 -07001236func PathForNdkInstall(ctx PathContext, paths ...string) InstallPath {
1237 paths = append([]string{"ndk"}, paths...)
1238 path, err := validatePath(paths...)
1239 if err != nil {
1240 reportPathError(ctx, err)
1241 }
Colin Crossff6c33d2019-10-02 16:01:35 -07001242 return InstallPath{basePath{path, ctx.Config(), ""}, ""}
Colin Cross70dda7e2019-10-01 22:05:35 -07001243}
1244
1245func InstallPathToOnDevicePath(ctx PathContext, path InstallPath) string {
Colin Cross43f08db2018-11-12 10:13:39 -08001246 rel := Rel(ctx, PathForOutput(ctx, "target", "product", ctx.Config().DeviceName()).String(), path.String())
1247
1248 return "/" + rel
1249}
1250
1251func modulePartition(ctx ModuleInstallPathContext) string {
1252 var partition string
1253 if ctx.InstallInData() {
1254 partition = "data"
Jaewoong Jung0949f312019-09-11 10:25:18 -07001255 } else if ctx.InstallInTestcases() {
1256 partition = "testcases"
Colin Cross43f08db2018-11-12 10:13:39 -08001257 } else if ctx.InstallInRecovery() {
Colin Cross90ba5f42019-10-02 11:10:58 -07001258 if ctx.InstallInRoot() {
1259 partition = "recovery/root"
1260 } else {
1261 // the layout of recovery partion is the same as that of system partition
1262 partition = "recovery/root/system"
1263 }
Colin Cross43f08db2018-11-12 10:13:39 -08001264 } else if ctx.SocSpecific() {
1265 partition = ctx.DeviceConfig().VendorPath()
1266 } else if ctx.DeviceSpecific() {
1267 partition = ctx.DeviceConfig().OdmPath()
1268 } else if ctx.ProductSpecific() {
1269 partition = ctx.DeviceConfig().ProductPath()
Justin Yund5f6c822019-06-25 16:47:17 +09001270 } else if ctx.SystemExtSpecific() {
1271 partition = ctx.DeviceConfig().SystemExtPath()
Colin Cross90ba5f42019-10-02 11:10:58 -07001272 } else if ctx.InstallInRoot() {
1273 partition = "root"
Colin Cross43f08db2018-11-12 10:13:39 -08001274 } else {
1275 partition = "system"
1276 }
1277 if ctx.InstallInSanitizerDir() {
1278 partition = "data/asan/" + partition
1279 }
1280 return partition
1281}
1282
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001283// validateSafePath validates a path that we trust (may contain ninja variables).
Dan Willemsen80a7c2a2015-12-21 14:57:11 -08001284// Ensures that each path component does not attempt to leave its component.
Colin Cross1ccfcc32018-02-22 13:54:26 -08001285func validateSafePath(pathComponents ...string) (string, error) {
Jeff Gaston734e3802017-04-10 15:47:24 -07001286 for _, path := range pathComponents {
Dan Willemsen80a7c2a2015-12-21 14:57:11 -08001287 path := filepath.Clean(path)
1288 if path == ".." || strings.HasPrefix(path, "../") || strings.HasPrefix(path, "/") {
Colin Cross1ccfcc32018-02-22 13:54:26 -08001289 return "", fmt.Errorf("Path is outside directory: %s", path)
Dan Willemsen80a7c2a2015-12-21 14:57:11 -08001290 }
1291 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001292 // TODO: filepath.Join isn't necessarily correct with embedded ninja
1293 // variables. '..' may remove the entire ninja variable, even if it
1294 // will be expanded to multiple nested directories.
Colin Cross1ccfcc32018-02-22 13:54:26 -08001295 return filepath.Join(pathComponents...), nil
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001296}
1297
Dan Willemsen80a7c2a2015-12-21 14:57:11 -08001298// validatePath validates that a path does not include ninja variables, and that
1299// each path component does not attempt to leave its component. Returns a joined
1300// version of each path component.
Colin Cross1ccfcc32018-02-22 13:54:26 -08001301func validatePath(pathComponents ...string) (string, error) {
Jeff Gaston734e3802017-04-10 15:47:24 -07001302 for _, path := range pathComponents {
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001303 if strings.Contains(path, "$") {
Colin Cross1ccfcc32018-02-22 13:54:26 -08001304 return "", fmt.Errorf("Path contains invalid character($): %s", path)
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001305 }
1306 }
Colin Cross1ccfcc32018-02-22 13:54:26 -08001307 return validateSafePath(pathComponents...)
Colin Cross6e18ca42015-07-14 18:55:36 -07001308}
Colin Cross5b529592017-05-09 13:34:34 -07001309
Colin Cross0875c522017-11-28 17:34:01 -08001310func PathForPhony(ctx PathContext, phony string) WritablePath {
1311 if strings.ContainsAny(phony, "$/") {
Colin Cross1ccfcc32018-02-22 13:54:26 -08001312 reportPathErrorf(ctx, "Phony target contains invalid character ($ or /): %s", phony)
Colin Cross0875c522017-11-28 17:34:01 -08001313 }
Colin Cross74e3fe42017-12-11 15:51:44 -08001314 return PhonyPath{basePath{phony, ctx.Config(), ""}}
Colin Cross0875c522017-11-28 17:34:01 -08001315}
1316
Colin Cross74e3fe42017-12-11 15:51:44 -08001317type PhonyPath struct {
1318 basePath
1319}
1320
1321func (p PhonyPath) writablePath() {}
1322
Paul Duffin9b478b02019-12-10 13:41:51 +00001323func (p PhonyPath) buildDir() string {
1324 return p.config.buildDir
1325}
1326
Colin Cross74e3fe42017-12-11 15:51:44 -08001327var _ Path = PhonyPath{}
1328var _ WritablePath = PhonyPath{}
1329
Colin Cross5b529592017-05-09 13:34:34 -07001330type testPath struct {
1331 basePath
1332}
1333
1334func (p testPath) String() string {
1335 return p.path
1336}
1337
Colin Cross40e33732019-02-15 11:08:35 -08001338// PathForTesting returns a Path constructed from joining the elements of paths with '/'. It should only be used from
1339// within tests.
Colin Cross5b529592017-05-09 13:34:34 -07001340func PathForTesting(paths ...string) Path {
Colin Cross1ccfcc32018-02-22 13:54:26 -08001341 p, err := validateSafePath(paths...)
1342 if err != nil {
1343 panic(err)
1344 }
Colin Cross5b529592017-05-09 13:34:34 -07001345 return testPath{basePath{path: p, rel: p}}
1346}
1347
Colin Cross40e33732019-02-15 11:08:35 -08001348// PathsForTesting returns a Path constructed from each element in strs. It should only be used from within tests.
1349func PathsForTesting(strs ...string) Paths {
Colin Cross5b529592017-05-09 13:34:34 -07001350 p := make(Paths, len(strs))
1351 for i, s := range strs {
1352 p[i] = PathForTesting(s)
1353 }
1354
1355 return p
1356}
Colin Cross43f08db2018-11-12 10:13:39 -08001357
Colin Cross40e33732019-02-15 11:08:35 -08001358type testPathContext struct {
1359 config Config
Colin Cross40e33732019-02-15 11:08:35 -08001360}
1361
Colin Cross40e33732019-02-15 11:08:35 -08001362func (x *testPathContext) Config() Config { return x.config }
1363func (x *testPathContext) AddNinjaFileDeps(...string) {}
1364
1365// PathContextForTesting returns a PathContext that can be used in tests, for example to create an OutputPath with
1366// PathForOutput.
Colin Cross98be1bb2019-12-13 20:41:13 -08001367func PathContextForTesting(config Config) PathContext {
Colin Cross40e33732019-02-15 11:08:35 -08001368 return &testPathContext{
1369 config: config,
Colin Cross40e33732019-02-15 11:08:35 -08001370 }
1371}
1372
Colin Cross43f08db2018-11-12 10:13:39 -08001373// Rel performs the same function as filepath.Rel, but reports errors to a PathContext, and reports an error if
1374// targetPath is not inside basePath.
1375func Rel(ctx PathContext, basePath string, targetPath string) string {
1376 rel, isRel := MaybeRel(ctx, basePath, targetPath)
1377 if !isRel {
1378 reportPathErrorf(ctx, "path %q is not under path %q", targetPath, basePath)
1379 return ""
1380 }
1381 return rel
1382}
1383
1384// MaybeRel performs the same function as filepath.Rel, but reports errors to a PathContext, and returns false if
1385// targetPath is not inside basePath.
1386func MaybeRel(ctx PathContext, basePath string, targetPath string) (string, bool) {
Dan Willemsen633c5022019-04-12 11:11:38 -07001387 rel, isRel, err := maybeRelErr(basePath, targetPath)
1388 if err != nil {
1389 reportPathError(ctx, err)
1390 }
1391 return rel, isRel
1392}
1393
1394func maybeRelErr(basePath string, targetPath string) (string, bool, error) {
Colin Cross43f08db2018-11-12 10:13:39 -08001395 // filepath.Rel returns an error if one path is absolute and the other is not, handle that case first.
1396 if filepath.IsAbs(basePath) != filepath.IsAbs(targetPath) {
Dan Willemsen633c5022019-04-12 11:11:38 -07001397 return "", false, nil
Colin Cross43f08db2018-11-12 10:13:39 -08001398 }
1399 rel, err := filepath.Rel(basePath, targetPath)
1400 if err != nil {
Dan Willemsen633c5022019-04-12 11:11:38 -07001401 return "", false, err
Colin Cross43f08db2018-11-12 10:13:39 -08001402 } else if rel == ".." || strings.HasPrefix(rel, "../") || strings.HasPrefix(rel, "/") {
Dan Willemsen633c5022019-04-12 11:11:38 -07001403 return "", false, nil
Colin Cross43f08db2018-11-12 10:13:39 -08001404 }
Dan Willemsen633c5022019-04-12 11:11:38 -07001405 return rel, true, nil
Colin Cross43f08db2018-11-12 10:13:39 -08001406}
Colin Cross988414c2020-01-11 01:11:46 +00001407
1408// Writes a file to the output directory. Attempting to write directly to the output directory
1409// will fail due to the sandbox of the soong_build process.
1410func WriteFileToOutputDir(path WritablePath, data []byte, perm os.FileMode) error {
1411 return ioutil.WriteFile(absolutePath(path.String()), data, perm)
1412}
1413
1414func absolutePath(path string) string {
1415 if filepath.IsAbs(path) {
1416 return path
1417 }
1418 return filepath.Join(absSrcDir, path)
1419}