github.com/westcoastroms/westcoastroms-build@v0.0.0-20190928114312-2350e5a73030/build/soong/android/paths.go (about)

     1  // 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  
    15  package android
    16  
    17  import (
    18  	"fmt"
    19  	"path/filepath"
    20  	"reflect"
    21  	"sort"
    22  	"strings"
    23  
    24  	"github.com/google/blueprint"
    25  	"github.com/google/blueprint/pathtools"
    26  )
    27  
    28  // PathContext is the subset of a (Module|Singleton)Context required by the
    29  // Path methods.
    30  type PathContext interface {
    31  	Fs() pathtools.FileSystem
    32  	Config() Config
    33  	AddNinjaFileDeps(deps ...string)
    34  }
    35  
    36  type PathGlobContext interface {
    37  	GlobWithDeps(globPattern string, excludes []string) ([]string, error)
    38  }
    39  
    40  var _ PathContext = SingletonContext(nil)
    41  var _ PathContext = ModuleContext(nil)
    42  
    43  type ModuleInstallPathContext interface {
    44  	PathContext
    45  
    46  	androidBaseContext
    47  
    48  	InstallInData() bool
    49  	InstallInSanitizerDir() bool
    50  }
    51  
    52  var _ ModuleInstallPathContext = ModuleContext(nil)
    53  
    54  // errorfContext is the interface containing the Errorf method matching the
    55  // Errorf method in blueprint.SingletonContext.
    56  type errorfContext interface {
    57  	Errorf(format string, args ...interface{})
    58  }
    59  
    60  var _ errorfContext = blueprint.SingletonContext(nil)
    61  
    62  // moduleErrorf is the interface containing the ModuleErrorf method matching
    63  // the ModuleErrorf method in blueprint.ModuleContext.
    64  type moduleErrorf interface {
    65  	ModuleErrorf(format string, args ...interface{})
    66  }
    67  
    68  var _ moduleErrorf = blueprint.ModuleContext(nil)
    69  
    70  // reportPathError will register an error with the attached context. It
    71  // attempts ctx.ModuleErrorf for a better error message first, then falls
    72  // back to ctx.Errorf.
    73  func reportPathError(ctx PathContext, err error) {
    74  	reportPathErrorf(ctx, "%s", err.Error())
    75  }
    76  
    77  // reportPathErrorf will register an error with the attached context. It
    78  // attempts ctx.ModuleErrorf for a better error message first, then falls
    79  // back to ctx.Errorf.
    80  func reportPathErrorf(ctx PathContext, format string, args ...interface{}) {
    81  	if mctx, ok := ctx.(moduleErrorf); ok {
    82  		mctx.ModuleErrorf(format, args...)
    83  	} else if ectx, ok := ctx.(errorfContext); ok {
    84  		ectx.Errorf(format, args...)
    85  	} else {
    86  		panic(fmt.Sprintf(format, args...))
    87  	}
    88  }
    89  
    90  type Path interface {
    91  	// Returns the path in string form
    92  	String() string
    93  
    94  	// Ext returns the extension of the last element of the path
    95  	Ext() string
    96  
    97  	// Base returns the last element of the path
    98  	Base() string
    99  
   100  	// Rel returns the portion of the path relative to the directory it was created from.  For
   101  	// example, Rel on a PathsForModuleSrc would return the path relative to the module source
   102  	// directory, and OutputPath.Join("foo").Rel() would return "foo".
   103  	Rel() string
   104  }
   105  
   106  // WritablePath is a type of path that can be used as an output for build rules.
   107  type WritablePath interface {
   108  	Path
   109  
   110  	// the writablePath method doesn't directly do anything,
   111  	// but it allows a struct to distinguish between whether or not it implements the WritablePath interface
   112  	writablePath()
   113  }
   114  
   115  type genPathProvider interface {
   116  	genPathWithExt(ctx ModuleContext, subdir, ext string) ModuleGenPath
   117  }
   118  type objPathProvider interface {
   119  	objPathWithExt(ctx ModuleContext, subdir, ext string) ModuleObjPath
   120  }
   121  type resPathProvider interface {
   122  	resPathWithName(ctx ModuleContext, name string) ModuleResPath
   123  }
   124  
   125  // GenPathWithExt derives a new file path in ctx's generated sources directory
   126  // from the current path, but with the new extension.
   127  func GenPathWithExt(ctx ModuleContext, subdir string, p Path, ext string) ModuleGenPath {
   128  	if path, ok := p.(genPathProvider); ok {
   129  		return path.genPathWithExt(ctx, subdir, ext)
   130  	}
   131  	reportPathErrorf(ctx, "Tried to create generated file from unsupported path: %s(%s)", reflect.TypeOf(p).Name(), p)
   132  	return PathForModuleGen(ctx)
   133  }
   134  
   135  // ObjPathWithExt derives a new file path in ctx's object directory from the
   136  // current path, but with the new extension.
   137  func ObjPathWithExt(ctx ModuleContext, subdir string, p Path, ext string) ModuleObjPath {
   138  	if path, ok := p.(objPathProvider); ok {
   139  		return path.objPathWithExt(ctx, subdir, ext)
   140  	}
   141  	reportPathErrorf(ctx, "Tried to create object file from unsupported path: %s (%s)", reflect.TypeOf(p).Name(), p)
   142  	return PathForModuleObj(ctx)
   143  }
   144  
   145  // ResPathWithName derives a new path in ctx's output resource directory, using
   146  // the current path to create the directory name, and the `name` argument for
   147  // the filename.
   148  func ResPathWithName(ctx ModuleContext, p Path, name string) ModuleResPath {
   149  	if path, ok := p.(resPathProvider); ok {
   150  		return path.resPathWithName(ctx, name)
   151  	}
   152  	reportPathErrorf(ctx, "Tried to create res file from unsupported path: %s (%s)", reflect.TypeOf(p).Name(), p)
   153  	return PathForModuleRes(ctx)
   154  }
   155  
   156  // OptionalPath is a container that may or may not contain a valid Path.
   157  type OptionalPath struct {
   158  	valid bool
   159  	path  Path
   160  }
   161  
   162  // OptionalPathForPath returns an OptionalPath containing the path.
   163  func OptionalPathForPath(path Path) OptionalPath {
   164  	if path == nil {
   165  		return OptionalPath{}
   166  	}
   167  	return OptionalPath{valid: true, path: path}
   168  }
   169  
   170  // Valid returns whether there is a valid path
   171  func (p OptionalPath) Valid() bool {
   172  	return p.valid
   173  }
   174  
   175  // Path returns the Path embedded in this OptionalPath. You must be sure that
   176  // there is a valid path, since this method will panic if there is not.
   177  func (p OptionalPath) Path() Path {
   178  	if !p.valid {
   179  		panic("Requesting an invalid path")
   180  	}
   181  	return p.path
   182  }
   183  
   184  // String returns the string version of the Path, or "" if it isn't valid.
   185  func (p OptionalPath) String() string {
   186  	if p.valid {
   187  		return p.path.String()
   188  	} else {
   189  		return ""
   190  	}
   191  }
   192  
   193  // Paths is a slice of Path objects, with helpers to operate on the collection.
   194  type Paths []Path
   195  
   196  // PathsForSource returns Paths rooted from SrcDir
   197  func PathsForSource(ctx PathContext, paths []string) Paths {
   198  	ret := make(Paths, len(paths))
   199  	for i, path := range paths {
   200  		ret[i] = PathForSource(ctx, path)
   201  	}
   202  	return ret
   203  }
   204  
   205  // ExistentPathsForSources returns a list of Paths rooted from SrcDir that are
   206  // found in the tree. If any are not found, they are omitted from the list,
   207  // and dependencies are added so that we're re-run when they are added.
   208  func ExistentPathsForSources(ctx PathContext, paths []string) Paths {
   209  	ret := make(Paths, 0, len(paths))
   210  	for _, path := range paths {
   211  		p := ExistentPathForSource(ctx, path)
   212  		if p.Valid() {
   213  			ret = append(ret, p.Path())
   214  		}
   215  	}
   216  	return ret
   217  }
   218  
   219  // PathsForModuleSrc returns Paths rooted from the module's local source
   220  // directory
   221  func PathsForModuleSrc(ctx ModuleContext, paths []string) Paths {
   222  	ret := make(Paths, len(paths))
   223  	for i, path := range paths {
   224  		ret[i] = PathForModuleSrc(ctx, path)
   225  	}
   226  	return ret
   227  }
   228  
   229  // pathsForModuleSrcFromFullPath returns Paths rooted from the module's local
   230  // source directory, but strip the local source directory from the beginning of
   231  // each string. If incDirs is false, strip paths with a trailing '/' from the list.
   232  func pathsForModuleSrcFromFullPath(ctx ModuleContext, paths []string, incDirs bool) Paths {
   233  	prefix := filepath.Join(ctx.Config().srcDir, ctx.ModuleDir()) + "/"
   234  	if prefix == "./" {
   235  		prefix = ""
   236  	}
   237  	ret := make(Paths, 0, len(paths))
   238  	for _, p := range paths {
   239  		if !incDirs && strings.HasSuffix(p, "/") {
   240  			continue
   241  		}
   242  		path := filepath.Clean(p)
   243  		if !strings.HasPrefix(path, prefix) {
   244  			reportPathErrorf(ctx, "Path '%s' is not in module source directory '%s'", p, prefix)
   245  			continue
   246  		}
   247  		ret = append(ret, PathForModuleSrc(ctx, path[len(prefix):]))
   248  	}
   249  	return ret
   250  }
   251  
   252  // PathsWithOptionalDefaultForModuleSrc returns Paths rooted from the module's
   253  // local source directory. If none are provided, use the default if it exists.
   254  func PathsWithOptionalDefaultForModuleSrc(ctx ModuleContext, input []string, def string) Paths {
   255  	if len(input) > 0 {
   256  		return PathsForModuleSrc(ctx, input)
   257  	}
   258  	// Use Glob so that if the default doesn't exist, a dependency is added so that when it
   259  	// is created, we're run again.
   260  	path := filepath.Join(ctx.Config().srcDir, ctx.ModuleDir(), def)
   261  	return ctx.Glob(path, nil)
   262  }
   263  
   264  // Strings returns the Paths in string form
   265  func (p Paths) Strings() []string {
   266  	if p == nil {
   267  		return nil
   268  	}
   269  	ret := make([]string, len(p))
   270  	for i, path := range p {
   271  		ret[i] = path.String()
   272  	}
   273  	return ret
   274  }
   275  
   276  // FirstUniquePaths returns all unique elements of a Paths, keeping the first copy of each.  It
   277  // modifies the Paths slice contents in place, and returns a subslice of the original slice.
   278  func FirstUniquePaths(list Paths) Paths {
   279  	k := 0
   280  outer:
   281  	for i := 0; i < len(list); i++ {
   282  		for j := 0; j < k; j++ {
   283  			if list[i] == list[j] {
   284  				continue outer
   285  			}
   286  		}
   287  		list[k] = list[i]
   288  		k++
   289  	}
   290  	return list[:k]
   291  }
   292  
   293  // LastUniquePaths returns all unique elements of a Paths, keeping the last copy of each.  It
   294  // modifies the Paths slice contents in place, and returns a subslice of the original slice.
   295  func LastUniquePaths(list Paths) Paths {
   296  	totalSkip := 0
   297  	for i := len(list) - 1; i >= totalSkip; i-- {
   298  		skip := 0
   299  		for j := i - 1; j >= totalSkip; j-- {
   300  			if list[i] == list[j] {
   301  				skip++
   302  			} else {
   303  				list[j+skip] = list[j]
   304  			}
   305  		}
   306  		totalSkip += skip
   307  	}
   308  	return list[totalSkip:]
   309  }
   310  
   311  // ReversePaths returns a copy of a Paths in reverse order.
   312  func ReversePaths(list Paths) Paths {
   313  	if list == nil {
   314  		return nil
   315  	}
   316  	ret := make(Paths, len(list))
   317  	for i := range list {
   318  		ret[i] = list[len(list)-1-i]
   319  	}
   320  	return ret
   321  }
   322  
   323  func indexPathList(s Path, list []Path) int {
   324  	for i, l := range list {
   325  		if l == s {
   326  			return i
   327  		}
   328  	}
   329  
   330  	return -1
   331  }
   332  
   333  func inPathList(p Path, list []Path) bool {
   334  	return indexPathList(p, list) != -1
   335  }
   336  
   337  func FilterPathList(list []Path, filter []Path) (remainder []Path, filtered []Path) {
   338  	for _, l := range list {
   339  		if inPathList(l, filter) {
   340  			filtered = append(filtered, l)
   341  		} else {
   342  			remainder = append(remainder, l)
   343  		}
   344  	}
   345  
   346  	return
   347  }
   348  
   349  // HasExt returns true of any of the paths have extension ext, otherwise false
   350  func (p Paths) HasExt(ext string) bool {
   351  	for _, path := range p {
   352  		if path.Ext() == ext {
   353  			return true
   354  		}
   355  	}
   356  
   357  	return false
   358  }
   359  
   360  // FilterByExt returns the subset of the paths that have extension ext
   361  func (p Paths) FilterByExt(ext string) Paths {
   362  	ret := make(Paths, 0, len(p))
   363  	for _, path := range p {
   364  		if path.Ext() == ext {
   365  			ret = append(ret, path)
   366  		}
   367  	}
   368  	return ret
   369  }
   370  
   371  // FilterOutByExt returns the subset of the paths that do not have extension ext
   372  func (p Paths) FilterOutByExt(ext string) Paths {
   373  	ret := make(Paths, 0, len(p))
   374  	for _, path := range p {
   375  		if path.Ext() != ext {
   376  			ret = append(ret, path)
   377  		}
   378  	}
   379  	return ret
   380  }
   381  
   382  // DirectorySortedPaths is a slice of paths that are sorted such that all files in a directory
   383  // (including subdirectories) are in a contiguous subslice of the list, and can be found in
   384  // O(log(N)) time using a binary search on the directory prefix.
   385  type DirectorySortedPaths Paths
   386  
   387  func PathsToDirectorySortedPaths(paths Paths) DirectorySortedPaths {
   388  	ret := append(DirectorySortedPaths(nil), paths...)
   389  	sort.Slice(ret, func(i, j int) bool {
   390  		return ret[i].String() < ret[j].String()
   391  	})
   392  	return ret
   393  }
   394  
   395  // PathsInDirectory returns a subslice of the DirectorySortedPaths as a Paths that contains all entries
   396  // that are in the specified directory and its subdirectories.
   397  func (p DirectorySortedPaths) PathsInDirectory(dir string) Paths {
   398  	prefix := filepath.Clean(dir) + "/"
   399  	start := sort.Search(len(p), func(i int) bool {
   400  		return prefix < p[i].String()
   401  	})
   402  
   403  	ret := p[start:]
   404  
   405  	end := sort.Search(len(ret), func(i int) bool {
   406  		return !strings.HasPrefix(ret[i].String(), prefix)
   407  	})
   408  
   409  	ret = ret[:end]
   410  
   411  	return Paths(ret)
   412  }
   413  
   414  // WritablePaths is a slice of WritablePaths, used for multiple outputs.
   415  type WritablePaths []WritablePath
   416  
   417  // Strings returns the string forms of the writable paths.
   418  func (p WritablePaths) Strings() []string {
   419  	if p == nil {
   420  		return nil
   421  	}
   422  	ret := make([]string, len(p))
   423  	for i, path := range p {
   424  		ret[i] = path.String()
   425  	}
   426  	return ret
   427  }
   428  
   429  // Paths returns the WritablePaths as a Paths
   430  func (p WritablePaths) Paths() Paths {
   431  	if p == nil {
   432  		return nil
   433  	}
   434  	ret := make(Paths, len(p))
   435  	for i, path := range p {
   436  		ret[i] = path
   437  	}
   438  	return ret
   439  }
   440  
   441  type basePath struct {
   442  	path   string
   443  	config Config
   444  	rel    string
   445  }
   446  
   447  func (p basePath) Ext() string {
   448  	return filepath.Ext(p.path)
   449  }
   450  
   451  func (p basePath) Base() string {
   452  	return filepath.Base(p.path)
   453  }
   454  
   455  func (p basePath) Rel() string {
   456  	if p.rel != "" {
   457  		return p.rel
   458  	}
   459  	return p.path
   460  }
   461  
   462  func (p basePath) String() string {
   463  	return p.path
   464  }
   465  
   466  func (p basePath) withRel(rel string) basePath {
   467  	p.path = filepath.Join(p.path, rel)
   468  	p.rel = rel
   469  	return p
   470  }
   471  
   472  // SourcePath is a Path representing a file path rooted from SrcDir
   473  type SourcePath struct {
   474  	basePath
   475  }
   476  
   477  var _ Path = SourcePath{}
   478  
   479  func (p SourcePath) withRel(rel string) SourcePath {
   480  	p.basePath = p.basePath.withRel(rel)
   481  	return p
   482  }
   483  
   484  // safePathForSource is for paths that we expect are safe -- only for use by go
   485  // code that is embedding ninja variables in paths
   486  func safePathForSource(ctx PathContext, path string) SourcePath {
   487  	p, err := validateSafePath(path)
   488  	if err != nil {
   489  		reportPathError(ctx, err)
   490  	}
   491  	ret := SourcePath{basePath{p, ctx.Config(), ""}}
   492  
   493  	abs, err := filepath.Abs(ret.String())
   494  	if err != nil {
   495  		reportPathError(ctx, err)
   496  		return ret
   497  	}
   498  	buildroot, err := filepath.Abs(ctx.Config().buildDir)
   499  	if err != nil {
   500  		reportPathError(ctx, err)
   501  		return ret
   502  	}
   503  	if strings.HasPrefix(abs, buildroot) {
   504  		reportPathErrorf(ctx, "source path %s is in output", abs)
   505  		return ret
   506  	}
   507  
   508  	return ret
   509  }
   510  
   511  // pathForSource creates a SourcePath from pathComponents, but does not check that it exists.
   512  func pathForSource(ctx PathContext, pathComponents ...string) (SourcePath, error) {
   513  	p, err := validatePath(pathComponents...)
   514  	ret := SourcePath{basePath{p, ctx.Config(), ""}}
   515  	if err != nil {
   516  		return ret, err
   517  	}
   518  
   519  	abs, err := filepath.Abs(ret.String())
   520  	if err != nil {
   521  		return ret, err
   522  	}
   523  	buildroot, err := filepath.Abs(ctx.Config().buildDir)
   524  	if err != nil {
   525  		return ret, err
   526  	}
   527  	if strings.HasPrefix(abs, buildroot) {
   528  		return ret, fmt.Errorf("source path %s is in output", abs)
   529  	}
   530  
   531  	if pathtools.IsGlob(ret.String()) {
   532  		return ret, fmt.Errorf("path may not contain a glob: %s", ret.String())
   533  	}
   534  
   535  	return ret, nil
   536  }
   537  
   538  // existsWithDependencies returns true if the path exists, and adds appropriate dependencies to rerun if the
   539  // path does not exist.
   540  func existsWithDependencies(ctx PathContext, path SourcePath) (exists bool, err error) {
   541  	var files []string
   542  
   543  	if gctx, ok := ctx.(PathGlobContext); ok {
   544  		// Use glob to produce proper dependencies, even though we only want
   545  		// a single file.
   546  		files, err = gctx.GlobWithDeps(path.String(), nil)
   547  	} else {
   548  		var deps []string
   549  		// We cannot add build statements in this context, so we fall back to
   550  		// AddNinjaFileDeps
   551  		files, deps, err = pathtools.Glob(path.String(), nil)
   552  		ctx.AddNinjaFileDeps(deps...)
   553  	}
   554  
   555  	if err != nil {
   556  		return false, fmt.Errorf("glob: %s", err.Error())
   557  	}
   558  
   559  	return len(files) > 0, nil
   560  }
   561  
   562  // PathForSource joins the provided path components and validates that the result
   563  // neither escapes the source dir nor is in the out dir.
   564  // On error, it will return a usable, but invalid SourcePath, and report a ModuleError.
   565  func PathForSource(ctx PathContext, pathComponents ...string) SourcePath {
   566  	path, err := pathForSource(ctx, pathComponents...)
   567  	if err != nil {
   568  		reportPathError(ctx, err)
   569  	}
   570  
   571  	if modCtx, ok := ctx.(ModuleContext); ok && ctx.Config().AllowMissingDependencies() {
   572  		exists, err := existsWithDependencies(ctx, path)
   573  		if err != nil {
   574  			reportPathError(ctx, err)
   575  		}
   576  		if !exists {
   577  			modCtx.AddMissingDependencies([]string{path.String()})
   578  		}
   579  	} else if exists, _, err := ctx.Fs().Exists(path.String()); err != nil {
   580  		reportPathErrorf(ctx, "%s: %s", path, err.Error())
   581  	} else if !exists {
   582  		reportPathErrorf(ctx, "source path %s does not exist", path)
   583  	}
   584  	return path
   585  }
   586  
   587  // ExistentPathForSource returns an OptionalPath with the SourcePath if the
   588  // path exists, or an empty OptionalPath if it doesn't exist. Dependencies are added
   589  // so that the ninja file will be regenerated if the state of the path changes.
   590  func ExistentPathForSource(ctx PathContext, pathComponents ...string) OptionalPath {
   591  	path, err := pathForSource(ctx, pathComponents...)
   592  	if err != nil {
   593  		reportPathError(ctx, err)
   594  		return OptionalPath{}
   595  	}
   596  
   597  	exists, err := existsWithDependencies(ctx, path)
   598  	if err != nil {
   599  		reportPathError(ctx, err)
   600  		return OptionalPath{}
   601  	}
   602  	if !exists {
   603  		return OptionalPath{}
   604  	}
   605  	return OptionalPathForPath(path)
   606  }
   607  
   608  func (p SourcePath) String() string {
   609  	return filepath.Join(p.config.srcDir, p.path)
   610  }
   611  
   612  // Join creates a new SourcePath with paths... joined with the current path. The
   613  // provided paths... may not use '..' to escape from the current path.
   614  func (p SourcePath) Join(ctx PathContext, paths ...string) SourcePath {
   615  	path, err := validatePath(paths...)
   616  	if err != nil {
   617  		reportPathError(ctx, err)
   618  	}
   619  	return p.withRel(path)
   620  }
   621  
   622  // OverlayPath returns the overlay for `path' if it exists. This assumes that the
   623  // SourcePath is the path to a resource overlay directory.
   624  func (p SourcePath) OverlayPath(ctx ModuleContext, path Path) OptionalPath {
   625  	var relDir string
   626  	if moduleSrcPath, ok := path.(ModuleSrcPath); ok {
   627  		relDir = moduleSrcPath.path
   628  	} else if srcPath, ok := path.(SourcePath); ok {
   629  		relDir = srcPath.path
   630  	} else {
   631  		reportPathErrorf(ctx, "Cannot find relative path for %s(%s)", reflect.TypeOf(path).Name(), path)
   632  		return OptionalPath{}
   633  	}
   634  	dir := filepath.Join(p.config.srcDir, p.path, relDir)
   635  	// Use Glob so that we are run again if the directory is added.
   636  	if pathtools.IsGlob(dir) {
   637  		reportPathErrorf(ctx, "Path may not contain a glob: %s", dir)
   638  	}
   639  	paths, err := ctx.GlobWithDeps(dir, nil)
   640  	if err != nil {
   641  		reportPathErrorf(ctx, "glob: %s", err.Error())
   642  		return OptionalPath{}
   643  	}
   644  	if len(paths) == 0 {
   645  		return OptionalPath{}
   646  	}
   647  	relPath, err := filepath.Rel(p.config.srcDir, paths[0])
   648  	if err != nil {
   649  		reportPathError(ctx, err)
   650  		return OptionalPath{}
   651  	}
   652  	return OptionalPathForPath(PathForSource(ctx, relPath))
   653  }
   654  
   655  // OutputPath is a Path representing a file path rooted from the build directory
   656  type OutputPath struct {
   657  	basePath
   658  }
   659  
   660  func (p OutputPath) withRel(rel string) OutputPath {
   661  	p.basePath = p.basePath.withRel(rel)
   662  	return p
   663  }
   664  
   665  var _ Path = OutputPath{}
   666  
   667  // PathForOutput joins the provided paths and returns an OutputPath that is
   668  // validated to not escape the build dir.
   669  // On error, it will return a usable, but invalid OutputPath, and report a ModuleError.
   670  func PathForOutput(ctx PathContext, pathComponents ...string) OutputPath {
   671  	path, err := validatePath(pathComponents...)
   672  	if err != nil {
   673  		reportPathError(ctx, err)
   674  	}
   675  	return OutputPath{basePath{path, ctx.Config(), ""}}
   676  }
   677  
   678  func (p OutputPath) writablePath() {}
   679  
   680  func (p OutputPath) String() string {
   681  	return filepath.Join(p.config.buildDir, p.path)
   682  }
   683  
   684  func (p OutputPath) RelPathString() string {
   685  	return p.path
   686  }
   687  
   688  // Join creates a new OutputPath with paths... joined with the current path. The
   689  // provided paths... may not use '..' to escape from the current path.
   690  func (p OutputPath) Join(ctx PathContext, paths ...string) OutputPath {
   691  	path, err := validatePath(paths...)
   692  	if err != nil {
   693  		reportPathError(ctx, err)
   694  	}
   695  	return p.withRel(path)
   696  }
   697  
   698  // PathForIntermediates returns an OutputPath representing the top-level
   699  // intermediates directory.
   700  func PathForIntermediates(ctx PathContext, paths ...string) OutputPath {
   701  	path, err := validatePath(paths...)
   702  	if err != nil {
   703  		reportPathError(ctx, err)
   704  	}
   705  	return PathForOutput(ctx, ".intermediates", path)
   706  }
   707  
   708  // DistPath is a Path representing a file path rooted from the dist directory
   709  type DistPath struct {
   710  	basePath
   711  }
   712  
   713  func (p DistPath) withRel(rel string) DistPath {
   714  	p.basePath = p.basePath.withRel(rel)
   715  	return p
   716  }
   717  
   718  var _ Path = DistPath{}
   719  
   720  // PathForDist joins the provided paths and returns a DistPath that is
   721  // validated to not escape the dist dir.
   722  // On error, it will return a usable, but invalid DistPath, and report a ModuleError.
   723  func PathForDist(ctx PathContext, pathComponents ...string) DistPath {
   724  	path, err := validatePath(pathComponents...)
   725  	if err != nil {
   726  		reportPathError(ctx, err)
   727  	}
   728  	return DistPath{basePath{path, ctx.Config(), ""}}
   729  }
   730  
   731  func (p DistPath) writablePath() {}
   732  
   733  func (p DistPath) Valid() bool {
   734  	return p.config.productVariables.DistDir != nil && *p.config.productVariables.DistDir != ""
   735  }
   736  
   737  func (p DistPath) String() string {
   738  	if !p.Valid() {
   739  		panic("Requesting an invalid path")
   740  	}
   741  	return filepath.Join(*p.config.productVariables.DistDir, p.path)
   742  }
   743  
   744  func (p DistPath) RelPathString() string {
   745  	return p.path
   746  }
   747  
   748  // ModuleSrcPath is a Path representing a file rooted from a module's local source dir
   749  type ModuleSrcPath struct {
   750  	SourcePath
   751  }
   752  
   753  var _ Path = ModuleSrcPath{}
   754  var _ genPathProvider = ModuleSrcPath{}
   755  var _ objPathProvider = ModuleSrcPath{}
   756  var _ resPathProvider = ModuleSrcPath{}
   757  
   758  // PathForModuleSrc returns a ModuleSrcPath representing the paths... under the
   759  // module's local source directory.
   760  func PathForModuleSrc(ctx ModuleContext, paths ...string) ModuleSrcPath {
   761  	p, err := validatePath(paths...)
   762  	if err != nil {
   763  		reportPathError(ctx, err)
   764  	}
   765  
   766  	srcPath, err := pathForSource(ctx, ctx.ModuleDir(), p)
   767  	if err != nil {
   768  		reportPathError(ctx, err)
   769  	}
   770  
   771  	path := ModuleSrcPath{srcPath}
   772  	path.basePath.rel = p
   773  
   774  	if exists, _, err := ctx.Fs().Exists(path.String()); err != nil {
   775  		reportPathErrorf(ctx, "%s: %s", path, err.Error())
   776  	} else if !exists {
   777  		reportPathErrorf(ctx, "module source path %s does not exist", path)
   778  	}
   779  
   780  	return path
   781  }
   782  
   783  // OptionalPathForModuleSrc returns an OptionalPath. The OptionalPath contains a
   784  // valid path if p is non-nil.
   785  func OptionalPathForModuleSrc(ctx ModuleContext, p *string) OptionalPath {
   786  	if p == nil {
   787  		return OptionalPath{}
   788  	}
   789  	return OptionalPathForPath(PathForModuleSrc(ctx, *p))
   790  }
   791  
   792  func (p ModuleSrcPath) genPathWithExt(ctx ModuleContext, subdir, ext string) ModuleGenPath {
   793  	return PathForModuleGen(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
   794  }
   795  
   796  func (p ModuleSrcPath) objPathWithExt(ctx ModuleContext, subdir, ext string) ModuleObjPath {
   797  	return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
   798  }
   799  
   800  func (p ModuleSrcPath) resPathWithName(ctx ModuleContext, name string) ModuleResPath {
   801  	// TODO: Use full directory if the new ctx is not the current ctx?
   802  	return PathForModuleRes(ctx, p.path, name)
   803  }
   804  
   805  func (p ModuleSrcPath) WithSubDir(ctx ModuleContext, subdir string) ModuleSrcPath {
   806  	subdir = PathForModuleSrc(ctx, subdir).String()
   807  	var err error
   808  	rel, err := filepath.Rel(subdir, p.path)
   809  	if err != nil {
   810  		ctx.ModuleErrorf("source file %q is not under path %q", p.path, subdir)
   811  		return p
   812  	}
   813  	p.rel = rel
   814  	return p
   815  }
   816  
   817  // ModuleOutPath is a Path representing a module's output directory.
   818  type ModuleOutPath struct {
   819  	OutputPath
   820  }
   821  
   822  var _ Path = ModuleOutPath{}
   823  
   824  func pathForModule(ctx ModuleContext) OutputPath {
   825  	return PathForOutput(ctx, ".intermediates", ctx.ModuleDir(), ctx.ModuleName(), ctx.ModuleSubDir())
   826  }
   827  
   828  // PathForVndkRefDump returns an OptionalPath representing the path of the reference
   829  // abi dump for the given module. This is not guaranteed to be valid.
   830  func PathForVndkRefAbiDump(ctx ModuleContext, version, fileName string, vndkOrNdk, isSourceDump bool) OptionalPath {
   831  	arches := ctx.DeviceConfig().Arches()
   832  	currentArch := ctx.Arch()
   833  	archNameAndVariant := currentArch.ArchType.String()
   834  	if currentArch.ArchVariant != "" {
   835  		archNameAndVariant += "_" + currentArch.ArchVariant
   836  	}
   837  	var sourceOrBinaryDir string
   838  	var vndkOrNdkDir string
   839  	var ext string
   840  	if isSourceDump {
   841  		ext = ".lsdump.gz"
   842  		sourceOrBinaryDir = "source-based"
   843  	} else {
   844  		ext = ".bdump.gz"
   845  		sourceOrBinaryDir = "binary-based"
   846  	}
   847  	if vndkOrNdk {
   848  		vndkOrNdkDir = "vndk"
   849  	} else {
   850  		vndkOrNdkDir = "ndk"
   851  	}
   852  	if len(arches) == 0 {
   853  		panic("device build with no primary arch")
   854  	}
   855  	binderBitness := ctx.DeviceConfig().BinderBitness()
   856  	refDumpFileStr := "prebuilts/abi-dumps/" + vndkOrNdkDir + "/" + version + "/" + binderBitness + "/" +
   857  		archNameAndVariant + "/" + sourceOrBinaryDir + "/" + fileName + ext
   858  	return ExistentPathForSource(ctx, refDumpFileStr)
   859  }
   860  
   861  // PathForModuleOut returns a Path representing the paths... under the module's
   862  // output directory.
   863  func PathForModuleOut(ctx ModuleContext, paths ...string) ModuleOutPath {
   864  	p, err := validatePath(paths...)
   865  	if err != nil {
   866  		reportPathError(ctx, err)
   867  	}
   868  	return ModuleOutPath{
   869  		OutputPath: pathForModule(ctx).withRel(p),
   870  	}
   871  }
   872  
   873  // ModuleGenPath is a Path representing the 'gen' directory in a module's output
   874  // directory. Mainly used for generated sources.
   875  type ModuleGenPath struct {
   876  	ModuleOutPath
   877  }
   878  
   879  var _ Path = ModuleGenPath{}
   880  var _ genPathProvider = ModuleGenPath{}
   881  var _ objPathProvider = ModuleGenPath{}
   882  
   883  // PathForModuleGen returns a Path representing the paths... under the module's
   884  // `gen' directory.
   885  func PathForModuleGen(ctx ModuleContext, paths ...string) ModuleGenPath {
   886  	p, err := validatePath(paths...)
   887  	if err != nil {
   888  		reportPathError(ctx, err)
   889  	}
   890  	return ModuleGenPath{
   891  		ModuleOutPath: ModuleOutPath{
   892  			OutputPath: pathForModule(ctx).withRel("gen").withRel(p),
   893  		},
   894  	}
   895  }
   896  
   897  func (p ModuleGenPath) genPathWithExt(ctx ModuleContext, subdir, ext string) ModuleGenPath {
   898  	// TODO: make a different path for local vs remote generated files?
   899  	return PathForModuleGen(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
   900  }
   901  
   902  func (p ModuleGenPath) objPathWithExt(ctx ModuleContext, subdir, ext string) ModuleObjPath {
   903  	return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
   904  }
   905  
   906  // ModuleObjPath is a Path representing the 'obj' directory in a module's output
   907  // directory. Used for compiled objects.
   908  type ModuleObjPath struct {
   909  	ModuleOutPath
   910  }
   911  
   912  var _ Path = ModuleObjPath{}
   913  
   914  // PathForModuleObj returns a Path representing the paths... under the module's
   915  // 'obj' directory.
   916  func PathForModuleObj(ctx ModuleContext, pathComponents ...string) ModuleObjPath {
   917  	p, err := validatePath(pathComponents...)
   918  	if err != nil {
   919  		reportPathError(ctx, err)
   920  	}
   921  	return ModuleObjPath{PathForModuleOut(ctx, "obj", p)}
   922  }
   923  
   924  // ModuleResPath is a a Path representing the 'res' directory in a module's
   925  // output directory.
   926  type ModuleResPath struct {
   927  	ModuleOutPath
   928  }
   929  
   930  var _ Path = ModuleResPath{}
   931  
   932  // PathForModuleRes returns a Path representing the paths... under the module's
   933  // 'res' directory.
   934  func PathForModuleRes(ctx ModuleContext, pathComponents ...string) ModuleResPath {
   935  	p, err := validatePath(pathComponents...)
   936  	if err != nil {
   937  		reportPathError(ctx, err)
   938  	}
   939  
   940  	return ModuleResPath{PathForModuleOut(ctx, "res", p)}
   941  }
   942  
   943  // PathForModuleInstall returns a Path representing the install path for the
   944  // module appended with paths...
   945  func PathForModuleInstall(ctx ModuleInstallPathContext, pathComponents ...string) OutputPath {
   946  	var outPaths []string
   947  	if ctx.Device() {
   948  		var partition string
   949  		if ctx.InstallInData() {
   950  			partition = "data"
   951  		} else if ctx.SocSpecific() {
   952  			partition = ctx.DeviceConfig().VendorPath()
   953  		} else if ctx.DeviceSpecific() {
   954  			partition = ctx.DeviceConfig().OdmPath()
   955  		} else if ctx.ProductSpecific() {
   956  			partition = ctx.DeviceConfig().ProductPath()
   957  		} else {
   958  			partition = "system"
   959  		}
   960  
   961  		if ctx.InstallInSanitizerDir() {
   962  			partition = "data/asan/" + partition
   963  		}
   964  		outPaths = []string{"target", "product", ctx.Config().DeviceName(), partition}
   965  	} else {
   966  		switch ctx.Os() {
   967  		case Linux:
   968  			outPaths = []string{"host", "linux-x86"}
   969  		case LinuxBionic:
   970  			// TODO: should this be a separate top level, or shared with linux-x86?
   971  			outPaths = []string{"host", "linux_bionic-x86"}
   972  		default:
   973  			outPaths = []string{"host", ctx.Os().String() + "-x86"}
   974  		}
   975  	}
   976  	if ctx.Debug() {
   977  		outPaths = append([]string{"debug"}, outPaths...)
   978  	}
   979  	outPaths = append(outPaths, pathComponents...)
   980  	return PathForOutput(ctx, outPaths...)
   981  }
   982  
   983  // validateSafePath validates a path that we trust (may contain ninja variables).
   984  // Ensures that each path component does not attempt to leave its component.
   985  func validateSafePath(pathComponents ...string) (string, error) {
   986  	for _, path := range pathComponents {
   987  		path := filepath.Clean(path)
   988  		if path == ".." || strings.HasPrefix(path, "../") || strings.HasPrefix(path, "/") {
   989  			return "", fmt.Errorf("Path is outside directory: %s", path)
   990  		}
   991  	}
   992  	// TODO: filepath.Join isn't necessarily correct with embedded ninja
   993  	// variables. '..' may remove the entire ninja variable, even if it
   994  	// will be expanded to multiple nested directories.
   995  	return filepath.Join(pathComponents...), nil
   996  }
   997  
   998  // validatePath validates that a path does not include ninja variables, and that
   999  // each path component does not attempt to leave its component. Returns a joined
  1000  // version of each path component.
  1001  func validatePath(pathComponents ...string) (string, error) {
  1002  	for _, path := range pathComponents {
  1003  		if strings.Contains(path, "$") {
  1004  			return "", fmt.Errorf("Path contains invalid character($): %s", path)
  1005  		}
  1006  	}
  1007  	return validateSafePath(pathComponents...)
  1008  }
  1009  
  1010  func PathForPhony(ctx PathContext, phony string) WritablePath {
  1011  	if strings.ContainsAny(phony, "$/") {
  1012  		reportPathErrorf(ctx, "Phony target contains invalid character ($ or /): %s", phony)
  1013  	}
  1014  	return PhonyPath{basePath{phony, ctx.Config(), ""}}
  1015  }
  1016  
  1017  type PhonyPath struct {
  1018  	basePath
  1019  }
  1020  
  1021  func (p PhonyPath) writablePath() {}
  1022  
  1023  var _ Path = PhonyPath{}
  1024  var _ WritablePath = PhonyPath{}
  1025  
  1026  type testPath struct {
  1027  	basePath
  1028  }
  1029  
  1030  func (p testPath) String() string {
  1031  	return p.path
  1032  }
  1033  
  1034  func PathForTesting(paths ...string) Path {
  1035  	p, err := validateSafePath(paths...)
  1036  	if err != nil {
  1037  		panic(err)
  1038  	}
  1039  	return testPath{basePath{path: p, rel: p}}
  1040  }
  1041  
  1042  func PathsForTesting(strs []string) Paths {
  1043  	p := make(Paths, len(strs))
  1044  	for i, s := range strs {
  1045  		p[i] = PathForTesting(s)
  1046  	}
  1047  
  1048  	return p
  1049  }