blob: f5bdb38674bc85969b64554271f0dff5bbb1528b [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 "os"
Colin Cross6a745c62015-06-16 16:38:10 -070020 "path/filepath"
Dan Willemsen34cc69e2015-09-23 15:26:20 -070021 "reflect"
Chris Wailesb2703ad2021-07-30 13:25:42 -070022 "regexp"
Colin Cross5e6cfbe2017-11-03 15:20:35 -070023 "sort"
Dan Willemsen34cc69e2015-09-23 15:26:20 -070024 "strings"
25
26 "github.com/google/blueprint"
Colin Cross0e446152021-05-03 13:35:32 -070027 "github.com/google/blueprint/bootstrap"
Dan Willemsen34cc69e2015-09-23 15:26:20 -070028 "github.com/google/blueprint/pathtools"
Colin Cross3f40fa42015-01-30 17:27:36 -080029)
30
Colin Cross988414c2020-01-11 01:11:46 +000031var absSrcDir string
32
Dan Willemsen34cc69e2015-09-23 15:26:20 -070033// PathContext is the subset of a (Module|Singleton)Context required by the
34// Path methods.
35type PathContext interface {
Colin Crossaabf6792017-11-29 00:27:14 -080036 Config() Config
Dan Willemsen7b310ee2015-12-18 15:11:17 -080037 AddNinjaFileDeps(deps ...string)
Colin Cross3f40fa42015-01-30 17:27:36 -080038}
39
Colin Cross7f19f372016-11-01 11:10:25 -070040type PathGlobContext interface {
Colin Cross662d6142022-11-03 20:38:01 -070041 PathContext
Colin Cross7f19f372016-11-01 11:10:25 -070042 GlobWithDeps(globPattern string, excludes []string) ([]string, error)
43}
44
Colin Crossaabf6792017-11-29 00:27:14 -080045var _ PathContext = SingletonContext(nil)
46var _ PathContext = ModuleContext(nil)
Dan Willemsen34cc69e2015-09-23 15:26:20 -070047
Ulya Trafimovich8640ab92020-05-11 18:06:15 +010048// "Null" path context is a minimal path context for a given config.
49type NullPathContext struct {
50 config Config
51}
52
53func (NullPathContext) AddNinjaFileDeps(...string) {}
54func (ctx NullPathContext) Config() Config { return ctx.config }
55
Liz Kammera830f3a2020-11-10 10:50:34 -080056// EarlyModulePathContext is a subset of EarlyModuleContext methods required by the
57// Path methods. These path methods can be called before any mutators have run.
58type EarlyModulePathContext interface {
Liz Kammera830f3a2020-11-10 10:50:34 -080059 PathGlobContext
60
61 ModuleDir() string
62 ModuleErrorf(fmt string, args ...interface{})
Cole Faust0e0d7492024-04-11 17:43:00 -070063 OtherModulePropertyErrorf(module Module, property, fmt string, args ...interface{})
Liz Kammera830f3a2020-11-10 10:50:34 -080064}
65
66var _ EarlyModulePathContext = ModuleContext(nil)
67
68// Glob globs files and directories matching globPattern relative to ModuleDir(),
69// paths in the excludes parameter will be omitted.
70func Glob(ctx EarlyModulePathContext, globPattern string, excludes []string) Paths {
71 ret, err := ctx.GlobWithDeps(globPattern, excludes)
72 if err != nil {
73 ctx.ModuleErrorf("glob: %s", err.Error())
74 }
75 return pathsForModuleSrcFromFullPath(ctx, ret, true)
76}
77
78// GlobFiles globs *only* files (not directories) matching globPattern relative to ModuleDir().
79// Paths in the excludes parameter will be omitted.
80func GlobFiles(ctx EarlyModulePathContext, globPattern string, excludes []string) Paths {
81 ret, err := ctx.GlobWithDeps(globPattern, excludes)
82 if err != nil {
83 ctx.ModuleErrorf("glob: %s", err.Error())
84 }
85 return pathsForModuleSrcFromFullPath(ctx, ret, false)
86}
87
88// ModuleWithDepsPathContext is a subset of *ModuleContext methods required by
89// the Path methods that rely on module dependencies having been resolved.
90type ModuleWithDepsPathContext interface {
91 EarlyModulePathContext
Paul Duffin40131a32021-07-09 17:10:35 +010092 VisitDirectDepsBlueprint(visit func(blueprint.Module))
93 OtherModuleDependencyTag(m blueprint.Module) blueprint.DependencyTag
Liz Kammera830f3a2020-11-10 10:50:34 -080094}
95
96// ModuleMissingDepsPathContext is a subset of *ModuleContext methods required by
97// the Path methods that rely on module dependencies having been resolved and ability to report
98// missing dependency errors.
99type ModuleMissingDepsPathContext interface {
100 ModuleWithDepsPathContext
101 AddMissingDependencies(missingDeps []string)
102}
103
Dan Willemsen00269f22017-07-06 16:59:48 -0700104type ModuleInstallPathContext interface {
Colin Cross0ea8ba82019-06-06 14:33:29 -0700105 BaseModuleContext
Dan Willemsen00269f22017-07-06 16:59:48 -0700106
107 InstallInData() bool
Jaewoong Jung0949f312019-09-11 10:25:18 -0700108 InstallInTestcases() bool
Dan Willemsen00269f22017-07-06 16:59:48 -0700109 InstallInSanitizerDir() bool
Yifan Hong1b3348d2020-01-21 15:53:22 -0800110 InstallInRamdisk() bool
Yifan Hong60e0cfb2020-10-21 15:17:56 -0700111 InstallInVendorRamdisk() bool
Inseob Kim08758f02021-04-08 21:13:22 +0900112 InstallInDebugRamdisk() bool
Jiyong Parkf9332f12018-02-01 00:54:12 +0900113 InstallInRecovery() bool
Colin Cross90ba5f42019-10-02 11:10:58 -0700114 InstallInRoot() bool
Colin Crossea30d852023-11-29 16:00:16 -0800115 InstallInOdm() bool
116 InstallInProduct() bool
117 InstallInVendor() bool
Jiyong Park87788b52020-09-01 12:37:45 +0900118 InstallForceOS() (*OsType, *ArchType)
Dan Willemsen00269f22017-07-06 16:59:48 -0700119}
120
121var _ ModuleInstallPathContext = ModuleContext(nil)
122
Cole Faust11edf552023-10-13 11:32:14 -0700123type baseModuleContextToModuleInstallPathContext struct {
124 BaseModuleContext
125}
126
127func (ctx *baseModuleContextToModuleInstallPathContext) InstallInData() bool {
128 return ctx.Module().InstallInData()
129}
130
131func (ctx *baseModuleContextToModuleInstallPathContext) InstallInTestcases() bool {
132 return ctx.Module().InstallInTestcases()
133}
134
135func (ctx *baseModuleContextToModuleInstallPathContext) InstallInSanitizerDir() bool {
136 return ctx.Module().InstallInSanitizerDir()
137}
138
139func (ctx *baseModuleContextToModuleInstallPathContext) InstallInRamdisk() bool {
140 return ctx.Module().InstallInRamdisk()
141}
142
143func (ctx *baseModuleContextToModuleInstallPathContext) InstallInVendorRamdisk() bool {
144 return ctx.Module().InstallInVendorRamdisk()
145}
146
147func (ctx *baseModuleContextToModuleInstallPathContext) InstallInDebugRamdisk() bool {
148 return ctx.Module().InstallInDebugRamdisk()
149}
150
151func (ctx *baseModuleContextToModuleInstallPathContext) InstallInRecovery() bool {
152 return ctx.Module().InstallInRecovery()
153}
154
155func (ctx *baseModuleContextToModuleInstallPathContext) InstallInRoot() bool {
156 return ctx.Module().InstallInRoot()
157}
158
Colin Crossea30d852023-11-29 16:00:16 -0800159func (ctx *baseModuleContextToModuleInstallPathContext) InstallInOdm() bool {
160 return ctx.Module().InstallInOdm()
161}
162
163func (ctx *baseModuleContextToModuleInstallPathContext) InstallInProduct() bool {
164 return ctx.Module().InstallInProduct()
165}
166
167func (ctx *baseModuleContextToModuleInstallPathContext) InstallInVendor() bool {
168 return ctx.Module().InstallInVendor()
169}
170
Cole Faust11edf552023-10-13 11:32:14 -0700171func (ctx *baseModuleContextToModuleInstallPathContext) InstallForceOS() (*OsType, *ArchType) {
172 return ctx.Module().InstallForceOS()
173}
174
175var _ ModuleInstallPathContext = (*baseModuleContextToModuleInstallPathContext)(nil)
176
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700177// errorfContext is the interface containing the Errorf method matching the
178// Errorf method in blueprint.SingletonContext.
179type errorfContext interface {
180 Errorf(format string, args ...interface{})
Colin Cross3f40fa42015-01-30 17:27:36 -0800181}
182
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700183var _ errorfContext = blueprint.SingletonContext(nil)
184
Spandan Das59a4a2b2024-01-09 21:35:56 +0000185// ModuleErrorfContext is the interface containing the ModuleErrorf method matching
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700186// the ModuleErrorf method in blueprint.ModuleContext.
Spandan Das59a4a2b2024-01-09 21:35:56 +0000187type ModuleErrorfContext interface {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700188 ModuleErrorf(format string, args ...interface{})
Colin Cross3f40fa42015-01-30 17:27:36 -0800189}
190
Spandan Das59a4a2b2024-01-09 21:35:56 +0000191var _ ModuleErrorfContext = blueprint.ModuleContext(nil)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700192
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700193// reportPathError will register an error with the attached context. It
194// attempts ctx.ModuleErrorf for a better error message first, then falls
195// back to ctx.Errorf.
Colin Cross1ccfcc32018-02-22 13:54:26 -0800196func reportPathError(ctx PathContext, err error) {
Ulya Trafimovich5ab276a2020-08-25 12:45:15 +0100197 ReportPathErrorf(ctx, "%s", err.Error())
Colin Cross1ccfcc32018-02-22 13:54:26 -0800198}
199
Ulya Trafimovich5ab276a2020-08-25 12:45:15 +0100200// ReportPathErrorf will register an error with the attached context. It
Colin Cross1ccfcc32018-02-22 13:54:26 -0800201// attempts ctx.ModuleErrorf for a better error message first, then falls
202// back to ctx.Errorf.
Ulya Trafimovich5ab276a2020-08-25 12:45:15 +0100203func ReportPathErrorf(ctx PathContext, format string, args ...interface{}) {
Spandan Das59a4a2b2024-01-09 21:35:56 +0000204 if mctx, ok := ctx.(ModuleErrorfContext); ok {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700205 mctx.ModuleErrorf(format, args...)
206 } else if ectx, ok := ctx.(errorfContext); ok {
207 ectx.Errorf(format, args...)
208 } else {
209 panic(fmt.Sprintf(format, args...))
Colin Crossf2298272015-05-12 11:36:53 -0700210 }
211}
212
Colin Cross5e708052019-08-06 13:59:50 -0700213func pathContextName(ctx PathContext, module blueprint.Module) string {
214 if x, ok := ctx.(interface{ ModuleName(blueprint.Module) string }); ok {
215 return x.ModuleName(module)
216 } else if x, ok := ctx.(interface{ OtherModuleName(blueprint.Module) string }); ok {
217 return x.OtherModuleName(module)
218 }
219 return "unknown"
220}
221
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700222type Path interface {
223 // Returns the path in string form
224 String() string
225
Colin Cross4f6fc9c2016-10-26 10:05:25 -0700226 // Ext returns the extension of the last element of the path
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700227 Ext() string
Colin Cross4f6fc9c2016-10-26 10:05:25 -0700228
229 // Base returns the last element of the path
230 Base() string
Colin Crossfaeb7aa2017-02-01 14:12:44 -0800231
232 // Rel returns the portion of the path relative to the directory it was created from. For
233 // example, Rel on a PathsForModuleSrc would return the path relative to the module source
Colin Cross0db55682017-12-05 15:36:55 -0800234 // directory, and OutputPath.Join("foo").Rel() would return "foo".
Colin Crossfaeb7aa2017-02-01 14:12:44 -0800235 Rel() string
Paul Duffin85d8f0d2021-03-24 10:18:18 +0000236
237 // RelativeToTop returns a new path relative to the top, it is provided solely for use in tests.
238 //
239 // It is guaranteed to always return the same type as it is called on, e.g. if called on an
240 // InstallPath then the returned value can be converted to an InstallPath.
241 //
242 // A standard build has the following structure:
243 // ../top/
244 // out/ - make install files go here.
Lukacs T. Berki9f6c24a2021-08-26 15:07:24 +0200245 // out/soong - this is the soongOutDir passed to NewTestConfig()
Paul Duffin85d8f0d2021-03-24 10:18:18 +0000246 // ... - the source files
247 //
248 // This function converts a path so that it appears relative to the ../top/ directory, i.e.
Lukacs T. Berki9f6c24a2021-08-26 15:07:24 +0200249 // * Make install paths, which have the pattern "soongOutDir/../<path>" are converted into the top
Paul Duffin85d8f0d2021-03-24 10:18:18 +0000250 // relative path "out/<path>"
Lukacs T. Berki9f6c24a2021-08-26 15:07:24 +0200251 // * Soong install paths and other writable paths, which have the pattern "soongOutDir/<path>" are
Paul Duffin85d8f0d2021-03-24 10:18:18 +0000252 // converted into the top relative path "out/soong/<path>".
253 // * Source paths are already relative to the top.
254 // * Phony paths are not relative to anything.
255 // * toolDepPath have an absolute but known value in so don't need making relative to anything in
256 // order to test.
257 RelativeToTop() Path
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700258}
259
Paul Duffin85d8f0d2021-03-24 10:18:18 +0000260const (
261 OutDir = "out"
262 OutSoongDir = OutDir + "/soong"
263)
264
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700265// WritablePath is a type of path that can be used as an output for build rules.
266type WritablePath interface {
267 Path
268
Paul Duffin9b478b02019-12-10 13:41:51 +0000269 // return the path to the build directory.
Lukacs T. Berki9f6c24a2021-08-26 15:07:24 +0200270 getSoongOutDir() string
Paul Duffin9b478b02019-12-10 13:41:51 +0000271
Jeff Gaston734e3802017-04-10 15:47:24 -0700272 // the writablePath method doesn't directly do anything,
273 // but it allows a struct to distinguish between whether or not it implements the WritablePath interface
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700274 writablePath()
Hans MÃ¥nssond3f2bd72020-11-27 12:37:28 +0100275
276 ReplaceExtension(ctx PathContext, ext string) OutputPath
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700277}
278
279type genPathProvider interface {
Liz Kammera830f3a2020-11-10 10:50:34 -0800280 genPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleGenPath
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700281}
282type objPathProvider interface {
Liz Kammera830f3a2020-11-10 10:50:34 -0800283 objPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleObjPath
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700284}
285type resPathProvider interface {
Liz Kammera830f3a2020-11-10 10:50:34 -0800286 resPathWithName(ctx ModuleOutPathContext, name string) ModuleResPath
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700287}
288
289// GenPathWithExt derives a new file path in ctx's generated sources directory
290// from the current path, but with the new extension.
Liz Kammera830f3a2020-11-10 10:50:34 -0800291func GenPathWithExt(ctx ModuleOutPathContext, subdir string, p Path, ext string) ModuleGenPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700292 if path, ok := p.(genPathProvider); ok {
Dan Willemsen21ec4902016-11-02 20:43:13 -0700293 return path.genPathWithExt(ctx, subdir, ext)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700294 }
Ulya Trafimovich5ab276a2020-08-25 12:45:15 +0100295 ReportPathErrorf(ctx, "Tried to create generated file from unsupported path: %s(%s)", reflect.TypeOf(p).Name(), p)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700296 return PathForModuleGen(ctx)
297}
298
299// ObjPathWithExt derives a new file path in ctx's object directory from the
300// current path, but with the new extension.
Liz Kammera830f3a2020-11-10 10:50:34 -0800301func ObjPathWithExt(ctx ModuleOutPathContext, subdir string, p Path, ext string) ModuleObjPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700302 if path, ok := p.(objPathProvider); ok {
303 return path.objPathWithExt(ctx, subdir, ext)
304 }
Ulya Trafimovich5ab276a2020-08-25 12:45:15 +0100305 ReportPathErrorf(ctx, "Tried to create object file from unsupported path: %s (%s)", reflect.TypeOf(p).Name(), p)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700306 return PathForModuleObj(ctx)
307}
308
309// ResPathWithName derives a new path in ctx's output resource directory, using
310// the current path to create the directory name, and the `name` argument for
311// the filename.
Liz Kammera830f3a2020-11-10 10:50:34 -0800312func ResPathWithName(ctx ModuleOutPathContext, p Path, name string) ModuleResPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700313 if path, ok := p.(resPathProvider); ok {
314 return path.resPathWithName(ctx, name)
315 }
Ulya Trafimovich5ab276a2020-08-25 12:45:15 +0100316 ReportPathErrorf(ctx, "Tried to create res file from unsupported path: %s (%s)", reflect.TypeOf(p).Name(), p)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700317 return PathForModuleRes(ctx)
318}
319
320// OptionalPath is a container that may or may not contain a valid Path.
321type OptionalPath struct {
Martin Stjernholmc32dd1c2021-09-15 02:39:00 +0100322 path Path // nil if invalid.
323 invalidReason string // Not applicable if path != nil. "" if the reason is unknown.
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700324}
325
326// OptionalPathForPath returns an OptionalPath containing the path.
327func OptionalPathForPath(path Path) OptionalPath {
Martin Stjernholm2fee27f2021-09-16 14:11:12 +0100328 return OptionalPath{path: path}
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700329}
330
Martin Stjernholmc32dd1c2021-09-15 02:39:00 +0100331// InvalidOptionalPath returns an OptionalPath that is invalid with the given reason.
332func InvalidOptionalPath(reason string) OptionalPath {
333
334 return OptionalPath{invalidReason: reason}
335}
336
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700337// Valid returns whether there is a valid path
338func (p OptionalPath) Valid() bool {
Martin Stjernholm2fee27f2021-09-16 14:11:12 +0100339 return p.path != nil
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700340}
341
342// Path returns the Path embedded in this OptionalPath. You must be sure that
343// there is a valid path, since this method will panic if there is not.
344func (p OptionalPath) Path() Path {
Martin Stjernholm2fee27f2021-09-16 14:11:12 +0100345 if p.path == nil {
Martin Stjernholmc32dd1c2021-09-15 02:39:00 +0100346 msg := "Requesting an invalid path"
347 if p.invalidReason != "" {
348 msg += ": " + p.invalidReason
349 }
350 panic(msg)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700351 }
352 return p.path
353}
354
Martin Stjernholmc32dd1c2021-09-15 02:39:00 +0100355// InvalidReason returns the reason that the optional path is invalid, or "" if it is valid.
356func (p OptionalPath) InvalidReason() string {
357 if p.path != nil {
358 return ""
359 }
360 if p.invalidReason == "" {
361 return "unknown"
362 }
363 return p.invalidReason
364}
365
Paul Duffinef081852021-05-13 11:11:15 +0100366// AsPaths converts the OptionalPath into Paths.
367//
368// It returns nil if this is not valid, or a single length slice containing the Path embedded in
369// this OptionalPath.
370func (p OptionalPath) AsPaths() Paths {
Martin Stjernholm2fee27f2021-09-16 14:11:12 +0100371 if p.path == nil {
Paul Duffinef081852021-05-13 11:11:15 +0100372 return nil
373 }
374 return Paths{p.path}
375}
376
Paul Duffinafdd4062021-03-30 19:44:07 +0100377// RelativeToTop returns an OptionalPath with the path that was embedded having been replaced by the
378// result of calling Path.RelativeToTop on it.
379func (p OptionalPath) RelativeToTop() OptionalPath {
Martin Stjernholm2fee27f2021-09-16 14:11:12 +0100380 if p.path == nil {
Paul Duffina5b81352021-03-28 23:57:19 +0100381 return p
382 }
383 p.path = p.path.RelativeToTop()
384 return p
385}
386
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700387// String returns the string version of the Path, or "" if it isn't valid.
388func (p OptionalPath) String() string {
Martin Stjernholm2fee27f2021-09-16 14:11:12 +0100389 if p.path != nil {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700390 return p.path.String()
391 } else {
392 return ""
Colin Crossf2298272015-05-12 11:36:53 -0700393 }
394}
Colin Cross6e18ca42015-07-14 18:55:36 -0700395
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700396// Paths is a slice of Path objects, with helpers to operate on the collection.
397type Paths []Path
398
Paul Duffin85d8f0d2021-03-24 10:18:18 +0000399// RelativeToTop creates a new Paths containing the result of calling Path.RelativeToTop on each
400// item in this slice.
401func (p Paths) RelativeToTop() Paths {
402 ensureTestOnly()
403 if p == nil {
404 return p
405 }
406 ret := make(Paths, len(p))
407 for i, path := range p {
408 ret[i] = path.RelativeToTop()
409 }
410 return ret
411}
412
Jingwen Chen40fd90a2020-06-15 05:24:19 +0000413func (paths Paths) containsPath(path Path) bool {
414 for _, p := range paths {
415 if p == path {
416 return true
417 }
418 }
419 return false
420}
421
Liz Kammer7aa52882021-02-11 09:16:14 -0500422// PathsForSource returns Paths rooted from SrcDir, *not* rooted from the module's local source
423// directory
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700424func PathsForSource(ctx PathContext, paths []string) Paths {
425 ret := make(Paths, len(paths))
426 for i, path := range paths {
427 ret[i] = PathForSource(ctx, path)
428 }
429 return ret
430}
431
Liz Kammer7aa52882021-02-11 09:16:14 -0500432// ExistentPathsForSources returns a list of Paths rooted from SrcDir, *not* rooted from the
433// module's local source directory, that are found in the tree. If any are not found, they are
434// omitted from the list, and dependencies are added so that we're re-run when they are added.
Colin Cross662d6142022-11-03 20:38:01 -0700435func ExistentPathsForSources(ctx PathGlobContext, paths []string) Paths {
Dan Willemsen7b310ee2015-12-18 15:11:17 -0800436 ret := make(Paths, 0, len(paths))
437 for _, path := range paths {
Colin Cross32f38982018-02-22 11:47:25 -0800438 p := ExistentPathForSource(ctx, path)
Dan Willemsen7b310ee2015-12-18 15:11:17 -0800439 if p.Valid() {
440 ret = append(ret, p.Path())
441 }
442 }
443 return ret
444}
445
Liz Kammer620dea62021-04-14 17:36:10 -0400446// PathsForModuleSrc returns a Paths{} containing the resolved references in paths:
Colin Crossd079e0b2022-08-16 10:27:33 -0700447// - filepath, relative to local module directory, resolves as a filepath relative to the local
448// source directory
449// - glob, relative to the local module directory, resolves as filepath(s), relative to the local
450// source directory.
451// - other modules using the ":name{.tag}" syntax. These modules must implement SourceFileProducer
452// or OutputFileProducer. These resolve as a filepath to an output filepath or generated source
453// filepath.
454//
Liz Kammer620dea62021-04-14 17:36:10 -0400455// Properties passed as the paths argument must have been annotated with struct tag
Colin Cross41955e82019-05-29 14:40:35 -0700456// `android:"path"` so that dependencies on SourceFileProducer modules will have already been handled by the
Spandan Das950091c2023-07-19 22:26:37 +0000457// pathdeps mutator.
Liz Kammer620dea62021-04-14 17:36:10 -0400458// If a requested module is not found as a dependency:
Colin Crossd079e0b2022-08-16 10:27:33 -0700459// - if ctx.Config().AllowMissingDependencies() is true, this module to be marked as having
Liz Kammer620dea62021-04-14 17:36:10 -0400460// missing dependencies
Colin Crossd079e0b2022-08-16 10:27:33 -0700461// - otherwise, a ModuleError is thrown.
Liz Kammera830f3a2020-11-10 10:50:34 -0800462func PathsForModuleSrc(ctx ModuleMissingDepsPathContext, paths []string) Paths {
Colin Cross8a497952019-03-05 22:25:09 -0800463 return PathsForModuleSrcExcludes(ctx, paths, nil)
464}
465
Liz Kammer619be462022-01-28 15:13:39 -0500466type SourceInput struct {
467 Context ModuleMissingDepsPathContext
468 Paths []string
469 ExcludePaths []string
470 IncludeDirs bool
471}
472
Liz Kammer620dea62021-04-14 17:36:10 -0400473// PathsForModuleSrcExcludes returns a Paths{} containing the resolved references in paths, minus
474// those listed in excludes. Elements of paths and excludes are resolved as:
Colin Crossd079e0b2022-08-16 10:27:33 -0700475// - filepath, relative to local module directory, resolves as a filepath relative to the local
476// source directory
477// - glob, relative to the local module directory, resolves as filepath(s), relative to the local
478// source directory. Not valid in excludes.
479// - other modules using the ":name{.tag}" syntax. These modules must implement SourceFileProducer
480// or OutputFileProducer. These resolve as a filepath to an output filepath or generated source
481// filepath.
482//
Liz Kammer620dea62021-04-14 17:36:10 -0400483// excluding the items (similarly resolved
484// Properties passed as the paths argument must have been annotated with struct tag
485// `android:"path"` so that dependencies on SourceFileProducer modules will have already been handled by the
Spandan Das950091c2023-07-19 22:26:37 +0000486// pathdeps mutator.
Liz Kammer620dea62021-04-14 17:36:10 -0400487// If a requested module is not found as a dependency:
Colin Crossd079e0b2022-08-16 10:27:33 -0700488// - if ctx.Config().AllowMissingDependencies() is true, this module to be marked as having
Liz Kammer620dea62021-04-14 17:36:10 -0400489// missing dependencies
Colin Crossd079e0b2022-08-16 10:27:33 -0700490// - otherwise, a ModuleError is thrown.
Liz Kammera830f3a2020-11-10 10:50:34 -0800491func PathsForModuleSrcExcludes(ctx ModuleMissingDepsPathContext, paths, excludes []string) Paths {
Liz Kammer619be462022-01-28 15:13:39 -0500492 return PathsRelativeToModuleSourceDir(SourceInput{
493 Context: ctx,
494 Paths: paths,
495 ExcludePaths: excludes,
496 IncludeDirs: true,
497 })
498}
499
500func PathsRelativeToModuleSourceDir(input SourceInput) Paths {
501 ret, missingDeps := PathsAndMissingDepsRelativeToModuleSourceDir(input)
502 if input.Context.Config().AllowMissingDependencies() {
503 input.Context.AddMissingDependencies(missingDeps)
Colin Crossba71a3f2019-03-18 12:12:48 -0700504 } else {
505 for _, m := range missingDeps {
Liz Kammer619be462022-01-28 15:13:39 -0500506 input.Context.ModuleErrorf(`missing dependency on %q, is the property annotated with android:"path"?`, m)
Colin Crossba71a3f2019-03-18 12:12:48 -0700507 }
508 }
509 return ret
510}
511
Ulya Trafimovich4d2eeed2019-11-08 10:54:21 +0000512// OutputPaths is a slice of OutputPath objects, with helpers to operate on the collection.
513type OutputPaths []OutputPath
514
515// Paths returns the OutputPaths as a Paths
516func (p OutputPaths) Paths() Paths {
517 if p == nil {
518 return nil
519 }
520 ret := make(Paths, len(p))
521 for i, path := range p {
522 ret[i] = path
523 }
524 return ret
525}
526
527// Strings returns the string forms of the writable paths.
528func (p OutputPaths) Strings() []string {
529 if p == nil {
530 return nil
531 }
532 ret := make([]string, len(p))
533 for i, path := range p {
534 ret[i] = path.String()
535 }
536 return ret
537}
538
Colin Crossa44551f2021-10-25 15:36:21 -0700539// PathForGoBinary returns the path to the installed location of a bootstrap_go_binary module.
540func PathForGoBinary(ctx PathContext, goBinary bootstrap.GoBinaryTool) Path {
Cole Faust3b703f32023-10-16 13:30:51 -0700541 goBinaryInstallDir := pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, "bin")
Colin Crossa44551f2021-10-25 15:36:21 -0700542 rel := Rel(ctx, goBinaryInstallDir.String(), goBinary.InstallPath())
543 return goBinaryInstallDir.Join(ctx, rel)
544}
545
Liz Kammera830f3a2020-11-10 10:50:34 -0800546// Expands Paths to a SourceFileProducer or OutputFileProducer module dependency referenced via ":name" or ":name{.tag}" syntax.
547// If the dependency is not found, a missingErrorDependency is returned.
548// If the module dependency is not a SourceFileProducer or OutputFileProducer, appropriate errors will be returned.
549func getPathsFromModuleDep(ctx ModuleWithDepsPathContext, path, moduleName, tag string) (Paths, error) {
Paul Duffind5cf92e2021-07-09 17:38:55 +0100550 module := GetModuleFromPathDep(ctx, moduleName, tag)
Liz Kammera830f3a2020-11-10 10:50:34 -0800551 if module == nil {
552 return nil, missingDependencyError{[]string{moduleName}}
553 }
Cole Faust0e0d7492024-04-11 17:43:00 -0700554 if aModule, ok := module.(Module); ok && !aModule.Enabled(ctx) {
Colin Crossfa65cee2021-03-22 17:05:59 -0700555 return nil, missingDependencyError{[]string{moduleName}}
556 }
Liz Kammera830f3a2020-11-10 10:50:34 -0800557 if outProducer, ok := module.(OutputFileProducer); ok {
558 outputFiles, err := outProducer.OutputFiles(tag)
559 if err != nil {
560 return nil, fmt.Errorf("path dependency %q: %s", path, err)
561 }
562 return outputFiles, nil
563 } else if tag != "" {
564 return nil, fmt.Errorf("path dependency %q is not an output file producing module", path)
Colin Cross0e446152021-05-03 13:35:32 -0700565 } else if goBinary, ok := module.(bootstrap.GoBinaryTool); ok {
Colin Crossa44551f2021-10-25 15:36:21 -0700566 goBinaryPath := PathForGoBinary(ctx, goBinary)
567 return Paths{goBinaryPath}, nil
Liz Kammera830f3a2020-11-10 10:50:34 -0800568 } else if srcProducer, ok := module.(SourceFileProducer); ok {
569 return srcProducer.Srcs(), nil
570 } else {
571 return nil, fmt.Errorf("path dependency %q is not a source file producing module", path)
572 }
573}
574
Paul Duffind5cf92e2021-07-09 17:38:55 +0100575// GetModuleFromPathDep will return the module that was added as a dependency automatically for
576// properties tagged with `android:"path"` or manually using ExtractSourceDeps or
577// ExtractSourcesDeps.
578//
579// The moduleName and tag supplied to this should be the values returned from SrcIsModuleWithTag.
580// Or, if no tag is expected then the moduleName should be the value returned by SrcIsModule and
581// the tag must be "".
582//
583// If tag is "" then the returned module will be the dependency that was added for ":moduleName".
584// Otherwise, it is the dependency that was added for ":moduleName{tag}".
Paul Duffind5cf92e2021-07-09 17:38:55 +0100585func GetModuleFromPathDep(ctx ModuleWithDepsPathContext, moduleName, tag string) blueprint.Module {
Paul Duffin40131a32021-07-09 17:10:35 +0100586 var found blueprint.Module
587 // The sourceOrOutputDepTag uniquely identifies the module dependency as it contains both the
588 // module name and the tag. Dependencies added automatically for properties tagged with
589 // `android:"path"` are deduped so are guaranteed to be unique. It is possible for duplicate
590 // dependencies to be added manually using ExtractSourcesDeps or ExtractSourceDeps but even then
591 // it will always be the case that the dependencies will be identical, i.e. the same tag and same
592 // moduleName referring to the same dependency module.
593 //
594 // It does not matter whether the moduleName is a fully qualified name or if the module
595 // dependency is a prebuilt module. All that matters is the same information is supplied to
596 // create the tag here as was supplied to create the tag when the dependency was added so that
597 // this finds the matching dependency module.
598 expectedTag := sourceOrOutputDepTag(moduleName, tag)
599 ctx.VisitDirectDepsBlueprint(func(module blueprint.Module) {
600 depTag := ctx.OtherModuleDependencyTag(module)
601 if depTag == expectedTag {
602 found = module
603 }
604 })
605 return found
Paul Duffind5cf92e2021-07-09 17:38:55 +0100606}
607
Liz Kammer620dea62021-04-14 17:36:10 -0400608// PathsAndMissingDepsForModuleSrcExcludes returns a Paths{} containing the resolved references in
609// paths, minus those listed in excludes. Elements of paths and excludes are resolved as:
Colin Crossd079e0b2022-08-16 10:27:33 -0700610// - filepath, relative to local module directory, resolves as a filepath relative to the local
611// source directory
612// - glob, relative to the local module directory, resolves as filepath(s), relative to the local
613// source directory. Not valid in excludes.
614// - other modules using the ":name{.tag}" syntax. These modules must implement SourceFileProducer
615// or OutputFileProducer. These resolve as a filepath to an output filepath or generated source
616// filepath.
617//
Liz Kammer620dea62021-04-14 17:36:10 -0400618// and a list of the module names of missing module dependencies are returned as the second return.
619// Properties passed as the paths argument must have been annotated with struct tag
Colin Cross41955e82019-05-29 14:40:35 -0700620// `android:"path"` so that dependencies on SourceFileProducer modules will have already been handled by the
Spandan Das950091c2023-07-19 22:26:37 +0000621// pathdeps mutator.
Liz Kammer619be462022-01-28 15:13:39 -0500622func PathsAndMissingDepsForModuleSrcExcludes(ctx ModuleMissingDepsPathContext, paths, excludes []string) (Paths, []string) {
623 return PathsAndMissingDepsRelativeToModuleSourceDir(SourceInput{
624 Context: ctx,
625 Paths: paths,
626 ExcludePaths: excludes,
627 IncludeDirs: true,
628 })
629}
630
631func PathsAndMissingDepsRelativeToModuleSourceDir(input SourceInput) (Paths, []string) {
632 prefix := pathForModuleSrc(input.Context).String()
Colin Cross8a497952019-03-05 22:25:09 -0800633
634 var expandedExcludes []string
Liz Kammer619be462022-01-28 15:13:39 -0500635 if input.ExcludePaths != nil {
636 expandedExcludes = make([]string, 0, len(input.ExcludePaths))
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700637 }
Colin Cross8a497952019-03-05 22:25:09 -0800638
Colin Crossba71a3f2019-03-18 12:12:48 -0700639 var missingExcludeDeps []string
Liz Kammer619be462022-01-28 15:13:39 -0500640 for _, e := range input.ExcludePaths {
Colin Cross41955e82019-05-29 14:40:35 -0700641 if m, t := SrcIsModuleWithTag(e); m != "" {
Liz Kammer619be462022-01-28 15:13:39 -0500642 modulePaths, err := getPathsFromModuleDep(input.Context, e, m, t)
Liz Kammera830f3a2020-11-10 10:50:34 -0800643 if m, ok := err.(missingDependencyError); ok {
644 missingExcludeDeps = append(missingExcludeDeps, m.missingDeps...)
645 } else if err != nil {
Liz Kammer619be462022-01-28 15:13:39 -0500646 reportPathError(input.Context, err)
Colin Cross8a497952019-03-05 22:25:09 -0800647 } else {
Liz Kammera830f3a2020-11-10 10:50:34 -0800648 expandedExcludes = append(expandedExcludes, modulePaths.Strings()...)
Colin Cross8a497952019-03-05 22:25:09 -0800649 }
650 } else {
651 expandedExcludes = append(expandedExcludes, filepath.Join(prefix, e))
652 }
653 }
654
Liz Kammer619be462022-01-28 15:13:39 -0500655 if input.Paths == nil {
Colin Crossba71a3f2019-03-18 12:12:48 -0700656 return nil, missingExcludeDeps
Colin Cross8a497952019-03-05 22:25:09 -0800657 }
658
Colin Crossba71a3f2019-03-18 12:12:48 -0700659 var missingDeps []string
660
Liz Kammer619be462022-01-28 15:13:39 -0500661 expandedSrcFiles := make(Paths, 0, len(input.Paths))
662 for _, s := range input.Paths {
663 srcFiles, err := expandOneSrcPath(sourcePathInput{
664 context: input.Context,
665 path: s,
666 expandedExcludes: expandedExcludes,
667 includeDirs: input.IncludeDirs,
668 })
Colin Cross8a497952019-03-05 22:25:09 -0800669 if depErr, ok := err.(missingDependencyError); ok {
Colin Crossba71a3f2019-03-18 12:12:48 -0700670 missingDeps = append(missingDeps, depErr.missingDeps...)
Colin Cross8a497952019-03-05 22:25:09 -0800671 } else if err != nil {
Liz Kammer619be462022-01-28 15:13:39 -0500672 reportPathError(input.Context, err)
Colin Cross8a497952019-03-05 22:25:09 -0800673 }
674 expandedSrcFiles = append(expandedSrcFiles, srcFiles...)
675 }
Colin Crossba71a3f2019-03-18 12:12:48 -0700676
Jihoon Kang0e3a5352024-04-12 00:45:50 +0000677 // TODO: b/334169722 - Replace with an error instead of implicitly removing duplicates.
678 return FirstUniquePaths(expandedSrcFiles), append(missingDeps, missingExcludeDeps...)
Colin Cross8a497952019-03-05 22:25:09 -0800679}
680
681type missingDependencyError struct {
682 missingDeps []string
683}
684
685func (e missingDependencyError) Error() string {
686 return "missing dependencies: " + strings.Join(e.missingDeps, ", ")
687}
688
Liz Kammer619be462022-01-28 15:13:39 -0500689type sourcePathInput struct {
690 context ModuleWithDepsPathContext
691 path string
692 expandedExcludes []string
693 includeDirs bool
694}
695
Liz Kammera830f3a2020-11-10 10:50:34 -0800696// Expands one path string to Paths rooted from the module's local source
697// directory, excluding those listed in the expandedExcludes.
698// Expands globs, references to SourceFileProducer or OutputFileProducer modules using the ":name" and ":name{.tag}" syntax.
Liz Kammer619be462022-01-28 15:13:39 -0500699func expandOneSrcPath(input sourcePathInput) (Paths, error) {
Jooyung Han7607dd32020-07-05 10:23:14 +0900700 excludePaths := func(paths Paths) Paths {
Liz Kammer619be462022-01-28 15:13:39 -0500701 if len(input.expandedExcludes) == 0 {
Jooyung Han7607dd32020-07-05 10:23:14 +0900702 return paths
703 }
704 remainder := make(Paths, 0, len(paths))
705 for _, p := range paths {
Liz Kammer619be462022-01-28 15:13:39 -0500706 if !InList(p.String(), input.expandedExcludes) {
Jooyung Han7607dd32020-07-05 10:23:14 +0900707 remainder = append(remainder, p)
708 }
709 }
710 return remainder
711 }
Liz Kammer619be462022-01-28 15:13:39 -0500712 if m, t := SrcIsModuleWithTag(input.path); m != "" {
713 modulePaths, err := getPathsFromModuleDep(input.context, input.path, m, t)
Liz Kammera830f3a2020-11-10 10:50:34 -0800714 if err != nil {
715 return nil, err
Colin Cross8a497952019-03-05 22:25:09 -0800716 } else {
Liz Kammera830f3a2020-11-10 10:50:34 -0800717 return excludePaths(modulePaths), nil
Colin Cross8a497952019-03-05 22:25:09 -0800718 }
Colin Cross8a497952019-03-05 22:25:09 -0800719 } else {
Liz Kammer619be462022-01-28 15:13:39 -0500720 p := pathForModuleSrc(input.context, input.path)
721 if pathtools.IsGlob(input.path) {
722 paths := GlobFiles(input.context, p.String(), input.expandedExcludes)
723 return PathsWithModuleSrcSubDir(input.context, paths, ""), nil
724 } else {
725 if exists, _, err := input.context.Config().fs.Exists(p.String()); err != nil {
726 ReportPathErrorf(input.context, "%s: %s", p, err.Error())
727 } else if !exists && !input.context.Config().TestAllowNonExistentPaths {
728 ReportPathErrorf(input.context, "module source path %q does not exist", p)
729 } else if !input.includeDirs {
730 if isDir, err := input.context.Config().fs.IsDir(p.String()); exists && err != nil {
731 ReportPathErrorf(input.context, "%s: %s", p, err.Error())
732 } else if isDir {
733 ReportPathErrorf(input.context, "module source path %q is a directory", p)
734 }
735 }
Colin Cross8a497952019-03-05 22:25:09 -0800736
Liz Kammer619be462022-01-28 15:13:39 -0500737 if InList(p.String(), input.expandedExcludes) {
738 return nil, nil
739 }
740 return Paths{p}, nil
Colin Cross8a497952019-03-05 22:25:09 -0800741 }
Colin Cross8a497952019-03-05 22:25:09 -0800742 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700743}
744
745// pathsForModuleSrcFromFullPath returns Paths rooted from the module's local
746// source directory, but strip the local source directory from the beginning of
Dan Willemsen540a78c2018-02-26 21:50:08 -0800747// each string. If incDirs is false, strip paths with a trailing '/' from the list.
Colin Crossfe4bc362018-09-12 10:02:13 -0700748// It intended for use in globs that only list files that exist, so it allows '$' in
749// filenames.
Liz Kammera830f3a2020-11-10 10:50:34 -0800750func pathsForModuleSrcFromFullPath(ctx EarlyModulePathContext, paths []string, incDirs bool) Paths {
Lukacs T. Berkif7e36d82021-08-16 17:05:09 +0200751 prefix := ctx.ModuleDir() + "/"
Colin Cross0f37af02017-09-27 17:42:05 -0700752 if prefix == "./" {
753 prefix = ""
754 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700755 ret := make(Paths, 0, len(paths))
756 for _, p := range paths {
Dan Willemsen540a78c2018-02-26 21:50:08 -0800757 if !incDirs && strings.HasSuffix(p, "/") {
758 continue
759 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700760 path := filepath.Clean(p)
761 if !strings.HasPrefix(path, prefix) {
Ulya Trafimovich5ab276a2020-08-25 12:45:15 +0100762 ReportPathErrorf(ctx, "Path %q is not in module source directory %q", p, prefix)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700763 continue
764 }
Colin Crosse3924e12018-08-15 20:18:53 -0700765
Colin Crossfe4bc362018-09-12 10:02:13 -0700766 srcPath, err := safePathForSource(ctx, ctx.ModuleDir(), path[len(prefix):])
Colin Crosse3924e12018-08-15 20:18:53 -0700767 if err != nil {
768 reportPathError(ctx, err)
769 continue
770 }
771
Colin Cross07e51612019-03-05 12:46:40 -0800772 srcPath.basePath.rel = srcPath.path
Colin Crosse3924e12018-08-15 20:18:53 -0700773
Colin Cross07e51612019-03-05 12:46:40 -0800774 ret = append(ret, srcPath)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700775 }
776 return ret
777}
778
Liz Kammera830f3a2020-11-10 10:50:34 -0800779// PathsWithOptionalDefaultForModuleSrc returns Paths rooted from the module's local source
780// directory. If input is nil, use the default if it exists. If input is empty, returns nil.
781func PathsWithOptionalDefaultForModuleSrc(ctx ModuleMissingDepsPathContext, input []string, def string) Paths {
Colin Cross0ddae7f2019-02-07 15:30:01 -0800782 if input != nil {
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700783 return PathsForModuleSrc(ctx, input)
784 }
785 // Use Glob so that if the default doesn't exist, a dependency is added so that when it
786 // is created, we're run again.
Lukacs T. Berkif7e36d82021-08-16 17:05:09 +0200787 path := filepath.Join(ctx.ModuleDir(), def)
Liz Kammera830f3a2020-11-10 10:50:34 -0800788 return Glob(ctx, path, nil)
Dan Willemsen34cc69e2015-09-23 15:26:20 -0700789}
790
791// Strings returns the Paths in string form
792func (p Paths) Strings() []string {
793 if p == nil {
794 return nil
795 }
796 ret := make([]string, len(p))
797 for i, path := range p {
798 ret[i] = path.String()
799 }
800 return ret
801}
802
Colin Crossc0efd1d2020-07-03 11:56:24 -0700803func CopyOfPaths(paths Paths) Paths {
804 return append(Paths(nil), paths...)
805}
806
Colin Crossb6715442017-10-24 11:13:31 -0700807// FirstUniquePaths returns all unique elements of a Paths, keeping the first copy of each. It
808// modifies the Paths slice contents in place, and returns a subslice of the original slice.
Dan Willemsenfe92c962017-08-29 12:28:37 -0700809func FirstUniquePaths(list Paths) Paths {
Colin Cross27027c72020-02-28 15:34:17 -0800810 // 128 was chosen based on BenchmarkFirstUniquePaths results.
811 if len(list) > 128 {
812 return firstUniquePathsMap(list)
813 }
814 return firstUniquePathsList(list)
815}
816
Colin Crossc0efd1d2020-07-03 11:56:24 -0700817// SortedUniquePaths returns all unique elements of a Paths in sorted order. It modifies the
818// Paths slice contents in place, and returns a subslice of the original slice.
Jiyong Park33c77362020-05-29 22:00:16 +0900819func SortedUniquePaths(list Paths) Paths {
820 unique := FirstUniquePaths(list)
821 sort.Slice(unique, func(i, j int) bool {
822 return unique[i].String() < unique[j].String()
823 })
824 return unique
825}
826
Colin Cross27027c72020-02-28 15:34:17 -0800827func firstUniquePathsList(list Paths) Paths {
Dan Willemsenfe92c962017-08-29 12:28:37 -0700828 k := 0
829outer:
830 for i := 0; i < len(list); i++ {
831 for j := 0; j < k; j++ {
832 if list[i] == list[j] {
833 continue outer
834 }
835 }
836 list[k] = list[i]
837 k++
838 }
839 return list[:k]
840}
841
Colin Cross27027c72020-02-28 15:34:17 -0800842func firstUniquePathsMap(list Paths) Paths {
843 k := 0
844 seen := make(map[Path]bool, len(list))
845 for i := 0; i < len(list); i++ {
846 if seen[list[i]] {
847 continue
848 }
849 seen[list[i]] = true
850 list[k] = list[i]
851 k++
852 }
853 return list[:k]
854}
855
Colin Cross5d583952020-11-24 16:21:24 -0800856// FirstUniqueInstallPaths returns all unique elements of an InstallPaths, keeping the first copy of each. It
857// modifies the InstallPaths slice contents in place, and returns a subslice of the original slice.
858func FirstUniqueInstallPaths(list InstallPaths) InstallPaths {
859 // 128 was chosen based on BenchmarkFirstUniquePaths results.
860 if len(list) > 128 {
861 return firstUniqueInstallPathsMap(list)
862 }
863 return firstUniqueInstallPathsList(list)
864}
865
866func firstUniqueInstallPathsList(list InstallPaths) InstallPaths {
867 k := 0
868outer:
869 for i := 0; i < len(list); i++ {
870 for j := 0; j < k; j++ {
871 if list[i] == list[j] {
872 continue outer
873 }
874 }
875 list[k] = list[i]
876 k++
877 }
878 return list[:k]
879}
880
881func firstUniqueInstallPathsMap(list InstallPaths) InstallPaths {
882 k := 0
883 seen := make(map[InstallPath]bool, len(list))
884 for i := 0; i < len(list); i++ {
885 if seen[list[i]] {
886 continue
887 }
888 seen[list[i]] = true
889 list[k] = list[i]
890 k++
891 }
892 return list[:k]
893}
894
Colin Crossb6715442017-10-24 11:13:31 -0700895// LastUniquePaths returns all unique elements of a Paths, keeping the last copy of each. It
896// modifies the Paths slice contents in place, and returns a subslice of the original slice.
897func LastUniquePaths(list Paths) Paths {
898 totalSkip := 0
899 for i := len(list) - 1; i >= totalSkip; i-- {
900 skip := 0
901 for j := i - 1; j >= totalSkip; j-- {
902 if list[i] == list[j] {
903 skip++
904 } else {
905 list[j+skip] = list[j]
906 }
907 }
908 totalSkip += skip
909 }
910 return list[totalSkip:]
911}
912
Colin Crossa140bb02018-04-17 10:52:26 -0700913// ReversePaths returns a copy of a Paths in reverse order.
914func ReversePaths(list Paths) Paths {
915 if list == nil {
916 return nil
917 }
918 ret := make(Paths, len(list))
919 for i := range list {
920 ret[i] = list[len(list)-1-i]
921 }
922 return ret
923}
924
Jeff Gaston294356f2017-09-27 17:05:30 -0700925func indexPathList(s Path, list []Path) int {
926 for i, l := range list {
927 if l == s {
928 return i
929 }
930 }
931
932 return -1
933}
934
935func inPathList(p Path, list []Path) bool {
936 return indexPathList(p, list) != -1
937}
938
939func FilterPathList(list []Path, filter []Path) (remainder []Path, filtered []Path) {
Paul Duffin57b9e1d2019-12-13 00:03:35 +0000940 return FilterPathListPredicate(list, func(p Path) bool { return inPathList(p, filter) })
941}
942
943func FilterPathListPredicate(list []Path, predicate func(Path) bool) (remainder []Path, filtered []Path) {
Jeff Gaston294356f2017-09-27 17:05:30 -0700944 for _, l := range list {
Paul Duffin57b9e1d2019-12-13 00:03:35 +0000945 if predicate(l) {
Jeff Gaston294356f2017-09-27 17:05:30 -0700946 filtered = append(filtered, l)
947 } else {
948 remainder = append(remainder, l)
949 }
950 }
951
952 return
953}
954
Colin Cross93e85952017-08-15 13:34:18 -0700955// HasExt returns true of any of the paths have extension ext, otherwise false
956func (p Paths) HasExt(ext string) bool {
957 for _, path := range p {
958 if path.Ext() == ext {
959 return true
960 }
961 }
962
963 return false
964}
965
966// FilterByExt returns the subset of the paths that have extension ext
967func (p Paths) FilterByExt(ext string) Paths {
968 ret := make(Paths, 0, len(p))
969 for _, path := range p {
970 if path.Ext() == ext {
971 ret = append(ret, path)
972 }
973 }
974 return ret
975}
976
977// FilterOutByExt returns the subset of the paths that do not have extension ext
978func (p Paths) FilterOutByExt(ext string) Paths {
979 ret := make(Paths, 0, len(p))
980 for _, path := range p {
981 if path.Ext() != ext {
982 ret = append(ret, path)
983 }
984 }
985 return ret
986}
987
Colin Cross5e6cfbe2017-11-03 15:20:35 -0700988// DirectorySortedPaths is a slice of paths that are sorted such that all files in a directory
989// (including subdirectories) are in a contiguous subslice of the list, and can be found in
990// O(log(N)) time using a binary search on the directory prefix.
991type DirectorySortedPaths Paths
992
993func PathsToDirectorySortedPaths(paths Paths) DirectorySortedPaths {
994 ret := append(DirectorySortedPaths(nil), paths...)
995 sort.Slice(ret, func(i, j int) bool {
996 return ret[i].String() < ret[j].String()
997 })
998 return ret
999}
1000
1001// PathsInDirectory returns a subslice of the DirectorySortedPaths as a Paths that contains all entries
1002// that are in the specified directory and its subdirectories.
1003func (p DirectorySortedPaths) PathsInDirectory(dir string) Paths {
1004 prefix := filepath.Clean(dir) + "/"
1005 start := sort.Search(len(p), func(i int) bool {
1006 return prefix < p[i].String()
1007 })
1008
1009 ret := p[start:]
1010
1011 end := sort.Search(len(ret), func(i int) bool {
1012 return !strings.HasPrefix(ret[i].String(), prefix)
1013 })
1014
1015 ret = ret[:end]
1016
1017 return Paths(ret)
1018}
1019
Alex Humesky29e3bbe2020-11-20 21:30:13 -05001020// WritablePaths is a slice of WritablePath, used for multiple outputs.
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001021type WritablePaths []WritablePath
1022
Paul Duffin85d8f0d2021-03-24 10:18:18 +00001023// RelativeToTop creates a new WritablePaths containing the result of calling Path.RelativeToTop on
1024// each item in this slice.
1025func (p WritablePaths) RelativeToTop() WritablePaths {
1026 ensureTestOnly()
1027 if p == nil {
1028 return p
1029 }
1030 ret := make(WritablePaths, len(p))
1031 for i, path := range p {
1032 ret[i] = path.RelativeToTop().(WritablePath)
1033 }
1034 return ret
1035}
1036
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001037// Strings returns the string forms of the writable paths.
1038func (p WritablePaths) Strings() []string {
1039 if p == nil {
1040 return nil
1041 }
1042 ret := make([]string, len(p))
1043 for i, path := range p {
1044 ret[i] = path.String()
1045 }
1046 return ret
1047}
1048
Colin Cross3bc7ffa2017-11-22 16:19:37 -08001049// Paths returns the WritablePaths as a Paths
1050func (p WritablePaths) Paths() Paths {
1051 if p == nil {
1052 return nil
1053 }
1054 ret := make(Paths, len(p))
1055 for i, path := range p {
1056 ret[i] = path
1057 }
1058 return ret
1059}
1060
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001061type basePath struct {
Paul Duffin74abc5d2021-03-24 09:24:59 +00001062 path string
1063 rel string
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001064}
1065
1066func (p basePath) Ext() string {
1067 return filepath.Ext(p.path)
1068}
1069
Colin Cross4f6fc9c2016-10-26 10:05:25 -07001070func (p basePath) Base() string {
1071 return filepath.Base(p.path)
1072}
1073
Colin Crossfaeb7aa2017-02-01 14:12:44 -08001074func (p basePath) Rel() string {
1075 if p.rel != "" {
1076 return p.rel
1077 }
1078 return p.path
1079}
1080
Colin Cross0875c522017-11-28 17:34:01 -08001081func (p basePath) String() string {
1082 return p.path
1083}
1084
Colin Cross0db55682017-12-05 15:36:55 -08001085func (p basePath) withRel(rel string) basePath {
1086 p.path = filepath.Join(p.path, rel)
1087 p.rel = rel
1088 return p
1089}
1090
Cole Faustbc65a3f2023-08-01 16:38:55 +00001091func (p basePath) RelativeToTop() Path {
1092 ensureTestOnly()
1093 return p
1094}
1095
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001096// SourcePath is a Path representing a file path rooted from SrcDir
1097type SourcePath struct {
1098 basePath
1099}
1100
1101var _ Path = SourcePath{}
1102
Colin Cross0db55682017-12-05 15:36:55 -08001103func (p SourcePath) withRel(rel string) SourcePath {
1104 p.basePath = p.basePath.withRel(rel)
1105 return p
1106}
1107
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001108// safePathForSource is for paths that we expect are safe -- only for use by go
1109// code that is embedding ninja variables in paths
Colin Crossfe4bc362018-09-12 10:02:13 -07001110func safePathForSource(ctx PathContext, pathComponents ...string) (SourcePath, error) {
1111 p, err := validateSafePath(pathComponents...)
Cole Faust483d1f72023-01-09 14:35:27 -08001112 ret := SourcePath{basePath{p, ""}}
Colin Crossfe4bc362018-09-12 10:02:13 -07001113 if err != nil {
1114 return ret, err
1115 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001116
Colin Cross7b3dcc32019-01-24 13:14:39 -08001117 // absolute path already checked by validateSafePath
Inseob Kim5eb7ee92022-04-27 10:30:34 +09001118 // special-case api surface gen files for now
1119 if strings.HasPrefix(ret.String(), ctx.Config().soongOutDir) && !strings.Contains(ret.String(), ctx.Config().soongOutDir+"/.export") {
Mikhail Naganovab1f5182019-02-08 13:17:55 -08001120 return ret, fmt.Errorf("source path %q is in output", ret.String())
Colin Cross6e18ca42015-07-14 18:55:36 -07001121 }
1122
Colin Crossfe4bc362018-09-12 10:02:13 -07001123 return ret, err
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001124}
1125
Colin Cross192e97a2018-02-22 14:21:02 -08001126// pathForSource creates a SourcePath from pathComponents, but does not check that it exists.
1127func pathForSource(ctx PathContext, pathComponents ...string) (SourcePath, error) {
Colin Crossc48c1432018-02-23 07:09:01 +00001128 p, err := validatePath(pathComponents...)
Cole Faust483d1f72023-01-09 14:35:27 -08001129 ret := SourcePath{basePath{p, ""}}
Colin Cross94a32102018-02-22 14:21:02 -08001130 if err != nil {
Colin Cross192e97a2018-02-22 14:21:02 -08001131 return ret, err
Colin Cross94a32102018-02-22 14:21:02 -08001132 }
1133
Colin Cross7b3dcc32019-01-24 13:14:39 -08001134 // absolute path already checked by validatePath
Inseob Kim5eb7ee92022-04-27 10:30:34 +09001135 // special-case for now
1136 if strings.HasPrefix(ret.String(), ctx.Config().soongOutDir) && !strings.Contains(ret.String(), ctx.Config().soongOutDir+"/.export") {
Mikhail Naganovab1f5182019-02-08 13:17:55 -08001137 return ret, fmt.Errorf("source path %q is in output", ret.String())
Colin Crossc48c1432018-02-23 07:09:01 +00001138 }
1139
Colin Cross192e97a2018-02-22 14:21:02 -08001140 return ret, nil
1141}
1142
1143// existsWithDependencies returns true if the path exists, and adds appropriate dependencies to rerun if the
1144// path does not exist.
Colin Cross662d6142022-11-03 20:38:01 -07001145func existsWithDependencies(ctx PathGlobContext, path SourcePath) (exists bool, err error) {
Colin Cross192e97a2018-02-22 14:21:02 -08001146 var files []string
1147
Colin Cross662d6142022-11-03 20:38:01 -07001148 // Use glob to produce proper dependencies, even though we only want
1149 // a single file.
1150 files, err = ctx.GlobWithDeps(path.String(), nil)
Colin Cross192e97a2018-02-22 14:21:02 -08001151
1152 if err != nil {
1153 return false, fmt.Errorf("glob: %s", err.Error())
1154 }
1155
1156 return len(files) > 0, nil
1157}
1158
1159// PathForSource joins the provided path components and validates that the result
1160// neither escapes the source dir nor is in the out dir.
1161// On error, it will return a usable, but invalid SourcePath, and report a ModuleError.
1162func PathForSource(ctx PathContext, pathComponents ...string) SourcePath {
1163 path, err := pathForSource(ctx, pathComponents...)
1164 if err != nil {
1165 reportPathError(ctx, err)
1166 }
1167
Colin Crosse3924e12018-08-15 20:18:53 -07001168 if pathtools.IsGlob(path.String()) {
Ulya Trafimovich5ab276a2020-08-25 12:45:15 +01001169 ReportPathErrorf(ctx, "path may not contain a glob: %s", path.String())
Colin Crosse3924e12018-08-15 20:18:53 -07001170 }
1171
Liz Kammera830f3a2020-11-10 10:50:34 -08001172 if modCtx, ok := ctx.(ModuleMissingDepsPathContext); ok && ctx.Config().AllowMissingDependencies() {
Colin Cross662d6142022-11-03 20:38:01 -07001173 exists, err := existsWithDependencies(modCtx, path)
Colin Cross192e97a2018-02-22 14:21:02 -08001174 if err != nil {
1175 reportPathError(ctx, err)
1176 }
1177 if !exists {
1178 modCtx.AddMissingDependencies([]string{path.String()})
1179 }
Colin Cross988414c2020-01-11 01:11:46 +00001180 } else if exists, _, err := ctx.Config().fs.Exists(path.String()); err != nil {
Ulya Trafimovich5ab276a2020-08-25 12:45:15 +01001181 ReportPathErrorf(ctx, "%s: %s", path, err.Error())
Pedro Loureiro5d190cc2021-02-15 15:41:33 +00001182 } else if !exists && !ctx.Config().TestAllowNonExistentPaths {
Ulya Trafimovich5ab276a2020-08-25 12:45:15 +01001183 ReportPathErrorf(ctx, "source path %q does not exist", path)
Colin Cross192e97a2018-02-22 14:21:02 -08001184 }
1185 return path
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001186}
1187
Cole Faustbc65a3f2023-08-01 16:38:55 +00001188// PathForArbitraryOutput creates a path for the given components. Unlike PathForOutput,
1189// the path is relative to the root of the output folder, not the out/soong folder.
1190func PathForArbitraryOutput(ctx PathContext, pathComponents ...string) Path {
1191 p, err := validatePath(pathComponents...)
1192 if err != nil {
1193 reportPathError(ctx, err)
1194 }
1195 return basePath{path: filepath.Join(ctx.Config().OutDir(), p)}
1196}
1197
Spandan Dasc6c10fa2022-10-21 21:52:13 +00001198// MaybeExistentPathForSource joins the provided path components and validates that the result
1199// neither escapes the source dir nor is in the out dir.
1200// It does not validate whether the path exists.
1201func MaybeExistentPathForSource(ctx PathContext, pathComponents ...string) SourcePath {
1202 path, err := pathForSource(ctx, pathComponents...)
1203 if err != nil {
1204 reportPathError(ctx, err)
1205 }
1206
1207 if pathtools.IsGlob(path.String()) {
1208 ReportPathErrorf(ctx, "path may not contain a glob: %s", path.String())
1209 }
1210 return path
1211}
1212
Liz Kammer7aa52882021-02-11 09:16:14 -05001213// ExistentPathForSource returns an OptionalPath with the SourcePath, rooted from SrcDir, *not*
1214// rooted from the module's local source directory, if the path exists, or an empty OptionalPath if
1215// it doesn't exist. Dependencies are added so that the ninja file will be regenerated if the state
1216// of the path changes.
Colin Cross662d6142022-11-03 20:38:01 -07001217func ExistentPathForSource(ctx PathGlobContext, pathComponents ...string) OptionalPath {
Colin Cross192e97a2018-02-22 14:21:02 -08001218 path, err := pathForSource(ctx, pathComponents...)
Colin Cross1ccfcc32018-02-22 13:54:26 -08001219 if err != nil {
1220 reportPathError(ctx, err)
Martin Stjernholmc32dd1c2021-09-15 02:39:00 +01001221 // No need to put the error message into the returned path since it has been reported already.
Colin Cross1ccfcc32018-02-22 13:54:26 -08001222 return OptionalPath{}
1223 }
Colin Crossc48c1432018-02-23 07:09:01 +00001224
Colin Crosse3924e12018-08-15 20:18:53 -07001225 if pathtools.IsGlob(path.String()) {
Ulya Trafimovich5ab276a2020-08-25 12:45:15 +01001226 ReportPathErrorf(ctx, "path may not contain a glob: %s", path.String())
Colin Crosse3924e12018-08-15 20:18:53 -07001227 return OptionalPath{}
1228 }
1229
Colin Cross192e97a2018-02-22 14:21:02 -08001230 exists, err := existsWithDependencies(ctx, path)
Colin Crossc48c1432018-02-23 07:09:01 +00001231 if err != nil {
1232 reportPathError(ctx, err)
1233 return OptionalPath{}
1234 }
Colin Cross192e97a2018-02-22 14:21:02 -08001235 if !exists {
Martin Stjernholmc32dd1c2021-09-15 02:39:00 +01001236 return InvalidOptionalPath(path.String() + " does not exist")
Colin Crossc48c1432018-02-23 07:09:01 +00001237 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001238 return OptionalPathForPath(path)
1239}
1240
1241func (p SourcePath) String() string {
Cole Faust483d1f72023-01-09 14:35:27 -08001242 if p.path == "" {
1243 return "."
1244 }
1245 return p.path
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001246}
1247
1248// Join creates a new SourcePath with paths... joined with the current path. The
1249// provided paths... may not use '..' to escape from the current path.
1250func (p SourcePath) Join(ctx PathContext, paths ...string) SourcePath {
Colin Cross1ccfcc32018-02-22 13:54:26 -08001251 path, err := validatePath(paths...)
1252 if err != nil {
1253 reportPathError(ctx, err)
1254 }
Colin Cross0db55682017-12-05 15:36:55 -08001255 return p.withRel(path)
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001256}
1257
Colin Cross2fafa3e2019-03-05 12:39:51 -08001258// join is like Join but does less path validation.
1259func (p SourcePath) join(ctx PathContext, paths ...string) SourcePath {
1260 path, err := validateSafePath(paths...)
1261 if err != nil {
1262 reportPathError(ctx, err)
1263 }
1264 return p.withRel(path)
1265}
1266
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001267// OverlayPath returns the overlay for `path' if it exists. This assumes that the
1268// SourcePath is the path to a resource overlay directory.
Liz Kammera830f3a2020-11-10 10:50:34 -08001269func (p SourcePath) OverlayPath(ctx ModuleMissingDepsPathContext, path Path) OptionalPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001270 var relDir string
Colin Cross07e51612019-03-05 12:46:40 -08001271 if srcPath, ok := path.(SourcePath); ok {
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001272 relDir = srcPath.path
1273 } else {
Ulya Trafimovich5ab276a2020-08-25 12:45:15 +01001274 ReportPathErrorf(ctx, "Cannot find relative path for %s(%s)", reflect.TypeOf(path).Name(), path)
Martin Stjernholmc32dd1c2021-09-15 02:39:00 +01001275 // No need to put the error message into the returned path since it has been reported already.
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001276 return OptionalPath{}
1277 }
Cole Faust483d1f72023-01-09 14:35:27 -08001278 dir := filepath.Join(p.path, relDir)
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001279 // Use Glob so that we are run again if the directory is added.
Colin Cross7f19f372016-11-01 11:10:25 -07001280 if pathtools.IsGlob(dir) {
Ulya Trafimovich5ab276a2020-08-25 12:45:15 +01001281 ReportPathErrorf(ctx, "Path may not contain a glob: %s", dir)
Dan Willemsen7b310ee2015-12-18 15:11:17 -08001282 }
Colin Cross461b4452018-02-23 09:22:42 -08001283 paths, err := ctx.GlobWithDeps(dir, nil)
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001284 if err != nil {
Ulya Trafimovich5ab276a2020-08-25 12:45:15 +01001285 ReportPathErrorf(ctx, "glob: %s", err.Error())
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001286 return OptionalPath{}
1287 }
1288 if len(paths) == 0 {
Martin Stjernholmc32dd1c2021-09-15 02:39:00 +01001289 return InvalidOptionalPath(dir + " does not exist")
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001290 }
Cole Faust483d1f72023-01-09 14:35:27 -08001291 return OptionalPathForPath(PathForSource(ctx, paths[0]))
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001292}
1293
Colin Cross70dda7e2019-10-01 22:05:35 -07001294// OutputPath is a Path representing an intermediates file path rooted from the build directory
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001295type OutputPath struct {
1296 basePath
Paul Duffind65c58b2021-03-24 09:22:07 +00001297
Lukacs T. Berkib078ade2021-08-31 10:42:08 +02001298 // The soong build directory, i.e. Config.SoongOutDir()
Lukacs T. Berki9f6c24a2021-08-26 15:07:24 +02001299 soongOutDir string
Paul Duffind65c58b2021-03-24 09:22:07 +00001300
Colin Crossd63c9a72020-01-29 16:52:50 -08001301 fullPath string
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001302}
1303
Colin Cross702e0f82017-10-18 17:27:54 -07001304func (p OutputPath) withRel(rel string) OutputPath {
Colin Cross0db55682017-12-05 15:36:55 -08001305 p.basePath = p.basePath.withRel(rel)
Colin Crossd63c9a72020-01-29 16:52:50 -08001306 p.fullPath = filepath.Join(p.fullPath, rel)
Colin Cross702e0f82017-10-18 17:27:54 -07001307 return p
1308}
1309
Colin Cross3063b782018-08-15 11:19:12 -07001310func (p OutputPath) WithoutRel() OutputPath {
1311 p.basePath.rel = filepath.Base(p.basePath.path)
1312 return p
1313}
1314
Lukacs T. Berki9f6c24a2021-08-26 15:07:24 +02001315func (p OutputPath) getSoongOutDir() string {
1316 return p.soongOutDir
Paul Duffin9b478b02019-12-10 13:41:51 +00001317}
1318
Paul Duffin85d8f0d2021-03-24 10:18:18 +00001319func (p OutputPath) RelativeToTop() Path {
1320 return p.outputPathRelativeToTop()
1321}
1322
1323func (p OutputPath) outputPathRelativeToTop() OutputPath {
Lukacs T. Berki9f6c24a2021-08-26 15:07:24 +02001324 p.fullPath = StringPathRelativeToTop(p.soongOutDir, p.fullPath)
1325 p.soongOutDir = OutSoongDir
Paul Duffin85d8f0d2021-03-24 10:18:18 +00001326 return p
1327}
1328
Paul Duffin0267d492021-02-02 10:05:52 +00001329func (p OutputPath) objPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleObjPath {
1330 return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
1331}
1332
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001333var _ Path = OutputPath{}
Paul Duffin9b478b02019-12-10 13:41:51 +00001334var _ WritablePath = OutputPath{}
Paul Duffin0267d492021-02-02 10:05:52 +00001335var _ objPathProvider = OutputPath{}
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001336
Chris Parsons8f232a22020-06-23 17:37:05 -04001337// toolDepPath is a Path representing a dependency of the build tool.
1338type toolDepPath struct {
1339 basePath
1340}
1341
Paul Duffin85d8f0d2021-03-24 10:18:18 +00001342func (t toolDepPath) RelativeToTop() Path {
1343 ensureTestOnly()
1344 return t
1345}
1346
Chris Parsons8f232a22020-06-23 17:37:05 -04001347var _ Path = toolDepPath{}
1348
1349// pathForBuildToolDep returns a toolDepPath representing the given path string.
1350// There is no validation for the path, as it is "trusted": It may fail
1351// normal validation checks. For example, it may be an absolute path.
1352// Only use this function to construct paths for dependencies of the build
1353// tool invocation.
1354func pathForBuildToolDep(ctx PathContext, path string) toolDepPath {
Paul Duffin74abc5d2021-03-24 09:24:59 +00001355 return toolDepPath{basePath{path, ""}}
Chris Parsons8f232a22020-06-23 17:37:05 -04001356}
1357
Jeff Gaston734e3802017-04-10 15:47:24 -07001358// PathForOutput joins the provided paths and returns an OutputPath that is
1359// validated to not escape the build dir.
1360// On error, it will return a usable, but invalid OutputPath, and report a ModuleError.
1361func PathForOutput(ctx PathContext, pathComponents ...string) OutputPath {
Colin Cross1ccfcc32018-02-22 13:54:26 -08001362 path, err := validatePath(pathComponents...)
1363 if err != nil {
1364 reportPathError(ctx, err)
1365 }
Lukacs T. Berki9f6c24a2021-08-26 15:07:24 +02001366 fullPath := filepath.Join(ctx.Config().soongOutDir, path)
Colin Crossd63c9a72020-01-29 16:52:50 -08001367 path = fullPath[len(fullPath)-len(path):]
Lukacs T. Berki9f6c24a2021-08-26 15:07:24 +02001368 return OutputPath{basePath{path, ""}, ctx.Config().soongOutDir, fullPath}
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001369}
1370
Lukacs T. Berki9f6c24a2021-08-26 15:07:24 +02001371// PathsForOutput returns Paths rooted from soongOutDir
Colin Cross40e33732019-02-15 11:08:35 -08001372func PathsForOutput(ctx PathContext, paths []string) WritablePaths {
1373 ret := make(WritablePaths, len(paths))
1374 for i, path := range paths {
1375 ret[i] = PathForOutput(ctx, path)
1376 }
1377 return ret
1378}
1379
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001380func (p OutputPath) writablePath() {}
1381
1382func (p OutputPath) String() string {
Colin Crossd63c9a72020-01-29 16:52:50 -08001383 return p.fullPath
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001384}
1385
1386// Join creates a new OutputPath with paths... joined with the current path. The
1387// provided paths... may not use '..' to escape from the current path.
1388func (p OutputPath) Join(ctx PathContext, paths ...string) OutputPath {
Colin Cross1ccfcc32018-02-22 13:54:26 -08001389 path, err := validatePath(paths...)
1390 if err != nil {
1391 reportPathError(ctx, err)
1392 }
Colin Cross0db55682017-12-05 15:36:55 -08001393 return p.withRel(path)
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001394}
1395
Colin Cross8854a5a2019-02-11 14:14:16 -08001396// ReplaceExtension creates a new OutputPath with the extension replaced with ext.
1397func (p OutputPath) ReplaceExtension(ctx PathContext, ext string) OutputPath {
1398 if strings.Contains(ext, "/") {
Ulya Trafimovich5ab276a2020-08-25 12:45:15 +01001399 ReportPathErrorf(ctx, "extension %q cannot contain /", ext)
Colin Cross8854a5a2019-02-11 14:14:16 -08001400 }
1401 ret := PathForOutput(ctx, pathtools.ReplaceExtension(p.path, ext))
Colin Cross2cdd5df2019-02-25 10:25:24 -08001402 ret.rel = pathtools.ReplaceExtension(p.rel, ext)
Colin Cross8854a5a2019-02-11 14:14:16 -08001403 return ret
1404}
1405
Colin Cross40e33732019-02-15 11:08:35 -08001406// InSameDir creates a new OutputPath from the directory of the current OutputPath joined with the elements in paths.
1407func (p OutputPath) InSameDir(ctx PathContext, paths ...string) OutputPath {
1408 path, err := validatePath(paths...)
1409 if err != nil {
1410 reportPathError(ctx, err)
1411 }
1412
1413 ret := PathForOutput(ctx, filepath.Dir(p.path), path)
Colin Cross2cdd5df2019-02-25 10:25:24 -08001414 ret.rel = filepath.Join(filepath.Dir(p.rel), path)
Colin Cross40e33732019-02-15 11:08:35 -08001415 return ret
1416}
1417
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001418// PathForIntermediates returns an OutputPath representing the top-level
1419// intermediates directory.
1420func PathForIntermediates(ctx PathContext, paths ...string) OutputPath {
Colin Cross1ccfcc32018-02-22 13:54:26 -08001421 path, err := validatePath(paths...)
1422 if err != nil {
1423 reportPathError(ctx, err)
1424 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001425 return PathForOutput(ctx, ".intermediates", path)
1426}
1427
Colin Cross07e51612019-03-05 12:46:40 -08001428var _ genPathProvider = SourcePath{}
1429var _ objPathProvider = SourcePath{}
1430var _ resPathProvider = SourcePath{}
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001431
Colin Cross07e51612019-03-05 12:46:40 -08001432// PathForModuleSrc returns a Path representing the paths... under the
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001433// module's local source directory.
Liz Kammera830f3a2020-11-10 10:50:34 -08001434func PathForModuleSrc(ctx ModuleMissingDepsPathContext, pathComponents ...string) Path {
Paul Duffin407501b2021-07-09 16:56:35 +01001435 // Just join the components textually just to make sure that it does not corrupt a fully qualified
1436 // module reference, e.g. if the pathComponents is "://other:foo" then using filepath.Join() or
1437 // validatePath() will corrupt it, e.g. replace "//" with "/". If the path is not a module
1438 // reference then it will be validated by expandOneSrcPath anyway when it calls expandOneSrcPath.
1439 p := strings.Join(pathComponents, string(filepath.Separator))
Liz Kammer619be462022-01-28 15:13:39 -05001440 paths, err := expandOneSrcPath(sourcePathInput{context: ctx, path: p, includeDirs: true})
Colin Cross8a497952019-03-05 22:25:09 -08001441 if err != nil {
1442 if depErr, ok := err.(missingDependencyError); ok {
1443 if ctx.Config().AllowMissingDependencies() {
1444 ctx.AddMissingDependencies(depErr.missingDeps)
1445 } else {
1446 ctx.ModuleErrorf(`%s, is the property annotated with android:"path"?`, depErr.Error())
1447 }
1448 } else {
1449 reportPathError(ctx, err)
1450 }
1451 return nil
1452 } else if len(paths) == 0 {
Ulya Trafimovich5ab276a2020-08-25 12:45:15 +01001453 ReportPathErrorf(ctx, "%q produced no files, expected exactly one", p)
Colin Cross8a497952019-03-05 22:25:09 -08001454 return nil
1455 } else if len(paths) > 1 {
Ulya Trafimovich5ab276a2020-08-25 12:45:15 +01001456 ReportPathErrorf(ctx, "%q produced %d files, expected exactly one", p, len(paths))
Colin Cross8a497952019-03-05 22:25:09 -08001457 }
1458 return paths[0]
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001459}
1460
Liz Kammera830f3a2020-11-10 10:50:34 -08001461func pathForModuleSrc(ctx EarlyModulePathContext, paths ...string) SourcePath {
Colin Cross07e51612019-03-05 12:46:40 -08001462 p, err := validatePath(paths...)
1463 if err != nil {
1464 reportPathError(ctx, err)
1465 }
1466
1467 path, err := pathForSource(ctx, ctx.ModuleDir(), p)
1468 if err != nil {
1469 reportPathError(ctx, err)
1470 }
1471
1472 path.basePath.rel = p
1473
1474 return path
1475}
1476
Colin Cross2fafa3e2019-03-05 12:39:51 -08001477// PathsWithModuleSrcSubDir takes a list of Paths and returns a new list of Paths where Rel() on each path
1478// will return the path relative to subDir in the module's source directory. If any input paths are not located
1479// inside subDir then a path error will be reported.
Liz Kammera830f3a2020-11-10 10:50:34 -08001480func PathsWithModuleSrcSubDir(ctx EarlyModulePathContext, paths Paths, subDir string) Paths {
Colin Cross2fafa3e2019-03-05 12:39:51 -08001481 paths = append(Paths(nil), paths...)
Colin Cross07e51612019-03-05 12:46:40 -08001482 subDirFullPath := pathForModuleSrc(ctx, subDir)
Colin Cross2fafa3e2019-03-05 12:39:51 -08001483 for i, path := range paths {
1484 rel := Rel(ctx, subDirFullPath.String(), path.String())
1485 paths[i] = subDirFullPath.join(ctx, rel)
1486 }
1487 return paths
1488}
1489
1490// PathWithModuleSrcSubDir takes a Path and returns a Path where Rel() will return the path relative to subDir in the
1491// module's source directory. If the input path is not located inside subDir then a path error will be reported.
Liz Kammera830f3a2020-11-10 10:50:34 -08001492func PathWithModuleSrcSubDir(ctx EarlyModulePathContext, path Path, subDir string) Path {
Colin Cross07e51612019-03-05 12:46:40 -08001493 subDirFullPath := pathForModuleSrc(ctx, subDir)
Colin Cross2fafa3e2019-03-05 12:39:51 -08001494 rel := Rel(ctx, subDirFullPath.String(), path.String())
1495 return subDirFullPath.Join(ctx, rel)
1496}
1497
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001498// OptionalPathForModuleSrc returns an OptionalPath. The OptionalPath contains a
1499// valid path if p is non-nil.
Liz Kammera830f3a2020-11-10 10:50:34 -08001500func OptionalPathForModuleSrc(ctx ModuleMissingDepsPathContext, p *string) OptionalPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001501 if p == nil {
1502 return OptionalPath{}
1503 }
1504 return OptionalPathForPath(PathForModuleSrc(ctx, *p))
1505}
1506
Liz Kammera830f3a2020-11-10 10:50:34 -08001507func (p SourcePath) genPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleGenPath {
Colin Cross7fc17db2017-02-01 14:07:55 -08001508 return PathForModuleGen(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001509}
1510
Liz Kammera830f3a2020-11-10 10:50:34 -08001511func (p SourcePath) objPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleObjPath {
Colin Cross7fc17db2017-02-01 14:07:55 -08001512 return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001513}
1514
Liz Kammera830f3a2020-11-10 10:50:34 -08001515func (p SourcePath) resPathWithName(ctx ModuleOutPathContext, name string) ModuleResPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001516 // TODO: Use full directory if the new ctx is not the current ctx?
1517 return PathForModuleRes(ctx, p.path, name)
1518}
1519
1520// ModuleOutPath is a Path representing a module's output directory.
1521type ModuleOutPath struct {
1522 OutputPath
1523}
1524
Paul Duffin85d8f0d2021-03-24 10:18:18 +00001525func (p ModuleOutPath) RelativeToTop() Path {
1526 p.OutputPath = p.outputPathRelativeToTop()
1527 return p
1528}
1529
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001530var _ Path = ModuleOutPath{}
Paul Duffin85d8f0d2021-03-24 10:18:18 +00001531var _ WritablePath = ModuleOutPath{}
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001532
Liz Kammera830f3a2020-11-10 10:50:34 -08001533func (p ModuleOutPath) objPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleObjPath {
Pete Bentleyfcf55bf2019-08-16 20:14:32 +01001534 return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
1535}
1536
Liz Kammera830f3a2020-11-10 10:50:34 -08001537// ModuleOutPathContext Subset of ModuleContext functions necessary for output path methods.
1538type ModuleOutPathContext interface {
1539 PathContext
1540
1541 ModuleName() string
1542 ModuleDir() string
1543 ModuleSubDir() string
Inseob Kim8ff69de2023-06-16 14:19:33 +09001544 SoongConfigTraceHash() string
Liz Kammera830f3a2020-11-10 10:50:34 -08001545}
1546
1547func pathForModuleOut(ctx ModuleOutPathContext) OutputPath {
Inseob Kim8ff69de2023-06-16 14:19:33 +09001548 return PathForOutput(ctx, ".intermediates", ctx.ModuleDir(), ctx.ModuleName(), ctx.ModuleSubDir(), ctx.SoongConfigTraceHash())
Colin Cross702e0f82017-10-18 17:27:54 -07001549}
1550
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001551// PathForModuleOut returns a Path representing the paths... under the module's
1552// output directory.
Liz Kammera830f3a2020-11-10 10:50:34 -08001553func PathForModuleOut(ctx ModuleOutPathContext, paths ...string) ModuleOutPath {
Colin Cross1ccfcc32018-02-22 13:54:26 -08001554 p, err := validatePath(paths...)
1555 if err != nil {
1556 reportPathError(ctx, err)
1557 }
Colin Cross702e0f82017-10-18 17:27:54 -07001558 return ModuleOutPath{
Liz Kammera830f3a2020-11-10 10:50:34 -08001559 OutputPath: pathForModuleOut(ctx).withRel(p),
Colin Cross702e0f82017-10-18 17:27:54 -07001560 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001561}
1562
1563// ModuleGenPath is a Path representing the 'gen' directory in a module's output
1564// directory. Mainly used for generated sources.
1565type ModuleGenPath struct {
1566 ModuleOutPath
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001567}
1568
Paul Duffin85d8f0d2021-03-24 10:18:18 +00001569func (p ModuleGenPath) RelativeToTop() Path {
1570 p.OutputPath = p.outputPathRelativeToTop()
1571 return p
1572}
1573
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001574var _ Path = ModuleGenPath{}
Paul Duffin85d8f0d2021-03-24 10:18:18 +00001575var _ WritablePath = ModuleGenPath{}
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001576var _ genPathProvider = ModuleGenPath{}
1577var _ objPathProvider = ModuleGenPath{}
1578
1579// PathForModuleGen returns a Path representing the paths... under the module's
1580// `gen' directory.
Liz Kammera830f3a2020-11-10 10:50:34 -08001581func PathForModuleGen(ctx ModuleOutPathContext, paths ...string) ModuleGenPath {
Colin Cross1ccfcc32018-02-22 13:54:26 -08001582 p, err := validatePath(paths...)
1583 if err != nil {
1584 reportPathError(ctx, err)
1585 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001586 return ModuleGenPath{
Colin Cross702e0f82017-10-18 17:27:54 -07001587 ModuleOutPath: ModuleOutPath{
Liz Kammera830f3a2020-11-10 10:50:34 -08001588 OutputPath: pathForModuleOut(ctx).withRel("gen").withRel(p),
Colin Cross702e0f82017-10-18 17:27:54 -07001589 },
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001590 }
1591}
1592
Liz Kammera830f3a2020-11-10 10:50:34 -08001593func (p ModuleGenPath) genPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleGenPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001594 // TODO: make a different path for local vs remote generated files?
Dan Willemsen21ec4902016-11-02 20:43:13 -07001595 return PathForModuleGen(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001596}
1597
Liz Kammera830f3a2020-11-10 10:50:34 -08001598func (p ModuleGenPath) objPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleObjPath {
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001599 return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
1600}
1601
1602// ModuleObjPath is a Path representing the 'obj' directory in a module's output
1603// directory. Used for compiled objects.
1604type ModuleObjPath struct {
1605 ModuleOutPath
1606}
1607
Paul Duffin85d8f0d2021-03-24 10:18:18 +00001608func (p ModuleObjPath) RelativeToTop() Path {
1609 p.OutputPath = p.outputPathRelativeToTop()
1610 return p
1611}
1612
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001613var _ Path = ModuleObjPath{}
Paul Duffin85d8f0d2021-03-24 10:18:18 +00001614var _ WritablePath = ModuleObjPath{}
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001615
1616// PathForModuleObj returns a Path representing the paths... under the module's
1617// 'obj' directory.
Liz Kammera830f3a2020-11-10 10:50:34 -08001618func PathForModuleObj(ctx ModuleOutPathContext, pathComponents ...string) ModuleObjPath {
Colin Cross1ccfcc32018-02-22 13:54:26 -08001619 p, err := validatePath(pathComponents...)
1620 if err != nil {
1621 reportPathError(ctx, err)
1622 }
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001623 return ModuleObjPath{PathForModuleOut(ctx, "obj", p)}
1624}
1625
1626// ModuleResPath is a a Path representing the 'res' directory in a module's
1627// output directory.
1628type ModuleResPath struct {
1629 ModuleOutPath
1630}
1631
Paul Duffin85d8f0d2021-03-24 10:18:18 +00001632func (p ModuleResPath) RelativeToTop() Path {
1633 p.OutputPath = p.outputPathRelativeToTop()
1634 return p
1635}
1636
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001637var _ Path = ModuleResPath{}
Paul Duffin85d8f0d2021-03-24 10:18:18 +00001638var _ WritablePath = ModuleResPath{}
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001639
1640// PathForModuleRes returns a Path representing the paths... under the module's
1641// 'res' directory.
Liz Kammera830f3a2020-11-10 10:50:34 -08001642func PathForModuleRes(ctx ModuleOutPathContext, pathComponents ...string) ModuleResPath {
Colin Cross1ccfcc32018-02-22 13:54:26 -08001643 p, err := validatePath(pathComponents...)
1644 if err != nil {
1645 reportPathError(ctx, err)
1646 }
1647
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001648 return ModuleResPath{PathForModuleOut(ctx, "res", p)}
1649}
1650
Colin Cross70dda7e2019-10-01 22:05:35 -07001651// InstallPath is a Path representing a installed file path rooted from the build directory
1652type InstallPath struct {
1653 basePath
Colin Crossff6c33d2019-10-02 16:01:35 -07001654
Lukacs T. Berkib078ade2021-08-31 10:42:08 +02001655 // The soong build directory, i.e. Config.SoongOutDir()
Lukacs T. Berki9f6c24a2021-08-26 15:07:24 +02001656 soongOutDir string
Paul Duffind65c58b2021-03-24 09:22:07 +00001657
Jiyong Park957bcd92020-10-20 18:23:33 +09001658 // partitionDir is the part of the InstallPath that is automatically determined according to the context.
1659 // For example, it is host/<os>-<arch> for host modules, and target/product/<device>/<partition> for device modules.
1660 partitionDir string
1661
Colin Crossb1692a32021-10-25 15:39:01 -07001662 partition string
1663
Jiyong Park957bcd92020-10-20 18:23:33 +09001664 // makePath indicates whether this path is for Soong (false) or Make (true).
1665 makePath bool
Colin Crossc0e42d52024-02-01 16:42:36 -08001666
1667 fullPath string
Colin Cross70dda7e2019-10-01 22:05:35 -07001668}
1669
Paul Duffin85d8f0d2021-03-24 10:18:18 +00001670// Will panic if called from outside a test environment.
1671func ensureTestOnly() {
Martin Stjernholm32312eb2021-03-27 18:54:49 +00001672 if PrefixInList(os.Args, "-test.") {
Paul Duffin85d8f0d2021-03-24 10:18:18 +00001673 return
1674 }
Martin Stjernholm32312eb2021-03-27 18:54:49 +00001675 panic(fmt.Errorf("Not in test. Command line:\n %s", strings.Join(os.Args, "\n ")))
Paul Duffin85d8f0d2021-03-24 10:18:18 +00001676}
1677
1678func (p InstallPath) RelativeToTop() Path {
1679 ensureTestOnly()
Colin Crossc0e42d52024-02-01 16:42:36 -08001680 if p.makePath {
1681 p.soongOutDir = OutDir
1682 } else {
1683 p.soongOutDir = OutSoongDir
1684 }
1685 p.fullPath = filepath.Join(p.soongOutDir, p.path)
Paul Duffin85d8f0d2021-03-24 10:18:18 +00001686 return p
1687}
1688
Lukacs T. Berki9f6c24a2021-08-26 15:07:24 +02001689func (p InstallPath) getSoongOutDir() string {
1690 return p.soongOutDir
Paul Duffin9b478b02019-12-10 13:41:51 +00001691}
1692
Hans MÃ¥nssond3f2bd72020-11-27 12:37:28 +01001693func (p InstallPath) ReplaceExtension(ctx PathContext, ext string) OutputPath {
1694 panic("Not implemented")
1695}
1696
Paul Duffin9b478b02019-12-10 13:41:51 +00001697var _ Path = InstallPath{}
1698var _ WritablePath = InstallPath{}
1699
Colin Cross70dda7e2019-10-01 22:05:35 -07001700func (p InstallPath) writablePath() {}
1701
1702func (p InstallPath) String() string {
Colin Crossc0e42d52024-02-01 16:42:36 -08001703 return p.fullPath
Jiyong Park957bcd92020-10-20 18:23:33 +09001704}
1705
1706// PartitionDir returns the path to the partition where the install path is rooted at. It is
1707// out/soong/target/product/<device>/<partition> for device modules, and out/soong/host/<os>-<arch> for host modules.
1708// The ./soong is dropped if the install path is for Make.
1709func (p InstallPath) PartitionDir() string {
1710 if p.makePath {
Lukacs T. Berki9f6c24a2021-08-26 15:07:24 +02001711 return filepath.Join(p.soongOutDir, "../", p.partitionDir)
Jiyong Park957bcd92020-10-20 18:23:33 +09001712 } else {
Lukacs T. Berki9f6c24a2021-08-26 15:07:24 +02001713 return filepath.Join(p.soongOutDir, p.partitionDir)
Jiyong Park957bcd92020-10-20 18:23:33 +09001714 }
Colin Cross70dda7e2019-10-01 22:05:35 -07001715}
1716
Jihoon Kangf78a8902022-09-01 22:47:07 +00001717func (p InstallPath) Partition() string {
1718 return p.partition
1719}
1720
Colin Cross70dda7e2019-10-01 22:05:35 -07001721// Join creates a new InstallPath with paths... joined with the current path. The
1722// provided paths... may not use '..' to escape from the current path.
1723func (p InstallPath) Join(ctx PathContext, paths ...string) InstallPath {
1724 path, err := validatePath(paths...)
1725 if err != nil {
1726 reportPathError(ctx, err)
1727 }
1728 return p.withRel(path)
1729}
1730
1731func (p InstallPath) withRel(rel string) InstallPath {
1732 p.basePath = p.basePath.withRel(rel)
Colin Crossc0e42d52024-02-01 16:42:36 -08001733 p.fullPath = filepath.Join(p.fullPath, rel)
Colin Cross70dda7e2019-10-01 22:05:35 -07001734 return p
1735}
1736
Colin Crossc68db4b2021-11-11 18:59:15 -08001737// Deprecated: ToMakePath is a noop, PathForModuleInstall always returns Make paths when building
1738// embedded in Make.
Colin Crossff6c33d2019-10-02 16:01:35 -07001739func (p InstallPath) ToMakePath() InstallPath {
Jiyong Park957bcd92020-10-20 18:23:33 +09001740 p.makePath = true
Colin Crossff6c33d2019-10-02 16:01:35 -07001741 return p
Colin Cross70dda7e2019-10-01 22:05:35 -07001742}
1743
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001744// PathForModuleInstall returns a Path representing the install path for the
1745// module appended with paths...
Colin Cross70dda7e2019-10-01 22:05:35 -07001746func PathForModuleInstall(ctx ModuleInstallPathContext, pathComponents ...string) InstallPath {
Spandan Das5d1b9292021-06-03 19:36:41 +00001747 os, arch := osAndArch(ctx)
Cole Faust11edf552023-10-13 11:32:14 -07001748 partition := modulePartition(ctx, os.Class == Device)
Cole Faust3b703f32023-10-16 13:30:51 -07001749 return pathForInstall(ctx, os, arch, partition, pathComponents...)
Spandan Das5d1b9292021-06-03 19:36:41 +00001750}
1751
Colin Cross1d0eb7a2021-11-03 14:08:20 -07001752// PathForHostDexInstall returns an InstallPath representing the install path for the
1753// module appended with paths...
1754func PathForHostDexInstall(ctx ModuleInstallPathContext, pathComponents ...string) InstallPath {
Cole Faust3b703f32023-10-16 13:30:51 -07001755 return pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, "", pathComponents...)
Colin Cross1d0eb7a2021-11-03 14:08:20 -07001756}
1757
Spandan Das5d1b9292021-06-03 19:36:41 +00001758// PathForModuleInPartitionInstall is similar to PathForModuleInstall but partition is provided by the caller
1759func PathForModuleInPartitionInstall(ctx ModuleInstallPathContext, partition string, pathComponents ...string) InstallPath {
1760 os, arch := osAndArch(ctx)
Cole Faust3b703f32023-10-16 13:30:51 -07001761 return pathForInstall(ctx, os, arch, partition, pathComponents...)
Spandan Das5d1b9292021-06-03 19:36:41 +00001762}
1763
1764func osAndArch(ctx ModuleInstallPathContext) (OsType, ArchType) {
Colin Cross6e359402020-02-10 15:29:54 -08001765 os := ctx.Os()
Jiyong Park87788b52020-09-01 12:37:45 +09001766 arch := ctx.Arch().ArchType
1767 forceOS, forceArch := ctx.InstallForceOS()
1768 if forceOS != nil {
Colin Cross6e359402020-02-10 15:29:54 -08001769 os = *forceOS
1770 }
Jiyong Park87788b52020-09-01 12:37:45 +09001771 if forceArch != nil {
1772 arch = *forceArch
1773 }
Spandan Das5d1b9292021-06-03 19:36:41 +00001774 return os, arch
1775}
Colin Cross609c49a2020-02-13 13:20:11 -08001776
Colin Crossc0e42d52024-02-01 16:42:36 -08001777func pathForPartitionInstallDir(ctx PathContext, partition, partitionPath string, makePath bool) InstallPath {
1778 fullPath := ctx.Config().SoongOutDir()
1779 if makePath {
1780 // Make path starts with out/ instead of out/soong.
1781 fullPath = filepath.Join(fullPath, "../", partitionPath)
1782 } else {
1783 fullPath = filepath.Join(fullPath, partitionPath)
1784 }
1785
1786 return InstallPath{
1787 basePath: basePath{partitionPath, ""},
1788 soongOutDir: ctx.Config().soongOutDir,
1789 partitionDir: partitionPath,
1790 partition: partition,
1791 makePath: makePath,
1792 fullPath: fullPath,
1793 }
1794}
1795
Cole Faust3b703f32023-10-16 13:30:51 -07001796func pathForInstall(ctx PathContext, os OsType, arch ArchType, partition string,
Colin Cross609c49a2020-02-13 13:20:11 -08001797 pathComponents ...string) InstallPath {
1798
Jiyong Park97859152023-02-14 17:05:48 +09001799 var partitionPaths []string
Colin Cross609c49a2020-02-13 13:20:11 -08001800
Colin Cross6e359402020-02-10 15:29:54 -08001801 if os.Class == Device {
Jiyong Park97859152023-02-14 17:05:48 +09001802 partitionPaths = []string{"target", "product", ctx.Config().DeviceName(), partition}
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001803 } else {
Jiyong Park87788b52020-09-01 12:37:45 +09001804 osName := os.String()
Colin Crossa9b2aac2022-06-15 17:25:51 -07001805 if os == Linux {
Jiyong Park87788b52020-09-01 12:37:45 +09001806 // instead of linux_glibc
1807 osName = "linux"
Dan Willemsen866b5632017-09-22 12:28:24 -07001808 }
Colin Crossa9b2aac2022-06-15 17:25:51 -07001809 if os == LinuxMusl && ctx.Config().UseHostMusl() {
1810 // When using musl instead of glibc, use "linux" instead of "linux_musl". When cross
1811 // compiling we will still use "linux_musl".
1812 osName = "linux"
1813 }
1814
Jiyong Park87788b52020-09-01 12:37:45 +09001815 // SOONG_HOST_OUT is set to out/host/$(HOST_OS)-$(HOST_PREBUILT_ARCH)
1816 // and HOST_PREBUILT_ARCH is forcibly set to x86 even on x86_64 hosts. We don't seem
1817 // to have a plan to fix it (see the comment in build/make/core/envsetup.mk).
1818 // Let's keep using x86 for the existing cases until we have a need to support
1819 // other architectures.
1820 archName := arch.String()
1821 if os.Class == Host && (arch == X86_64 || arch == Common) {
1822 archName = "x86"
1823 }
Jiyong Park97859152023-02-14 17:05:48 +09001824 partitionPaths = []string{"host", osName + "-" + archName, partition}
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001825 }
Colin Cross70dda7e2019-10-01 22:05:35 -07001826
Jiyong Park97859152023-02-14 17:05:48 +09001827 partitionPath, err := validatePath(partitionPaths...)
Colin Cross70dda7e2019-10-01 22:05:35 -07001828 if err != nil {
1829 reportPathError(ctx, err)
1830 }
Colin Crossff6c33d2019-10-02 16:01:35 -07001831
Colin Crossc0e42d52024-02-01 16:42:36 -08001832 base := pathForPartitionInstallDir(ctx, partition, partitionPath, ctx.Config().KatiEnabled())
Jiyong Park957bcd92020-10-20 18:23:33 +09001833 return base.Join(ctx, pathComponents...)
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001834}
1835
Spandan Dasf280b232024-04-04 21:25:51 +00001836func PathForNdkInstall(ctx PathContext, paths ...string) OutputPath {
1837 return PathForOutput(ctx, append([]string{"ndk"}, paths...)...)
Nicolas Geoffray1228e9c2020-02-27 13:45:35 +00001838}
1839
1840func PathForMainlineSdksInstall(ctx PathContext, paths ...string) InstallPath {
Spandan Dasf280b232024-04-04 21:25:51 +00001841 base := pathForPartitionInstallDir(ctx, "", "mainline-sdks", false)
1842 return base.Join(ctx, paths...)
Nicolas Geoffray1228e9c2020-02-27 13:45:35 +00001843}
1844
Colin Cross70dda7e2019-10-01 22:05:35 -07001845func InstallPathToOnDevicePath(ctx PathContext, path InstallPath) string {
Colin Crossb1692a32021-10-25 15:39:01 -07001846 rel := Rel(ctx, strings.TrimSuffix(path.PartitionDir(), path.partition), path.String())
Colin Cross43f08db2018-11-12 10:13:39 -08001847 return "/" + rel
1848}
1849
Cole Faust11edf552023-10-13 11:32:14 -07001850func modulePartition(ctx ModuleInstallPathContext, device bool) string {
Colin Cross43f08db2018-11-12 10:13:39 -08001851 var partition string
Colin Cross6e359402020-02-10 15:29:54 -08001852 if ctx.InstallInTestcases() {
1853 // "testcases" install directory can be used for host or device modules.
Jaewoong Jung0949f312019-09-11 10:25:18 -07001854 partition = "testcases"
Cole Faust11edf552023-10-13 11:32:14 -07001855 } else if device {
Colin Cross6e359402020-02-10 15:29:54 -08001856 if ctx.InstallInData() {
1857 partition = "data"
1858 } else if ctx.InstallInRamdisk() {
1859 if ctx.DeviceConfig().BoardUsesRecoveryAsBoot() {
1860 partition = "recovery/root/first_stage_ramdisk"
1861 } else {
1862 partition = "ramdisk"
1863 }
1864 if !ctx.InstallInRoot() {
1865 partition += "/system"
1866 }
Yifan Hong60e0cfb2020-10-21 15:17:56 -07001867 } else if ctx.InstallInVendorRamdisk() {
Yifan Hong39143a92020-10-26 12:43:12 -07001868 // The module is only available after switching root into
1869 // /first_stage_ramdisk. To expose the module before switching root
1870 // on a device without a dedicated recovery partition, install the
1871 // recovery variant.
Yifan Hongdd8dacc2020-10-21 15:40:17 -07001872 if ctx.DeviceConfig().BoardMoveRecoveryResourcesToVendorBoot() {
Petri Gyntherac229562021-03-02 23:44:02 -08001873 partition = "vendor_ramdisk/first_stage_ramdisk"
Yifan Hongdd8dacc2020-10-21 15:40:17 -07001874 } else {
Petri Gyntherac229562021-03-02 23:44:02 -08001875 partition = "vendor_ramdisk"
Yifan Hongdd8dacc2020-10-21 15:40:17 -07001876 }
1877 if !ctx.InstallInRoot() {
1878 partition += "/system"
1879 }
Inseob Kim08758f02021-04-08 21:13:22 +09001880 } else if ctx.InstallInDebugRamdisk() {
1881 partition = "debug_ramdisk"
Colin Cross6e359402020-02-10 15:29:54 -08001882 } else if ctx.InstallInRecovery() {
1883 if ctx.InstallInRoot() {
1884 partition = "recovery/root"
1885 } else {
1886 // the layout of recovery partion is the same as that of system partition
1887 partition = "recovery/root/system"
1888 }
Colin Crossea30d852023-11-29 16:00:16 -08001889 } else if ctx.SocSpecific() || ctx.InstallInVendor() {
Colin Cross6e359402020-02-10 15:29:54 -08001890 partition = ctx.DeviceConfig().VendorPath()
Colin Crossea30d852023-11-29 16:00:16 -08001891 } else if ctx.DeviceSpecific() || ctx.InstallInOdm() {
Colin Cross6e359402020-02-10 15:29:54 -08001892 partition = ctx.DeviceConfig().OdmPath()
Colin Crossea30d852023-11-29 16:00:16 -08001893 } else if ctx.ProductSpecific() || ctx.InstallInProduct() {
Colin Cross6e359402020-02-10 15:29:54 -08001894 partition = ctx.DeviceConfig().ProductPath()
1895 } else if ctx.SystemExtSpecific() {
1896 partition = ctx.DeviceConfig().SystemExtPath()
1897 } else if ctx.InstallInRoot() {
1898 partition = "root"
Yifan Hong82db7352020-01-21 16:12:26 -08001899 } else {
Colin Cross6e359402020-02-10 15:29:54 -08001900 partition = "system"
Yifan Hong82db7352020-01-21 16:12:26 -08001901 }
Colin Cross6e359402020-02-10 15:29:54 -08001902 if ctx.InstallInSanitizerDir() {
1903 partition = "data/asan/" + partition
Yifan Hong82db7352020-01-21 16:12:26 -08001904 }
Colin Cross43f08db2018-11-12 10:13:39 -08001905 }
1906 return partition
1907}
1908
Colin Cross609c49a2020-02-13 13:20:11 -08001909type InstallPaths []InstallPath
1910
1911// Paths returns the InstallPaths as a Paths
1912func (p InstallPaths) Paths() Paths {
1913 if p == nil {
1914 return nil
1915 }
1916 ret := make(Paths, len(p))
1917 for i, path := range p {
1918 ret[i] = path
1919 }
1920 return ret
1921}
1922
1923// Strings returns the string forms of the install paths.
1924func (p InstallPaths) Strings() []string {
1925 if p == nil {
1926 return nil
1927 }
1928 ret := make([]string, len(p))
1929 for i, path := range p {
1930 ret[i] = path.String()
1931 }
1932 return ret
1933}
1934
Jingwen Chen24d0c562023-02-07 09:29:36 +00001935// validatePathInternal ensures that a path does not leave its component, and
1936// optionally doesn't contain Ninja variables.
1937func validatePathInternal(allowNinjaVariables bool, pathComponents ...string) (string, error) {
Colin Crossbf9ed3f2023-10-24 14:17:03 -07001938 initialEmpty := 0
1939 finalEmpty := 0
1940 for i, path := range pathComponents {
Jingwen Chen24d0c562023-02-07 09:29:36 +00001941 if !allowNinjaVariables && strings.Contains(path, "$") {
1942 return "", fmt.Errorf("Path contains invalid character($): %s", path)
1943 }
1944
Dan Willemsen80a7c2a2015-12-21 14:57:11 -08001945 path := filepath.Clean(path)
1946 if path == ".." || strings.HasPrefix(path, "../") || strings.HasPrefix(path, "/") {
Colin Cross1ccfcc32018-02-22 13:54:26 -08001947 return "", fmt.Errorf("Path is outside directory: %s", path)
Dan Willemsen80a7c2a2015-12-21 14:57:11 -08001948 }
Colin Crossbf9ed3f2023-10-24 14:17:03 -07001949
1950 if i == initialEmpty && pathComponents[i] == "" {
1951 initialEmpty++
1952 }
1953 if i == finalEmpty && pathComponents[len(pathComponents)-1-i] == "" {
1954 finalEmpty++
1955 }
Dan Willemsen80a7c2a2015-12-21 14:57:11 -08001956 }
Colin Crossbf9ed3f2023-10-24 14:17:03 -07001957 // Optimization: filepath.Join("foo", "") returns a newly allocated copy
1958 // of "foo", while filepath.Join("foo") does not. Strip out any empty
1959 // path components.
1960 if initialEmpty == len(pathComponents) {
1961 return "", nil
1962 }
1963 nonEmptyPathComponents := pathComponents[initialEmpty : len(pathComponents)-finalEmpty]
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001964 // TODO: filepath.Join isn't necessarily correct with embedded ninja
1965 // variables. '..' may remove the entire ninja variable, even if it
1966 // will be expanded to multiple nested directories.
Colin Crossbf9ed3f2023-10-24 14:17:03 -07001967 return filepath.Join(nonEmptyPathComponents...), nil
Dan Willemsen34cc69e2015-09-23 15:26:20 -07001968}
1969
Jingwen Chen24d0c562023-02-07 09:29:36 +00001970// validateSafePath validates a path that we trust (may contain ninja
1971// variables). Ensures that each path component does not attempt to leave its
1972// component. Returns a joined version of each path component.
1973func validateSafePath(pathComponents ...string) (string, error) {
1974 return validatePathInternal(true, pathComponents...)
1975}
1976
Dan Willemsen80a7c2a2015-12-21 14:57:11 -08001977// validatePath validates that a path does not include ninja variables, and that
1978// each path component does not attempt to leave its component. Returns a joined
1979// version of each path component.
Colin Cross1ccfcc32018-02-22 13:54:26 -08001980func validatePath(pathComponents ...string) (string, error) {
Jingwen Chen24d0c562023-02-07 09:29:36 +00001981 return validatePathInternal(false, pathComponents...)
Colin Cross6e18ca42015-07-14 18:55:36 -07001982}
Colin Cross5b529592017-05-09 13:34:34 -07001983
Colin Cross0875c522017-11-28 17:34:01 -08001984func PathForPhony(ctx PathContext, phony string) WritablePath {
1985 if strings.ContainsAny(phony, "$/") {
Ulya Trafimovich5ab276a2020-08-25 12:45:15 +01001986 ReportPathErrorf(ctx, "Phony target contains invalid character ($ or /): %s", phony)
Colin Cross0875c522017-11-28 17:34:01 -08001987 }
Paul Duffin74abc5d2021-03-24 09:24:59 +00001988 return PhonyPath{basePath{phony, ""}}
Colin Cross0875c522017-11-28 17:34:01 -08001989}
1990
Colin Cross74e3fe42017-12-11 15:51:44 -08001991type PhonyPath struct {
1992 basePath
1993}
1994
1995func (p PhonyPath) writablePath() {}
1996
Lukacs T. Berki9f6c24a2021-08-26 15:07:24 +02001997func (p PhonyPath) getSoongOutDir() string {
Paul Duffind65c58b2021-03-24 09:22:07 +00001998 // A phone path cannot contain any / so cannot be relative to the build directory.
1999 return ""
Paul Duffin9b478b02019-12-10 13:41:51 +00002000}
2001
Paul Duffin85d8f0d2021-03-24 10:18:18 +00002002func (p PhonyPath) RelativeToTop() Path {
2003 ensureTestOnly()
2004 // A phony path cannot contain any / so does not have a build directory so switching to a new
2005 // build directory has no effect so just return this path.
2006 return p
2007}
2008
Hans MÃ¥nssond3f2bd72020-11-27 12:37:28 +01002009func (p PhonyPath) ReplaceExtension(ctx PathContext, ext string) OutputPath {
2010 panic("Not implemented")
2011}
2012
Colin Cross74e3fe42017-12-11 15:51:44 -08002013var _ Path = PhonyPath{}
2014var _ WritablePath = PhonyPath{}
2015
Colin Cross5b529592017-05-09 13:34:34 -07002016type testPath struct {
2017 basePath
2018}
2019
Paul Duffin85d8f0d2021-03-24 10:18:18 +00002020func (p testPath) RelativeToTop() Path {
2021 ensureTestOnly()
2022 return p
2023}
2024
Colin Cross5b529592017-05-09 13:34:34 -07002025func (p testPath) String() string {
2026 return p.path
2027}
2028
Paul Duffin85d8f0d2021-03-24 10:18:18 +00002029var _ Path = testPath{}
2030
Colin Cross40e33732019-02-15 11:08:35 -08002031// PathForTesting returns a Path constructed from joining the elements of paths with '/'. It should only be used from
2032// within tests.
Colin Cross5b529592017-05-09 13:34:34 -07002033func PathForTesting(paths ...string) Path {
Colin Cross1ccfcc32018-02-22 13:54:26 -08002034 p, err := validateSafePath(paths...)
2035 if err != nil {
2036 panic(err)
2037 }
Colin Cross5b529592017-05-09 13:34:34 -07002038 return testPath{basePath{path: p, rel: p}}
2039}
2040
Sam Delmerico2351eac2022-05-24 17:10:02 +00002041func PathForTestingWithRel(path, rel string) Path {
2042 p, err := validateSafePath(path, rel)
2043 if err != nil {
2044 panic(err)
2045 }
2046 r, err := validatePath(rel)
2047 if err != nil {
2048 panic(err)
2049 }
2050 return testPath{basePath{path: p, rel: r}}
2051}
2052
Colin Cross40e33732019-02-15 11:08:35 -08002053// PathsForTesting returns a Path constructed from each element in strs. It should only be used from within tests.
2054func PathsForTesting(strs ...string) Paths {
Colin Cross5b529592017-05-09 13:34:34 -07002055 p := make(Paths, len(strs))
2056 for i, s := range strs {
2057 p[i] = PathForTesting(s)
2058 }
2059
2060 return p
2061}
Colin Cross43f08db2018-11-12 10:13:39 -08002062
Colin Cross40e33732019-02-15 11:08:35 -08002063type testPathContext struct {
2064 config Config
Colin Cross40e33732019-02-15 11:08:35 -08002065}
2066
Colin Cross40e33732019-02-15 11:08:35 -08002067func (x *testPathContext) Config() Config { return x.config }
2068func (x *testPathContext) AddNinjaFileDeps(...string) {}
2069
2070// PathContextForTesting returns a PathContext that can be used in tests, for example to create an OutputPath with
2071// PathForOutput.
Colin Cross98be1bb2019-12-13 20:41:13 -08002072func PathContextForTesting(config Config) PathContext {
Colin Cross40e33732019-02-15 11:08:35 -08002073 return &testPathContext{
2074 config: config,
Colin Cross40e33732019-02-15 11:08:35 -08002075 }
2076}
2077
Ulya Trafimovichccc8c852020-10-14 11:29:07 +01002078type testModuleInstallPathContext struct {
2079 baseModuleContext
2080
2081 inData bool
2082 inTestcases bool
2083 inSanitizerDir bool
2084 inRamdisk bool
2085 inVendorRamdisk bool
Inseob Kim08758f02021-04-08 21:13:22 +09002086 inDebugRamdisk bool
Ulya Trafimovichccc8c852020-10-14 11:29:07 +01002087 inRecovery bool
2088 inRoot bool
Colin Crossea30d852023-11-29 16:00:16 -08002089 inOdm bool
2090 inProduct bool
2091 inVendor bool
Ulya Trafimovichccc8c852020-10-14 11:29:07 +01002092 forceOS *OsType
2093 forceArch *ArchType
2094}
2095
2096func (m testModuleInstallPathContext) Config() Config {
2097 return m.baseModuleContext.config
2098}
2099
2100func (testModuleInstallPathContext) AddNinjaFileDeps(deps ...string) {}
2101
2102func (m testModuleInstallPathContext) InstallInData() bool {
2103 return m.inData
2104}
2105
2106func (m testModuleInstallPathContext) InstallInTestcases() bool {
2107 return m.inTestcases
2108}
2109
2110func (m testModuleInstallPathContext) InstallInSanitizerDir() bool {
2111 return m.inSanitizerDir
2112}
2113
2114func (m testModuleInstallPathContext) InstallInRamdisk() bool {
2115 return m.inRamdisk
2116}
2117
2118func (m testModuleInstallPathContext) InstallInVendorRamdisk() bool {
2119 return m.inVendorRamdisk
2120}
2121
Inseob Kim08758f02021-04-08 21:13:22 +09002122func (m testModuleInstallPathContext) InstallInDebugRamdisk() bool {
2123 return m.inDebugRamdisk
2124}
2125
Ulya Trafimovichccc8c852020-10-14 11:29:07 +01002126func (m testModuleInstallPathContext) InstallInRecovery() bool {
2127 return m.inRecovery
2128}
2129
2130func (m testModuleInstallPathContext) InstallInRoot() bool {
2131 return m.inRoot
2132}
2133
Colin Crossea30d852023-11-29 16:00:16 -08002134func (m testModuleInstallPathContext) InstallInOdm() bool {
2135 return m.inOdm
2136}
2137
2138func (m testModuleInstallPathContext) InstallInProduct() bool {
2139 return m.inProduct
2140}
2141
2142func (m testModuleInstallPathContext) InstallInVendor() bool {
2143 return m.inVendor
2144}
2145
Ulya Trafimovichccc8c852020-10-14 11:29:07 +01002146func (m testModuleInstallPathContext) InstallForceOS() (*OsType, *ArchType) {
2147 return m.forceOS, m.forceArch
2148}
2149
2150// Construct a minimal ModuleInstallPathContext for testing. Note that baseModuleContext is
2151// default-initialized, which leaves blueprint.baseModuleContext set to nil, so methods that are
2152// delegated to it will panic.
2153func ModuleInstallPathContextForTesting(config Config) ModuleInstallPathContext {
2154 ctx := &testModuleInstallPathContext{}
2155 ctx.config = config
2156 ctx.os = Android
2157 return ctx
2158}
2159
Colin Cross43f08db2018-11-12 10:13:39 -08002160// Rel performs the same function as filepath.Rel, but reports errors to a PathContext, and reports an error if
2161// targetPath is not inside basePath.
2162func Rel(ctx PathContext, basePath string, targetPath string) string {
2163 rel, isRel := MaybeRel(ctx, basePath, targetPath)
2164 if !isRel {
Ulya Trafimovich5ab276a2020-08-25 12:45:15 +01002165 ReportPathErrorf(ctx, "path %q is not under path %q", targetPath, basePath)
Colin Cross43f08db2018-11-12 10:13:39 -08002166 return ""
2167 }
2168 return rel
2169}
2170
2171// MaybeRel performs the same function as filepath.Rel, but reports errors to a PathContext, and returns false if
2172// targetPath is not inside basePath.
2173func MaybeRel(ctx PathContext, basePath string, targetPath string) (string, bool) {
Dan Willemsen633c5022019-04-12 11:11:38 -07002174 rel, isRel, err := maybeRelErr(basePath, targetPath)
2175 if err != nil {
2176 reportPathError(ctx, err)
2177 }
2178 return rel, isRel
2179}
2180
2181func maybeRelErr(basePath string, targetPath string) (string, bool, error) {
Colin Cross43f08db2018-11-12 10:13:39 -08002182 // filepath.Rel returns an error if one path is absolute and the other is not, handle that case first.
2183 if filepath.IsAbs(basePath) != filepath.IsAbs(targetPath) {
Dan Willemsen633c5022019-04-12 11:11:38 -07002184 return "", false, nil
Colin Cross43f08db2018-11-12 10:13:39 -08002185 }
2186 rel, err := filepath.Rel(basePath, targetPath)
2187 if err != nil {
Dan Willemsen633c5022019-04-12 11:11:38 -07002188 return "", false, err
Colin Cross43f08db2018-11-12 10:13:39 -08002189 } else if rel == ".." || strings.HasPrefix(rel, "../") || strings.HasPrefix(rel, "/") {
Dan Willemsen633c5022019-04-12 11:11:38 -07002190 return "", false, nil
Colin Cross43f08db2018-11-12 10:13:39 -08002191 }
Dan Willemsen633c5022019-04-12 11:11:38 -07002192 return rel, true, nil
Colin Cross43f08db2018-11-12 10:13:39 -08002193}
Colin Cross988414c2020-01-11 01:11:46 +00002194
2195// Writes a file to the output directory. Attempting to write directly to the output directory
2196// will fail due to the sandbox of the soong_build process.
Chris Parsons1a12d032023-02-06 22:37:41 -05002197// Only writes the file if the file doesn't exist or if it has different contents, to prevent
2198// updating the timestamp if no changes would be made. (This is better for incremental
2199// performance.)
Colin Cross988414c2020-01-11 01:11:46 +00002200func WriteFileToOutputDir(path WritablePath, data []byte, perm os.FileMode) error {
Colin Crossd6421132021-11-09 12:32:34 -08002201 absPath := absolutePath(path.String())
2202 err := os.MkdirAll(filepath.Dir(absPath), 0777)
2203 if err != nil {
2204 return err
2205 }
Chris Parsons1a12d032023-02-06 22:37:41 -05002206 return pathtools.WriteFileIfChanged(absPath, data, perm)
Colin Cross988414c2020-01-11 01:11:46 +00002207}
2208
Liz Kammer2dd9ca42020-11-25 16:06:39 -08002209func RemoveAllOutputDir(path WritablePath) error {
2210 return os.RemoveAll(absolutePath(path.String()))
2211}
2212
2213func CreateOutputDirIfNonexistent(path WritablePath, perm os.FileMode) error {
2214 dir := absolutePath(path.String())
Liz Kammer09f947d2021-05-12 14:51:49 -04002215 return createDirIfNonexistent(dir, perm)
2216}
2217
2218func createDirIfNonexistent(dir string, perm os.FileMode) error {
Liz Kammer2dd9ca42020-11-25 16:06:39 -08002219 if _, err := os.Stat(dir); os.IsNotExist(err) {
2220 return os.MkdirAll(dir, os.ModePerm)
2221 } else {
2222 return err
2223 }
2224}
2225
Jingwen Chen78257e52021-05-21 02:34:24 +00002226// absolutePath is deliberately private so that Soong's Go plugins can't use it to find and
2227// read arbitrary files without going through the methods in the current package that track
2228// dependencies.
Colin Cross988414c2020-01-11 01:11:46 +00002229func absolutePath(path string) string {
2230 if filepath.IsAbs(path) {
2231 return path
2232 }
2233 return filepath.Join(absSrcDir, path)
2234}
Chris Parsons216e10a2020-07-09 17:12:52 -04002235
2236// A DataPath represents the path of a file to be used as data, for example
2237// a test library to be installed alongside a test.
2238// The data file should be installed (copied from `<SrcPath>`) to
2239// `<install_root>/<RelativeInstallPath>/<filename>`, or
2240// `<install_root>/<filename>` if RelativeInstallPath is empty.
2241type DataPath struct {
2242 // The path of the data file that should be copied into the data directory
2243 SrcPath Path
2244 // The install path of the data file, relative to the install root.
2245 RelativeInstallPath string
Colin Cross5c1d5fb2023-11-15 12:39:40 -08002246 // If WithoutRel is true, use SrcPath.Base() instead of SrcPath.Rel() as the filename.
2247 WithoutRel bool
Chris Parsons216e10a2020-07-09 17:12:52 -04002248}
Colin Crossdcf71b22021-02-01 13:59:03 -08002249
Colin Crossd442a0e2023-11-16 11:19:26 -08002250func (d *DataPath) ToRelativeInstallPath() string {
2251 relPath := d.SrcPath.Rel()
Colin Cross5c1d5fb2023-11-15 12:39:40 -08002252 if d.WithoutRel {
2253 relPath = d.SrcPath.Base()
2254 }
Colin Crossd442a0e2023-11-16 11:19:26 -08002255 if d.RelativeInstallPath != "" {
2256 relPath = filepath.Join(d.RelativeInstallPath, relPath)
2257 }
2258 return relPath
2259}
2260
Colin Crossdcf71b22021-02-01 13:59:03 -08002261// PathsIfNonNil returns a Paths containing only the non-nil input arguments.
2262func PathsIfNonNil(paths ...Path) Paths {
2263 if len(paths) == 0 {
2264 // Fast path for empty argument list
2265 return nil
2266 } else if len(paths) == 1 {
2267 // Fast path for a single argument
2268 if paths[0] != nil {
2269 return paths
2270 } else {
2271 return nil
2272 }
2273 }
2274 ret := make(Paths, 0, len(paths))
2275 for _, path := range paths {
2276 if path != nil {
2277 ret = append(ret, path)
2278 }
2279 }
2280 if len(ret) == 0 {
2281 return nil
2282 }
2283 return ret
2284}
Chris Wailesb2703ad2021-07-30 13:25:42 -07002285
2286var thirdPartyDirPrefixExceptions = []*regexp.Regexp{
2287 regexp.MustCompile("^vendor/[^/]*google[^/]*/"),
2288 regexp.MustCompile("^hardware/google/"),
2289 regexp.MustCompile("^hardware/interfaces/"),
2290 regexp.MustCompile("^hardware/libhardware[^/]*/"),
2291 regexp.MustCompile("^hardware/ril/"),
2292}
2293
2294func IsThirdPartyPath(path string) bool {
2295 thirdPartyDirPrefixes := []string{"external/", "vendor/", "hardware/"}
2296
2297 if HasAnyPrefix(path, thirdPartyDirPrefixes) {
2298 for _, prefix := range thirdPartyDirPrefixExceptions {
2299 if prefix.MatchString(path) {
2300 return false
2301 }
2302 }
2303 return true
2304 }
2305 return false
2306}