platform_build_soong/common/glob.go
Colin Cross 3e8ec07787 Support excludes in globs
Java resource support requires globbing directories while ignoring
specific filenames.  Add support for multiple -e options to
soong_glob to specify filenames to exclude, and split out Glob
into GlobRule to insert a rule to generate a glob file list.

Change-Id: Ia911dd68bd1638452881d18378572d015fd4e31a
2015-04-03 15:55:08 -07:00

147 lines
4.2 KiB
Go

// Copyright 2015 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package common
import (
"fmt"
"path/filepath"
"strings"
"github.com/google/blueprint"
"github.com/google/blueprint/bootstrap"
"android/soong/glob"
)
// This file supports globbing source files in Blueprints files.
//
// The build.ninja file needs to be regenerated any time a file matching the glob is added
// or removed. The naive solution is to have the build.ninja file depend on all the
// traversed directories, but this will cause the regeneration step to run every time a
// non-matching file is added to a traversed directory, including backup files created by
// editors.
//
// The solution implemented here optimizes out regenerations when the directory modifications
// don't match the glob by having the build.ninja file depend on an intermedate file that
// is only updated when a file matching the glob is added or removed. The intermediate file
// depends on the traversed directories via a depfile. The depfile is used to avoid build
// errors if a directory is deleted - a direct dependency on the deleted directory would result
// in a build failure with a "missing and no known rule to make it" error.
var (
globCmd = filepath.Join(bootstrap.BinDir, "soong_glob")
// globRule rule traverses directories to produce a list of files that match $glob
// and writes it to $out if it has changed, and writes the directories to $out.d
globRule = pctx.StaticRule("globRule",
blueprint.RuleParams{
Command: fmt.Sprintf(`%s -o $out $excludes "$glob"`, globCmd),
Description: "glob $glob",
Restat: true,
Generator: true,
Deps: blueprint.DepsGCC,
Depfile: "$out.d",
},
"glob", "excludes")
)
func hasGlob(in []string) bool {
for _, s := range in {
if glob.IsGlob(s) {
return true
}
}
return false
}
func ExpandGlobs(ctx AndroidModuleContext, in []string) []string {
if !hasGlob(in) {
return in
}
out := make([]string, 0, len(in))
for _, s := range in {
if glob.IsGlob(s) {
out = append(out, Glob(ctx, s)...)
} else {
out = append(out, s)
}
}
return out
}
func Glob(ctx AndroidModuleContext, globPattern string) []string {
fileListFile := filepath.Join(ModuleOutDir(ctx), "glob", globToString(globPattern))
depFile := fileListFile + ".d"
var excludes []string
// Get a globbed file list, and write out fileListFile and depFile
files, err := glob.GlobWithDepFile(globPattern, fileListFile, depFile, excludes)
if err != nil {
ctx.ModuleErrorf("glob: %s", err.Error())
return []string{globPattern}
}
GlobRule(ctx, globPattern, excludes, fileListFile, depFile)
// Make build.ninja depend on the fileListFile
ctx.AddNinjaFileDeps(fileListFile)
return files
}
func GlobRule(ctx AndroidModuleContext, globPattern string, excludes []string,
fileListFile, depFile string) {
var excludeArgs []string
for _, e := range excludes {
excludeArgs = append(excludeArgs, "-e "+e)
}
// Create a rule to rebuild fileListFile if a directory in depFile changes. fileListFile
// will only be rewritten if it has changed, preventing unnecesary build.ninja regenerations.
ctx.Build(pctx, blueprint.BuildParams{
Rule: globRule,
Outputs: []string{fileListFile},
Implicits: []string{globCmd},
Args: map[string]string{
"glob": globPattern,
"excludes": strings.Join(excludeArgs, " "),
},
})
// Phony rule so the cleanup phase doesn't delete the depFile
ctx.Build(pctx, blueprint.BuildParams{
Rule: blueprint.Phony,
Outputs: []string{depFile},
})
}
func globToString(glob string) string {
ret := ""
for _, c := range glob {
if c >= 'a' && c <= 'z' ||
c >= 'A' && c <= 'Z' ||
c >= '0' && c <= '9' ||
c == '_' || c == '-' || c == '/' {
ret += string(c)
}
}
return ret
}