blob: b13979dae2473f7702698f5152810b6240028278 [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
Ulya Trafimovich8640ab92020-05-11 18:06:15 +010046// "Null" path context is a minimal path context for a given config.
47type NullPathContext struct {
48 config Config
49}
50
51func (NullPathContext) AddNinjaFileDeps(...string) {}
52func (ctx NullPathContext) Config() Config { return ctx.config }
53
Dan Willemsen00269f22017-07-06 16:59:48 -070054type ModuleInstallPathContext interface {
Colin Cross0ea8ba82019-06-06 14:33:29 -070055 BaseModuleContext
Dan Willemsen00269f22017-07-06 16:59:48 -070056
57 InstallInData() bool
Jaewoong Jung0949f312019-09-11 10:25:18 -070058 InstallInTestcases() bool
Dan Willemsen00269f22017-07-06 16:59:48 -070059 InstallInSanitizerDir() bool
Yifan Hong1b3348d2020-01-21 15:53:22 -080060 InstallInRamdisk() bool
Yifan Hong60e0cfb2020-10-21 15:17:56 -070061 InstallInVendorRamdisk() bool
Jiyong Parkf9332f12018-02-01 00:54:12 +090062 InstallInRecovery() bool
Colin Cross90ba5f42019-10-02 11:10:58 -070063 InstallInRoot() bool
Colin Cross607d8582019-07-29 16:44:46 -070064 InstallBypassMake() bool
Jiyong Park87788b52020-09-01 12:37:45 +090065 InstallForceOS() (*OsType, *ArchType)
Dan Willemsen00269f22017-07-06 16:59:48 -070066}
67
68var _ ModuleInstallPathContext = ModuleContext(nil)
69
Dan Willemsen34cc69e2015-09-23 15:26:20 -070070// errorfContext is the interface containing the Errorf method matching the
71// Errorf method in blueprint.SingletonContext.
72type errorfContext interface {
73 Errorf(format string, args ...interface{})
Colin Cross3f40fa42015-01-30 17:27:36 -080074}
75
Dan Willemsen34cc69e2015-09-23 15:26:20 -070076var _ errorfContext = blueprint.SingletonContext(nil)
77
78// moduleErrorf is the interface containing the ModuleErrorf method matching
79// the ModuleErrorf method in blueprint.ModuleContext.
80type moduleErrorf interface {
81 ModuleErrorf(format string, args ...interface{})
Colin Cross3f40fa42015-01-30 17:27:36 -080082}
83
Dan Willemsen34cc69e2015-09-23 15:26:20 -070084var _ moduleErrorf = blueprint.ModuleContext(nil)
85
Dan Willemsen34cc69e2015-09-23 15:26:20 -070086// reportPathError will register an error with the attached context. It
87// attempts ctx.ModuleErrorf for a better error message first, then falls
88// back to ctx.Errorf.
Colin Cross1ccfcc32018-02-22 13:54:26 -080089func reportPathError(ctx PathContext, err error) {
Ulya Trafimovich5ab276a2020-08-25 12:45:15 +010090 ReportPathErrorf(ctx, "%s", err.Error())
Colin Cross1ccfcc32018-02-22 13:54:26 -080091}
92
Ulya Trafimovich5ab276a2020-08-25 12:45:15 +010093// ReportPathErrorf will register an error with the attached context. It
Colin Cross1ccfcc32018-02-22 13:54:26 -080094// attempts ctx.ModuleErrorf for a better error message first, then falls
95// back to ctx.Errorf.
Ulya Trafimovich5ab276a2020-08-25 12:45:15 +010096func ReportPathErrorf(ctx PathContext, format string, args ...interface{}) {
Dan Willemsen34cc69e2015-09-23 15:26:20 -070097 if mctx, ok := ctx.(moduleErrorf); ok {
98 mctx.ModuleErrorf(format, args...)
99 } else if ectx, ok := ctx.(errorfContext); ok {
100 ectx.Errorf(format, args...)
101 } else {
102 panic(fmt.Sprintf(format, args...))
Colin Crossf2298272015-05-12 11:36:53 -0700103 }
104}
105
Colin Cross5e708052019-08-06 13:59:50 -0700106func pathContextName(ctx PathContext, module blueprint.Module) string {
107 if x, ok := ctx.(interface{ ModuleName(blueprint.Module) string }); ok {
108 return x.ModuleName(module)
109 } else if x, ok := ctx.(interface{ OtherModuleName(blueprint.Module) string }); ok {
110 return x.OtherModuleName(module)
111 }
112 return "unknown"
113}
114
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700115type Path interface {
116 // Returns the path in string form
117 String() string
118
Colin Cross4f6fc9c2016-10-26 10:05:25 -0700119 // Ext returns the extension of the last element of the path
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700120 Ext() string
Colin Cross4f6fc9c2016-10-26 10:05:25 -0700121
122 // Base returns the last element of the path
123 Base() string
Colin Crossfaeb7aa2017-02-01 14:12:44 -0800124
125 // Rel returns the portion of the path relative to the directory it was created from. For
126 // example, Rel on a PathsForModuleSrc would return the path relative to the module source
Colin Cross0db55682017-12-05 15:36:55 -0800127 // directory, and OutputPath.Join("foo").Rel() would return "foo".
Colin Crossfaeb7aa2017-02-01 14:12:44 -0800128 Rel() string
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700129}
130
131// WritablePath is a type of path that can be used as an output for build rules.
132type WritablePath interface {
133 Path
134
Paul Duffin9b478b02019-12-10 13:41:51 +0000135 // return the path to the build directory.
136 buildDir() string
137
Jeff Gaston734e3802017-04-10 15:47:24 -0700138 // the writablePath method doesn't directly do anything,
139 // but it allows a struct to distinguish between whether or not it implements the WritablePath interface
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700140 writablePath()
141}
142
143type genPathProvider interface {
Dan Willemsen21ec4902016-11-02 20:43:13 -0700144 genPathWithExt(ctx ModuleContext, subdir, ext string) ModuleGenPath
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700145}
146type objPathProvider interface {
Colin Cross635c3b02016-05-18 15:37:25 -0700147 objPathWithExt(ctx ModuleContext, subdir, ext string) ModuleObjPath
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700148}
149type resPathProvider interface {
Colin Cross635c3b02016-05-18 15:37:25 -0700150 resPathWithName(ctx ModuleContext, name string) ModuleResPath
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700151}
152
153// GenPathWithExt derives a new file path in ctx's generated sources directory
154// from the current path, but with the new extension.
Dan Willemsen21ec4902016-11-02 20:43:13 -0700155func GenPathWithExt(ctx ModuleContext, subdir string, p Path, ext string) ModuleGenPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700156 if path, ok := p.(genPathProvider); ok {
Dan Willemsen21ec4902016-11-02 20:43:13 -0700157 return path.genPathWithExt(ctx, subdir, ext)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700158 }
Ulya Trafimovich5ab276a2020-08-25 12:45:15 +0100159 ReportPathErrorf(ctx, "Tried to create generated file from unsupported path: %s(%s)", reflect.TypeOf(p).Name(), p)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700160 return PathForModuleGen(ctx)
161}
162
163// ObjPathWithExt derives a new file path in ctx's object directory from the
164// current path, but with the new extension.
Dan Willemsen21ec4902016-11-02 20:43:13 -0700165func ObjPathWithExt(ctx ModuleContext, subdir string, p Path, ext string) ModuleObjPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700166 if path, ok := p.(objPathProvider); ok {
167 return path.objPathWithExt(ctx, subdir, ext)
168 }
Ulya Trafimovich5ab276a2020-08-25 12:45:15 +0100169 ReportPathErrorf(ctx, "Tried to create object file from unsupported path: %s (%s)", reflect.TypeOf(p).Name(), p)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700170 return PathForModuleObj(ctx)
171}
172
173// ResPathWithName derives a new path in ctx's output resource directory, using
174// the current path to create the directory name, and the `name` argument for
175// the filename.
Colin Cross635c3b02016-05-18 15:37:25 -0700176func ResPathWithName(ctx ModuleContext, p Path, name string) ModuleResPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700177 if path, ok := p.(resPathProvider); ok {
178 return path.resPathWithName(ctx, name)
179 }
Ulya Trafimovich5ab276a2020-08-25 12:45:15 +0100180 ReportPathErrorf(ctx, "Tried to create res file from unsupported path: %s (%s)", reflect.TypeOf(p).Name(), p)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700181 return PathForModuleRes(ctx)
182}
183
184// OptionalPath is a container that may or may not contain a valid Path.
185type OptionalPath struct {
186 valid bool
187 path Path
188}
189
190// OptionalPathForPath returns an OptionalPath containing the path.
191func OptionalPathForPath(path Path) OptionalPath {
192 if path == nil {
193 return OptionalPath{}
194 }
195 return OptionalPath{valid: true, path: path}
196}
197
198// Valid returns whether there is a valid path
199func (p OptionalPath) Valid() bool {
200 return p.valid
201}
202
203// Path returns the Path embedded in this OptionalPath. You must be sure that
204// there is a valid path, since this method will panic if there is not.
205func (p OptionalPath) Path() Path {
206 if !p.valid {
207 panic("Requesting an invalid path")
208 }
209 return p.path
210}
211
212// String returns the string version of the Path, or "" if it isn't valid.
213func (p OptionalPath) String() string {
214 if p.valid {
215 return p.path.String()
216 } else {
217 return ""
Colin Crossf2298272015-05-12 11:36:53 -0700218 }
219}
Colin Cross6e18ca42015-07-14 18:55:36 -0700220
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700221// Paths is a slice of Path objects, with helpers to operate on the collection.
222type Paths []Path
223
Jingwen Chen40fd90a2020-06-15 05:24:19 +0000224func (paths Paths) containsPath(path Path) bool {
225 for _, p := range paths {
226 if p == path {
227 return true
228 }
229 }
230 return false
231}
232
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700233// PathsForSource returns Paths rooted from SrcDir
234func PathsForSource(ctx PathContext, paths []string) Paths {
235 ret := make(Paths, len(paths))
236 for i, path := range paths {
237 ret[i] = PathForSource(ctx, path)
238 }
239 return ret
240}
241
Jeff Gaston734e3802017-04-10 15:47:24 -0700242// ExistentPathsForSources returns a list of Paths rooted from SrcDir that are
Dan Willemsen7b310ee2015-12-18 15:11:17 -0800243// found in the tree. If any are not found, they are omitted from the list,
244// and dependencies are added so that we're re-run when they are added.
Colin Cross32f38982018-02-22 11:47:25 -0800245func ExistentPathsForSources(ctx PathContext, paths []string) Paths {
Dan Willemsen7b310ee2015-12-18 15:11:17 -0800246 ret := make(Paths, 0, len(paths))
247 for _, path := range paths {
Colin Cross32f38982018-02-22 11:47:25 -0800248 p := ExistentPathForSource(ctx, path)
Dan Willemsen7b310ee2015-12-18 15:11:17 -0800249 if p.Valid() {
250 ret = append(ret, p.Path())
251 }
252 }
253 return ret
254}
255
Colin Cross41955e82019-05-29 14:40:35 -0700256// PathsForModuleSrc returns Paths rooted from the module's local source directory. It expands globs, references to
257// SourceFileProducer modules using the ":name" syntax, and references to OutputFileProducer modules using the
258// ":name{.tag}" syntax. Properties passed as the paths argument must have been annotated with struct tag
259// `android:"path"` so that dependencies on SourceFileProducer modules will have already been handled by the
260// path_properties mutator. If ctx.Config().AllowMissingDependencies() is true then any missing SourceFileProducer or
261// OutputFileProducer dependencies will cause the module to be marked as having missing dependencies.
Colin Cross635c3b02016-05-18 15:37:25 -0700262func PathsForModuleSrc(ctx ModuleContext, paths []string) Paths {
Colin Cross8a497952019-03-05 22:25:09 -0800263 return PathsForModuleSrcExcludes(ctx, paths, nil)
264}
265
Colin Crossba71a3f2019-03-18 12:12:48 -0700266// PathsForModuleSrcExcludes returns Paths rooted from the module's local source directory, excluding paths listed in
Colin Cross41955e82019-05-29 14:40:35 -0700267// the excludes arguments. It expands globs, references to SourceFileProducer modules using the ":name" syntax, and
268// references to OutputFileProducer modules using the ":name{.tag}" syntax. Properties passed as the paths or excludes
269// argument must have been annotated with struct tag `android:"path"` so that dependencies on SourceFileProducer modules
270// will have already been handled by the path_properties mutator. If ctx.Config().AllowMissingDependencies() is
Paul Duffin036cace2019-07-25 14:44:56 +0100271// true then any missing SourceFileProducer or OutputFileProducer dependencies will cause the module to be marked as
Colin Cross41955e82019-05-29 14:40:35 -0700272// having missing dependencies.
Colin Cross8a497952019-03-05 22:25:09 -0800273func PathsForModuleSrcExcludes(ctx ModuleContext, paths, excludes []string) Paths {
Colin Crossba71a3f2019-03-18 12:12:48 -0700274 ret, missingDeps := PathsAndMissingDepsForModuleSrcExcludes(ctx, paths, excludes)
275 if ctx.Config().AllowMissingDependencies() {
276 ctx.AddMissingDependencies(missingDeps)
277 } else {
278 for _, m := range missingDeps {
279 ctx.ModuleErrorf(`missing dependency on %q, is the property annotated with android:"path"?`, m)
280 }
281 }
282 return ret
283}
284
Ulya Trafimovich4d2eeed2019-11-08 10:54:21 +0000285// OutputPaths is a slice of OutputPath objects, with helpers to operate on the collection.
286type OutputPaths []OutputPath
287
288// Paths returns the OutputPaths as a Paths
289func (p OutputPaths) Paths() Paths {
290 if p == nil {
291 return nil
292 }
293 ret := make(Paths, len(p))
294 for i, path := range p {
295 ret[i] = path
296 }
297 return ret
298}
299
300// Strings returns the string forms of the writable paths.
301func (p OutputPaths) Strings() []string {
302 if p == nil {
303 return nil
304 }
305 ret := make([]string, len(p))
306 for i, path := range p {
307 ret[i] = path.String()
308 }
309 return ret
310}
311
Colin Crossba71a3f2019-03-18 12:12:48 -0700312// PathsAndMissingDepsForModuleSrcExcludes returns Paths rooted from the module's local source directory, excluding
Colin Cross41955e82019-05-29 14:40:35 -0700313// paths listed in the excludes arguments, and a list of missing dependencies. It expands globs, references to
314// SourceFileProducer modules using the ":name" syntax, and references to OutputFileProducer modules using the
315// ":name{.tag}" syntax. Properties passed as the paths or excludes argument must have been annotated with struct tag
316// `android:"path"` so that dependencies on SourceFileProducer modules will have already been handled by the
317// path_properties mutator. If ctx.Config().AllowMissingDependencies() is true then any missing SourceFileProducer or
318// OutputFileProducer dependencies will be returned, and they will NOT cause the module to be marked as having missing
319// dependencies.
Colin Crossba71a3f2019-03-18 12:12:48 -0700320func PathsAndMissingDepsForModuleSrcExcludes(ctx ModuleContext, paths, excludes []string) (Paths, []string) {
Colin Cross8a497952019-03-05 22:25:09 -0800321 prefix := pathForModuleSrc(ctx).String()
322
323 var expandedExcludes []string
324 if excludes != nil {
325 expandedExcludes = make([]string, 0, len(excludes))
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700326 }
Colin Cross8a497952019-03-05 22:25:09 -0800327
Colin Crossba71a3f2019-03-18 12:12:48 -0700328 var missingExcludeDeps []string
329
Colin Cross8a497952019-03-05 22:25:09 -0800330 for _, e := range excludes {
Colin Cross41955e82019-05-29 14:40:35 -0700331 if m, t := SrcIsModuleWithTag(e); m != "" {
332 module := ctx.GetDirectDepWithTag(m, sourceOrOutputDepTag(t))
Colin Cross8a497952019-03-05 22:25:09 -0800333 if module == nil {
Colin Crossba71a3f2019-03-18 12:12:48 -0700334 missingExcludeDeps = append(missingExcludeDeps, m)
Colin Cross8a497952019-03-05 22:25:09 -0800335 continue
336 }
Colin Cross41955e82019-05-29 14:40:35 -0700337 if outProducer, ok := module.(OutputFileProducer); ok {
338 outputFiles, err := outProducer.OutputFiles(t)
339 if err != nil {
340 ctx.ModuleErrorf("path dependency %q: %s", e, err)
341 }
342 expandedExcludes = append(expandedExcludes, outputFiles.Strings()...)
343 } else if t != "" {
344 ctx.ModuleErrorf("path dependency %q is not an output file producing module", e)
345 } else if srcProducer, ok := module.(SourceFileProducer); ok {
Colin Cross8a497952019-03-05 22:25:09 -0800346 expandedExcludes = append(expandedExcludes, srcProducer.Srcs().Strings()...)
347 } else {
Colin Cross41955e82019-05-29 14:40:35 -0700348 ctx.ModuleErrorf("path dependency %q is not a source file producing module", e)
Colin Cross8a497952019-03-05 22:25:09 -0800349 }
350 } else {
351 expandedExcludes = append(expandedExcludes, filepath.Join(prefix, e))
352 }
353 }
354
355 if paths == nil {
Colin Crossba71a3f2019-03-18 12:12:48 -0700356 return nil, missingExcludeDeps
Colin Cross8a497952019-03-05 22:25:09 -0800357 }
358
Colin Crossba71a3f2019-03-18 12:12:48 -0700359 var missingDeps []string
360
Colin Cross8a497952019-03-05 22:25:09 -0800361 expandedSrcFiles := make(Paths, 0, len(paths))
362 for _, s := range paths {
363 srcFiles, err := expandOneSrcPath(ctx, s, expandedExcludes)
364 if depErr, ok := err.(missingDependencyError); ok {
Colin Crossba71a3f2019-03-18 12:12:48 -0700365 missingDeps = append(missingDeps, depErr.missingDeps...)
Colin Cross8a497952019-03-05 22:25:09 -0800366 } else if err != nil {
367 reportPathError(ctx, err)
368 }
369 expandedSrcFiles = append(expandedSrcFiles, srcFiles...)
370 }
Colin Crossba71a3f2019-03-18 12:12:48 -0700371
372 return expandedSrcFiles, append(missingDeps, missingExcludeDeps...)
Colin Cross8a497952019-03-05 22:25:09 -0800373}
374
375type missingDependencyError struct {
376 missingDeps []string
377}
378
379func (e missingDependencyError) Error() string {
380 return "missing dependencies: " + strings.Join(e.missingDeps, ", ")
381}
382
383func expandOneSrcPath(ctx ModuleContext, s string, expandedExcludes []string) (Paths, error) {
Jooyung Han7607dd32020-07-05 10:23:14 +0900384 excludePaths := func(paths Paths) Paths {
385 if len(expandedExcludes) == 0 {
386 return paths
387 }
388 remainder := make(Paths, 0, len(paths))
389 for _, p := range paths {
390 if !InList(p.String(), expandedExcludes) {
391 remainder = append(remainder, p)
392 }
393 }
394 return remainder
395 }
Colin Cross41955e82019-05-29 14:40:35 -0700396 if m, t := SrcIsModuleWithTag(s); m != "" {
397 module := ctx.GetDirectDepWithTag(m, sourceOrOutputDepTag(t))
Colin Cross8a497952019-03-05 22:25:09 -0800398 if module == nil {
399 return nil, missingDependencyError{[]string{m}}
400 }
Colin Cross41955e82019-05-29 14:40:35 -0700401 if outProducer, ok := module.(OutputFileProducer); ok {
402 outputFiles, err := outProducer.OutputFiles(t)
403 if err != nil {
404 return nil, fmt.Errorf("path dependency %q: %s", s, err)
405 }
Jooyung Han7607dd32020-07-05 10:23:14 +0900406 return excludePaths(outputFiles), nil
Colin Cross41955e82019-05-29 14:40:35 -0700407 } else if t != "" {
408 return nil, fmt.Errorf("path dependency %q is not an output file producing module", s)
409 } else if srcProducer, ok := module.(SourceFileProducer); ok {
Jooyung Han7607dd32020-07-05 10:23:14 +0900410 return excludePaths(srcProducer.Srcs()), nil
Colin Cross8a497952019-03-05 22:25:09 -0800411 } else {
Colin Cross41955e82019-05-29 14:40:35 -0700412 return nil, fmt.Errorf("path dependency %q is not a source file producing module", s)
Colin Cross8a497952019-03-05 22:25:09 -0800413 }
414 } else if pathtools.IsGlob(s) {
415 paths := ctx.GlobFiles(pathForModuleSrc(ctx, s).String(), expandedExcludes)
416 return PathsWithModuleSrcSubDir(ctx, paths, ""), nil
417 } else {
418 p := pathForModuleSrc(ctx, s)
Colin Cross988414c2020-01-11 01:11:46 +0000419 if exists, _, err := ctx.Config().fs.Exists(p.String()); err != nil {
Ulya Trafimovich5ab276a2020-08-25 12:45:15 +0100420 ReportPathErrorf(ctx, "%s: %s", p, err.Error())
Colin Cross5e6a7972020-06-07 16:56:32 -0700421 } else if !exists && !ctx.Config().testAllowNonExistentPaths {
Ulya Trafimovich5ab276a2020-08-25 12:45:15 +0100422 ReportPathErrorf(ctx, "module source path %q does not exist", p)
Colin Cross8a497952019-03-05 22:25:09 -0800423 }
424
Jooyung Han7607dd32020-07-05 10:23:14 +0900425 if InList(p.String(), expandedExcludes) {
Colin Cross8a497952019-03-05 22:25:09 -0800426 return nil, nil
427 }
428 return Paths{p}, nil
429 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700430}
431
432// pathsForModuleSrcFromFullPath returns Paths rooted from the module's local
433// source directory, but strip the local source directory from the beginning of
Dan Willemsen540a78c2018-02-26 21:50:08 -0800434// each string. If incDirs is false, strip paths with a trailing '/' from the list.
Colin Crossfe4bc362018-09-12 10:02:13 -0700435// It intended for use in globs that only list files that exist, so it allows '$' in
436// filenames.
Colin Cross1184b642019-12-30 18:43:07 -0800437func pathsForModuleSrcFromFullPath(ctx EarlyModuleContext, paths []string, incDirs bool) Paths {
Colin Cross6510f912017-11-29 00:27:14 -0800438 prefix := filepath.Join(ctx.Config().srcDir, ctx.ModuleDir()) + "/"
Colin Cross0f37af02017-09-27 17:42:05 -0700439 if prefix == "./" {
440 prefix = ""
441 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700442 ret := make(Paths, 0, len(paths))
443 for _, p := range paths {
Dan Willemsen540a78c2018-02-26 21:50:08 -0800444 if !incDirs && strings.HasSuffix(p, "/") {
445 continue
446 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700447 path := filepath.Clean(p)
448 if !strings.HasPrefix(path, prefix) {
Ulya Trafimovich5ab276a2020-08-25 12:45:15 +0100449 ReportPathErrorf(ctx, "Path %q is not in module source directory %q", p, prefix)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700450 continue
451 }
Colin Crosse3924e12018-08-15 20:18:53 -0700452
Colin Crossfe4bc362018-09-12 10:02:13 -0700453 srcPath, err := safePathForSource(ctx, ctx.ModuleDir(), path[len(prefix):])
Colin Crosse3924e12018-08-15 20:18:53 -0700454 if err != nil {
455 reportPathError(ctx, err)
456 continue
457 }
458
Colin Cross07e51612019-03-05 12:46:40 -0800459 srcPath.basePath.rel = srcPath.path
Colin Crosse3924e12018-08-15 20:18:53 -0700460
Colin Cross07e51612019-03-05 12:46:40 -0800461 ret = append(ret, srcPath)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700462 }
463 return ret
464}
465
466// PathsWithOptionalDefaultForModuleSrc returns Paths rooted from the module's
Colin Cross0ddae7f2019-02-07 15:30:01 -0800467// 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 -0700468func PathsWithOptionalDefaultForModuleSrc(ctx ModuleContext, input []string, def string) Paths {
Colin Cross0ddae7f2019-02-07 15:30:01 -0800469 if input != nil {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700470 return PathsForModuleSrc(ctx, input)
471 }
472 // Use Glob so that if the default doesn't exist, a dependency is added so that when it
473 // is created, we're run again.
Colin Cross6510f912017-11-29 00:27:14 -0800474 path := filepath.Join(ctx.Config().srcDir, ctx.ModuleDir(), def)
Colin Cross461b4452018-02-23 09:22:42 -0800475 return ctx.Glob(path, nil)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700476}
477
478// Strings returns the Paths in string form
479func (p Paths) Strings() []string {
480 if p == nil {
481 return nil
482 }
483 ret := make([]string, len(p))
484 for i, path := range p {
485 ret[i] = path.String()
486 }
487 return ret
488}
489
Colin Crossc0efd1d2020-07-03 11:56:24 -0700490func CopyOfPaths(paths Paths) Paths {
491 return append(Paths(nil), paths...)
492}
493
Colin Crossb6715442017-10-24 11:13:31 -0700494// FirstUniquePaths returns all unique elements of a Paths, keeping the first copy of each. It
495// modifies the Paths slice contents in place, and returns a subslice of the original slice.
Dan Willemsenfe92c962017-08-29 12:28:37 -0700496func FirstUniquePaths(list Paths) Paths {
Colin Cross27027c72020-02-28 15:34:17 -0800497 // 128 was chosen based on BenchmarkFirstUniquePaths results.
498 if len(list) > 128 {
499 return firstUniquePathsMap(list)
500 }
501 return firstUniquePathsList(list)
502}
503
Colin Crossc0efd1d2020-07-03 11:56:24 -0700504// SortedUniquePaths returns all unique elements of a Paths in sorted order. It modifies the
505// Paths slice contents in place, and returns a subslice of the original slice.
Jiyong Park33c77362020-05-29 22:00:16 +0900506func SortedUniquePaths(list Paths) Paths {
507 unique := FirstUniquePaths(list)
508 sort.Slice(unique, func(i, j int) bool {
509 return unique[i].String() < unique[j].String()
510 })
511 return unique
512}
513
Colin Cross27027c72020-02-28 15:34:17 -0800514func firstUniquePathsList(list Paths) Paths {
Dan Willemsenfe92c962017-08-29 12:28:37 -0700515 k := 0
516outer:
517 for i := 0; i < len(list); i++ {
518 for j := 0; j < k; j++ {
519 if list[i] == list[j] {
520 continue outer
521 }
522 }
523 list[k] = list[i]
524 k++
525 }
526 return list[:k]
527}
528
Colin Cross27027c72020-02-28 15:34:17 -0800529func firstUniquePathsMap(list Paths) Paths {
530 k := 0
531 seen := make(map[Path]bool, len(list))
532 for i := 0; i < len(list); i++ {
533 if seen[list[i]] {
534 continue
535 }
536 seen[list[i]] = true
537 list[k] = list[i]
538 k++
539 }
540 return list[:k]
541}
542
Colin Crossb6715442017-10-24 11:13:31 -0700543// LastUniquePaths returns all unique elements of a Paths, keeping the last copy of each. It
544// modifies the Paths slice contents in place, and returns a subslice of the original slice.
545func LastUniquePaths(list Paths) Paths {
546 totalSkip := 0
547 for i := len(list) - 1; i >= totalSkip; i-- {
548 skip := 0
549 for j := i - 1; j >= totalSkip; j-- {
550 if list[i] == list[j] {
551 skip++
552 } else {
553 list[j+skip] = list[j]
554 }
555 }
556 totalSkip += skip
557 }
558 return list[totalSkip:]
559}
560
Colin Crossa140bb02018-04-17 10:52:26 -0700561// ReversePaths returns a copy of a Paths in reverse order.
562func ReversePaths(list Paths) Paths {
563 if list == nil {
564 return nil
565 }
566 ret := make(Paths, len(list))
567 for i := range list {
568 ret[i] = list[len(list)-1-i]
569 }
570 return ret
571}
572
Jeff Gaston294356f2017-09-27 17:05:30 -0700573func indexPathList(s Path, list []Path) int {
574 for i, l := range list {
575 if l == s {
576 return i
577 }
578 }
579
580 return -1
581}
582
583func inPathList(p Path, list []Path) bool {
584 return indexPathList(p, list) != -1
585}
586
587func FilterPathList(list []Path, filter []Path) (remainder []Path, filtered []Path) {
Paul Duffin57b9e1d2019-12-13 00:03:35 +0000588 return FilterPathListPredicate(list, func(p Path) bool { return inPathList(p, filter) })
589}
590
591func FilterPathListPredicate(list []Path, predicate func(Path) bool) (remainder []Path, filtered []Path) {
Jeff Gaston294356f2017-09-27 17:05:30 -0700592 for _, l := range list {
Paul Duffin57b9e1d2019-12-13 00:03:35 +0000593 if predicate(l) {
Jeff Gaston294356f2017-09-27 17:05:30 -0700594 filtered = append(filtered, l)
595 } else {
596 remainder = append(remainder, l)
597 }
598 }
599
600 return
601}
602
Colin Cross93e85952017-08-15 13:34:18 -0700603// HasExt returns true of any of the paths have extension ext, otherwise false
604func (p Paths) HasExt(ext string) bool {
605 for _, path := range p {
606 if path.Ext() == ext {
607 return true
608 }
609 }
610
611 return false
612}
613
614// FilterByExt returns the subset of the paths that have extension ext
615func (p Paths) FilterByExt(ext string) Paths {
616 ret := make(Paths, 0, len(p))
617 for _, path := range p {
618 if path.Ext() == ext {
619 ret = append(ret, path)
620 }
621 }
622 return ret
623}
624
625// FilterOutByExt returns the subset of the paths that do not have extension ext
626func (p Paths) FilterOutByExt(ext string) Paths {
627 ret := make(Paths, 0, len(p))
628 for _, path := range p {
629 if path.Ext() != ext {
630 ret = append(ret, path)
631 }
632 }
633 return ret
634}
635
Colin Cross5e6cfbe2017-11-03 15:20:35 -0700636// DirectorySortedPaths is a slice of paths that are sorted such that all files in a directory
637// (including subdirectories) are in a contiguous subslice of the list, and can be found in
638// O(log(N)) time using a binary search on the directory prefix.
639type DirectorySortedPaths Paths
640
641func PathsToDirectorySortedPaths(paths Paths) DirectorySortedPaths {
642 ret := append(DirectorySortedPaths(nil), paths...)
643 sort.Slice(ret, func(i, j int) bool {
644 return ret[i].String() < ret[j].String()
645 })
646 return ret
647}
648
649// PathsInDirectory returns a subslice of the DirectorySortedPaths as a Paths that contains all entries
650// that are in the specified directory and its subdirectories.
651func (p DirectorySortedPaths) PathsInDirectory(dir string) Paths {
652 prefix := filepath.Clean(dir) + "/"
653 start := sort.Search(len(p), func(i int) bool {
654 return prefix < p[i].String()
655 })
656
657 ret := p[start:]
658
659 end := sort.Search(len(ret), func(i int) bool {
660 return !strings.HasPrefix(ret[i].String(), prefix)
661 })
662
663 ret = ret[:end]
664
665 return Paths(ret)
666}
667
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700668// WritablePaths is a slice of WritablePaths, used for multiple outputs.
669type WritablePaths []WritablePath
670
671// Strings returns the string forms of the writable paths.
672func (p WritablePaths) Strings() []string {
673 if p == nil {
674 return nil
675 }
676 ret := make([]string, len(p))
677 for i, path := range p {
678 ret[i] = path.String()
679 }
680 return ret
681}
682
Colin Cross3bc7ffa2017-11-22 16:19:37 -0800683// Paths returns the WritablePaths as a Paths
684func (p WritablePaths) Paths() Paths {
685 if p == nil {
686 return nil
687 }
688 ret := make(Paths, len(p))
689 for i, path := range p {
690 ret[i] = path
691 }
692 return ret
693}
694
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700695type basePath struct {
696 path string
697 config Config
Colin Crossfaeb7aa2017-02-01 14:12:44 -0800698 rel string
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700699}
700
701func (p basePath) Ext() string {
702 return filepath.Ext(p.path)
703}
704
Colin Cross4f6fc9c2016-10-26 10:05:25 -0700705func (p basePath) Base() string {
706 return filepath.Base(p.path)
707}
708
Colin Crossfaeb7aa2017-02-01 14:12:44 -0800709func (p basePath) Rel() string {
710 if p.rel != "" {
711 return p.rel
712 }
713 return p.path
714}
715
Colin Cross0875c522017-11-28 17:34:01 -0800716func (p basePath) String() string {
717 return p.path
718}
719
Colin Cross0db55682017-12-05 15:36:55 -0800720func (p basePath) withRel(rel string) basePath {
721 p.path = filepath.Join(p.path, rel)
722 p.rel = rel
723 return p
724}
725
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700726// SourcePath is a Path representing a file path rooted from SrcDir
727type SourcePath struct {
728 basePath
729}
730
731var _ Path = SourcePath{}
732
Colin Cross0db55682017-12-05 15:36:55 -0800733func (p SourcePath) withRel(rel string) SourcePath {
734 p.basePath = p.basePath.withRel(rel)
735 return p
736}
737
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700738// safePathForSource is for paths that we expect are safe -- only for use by go
739// code that is embedding ninja variables in paths
Colin Crossfe4bc362018-09-12 10:02:13 -0700740func safePathForSource(ctx PathContext, pathComponents ...string) (SourcePath, error) {
741 p, err := validateSafePath(pathComponents...)
Colin Crossaabf6792017-11-29 00:27:14 -0800742 ret := SourcePath{basePath{p, ctx.Config(), ""}}
Colin Crossfe4bc362018-09-12 10:02:13 -0700743 if err != nil {
744 return ret, err
745 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700746
Colin Cross7b3dcc32019-01-24 13:14:39 -0800747 // absolute path already checked by validateSafePath
748 if strings.HasPrefix(ret.String(), ctx.Config().buildDir) {
Mikhail Naganovab1f5182019-02-08 13:17:55 -0800749 return ret, fmt.Errorf("source path %q is in output", ret.String())
Colin Cross6e18ca42015-07-14 18:55:36 -0700750 }
751
Colin Crossfe4bc362018-09-12 10:02:13 -0700752 return ret, err
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700753}
754
Colin Cross192e97a2018-02-22 14:21:02 -0800755// pathForSource creates a SourcePath from pathComponents, but does not check that it exists.
756func pathForSource(ctx PathContext, pathComponents ...string) (SourcePath, error) {
Colin Crossc48c1432018-02-23 07:09:01 +0000757 p, err := validatePath(pathComponents...)
758 ret := SourcePath{basePath{p, ctx.Config(), ""}}
Colin Cross94a32102018-02-22 14:21:02 -0800759 if err != nil {
Colin Cross192e97a2018-02-22 14:21:02 -0800760 return ret, err
Colin Cross94a32102018-02-22 14:21:02 -0800761 }
762
Colin Cross7b3dcc32019-01-24 13:14:39 -0800763 // absolute path already checked by validatePath
764 if strings.HasPrefix(ret.String(), ctx.Config().buildDir) {
Mikhail Naganovab1f5182019-02-08 13:17:55 -0800765 return ret, fmt.Errorf("source path %q is in output", ret.String())
Colin Crossc48c1432018-02-23 07:09:01 +0000766 }
767
Colin Cross192e97a2018-02-22 14:21:02 -0800768 return ret, nil
769}
770
771// existsWithDependencies returns true if the path exists, and adds appropriate dependencies to rerun if the
772// path does not exist.
773func existsWithDependencies(ctx PathContext, path SourcePath) (exists bool, err error) {
774 var files []string
775
776 if gctx, ok := ctx.(PathGlobContext); ok {
777 // Use glob to produce proper dependencies, even though we only want
778 // a single file.
779 files, err = gctx.GlobWithDeps(path.String(), nil)
780 } else {
781 var deps []string
782 // We cannot add build statements in this context, so we fall back to
783 // AddNinjaFileDeps
Colin Cross988414c2020-01-11 01:11:46 +0000784 files, deps, err = ctx.Config().fs.Glob(path.String(), nil, pathtools.FollowSymlinks)
Colin Cross192e97a2018-02-22 14:21:02 -0800785 ctx.AddNinjaFileDeps(deps...)
786 }
787
788 if err != nil {
789 return false, fmt.Errorf("glob: %s", err.Error())
790 }
791
792 return len(files) > 0, nil
793}
794
795// PathForSource joins the provided path components and validates that the result
796// neither escapes the source dir nor is in the out dir.
797// On error, it will return a usable, but invalid SourcePath, and report a ModuleError.
798func PathForSource(ctx PathContext, pathComponents ...string) SourcePath {
799 path, err := pathForSource(ctx, pathComponents...)
800 if err != nil {
801 reportPathError(ctx, err)
802 }
803
Colin Crosse3924e12018-08-15 20:18:53 -0700804 if pathtools.IsGlob(path.String()) {
Ulya Trafimovich5ab276a2020-08-25 12:45:15 +0100805 ReportPathErrorf(ctx, "path may not contain a glob: %s", path.String())
Colin Crosse3924e12018-08-15 20:18:53 -0700806 }
807
Colin Cross192e97a2018-02-22 14:21:02 -0800808 if modCtx, ok := ctx.(ModuleContext); ok && ctx.Config().AllowMissingDependencies() {
809 exists, err := existsWithDependencies(ctx, path)
810 if err != nil {
811 reportPathError(ctx, err)
812 }
813 if !exists {
814 modCtx.AddMissingDependencies([]string{path.String()})
815 }
Colin Cross988414c2020-01-11 01:11:46 +0000816 } else if exists, _, err := ctx.Config().fs.Exists(path.String()); err != nil {
Ulya Trafimovich5ab276a2020-08-25 12:45:15 +0100817 ReportPathErrorf(ctx, "%s: %s", path, err.Error())
Colin Cross5e6a7972020-06-07 16:56:32 -0700818 } else if !exists && !ctx.Config().testAllowNonExistentPaths {
Ulya Trafimovich5ab276a2020-08-25 12:45:15 +0100819 ReportPathErrorf(ctx, "source path %q does not exist", path)
Colin Cross192e97a2018-02-22 14:21:02 -0800820 }
821 return path
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700822}
823
Jeff Gaston734e3802017-04-10 15:47:24 -0700824// ExistentPathForSource returns an OptionalPath with the SourcePath if the
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700825// path exists, or an empty OptionalPath if it doesn't exist. Dependencies are added
826// so that the ninja file will be regenerated if the state of the path changes.
Colin Cross32f38982018-02-22 11:47:25 -0800827func ExistentPathForSource(ctx PathContext, pathComponents ...string) OptionalPath {
Colin Cross192e97a2018-02-22 14:21:02 -0800828 path, err := pathForSource(ctx, pathComponents...)
Colin Cross1ccfcc32018-02-22 13:54:26 -0800829 if err != nil {
830 reportPathError(ctx, err)
831 return OptionalPath{}
832 }
Colin Crossc48c1432018-02-23 07:09:01 +0000833
Colin Crosse3924e12018-08-15 20:18:53 -0700834 if pathtools.IsGlob(path.String()) {
Ulya Trafimovich5ab276a2020-08-25 12:45:15 +0100835 ReportPathErrorf(ctx, "path may not contain a glob: %s", path.String())
Colin Crosse3924e12018-08-15 20:18:53 -0700836 return OptionalPath{}
837 }
838
Colin Cross192e97a2018-02-22 14:21:02 -0800839 exists, err := existsWithDependencies(ctx, path)
Colin Crossc48c1432018-02-23 07:09:01 +0000840 if err != nil {
841 reportPathError(ctx, err)
842 return OptionalPath{}
843 }
Colin Cross192e97a2018-02-22 14:21:02 -0800844 if !exists {
Colin Crossc48c1432018-02-23 07:09:01 +0000845 return OptionalPath{}
846 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700847 return OptionalPathForPath(path)
848}
849
850func (p SourcePath) String() string {
851 return filepath.Join(p.config.srcDir, p.path)
852}
853
854// Join creates a new SourcePath with paths... joined with the current path. The
855// provided paths... may not use '..' to escape from the current path.
856func (p SourcePath) Join(ctx PathContext, paths ...string) SourcePath {
Colin Cross1ccfcc32018-02-22 13:54:26 -0800857 path, err := validatePath(paths...)
858 if err != nil {
859 reportPathError(ctx, err)
860 }
Colin Cross0db55682017-12-05 15:36:55 -0800861 return p.withRel(path)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700862}
863
Colin Cross2fafa3e2019-03-05 12:39:51 -0800864// join is like Join but does less path validation.
865func (p SourcePath) join(ctx PathContext, paths ...string) SourcePath {
866 path, err := validateSafePath(paths...)
867 if err != nil {
868 reportPathError(ctx, err)
869 }
870 return p.withRel(path)
871}
872
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700873// OverlayPath returns the overlay for `path' if it exists. This assumes that the
874// SourcePath is the path to a resource overlay directory.
Colin Cross635c3b02016-05-18 15:37:25 -0700875func (p SourcePath) OverlayPath(ctx ModuleContext, path Path) OptionalPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700876 var relDir string
Colin Cross07e51612019-03-05 12:46:40 -0800877 if srcPath, ok := path.(SourcePath); ok {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700878 relDir = srcPath.path
879 } else {
Ulya Trafimovich5ab276a2020-08-25 12:45:15 +0100880 ReportPathErrorf(ctx, "Cannot find relative path for %s(%s)", reflect.TypeOf(path).Name(), path)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700881 return OptionalPath{}
882 }
883 dir := filepath.Join(p.config.srcDir, p.path, relDir)
884 // Use Glob so that we are run again if the directory is added.
Colin Cross7f19f372016-11-01 11:10:25 -0700885 if pathtools.IsGlob(dir) {
Ulya Trafimovich5ab276a2020-08-25 12:45:15 +0100886 ReportPathErrorf(ctx, "Path may not contain a glob: %s", dir)
Dan Willemsen7b310ee2015-12-18 15:11:17 -0800887 }
Colin Cross461b4452018-02-23 09:22:42 -0800888 paths, err := ctx.GlobWithDeps(dir, nil)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700889 if err != nil {
Ulya Trafimovich5ab276a2020-08-25 12:45:15 +0100890 ReportPathErrorf(ctx, "glob: %s", err.Error())
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700891 return OptionalPath{}
892 }
893 if len(paths) == 0 {
894 return OptionalPath{}
895 }
Colin Cross43f08db2018-11-12 10:13:39 -0800896 relPath := Rel(ctx, p.config.srcDir, paths[0])
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700897 return OptionalPathForPath(PathForSource(ctx, relPath))
898}
899
Colin Cross70dda7e2019-10-01 22:05:35 -0700900// OutputPath is a Path representing an intermediates file path rooted from the build directory
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700901type OutputPath struct {
902 basePath
Colin Crossd63c9a72020-01-29 16:52:50 -0800903 fullPath string
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700904}
905
Colin Cross702e0f82017-10-18 17:27:54 -0700906func (p OutputPath) withRel(rel string) OutputPath {
Colin Cross0db55682017-12-05 15:36:55 -0800907 p.basePath = p.basePath.withRel(rel)
Colin Crossd63c9a72020-01-29 16:52:50 -0800908 p.fullPath = filepath.Join(p.fullPath, rel)
Colin Cross702e0f82017-10-18 17:27:54 -0700909 return p
910}
911
Colin Cross3063b782018-08-15 11:19:12 -0700912func (p OutputPath) WithoutRel() OutputPath {
913 p.basePath.rel = filepath.Base(p.basePath.path)
914 return p
915}
916
Paul Duffin9b478b02019-12-10 13:41:51 +0000917func (p OutputPath) buildDir() string {
918 return p.config.buildDir
919}
920
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700921var _ Path = OutputPath{}
Paul Duffin9b478b02019-12-10 13:41:51 +0000922var _ WritablePath = OutputPath{}
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700923
Chris Parsons8f232a22020-06-23 17:37:05 -0400924// toolDepPath is a Path representing a dependency of the build tool.
925type toolDepPath struct {
926 basePath
927}
928
929var _ Path = toolDepPath{}
930
931// pathForBuildToolDep returns a toolDepPath representing the given path string.
932// There is no validation for the path, as it is "trusted": It may fail
933// normal validation checks. For example, it may be an absolute path.
934// Only use this function to construct paths for dependencies of the build
935// tool invocation.
936func pathForBuildToolDep(ctx PathContext, path string) toolDepPath {
937 return toolDepPath{basePath{path, ctx.Config(), ""}}
938}
939
Jeff Gaston734e3802017-04-10 15:47:24 -0700940// PathForOutput joins the provided paths and returns an OutputPath that is
941// validated to not escape the build dir.
942// On error, it will return a usable, but invalid OutputPath, and report a ModuleError.
943func PathForOutput(ctx PathContext, pathComponents ...string) OutputPath {
Colin Cross1ccfcc32018-02-22 13:54:26 -0800944 path, err := validatePath(pathComponents...)
945 if err != nil {
946 reportPathError(ctx, err)
947 }
Colin Crossd63c9a72020-01-29 16:52:50 -0800948 fullPath := filepath.Join(ctx.Config().buildDir, path)
949 path = fullPath[len(fullPath)-len(path):]
950 return OutputPath{basePath{path, ctx.Config(), ""}, fullPath}
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700951}
952
Colin Cross40e33732019-02-15 11:08:35 -0800953// PathsForOutput returns Paths rooted from buildDir
954func PathsForOutput(ctx PathContext, paths []string) WritablePaths {
955 ret := make(WritablePaths, len(paths))
956 for i, path := range paths {
957 ret[i] = PathForOutput(ctx, path)
958 }
959 return ret
960}
961
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700962func (p OutputPath) writablePath() {}
963
964func (p OutputPath) String() string {
Colin Crossd63c9a72020-01-29 16:52:50 -0800965 return p.fullPath
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700966}
967
968// Join creates a new OutputPath with paths... joined with the current path. The
969// provided paths... may not use '..' to escape from the current path.
970func (p OutputPath) Join(ctx PathContext, paths ...string) OutputPath {
Colin Cross1ccfcc32018-02-22 13:54:26 -0800971 path, err := validatePath(paths...)
972 if err != nil {
973 reportPathError(ctx, err)
974 }
Colin Cross0db55682017-12-05 15:36:55 -0800975 return p.withRel(path)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700976}
977
Colin Cross8854a5a2019-02-11 14:14:16 -0800978// ReplaceExtension creates a new OutputPath with the extension replaced with ext.
979func (p OutputPath) ReplaceExtension(ctx PathContext, ext string) OutputPath {
980 if strings.Contains(ext, "/") {
Ulya Trafimovich5ab276a2020-08-25 12:45:15 +0100981 ReportPathErrorf(ctx, "extension %q cannot contain /", ext)
Colin Cross8854a5a2019-02-11 14:14:16 -0800982 }
983 ret := PathForOutput(ctx, pathtools.ReplaceExtension(p.path, ext))
Colin Cross2cdd5df2019-02-25 10:25:24 -0800984 ret.rel = pathtools.ReplaceExtension(p.rel, ext)
Colin Cross8854a5a2019-02-11 14:14:16 -0800985 return ret
986}
987
Colin Cross40e33732019-02-15 11:08:35 -0800988// InSameDir creates a new OutputPath from the directory of the current OutputPath joined with the elements in paths.
989func (p OutputPath) InSameDir(ctx PathContext, paths ...string) OutputPath {
990 path, err := validatePath(paths...)
991 if err != nil {
992 reportPathError(ctx, err)
993 }
994
995 ret := PathForOutput(ctx, filepath.Dir(p.path), path)
Colin Cross2cdd5df2019-02-25 10:25:24 -0800996 ret.rel = filepath.Join(filepath.Dir(p.rel), path)
Colin Cross40e33732019-02-15 11:08:35 -0800997 return ret
998}
999
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001000// PathForIntermediates returns an OutputPath representing the top-level
1001// intermediates directory.
1002func PathForIntermediates(ctx PathContext, paths ...string) OutputPath {
Colin Cross1ccfcc32018-02-22 13:54:26 -08001003 path, err := validatePath(paths...)
1004 if err != nil {
1005 reportPathError(ctx, err)
1006 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001007 return PathForOutput(ctx, ".intermediates", path)
1008}
1009
Colin Cross07e51612019-03-05 12:46:40 -08001010var _ genPathProvider = SourcePath{}
1011var _ objPathProvider = SourcePath{}
1012var _ resPathProvider = SourcePath{}
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001013
Colin Cross07e51612019-03-05 12:46:40 -08001014// PathForModuleSrc returns a Path representing the paths... under the
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001015// module's local source directory.
Colin Cross8a497952019-03-05 22:25:09 -08001016func PathForModuleSrc(ctx ModuleContext, pathComponents ...string) Path {
1017 p, err := validatePath(pathComponents...)
1018 if err != nil {
1019 reportPathError(ctx, err)
Colin Cross192e97a2018-02-22 14:21:02 -08001020 }
Colin Cross8a497952019-03-05 22:25:09 -08001021 paths, err := expandOneSrcPath(ctx, p, nil)
1022 if err != nil {
1023 if depErr, ok := err.(missingDependencyError); ok {
1024 if ctx.Config().AllowMissingDependencies() {
1025 ctx.AddMissingDependencies(depErr.missingDeps)
1026 } else {
1027 ctx.ModuleErrorf(`%s, is the property annotated with android:"path"?`, depErr.Error())
1028 }
1029 } else {
1030 reportPathError(ctx, err)
1031 }
1032 return nil
1033 } else if len(paths) == 0 {
Ulya Trafimovich5ab276a2020-08-25 12:45:15 +01001034 ReportPathErrorf(ctx, "%q produced no files, expected exactly one", p)
Colin Cross8a497952019-03-05 22:25:09 -08001035 return nil
1036 } else if len(paths) > 1 {
Ulya Trafimovich5ab276a2020-08-25 12:45:15 +01001037 ReportPathErrorf(ctx, "%q produced %d files, expected exactly one", p, len(paths))
Colin Cross8a497952019-03-05 22:25:09 -08001038 }
1039 return paths[0]
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001040}
1041
Colin Cross07e51612019-03-05 12:46:40 -08001042func pathForModuleSrc(ctx ModuleContext, paths ...string) SourcePath {
1043 p, err := validatePath(paths...)
1044 if err != nil {
1045 reportPathError(ctx, err)
1046 }
1047
1048 path, err := pathForSource(ctx, ctx.ModuleDir(), p)
1049 if err != nil {
1050 reportPathError(ctx, err)
1051 }
1052
1053 path.basePath.rel = p
1054
1055 return path
1056}
1057
Colin Cross2fafa3e2019-03-05 12:39:51 -08001058// PathsWithModuleSrcSubDir takes a list of Paths and returns a new list of Paths where Rel() on each path
1059// will return the path relative to subDir in the module's source directory. If any input paths are not located
1060// inside subDir then a path error will be reported.
1061func PathsWithModuleSrcSubDir(ctx ModuleContext, paths Paths, subDir string) Paths {
1062 paths = append(Paths(nil), paths...)
Colin Cross07e51612019-03-05 12:46:40 -08001063 subDirFullPath := pathForModuleSrc(ctx, subDir)
Colin Cross2fafa3e2019-03-05 12:39:51 -08001064 for i, path := range paths {
1065 rel := Rel(ctx, subDirFullPath.String(), path.String())
1066 paths[i] = subDirFullPath.join(ctx, rel)
1067 }
1068 return paths
1069}
1070
1071// PathWithModuleSrcSubDir takes a Path and returns a Path where Rel() will return the path relative to subDir in the
1072// module's source directory. If the input path is not located inside subDir then a path error will be reported.
1073func PathWithModuleSrcSubDir(ctx ModuleContext, path Path, subDir string) Path {
Colin Cross07e51612019-03-05 12:46:40 -08001074 subDirFullPath := pathForModuleSrc(ctx, subDir)
Colin Cross2fafa3e2019-03-05 12:39:51 -08001075 rel := Rel(ctx, subDirFullPath.String(), path.String())
1076 return subDirFullPath.Join(ctx, rel)
1077}
1078
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001079// OptionalPathForModuleSrc returns an OptionalPath. The OptionalPath contains a
1080// valid path if p is non-nil.
Colin Cross635c3b02016-05-18 15:37:25 -07001081func OptionalPathForModuleSrc(ctx ModuleContext, p *string) OptionalPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001082 if p == nil {
1083 return OptionalPath{}
1084 }
1085 return OptionalPathForPath(PathForModuleSrc(ctx, *p))
1086}
1087
Colin Cross07e51612019-03-05 12:46:40 -08001088func (p SourcePath) genPathWithExt(ctx ModuleContext, subdir, ext string) ModuleGenPath {
Colin Cross7fc17db2017-02-01 14:07:55 -08001089 return PathForModuleGen(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001090}
1091
Colin Cross07e51612019-03-05 12:46:40 -08001092func (p SourcePath) objPathWithExt(ctx ModuleContext, subdir, ext string) ModuleObjPath {
Colin Cross7fc17db2017-02-01 14:07:55 -08001093 return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001094}
1095
Colin Cross07e51612019-03-05 12:46:40 -08001096func (p SourcePath) resPathWithName(ctx ModuleContext, name string) ModuleResPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001097 // TODO: Use full directory if the new ctx is not the current ctx?
1098 return PathForModuleRes(ctx, p.path, name)
1099}
1100
1101// ModuleOutPath is a Path representing a module's output directory.
1102type ModuleOutPath struct {
1103 OutputPath
1104}
1105
1106var _ Path = ModuleOutPath{}
1107
Pete Bentleyfcf55bf2019-08-16 20:14:32 +01001108func (p ModuleOutPath) objPathWithExt(ctx ModuleContext, subdir, ext string) ModuleObjPath {
1109 return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
1110}
1111
Colin Cross702e0f82017-10-18 17:27:54 -07001112func pathForModule(ctx ModuleContext) OutputPath {
1113 return PathForOutput(ctx, ".intermediates", ctx.ModuleDir(), ctx.ModuleName(), ctx.ModuleSubDir())
1114}
1115
Logan Chien7eefdc42018-07-11 18:10:41 +08001116// PathForVndkRefAbiDump returns an OptionalPath representing the path of the
1117// reference abi dump for the given module. This is not guaranteed to be valid.
1118func PathForVndkRefAbiDump(ctx ModuleContext, version, fileName string,
Hsin-Yi Chen53489642019-07-31 17:10:45 +08001119 isNdk, isLlndkOrVndk, isGzip bool) OptionalPath {
Logan Chien7eefdc42018-07-11 18:10:41 +08001120
Jayant Chowdharyac066c62018-02-20 10:53:31 -08001121 arches := ctx.DeviceConfig().Arches()
Logan Chien7eefdc42018-07-11 18:10:41 +08001122 if len(arches) == 0 {
1123 panic("device build with no primary arch")
1124 }
Jayant Chowdharyac066c62018-02-20 10:53:31 -08001125 currentArch := ctx.Arch()
1126 archNameAndVariant := currentArch.ArchType.String()
1127 if currentArch.ArchVariant != "" {
1128 archNameAndVariant += "_" + currentArch.ArchVariant
1129 }
Logan Chien5237bed2018-07-11 17:15:57 +08001130
1131 var dirName string
Hsin-Yi Chen53489642019-07-31 17:10:45 +08001132 if isNdk {
Logan Chien5237bed2018-07-11 17:15:57 +08001133 dirName = "ndk"
Hsin-Yi Chen53489642019-07-31 17:10:45 +08001134 } else if isLlndkOrVndk {
Logan Chien5237bed2018-07-11 17:15:57 +08001135 dirName = "vndk"
Logan Chien41eabe62019-04-10 13:33:58 +08001136 } else {
1137 dirName = "platform" // opt-in libs
Jayant Chowdhary3e231fd2017-02-08 13:45:53 -08001138 }
Logan Chien5237bed2018-07-11 17:15:57 +08001139
Jayant Chowdhary34ce67d2018-03-08 11:00:50 -08001140 binderBitness := ctx.DeviceConfig().BinderBitness()
Logan Chien7eefdc42018-07-11 18:10:41 +08001141
1142 var ext string
1143 if isGzip {
1144 ext = ".lsdump.gz"
1145 } else {
1146 ext = ".lsdump"
1147 }
1148
1149 return ExistentPathForSource(ctx, "prebuilts", "abi-dumps", dirName,
1150 version, binderBitness, archNameAndVariant, "source-based",
1151 fileName+ext)
Jayant Chowdhary3e231fd2017-02-08 13:45:53 -08001152}
1153
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001154// PathForModuleOut returns a Path representing the paths... under the module's
1155// output directory.
Colin Cross635c3b02016-05-18 15:37:25 -07001156func PathForModuleOut(ctx ModuleContext, paths ...string) ModuleOutPath {
Colin Cross1ccfcc32018-02-22 13:54:26 -08001157 p, err := validatePath(paths...)
1158 if err != nil {
1159 reportPathError(ctx, err)
1160 }
Colin Cross702e0f82017-10-18 17:27:54 -07001161 return ModuleOutPath{
1162 OutputPath: pathForModule(ctx).withRel(p),
1163 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001164}
1165
1166// ModuleGenPath is a Path representing the 'gen' directory in a module's output
1167// directory. Mainly used for generated sources.
1168type ModuleGenPath struct {
1169 ModuleOutPath
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001170}
1171
1172var _ Path = ModuleGenPath{}
1173var _ genPathProvider = ModuleGenPath{}
1174var _ objPathProvider = ModuleGenPath{}
1175
1176// PathForModuleGen returns a Path representing the paths... under the module's
1177// `gen' directory.
Colin Cross635c3b02016-05-18 15:37:25 -07001178func PathForModuleGen(ctx ModuleContext, paths ...string) ModuleGenPath {
Colin Cross1ccfcc32018-02-22 13:54:26 -08001179 p, err := validatePath(paths...)
1180 if err != nil {
1181 reportPathError(ctx, err)
1182 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001183 return ModuleGenPath{
Colin Cross702e0f82017-10-18 17:27:54 -07001184 ModuleOutPath: ModuleOutPath{
1185 OutputPath: pathForModule(ctx).withRel("gen").withRel(p),
1186 },
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001187 }
1188}
1189
Dan Willemsen21ec4902016-11-02 20:43:13 -07001190func (p ModuleGenPath) genPathWithExt(ctx ModuleContext, subdir, ext string) ModuleGenPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001191 // TODO: make a different path for local vs remote generated files?
Dan Willemsen21ec4902016-11-02 20:43:13 -07001192 return PathForModuleGen(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001193}
1194
Colin Cross635c3b02016-05-18 15:37:25 -07001195func (p ModuleGenPath) objPathWithExt(ctx ModuleContext, subdir, ext string) ModuleObjPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001196 return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
1197}
1198
1199// ModuleObjPath is a Path representing the 'obj' directory in a module's output
1200// directory. Used for compiled objects.
1201type ModuleObjPath struct {
1202 ModuleOutPath
1203}
1204
1205var _ Path = ModuleObjPath{}
1206
1207// PathForModuleObj returns a Path representing the paths... under the module's
1208// 'obj' directory.
Jeff Gaston734e3802017-04-10 15:47:24 -07001209func PathForModuleObj(ctx ModuleContext, pathComponents ...string) ModuleObjPath {
Colin Cross1ccfcc32018-02-22 13:54:26 -08001210 p, err := validatePath(pathComponents...)
1211 if err != nil {
1212 reportPathError(ctx, err)
1213 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001214 return ModuleObjPath{PathForModuleOut(ctx, "obj", p)}
1215}
1216
1217// ModuleResPath is a a Path representing the 'res' directory in a module's
1218// output directory.
1219type ModuleResPath struct {
1220 ModuleOutPath
1221}
1222
1223var _ Path = ModuleResPath{}
1224
1225// PathForModuleRes returns a Path representing the paths... under the module's
1226// 'res' directory.
Jeff Gaston734e3802017-04-10 15:47:24 -07001227func PathForModuleRes(ctx ModuleContext, pathComponents ...string) ModuleResPath {
Colin Cross1ccfcc32018-02-22 13:54:26 -08001228 p, err := validatePath(pathComponents...)
1229 if err != nil {
1230 reportPathError(ctx, err)
1231 }
1232
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001233 return ModuleResPath{PathForModuleOut(ctx, "res", p)}
1234}
1235
Colin Cross70dda7e2019-10-01 22:05:35 -07001236// InstallPath is a Path representing a installed file path rooted from the build directory
1237type InstallPath struct {
1238 basePath
Colin Crossff6c33d2019-10-02 16:01:35 -07001239
Jiyong Park957bcd92020-10-20 18:23:33 +09001240 // partitionDir is the part of the InstallPath that is automatically determined according to the context.
1241 // For example, it is host/<os>-<arch> for host modules, and target/product/<device>/<partition> for device modules.
1242 partitionDir string
1243
1244 // makePath indicates whether this path is for Soong (false) or Make (true).
1245 makePath bool
Colin Cross70dda7e2019-10-01 22:05:35 -07001246}
1247
Paul Duffin9b478b02019-12-10 13:41:51 +00001248func (p InstallPath) buildDir() string {
1249 return p.config.buildDir
1250}
1251
1252var _ Path = InstallPath{}
1253var _ WritablePath = InstallPath{}
1254
Colin Cross70dda7e2019-10-01 22:05:35 -07001255func (p InstallPath) writablePath() {}
1256
1257func (p InstallPath) String() string {
Jiyong Park957bcd92020-10-20 18:23:33 +09001258 if p.makePath {
1259 // Make path starts with out/ instead of out/soong.
1260 return filepath.Join(p.config.buildDir, "../", p.path)
1261 } else {
1262 return filepath.Join(p.config.buildDir, p.path)
1263 }
1264}
1265
1266// PartitionDir returns the path to the partition where the install path is rooted at. It is
1267// out/soong/target/product/<device>/<partition> for device modules, and out/soong/host/<os>-<arch> for host modules.
1268// The ./soong is dropped if the install path is for Make.
1269func (p InstallPath) PartitionDir() string {
1270 if p.makePath {
1271 return filepath.Join(p.config.buildDir, "../", p.partitionDir)
1272 } else {
1273 return filepath.Join(p.config.buildDir, p.partitionDir)
1274 }
Colin Cross70dda7e2019-10-01 22:05:35 -07001275}
1276
1277// Join creates a new InstallPath with paths... joined with the current path. The
1278// provided paths... may not use '..' to escape from the current path.
1279func (p InstallPath) Join(ctx PathContext, paths ...string) InstallPath {
1280 path, err := validatePath(paths...)
1281 if err != nil {
1282 reportPathError(ctx, err)
1283 }
1284 return p.withRel(path)
1285}
1286
1287func (p InstallPath) withRel(rel string) InstallPath {
1288 p.basePath = p.basePath.withRel(rel)
1289 return p
1290}
1291
Colin Crossff6c33d2019-10-02 16:01:35 -07001292// ToMakePath returns a new InstallPath that points to Make's install directory instead of Soong's,
1293// i.e. out/ instead of out/soong/.
1294func (p InstallPath) ToMakePath() InstallPath {
Jiyong Park957bcd92020-10-20 18:23:33 +09001295 p.makePath = true
Colin Crossff6c33d2019-10-02 16:01:35 -07001296 return p
Colin Cross70dda7e2019-10-01 22:05:35 -07001297}
1298
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001299// PathForModuleInstall returns a Path representing the install path for the
1300// module appended with paths...
Colin Cross70dda7e2019-10-01 22:05:35 -07001301func PathForModuleInstall(ctx ModuleInstallPathContext, pathComponents ...string) InstallPath {
Colin Cross6e359402020-02-10 15:29:54 -08001302 os := ctx.Os()
Jiyong Park87788b52020-09-01 12:37:45 +09001303 arch := ctx.Arch().ArchType
1304 forceOS, forceArch := ctx.InstallForceOS()
1305 if forceOS != nil {
Colin Cross6e359402020-02-10 15:29:54 -08001306 os = *forceOS
1307 }
Jiyong Park87788b52020-09-01 12:37:45 +09001308 if forceArch != nil {
1309 arch = *forceArch
1310 }
Colin Cross6e359402020-02-10 15:29:54 -08001311 partition := modulePartition(ctx, os)
Colin Cross609c49a2020-02-13 13:20:11 -08001312
Jiyong Park87788b52020-09-01 12:37:45 +09001313 ret := pathForInstall(ctx, os, arch, partition, ctx.Debug(), pathComponents...)
Colin Cross609c49a2020-02-13 13:20:11 -08001314
1315 if ctx.InstallBypassMake() && ctx.Config().EmbeddedInMake() {
1316 ret = ret.ToMakePath()
1317 }
1318
1319 return ret
1320}
1321
Jiyong Park87788b52020-09-01 12:37:45 +09001322func pathForInstall(ctx PathContext, os OsType, arch ArchType, partition string, debug bool,
Colin Cross609c49a2020-02-13 13:20:11 -08001323 pathComponents ...string) InstallPath {
1324
Jiyong Park957bcd92020-10-20 18:23:33 +09001325 var partionPaths []string
Colin Cross609c49a2020-02-13 13:20:11 -08001326
Colin Cross6e359402020-02-10 15:29:54 -08001327 if os.Class == Device {
Jiyong Park957bcd92020-10-20 18:23:33 +09001328 partionPaths = []string{"target", "product", ctx.Config().DeviceName(), partition}
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001329 } else {
Jiyong Park87788b52020-09-01 12:37:45 +09001330 osName := os.String()
1331 if os == Linux {
1332 // instead of linux_glibc
1333 osName = "linux"
Dan Willemsen866b5632017-09-22 12:28:24 -07001334 }
Jiyong Park87788b52020-09-01 12:37:45 +09001335 // SOONG_HOST_OUT is set to out/host/$(HOST_OS)-$(HOST_PREBUILT_ARCH)
1336 // and HOST_PREBUILT_ARCH is forcibly set to x86 even on x86_64 hosts. We don't seem
1337 // to have a plan to fix it (see the comment in build/make/core/envsetup.mk).
1338 // Let's keep using x86 for the existing cases until we have a need to support
1339 // other architectures.
1340 archName := arch.String()
1341 if os.Class == Host && (arch == X86_64 || arch == Common) {
1342 archName = "x86"
1343 }
Jiyong Park957bcd92020-10-20 18:23:33 +09001344 partionPaths = []string{"host", osName + "-" + archName, partition}
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001345 }
Colin Cross609c49a2020-02-13 13:20:11 -08001346 if debug {
Jiyong Park957bcd92020-10-20 18:23:33 +09001347 partionPaths = append([]string{"debug"}, partionPaths...)
Dan Willemsen782a2d12015-12-21 14:55:28 -08001348 }
Colin Cross70dda7e2019-10-01 22:05:35 -07001349
Jiyong Park957bcd92020-10-20 18:23:33 +09001350 partionPath, err := validatePath(partionPaths...)
Colin Cross70dda7e2019-10-01 22:05:35 -07001351 if err != nil {
1352 reportPathError(ctx, err)
1353 }
Colin Crossff6c33d2019-10-02 16:01:35 -07001354
Jiyong Park957bcd92020-10-20 18:23:33 +09001355 base := InstallPath{
1356 basePath: basePath{partionPath, ctx.Config(), ""},
1357 partitionDir: partionPath,
1358 makePath: false,
1359 }
Colin Crossff6c33d2019-10-02 16:01:35 -07001360
Jiyong Park957bcd92020-10-20 18:23:33 +09001361 return base.Join(ctx, pathComponents...)
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001362}
1363
Nicolas Geoffray1228e9c2020-02-27 13:45:35 +00001364func pathForNdkOrSdkInstall(ctx PathContext, prefix string, paths []string) InstallPath {
Jiyong Park957bcd92020-10-20 18:23:33 +09001365 base := InstallPath{
1366 basePath: basePath{prefix, ctx.Config(), ""},
1367 partitionDir: prefix,
1368 makePath: false,
Colin Cross70dda7e2019-10-01 22:05:35 -07001369 }
Jiyong Park957bcd92020-10-20 18:23:33 +09001370 return base.Join(ctx, paths...)
Colin Cross70dda7e2019-10-01 22:05:35 -07001371}
1372
Nicolas Geoffray1228e9c2020-02-27 13:45:35 +00001373func PathForNdkInstall(ctx PathContext, paths ...string) InstallPath {
1374 return pathForNdkOrSdkInstall(ctx, "ndk", paths)
1375}
1376
1377func PathForMainlineSdksInstall(ctx PathContext, paths ...string) InstallPath {
1378 return pathForNdkOrSdkInstall(ctx, "mainline-sdks", paths)
1379}
1380
Colin Cross70dda7e2019-10-01 22:05:35 -07001381func InstallPathToOnDevicePath(ctx PathContext, path InstallPath) string {
Colin Cross43f08db2018-11-12 10:13:39 -08001382 rel := Rel(ctx, PathForOutput(ctx, "target", "product", ctx.Config().DeviceName()).String(), path.String())
1383
1384 return "/" + rel
1385}
1386
Colin Cross6e359402020-02-10 15:29:54 -08001387func modulePartition(ctx ModuleInstallPathContext, os OsType) string {
Colin Cross43f08db2018-11-12 10:13:39 -08001388 var partition string
Colin Cross6e359402020-02-10 15:29:54 -08001389 if ctx.InstallInTestcases() {
1390 // "testcases" install directory can be used for host or device modules.
Jaewoong Jung0949f312019-09-11 10:25:18 -07001391 partition = "testcases"
Colin Cross6e359402020-02-10 15:29:54 -08001392 } else if os.Class == Device {
1393 if ctx.InstallInData() {
1394 partition = "data"
1395 } else if ctx.InstallInRamdisk() {
1396 if ctx.DeviceConfig().BoardUsesRecoveryAsBoot() {
1397 partition = "recovery/root/first_stage_ramdisk"
1398 } else {
1399 partition = "ramdisk"
1400 }
1401 if !ctx.InstallInRoot() {
1402 partition += "/system"
1403 }
Yifan Hong60e0cfb2020-10-21 15:17:56 -07001404 } else if ctx.InstallInVendorRamdisk() {
Yifan Hong39143a92020-10-26 12:43:12 -07001405 // The module is only available after switching root into
1406 // /first_stage_ramdisk. To expose the module before switching root
1407 // on a device without a dedicated recovery partition, install the
1408 // recovery variant.
Yifan Hongdd8dacc2020-10-21 15:40:17 -07001409 if ctx.DeviceConfig().BoardMoveRecoveryResourcesToVendorBoot() {
Yifan Hong39143a92020-10-26 12:43:12 -07001410 partition = "vendor-ramdisk/first_stage_ramdisk"
Yifan Hongdd8dacc2020-10-21 15:40:17 -07001411 } else {
1412 partition = "vendor-ramdisk"
1413 }
1414 if !ctx.InstallInRoot() {
1415 partition += "/system"
1416 }
Colin Cross6e359402020-02-10 15:29:54 -08001417 } else if ctx.InstallInRecovery() {
1418 if ctx.InstallInRoot() {
1419 partition = "recovery/root"
1420 } else {
1421 // the layout of recovery partion is the same as that of system partition
1422 partition = "recovery/root/system"
1423 }
1424 } else if ctx.SocSpecific() {
1425 partition = ctx.DeviceConfig().VendorPath()
1426 } else if ctx.DeviceSpecific() {
1427 partition = ctx.DeviceConfig().OdmPath()
1428 } else if ctx.ProductSpecific() {
1429 partition = ctx.DeviceConfig().ProductPath()
1430 } else if ctx.SystemExtSpecific() {
1431 partition = ctx.DeviceConfig().SystemExtPath()
1432 } else if ctx.InstallInRoot() {
1433 partition = "root"
Yifan Hong82db7352020-01-21 16:12:26 -08001434 } else {
Colin Cross6e359402020-02-10 15:29:54 -08001435 partition = "system"
Yifan Hong82db7352020-01-21 16:12:26 -08001436 }
Colin Cross6e359402020-02-10 15:29:54 -08001437 if ctx.InstallInSanitizerDir() {
1438 partition = "data/asan/" + partition
Yifan Hong82db7352020-01-21 16:12:26 -08001439 }
Colin Cross43f08db2018-11-12 10:13:39 -08001440 }
1441 return partition
1442}
1443
Colin Cross609c49a2020-02-13 13:20:11 -08001444type InstallPaths []InstallPath
1445
1446// Paths returns the InstallPaths as a Paths
1447func (p InstallPaths) Paths() Paths {
1448 if p == nil {
1449 return nil
1450 }
1451 ret := make(Paths, len(p))
1452 for i, path := range p {
1453 ret[i] = path
1454 }
1455 return ret
1456}
1457
1458// Strings returns the string forms of the install paths.
1459func (p InstallPaths) Strings() []string {
1460 if p == nil {
1461 return nil
1462 }
1463 ret := make([]string, len(p))
1464 for i, path := range p {
1465 ret[i] = path.String()
1466 }
1467 return ret
1468}
1469
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001470// validateSafePath validates a path that we trust (may contain ninja variables).
Dan Willemsen80a7c2a2015-12-21 14:57:11 -08001471// Ensures that each path component does not attempt to leave its component.
Colin Cross1ccfcc32018-02-22 13:54:26 -08001472func validateSafePath(pathComponents ...string) (string, error) {
Jeff Gaston734e3802017-04-10 15:47:24 -07001473 for _, path := range pathComponents {
Dan Willemsen80a7c2a2015-12-21 14:57:11 -08001474 path := filepath.Clean(path)
1475 if path == ".." || strings.HasPrefix(path, "../") || strings.HasPrefix(path, "/") {
Colin Cross1ccfcc32018-02-22 13:54:26 -08001476 return "", fmt.Errorf("Path is outside directory: %s", path)
Dan Willemsen80a7c2a2015-12-21 14:57:11 -08001477 }
1478 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001479 // TODO: filepath.Join isn't necessarily correct with embedded ninja
1480 // variables. '..' may remove the entire ninja variable, even if it
1481 // will be expanded to multiple nested directories.
Colin Cross1ccfcc32018-02-22 13:54:26 -08001482 return filepath.Join(pathComponents...), nil
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001483}
1484
Dan Willemsen80a7c2a2015-12-21 14:57:11 -08001485// validatePath validates that a path does not include ninja variables, and that
1486// each path component does not attempt to leave its component. Returns a joined
1487// version of each path component.
Colin Cross1ccfcc32018-02-22 13:54:26 -08001488func validatePath(pathComponents ...string) (string, error) {
Jeff Gaston734e3802017-04-10 15:47:24 -07001489 for _, path := range pathComponents {
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001490 if strings.Contains(path, "$") {
Colin Cross1ccfcc32018-02-22 13:54:26 -08001491 return "", fmt.Errorf("Path contains invalid character($): %s", path)
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001492 }
1493 }
Colin Cross1ccfcc32018-02-22 13:54:26 -08001494 return validateSafePath(pathComponents...)
Colin Cross6e18ca42015-07-14 18:55:36 -07001495}
Colin Cross5b529592017-05-09 13:34:34 -07001496
Colin Cross0875c522017-11-28 17:34:01 -08001497func PathForPhony(ctx PathContext, phony string) WritablePath {
1498 if strings.ContainsAny(phony, "$/") {
Ulya Trafimovich5ab276a2020-08-25 12:45:15 +01001499 ReportPathErrorf(ctx, "Phony target contains invalid character ($ or /): %s", phony)
Colin Cross0875c522017-11-28 17:34:01 -08001500 }
Colin Cross74e3fe42017-12-11 15:51:44 -08001501 return PhonyPath{basePath{phony, ctx.Config(), ""}}
Colin Cross0875c522017-11-28 17:34:01 -08001502}
1503
Colin Cross74e3fe42017-12-11 15:51:44 -08001504type PhonyPath struct {
1505 basePath
1506}
1507
1508func (p PhonyPath) writablePath() {}
1509
Paul Duffin9b478b02019-12-10 13:41:51 +00001510func (p PhonyPath) buildDir() string {
1511 return p.config.buildDir
1512}
1513
Colin Cross74e3fe42017-12-11 15:51:44 -08001514var _ Path = PhonyPath{}
1515var _ WritablePath = PhonyPath{}
1516
Colin Cross5b529592017-05-09 13:34:34 -07001517type testPath struct {
1518 basePath
1519}
1520
1521func (p testPath) String() string {
1522 return p.path
1523}
1524
Colin Cross40e33732019-02-15 11:08:35 -08001525// PathForTesting returns a Path constructed from joining the elements of paths with '/'. It should only be used from
1526// within tests.
Colin Cross5b529592017-05-09 13:34:34 -07001527func PathForTesting(paths ...string) Path {
Colin Cross1ccfcc32018-02-22 13:54:26 -08001528 p, err := validateSafePath(paths...)
1529 if err != nil {
1530 panic(err)
1531 }
Colin Cross5b529592017-05-09 13:34:34 -07001532 return testPath{basePath{path: p, rel: p}}
1533}
1534
Colin Cross40e33732019-02-15 11:08:35 -08001535// PathsForTesting returns a Path constructed from each element in strs. It should only be used from within tests.
1536func PathsForTesting(strs ...string) Paths {
Colin Cross5b529592017-05-09 13:34:34 -07001537 p := make(Paths, len(strs))
1538 for i, s := range strs {
1539 p[i] = PathForTesting(s)
1540 }
1541
1542 return p
1543}
Colin Cross43f08db2018-11-12 10:13:39 -08001544
Colin Cross40e33732019-02-15 11:08:35 -08001545type testPathContext struct {
1546 config Config
Colin Cross40e33732019-02-15 11:08:35 -08001547}
1548
Colin Cross40e33732019-02-15 11:08:35 -08001549func (x *testPathContext) Config() Config { return x.config }
1550func (x *testPathContext) AddNinjaFileDeps(...string) {}
1551
1552// PathContextForTesting returns a PathContext that can be used in tests, for example to create an OutputPath with
1553// PathForOutput.
Colin Cross98be1bb2019-12-13 20:41:13 -08001554func PathContextForTesting(config Config) PathContext {
Colin Cross40e33732019-02-15 11:08:35 -08001555 return &testPathContext{
1556 config: config,
Colin Cross40e33732019-02-15 11:08:35 -08001557 }
1558}
1559
Ulya Trafimovichccc8c852020-10-14 11:29:07 +01001560type testModuleInstallPathContext struct {
1561 baseModuleContext
1562
1563 inData bool
1564 inTestcases bool
1565 inSanitizerDir bool
1566 inRamdisk bool
1567 inVendorRamdisk bool
1568 inRecovery bool
1569 inRoot bool
1570 forceOS *OsType
1571 forceArch *ArchType
1572}
1573
1574func (m testModuleInstallPathContext) Config() Config {
1575 return m.baseModuleContext.config
1576}
1577
1578func (testModuleInstallPathContext) AddNinjaFileDeps(deps ...string) {}
1579
1580func (m testModuleInstallPathContext) InstallInData() bool {
1581 return m.inData
1582}
1583
1584func (m testModuleInstallPathContext) InstallInTestcases() bool {
1585 return m.inTestcases
1586}
1587
1588func (m testModuleInstallPathContext) InstallInSanitizerDir() bool {
1589 return m.inSanitizerDir
1590}
1591
1592func (m testModuleInstallPathContext) InstallInRamdisk() bool {
1593 return m.inRamdisk
1594}
1595
1596func (m testModuleInstallPathContext) InstallInVendorRamdisk() bool {
1597 return m.inVendorRamdisk
1598}
1599
1600func (m testModuleInstallPathContext) InstallInRecovery() bool {
1601 return m.inRecovery
1602}
1603
1604func (m testModuleInstallPathContext) InstallInRoot() bool {
1605 return m.inRoot
1606}
1607
1608func (m testModuleInstallPathContext) InstallBypassMake() bool {
1609 return false
1610}
1611
1612func (m testModuleInstallPathContext) InstallForceOS() (*OsType, *ArchType) {
1613 return m.forceOS, m.forceArch
1614}
1615
1616// Construct a minimal ModuleInstallPathContext for testing. Note that baseModuleContext is
1617// default-initialized, which leaves blueprint.baseModuleContext set to nil, so methods that are
1618// delegated to it will panic.
1619func ModuleInstallPathContextForTesting(config Config) ModuleInstallPathContext {
1620 ctx := &testModuleInstallPathContext{}
1621 ctx.config = config
1622 ctx.os = Android
1623 return ctx
1624}
1625
Colin Cross43f08db2018-11-12 10:13:39 -08001626// Rel performs the same function as filepath.Rel, but reports errors to a PathContext, and reports an error if
1627// targetPath is not inside basePath.
1628func Rel(ctx PathContext, basePath string, targetPath string) string {
1629 rel, isRel := MaybeRel(ctx, basePath, targetPath)
1630 if !isRel {
Ulya Trafimovich5ab276a2020-08-25 12:45:15 +01001631 ReportPathErrorf(ctx, "path %q is not under path %q", targetPath, basePath)
Colin Cross43f08db2018-11-12 10:13:39 -08001632 return ""
1633 }
1634 return rel
1635}
1636
1637// MaybeRel performs the same function as filepath.Rel, but reports errors to a PathContext, and returns false if
1638// targetPath is not inside basePath.
1639func MaybeRel(ctx PathContext, basePath string, targetPath string) (string, bool) {
Dan Willemsen633c5022019-04-12 11:11:38 -07001640 rel, isRel, err := maybeRelErr(basePath, targetPath)
1641 if err != nil {
1642 reportPathError(ctx, err)
1643 }
1644 return rel, isRel
1645}
1646
1647func maybeRelErr(basePath string, targetPath string) (string, bool, error) {
Colin Cross43f08db2018-11-12 10:13:39 -08001648 // filepath.Rel returns an error if one path is absolute and the other is not, handle that case first.
1649 if filepath.IsAbs(basePath) != filepath.IsAbs(targetPath) {
Dan Willemsen633c5022019-04-12 11:11:38 -07001650 return "", false, nil
Colin Cross43f08db2018-11-12 10:13:39 -08001651 }
1652 rel, err := filepath.Rel(basePath, targetPath)
1653 if err != nil {
Dan Willemsen633c5022019-04-12 11:11:38 -07001654 return "", false, err
Colin Cross43f08db2018-11-12 10:13:39 -08001655 } else if rel == ".." || strings.HasPrefix(rel, "../") || strings.HasPrefix(rel, "/") {
Dan Willemsen633c5022019-04-12 11:11:38 -07001656 return "", false, nil
Colin Cross43f08db2018-11-12 10:13:39 -08001657 }
Dan Willemsen633c5022019-04-12 11:11:38 -07001658 return rel, true, nil
Colin Cross43f08db2018-11-12 10:13:39 -08001659}
Colin Cross988414c2020-01-11 01:11:46 +00001660
1661// Writes a file to the output directory. Attempting to write directly to the output directory
1662// will fail due to the sandbox of the soong_build process.
1663func WriteFileToOutputDir(path WritablePath, data []byte, perm os.FileMode) error {
1664 return ioutil.WriteFile(absolutePath(path.String()), data, perm)
1665}
1666
1667func absolutePath(path string) string {
1668 if filepath.IsAbs(path) {
1669 return path
1670 }
1671 return filepath.Join(absSrcDir, path)
1672}
Chris Parsons216e10a2020-07-09 17:12:52 -04001673
1674// A DataPath represents the path of a file to be used as data, for example
1675// a test library to be installed alongside a test.
1676// The data file should be installed (copied from `<SrcPath>`) to
1677// `<install_root>/<RelativeInstallPath>/<filename>`, or
1678// `<install_root>/<filename>` if RelativeInstallPath is empty.
1679type DataPath struct {
1680 // The path of the data file that should be copied into the data directory
1681 SrcPath Path
1682 // The install path of the data file, relative to the install root.
1683 RelativeInstallPath string
1684}