Home | History | Annotate | Download | only in common
      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 common
     16 
     17 import (
     18 	"fmt"
     19 	"path/filepath"
     20 
     21 	"github.com/google/blueprint"
     22 
     23 	"android/soong/glob"
     24 )
     25 
     26 // This file supports globbing source files in Blueprints files.
     27 //
     28 // The build.ninja file needs to be regenerated any time a file matching the glob is added
     29 // or removed.  The naive solution is to have the build.ninja file depend on all the
     30 // traversed directories, but this will cause the regeneration step to run every time a
     31 // non-matching file is added to a traversed directory, including backup files created by
     32 // editors.
     33 //
     34 // The solution implemented here optimizes out regenerations when the directory modifications
     35 // don't match the glob by having the build.ninja file depend on an intermedate file that
     36 // is only updated when a file matching the glob is added or removed.  The intermediate file
     37 // depends on the traversed directories via a depfile.  The depfile is used to avoid build
     38 // errors if a directory is deleted - a direct dependency on the deleted directory would result
     39 // in a build failure with a "missing and no known rule to make it" error.
     40 
     41 var (
     42 	globCmd = filepath.Join("${bootstrap.BinDir}", "soong_glob")
     43 
     44 	// globRule rule traverses directories to produce a list of files that match $glob
     45 	// and writes it to $out if it has changed, and writes the directories to $out.d
     46 	globRule = pctx.StaticRule("globRule",
     47 		blueprint.RuleParams{
     48 			Command:     fmt.Sprintf(`%s -o $out $excludes "$glob"`, globCmd),
     49 			CommandDeps: []string{globCmd},
     50 			Description: "glob $glob",
     51 
     52 			Restat:  true,
     53 			Deps:    blueprint.DepsGCC,
     54 			Depfile: "$out.d",
     55 		},
     56 		"glob", "excludes")
     57 )
     58 
     59 func hasGlob(in []string) bool {
     60 	for _, s := range in {
     61 		if glob.IsGlob(s) {
     62 			return true
     63 		}
     64 	}
     65 
     66 	return false
     67 }
     68 
     69 // The subset of ModuleContext and SingletonContext needed by Glob
     70 type globContext interface {
     71 	Build(pctx blueprint.PackageContext, params blueprint.BuildParams)
     72 	AddNinjaFileDeps(deps ...string)
     73 }
     74 
     75 func Glob(ctx globContext, outDir string, globPattern string, excludes []string) ([]string, error) {
     76 	fileListFile := filepath.Join(outDir, "glob", globToString(globPattern))
     77 	depFile := fileListFile + ".d"
     78 
     79 	// Get a globbed file list, and write out fileListFile and depFile
     80 	files, err := glob.GlobWithDepFile(globPattern, fileListFile, depFile, excludes)
     81 	if err != nil {
     82 		return nil, err
     83 	}
     84 
     85 	GlobRule(ctx, globPattern, excludes, fileListFile, depFile)
     86 
     87 	// Make build.ninja depend on the fileListFile
     88 	ctx.AddNinjaFileDeps(fileListFile)
     89 
     90 	return files, nil
     91 }
     92 
     93 func GlobRule(ctx globContext, globPattern string, excludes []string,
     94 	fileListFile, depFile string) {
     95 
     96 	// Create a rule to rebuild fileListFile if a directory in depFile changes.  fileListFile
     97 	// will only be rewritten if it has changed, preventing unnecesary build.ninja regenerations.
     98 	ctx.Build(pctx, blueprint.BuildParams{
     99 		Rule:    globRule,
    100 		Outputs: []string{fileListFile},
    101 		Args: map[string]string{
    102 			"glob":     globPattern,
    103 			"excludes": JoinWithPrefixAndQuote(excludes, "-e "),
    104 		},
    105 	})
    106 }
    107 
    108 func globToString(glob string) string {
    109 	ret := ""
    110 	for _, c := range glob {
    111 		if c >= 'a' && c <= 'z' ||
    112 			c >= 'A' && c <= 'Z' ||
    113 			c >= '0' && c <= '9' ||
    114 			c == '_' || c == '-' || c == '/' {
    115 			ret += string(c)
    116 		}
    117 	}
    118 
    119 	return ret
    120 }
    121