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
diff --git a/cmd/soong_glob/soong_glob.go b/cmd/soong_glob/soong_glob.go
index 227d7b0..6f56bb9 100644
--- a/cmd/soong_glob/soong_glob.go
+++ b/cmd/soong_glob/soong_glob.go
@@ -28,8 +28,29 @@
 
 var (
 	out = flag.String("o", "", "file to write list of files that match glob")
+
+	excludes multiArg
 )
 
+func init() {
+	flag.Var(&excludes, "e", "pattern to exclude from results")
+}
+
+type multiArg []string
+
+func (m *multiArg) String() string {
+	return `""`
+}
+
+func (m *multiArg) Set(s string) error {
+	*m = append(*m, s)
+	return nil
+}
+
+func (m *multiArg) Get() interface{} {
+	return m
+}
+
 func usage() {
 	fmt.Fprintf(os.Stderr, "usage: soong_glob -o out glob\n")
 	flag.PrintDefaults()
@@ -48,7 +69,7 @@
 		usage()
 	}
 
-	_, err := glob.GlobWithDepFile(flag.Arg(0), *out, *out+".d")
+	_, err := glob.GlobWithDepFile(flag.Arg(0), *out, *out+".d", excludes)
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "error: %s\n", err.Error())
 		os.Exit(1)
diff --git a/common/glob.go b/common/glob.go
index 5568cbc..a3cfab2 100644
--- a/common/glob.go
+++ b/common/glob.go
@@ -17,6 +17,7 @@
 import (
 	"fmt"
 	"path/filepath"
+	"strings"
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/bootstrap"
@@ -46,7 +47,7 @@
 	// 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 "$glob"`, globCmd),
+			Command:     fmt.Sprintf(`%s -o $out $excludes "$glob"`, globCmd),
 			Description: "glob $glob",
 
 			Restat:    true,
@@ -54,7 +55,7 @@
 			Deps:      blueprint.DepsGCC,
 			Depfile:   "$out.d",
 		},
-		"glob")
+		"glob", "excludes")
 )
 
 func hasGlob(in []string) bool {
@@ -88,13 +89,30 @@
 	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)
+	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{
@@ -102,7 +120,8 @@
 		Outputs:   []string{fileListFile},
 		Implicits: []string{globCmd},
 		Args: map[string]string{
-			"glob": globPattern,
+			"glob":     globPattern,
+			"excludes": strings.Join(excludeArgs, " "),
 		},
 	})
 
@@ -111,11 +130,6 @@
 		Rule:    blueprint.Phony,
 		Outputs: []string{depFile},
 	})
-
-	// Make build.ninja depend on the fileListFile
-	ctx.AddNinjaFileDeps(fileListFile)
-
-	return files
 }
 
 func globToString(glob string) string {
diff --git a/glob/glob.go b/glob/glob.go
index eb4d1ab..a8c348d 100644
--- a/glob/glob.go
+++ b/glob/glob.go
@@ -35,7 +35,7 @@
 // for a recursive glob.
 //
 // Returns a list of file paths, and an error.
-func GlobWithDepFile(glob, fileListFile, depFile string) (files []string, err error) {
+func GlobWithDepFile(glob, fileListFile, depFile string, excludes []string) (files []string, err error) {
 	globPattern := filepath.Base(glob)
 	globDir := filepath.Dir(glob)
 	recursive := false
@@ -64,6 +64,15 @@
 					return err
 				}
 				if match {
+					for _, e := range excludes {
+						excludeMatch, err := filepath.Match(e, info.Name())
+						if err != nil {
+							return err
+						}
+						if excludeMatch {
+							return nil
+						}
+					}
 					files = append(files, path)
 				}
 			}
@@ -71,7 +80,7 @@
 			return nil
 		})
 
-	fileList := strings.Join(files, "\n")
+	fileList := strings.Join(files, "\n") + "\n"
 
 	writeFileIfChanged(fileListFile, []byte(fileList), 0666)
 	deptools.WriteDepFile(depFile, fileListFile, dirs)