Add soong_build primary builder
Initial build logic for building android with soong. It can build
a variety of C and C++ files for arm/arm64 and host.
Change-Id: I10eb37c2c2a50be6af1bb5fd568c0962b9476bf0
diff --git a/common/glob.go b/common/glob.go
new file mode 100644
index 0000000..9aada95
--- /dev/null
+++ b/common/glob.go
@@ -0,0 +1,133 @@
+// 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"
+
+ "blueprint"
+ "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 "$glob"`, globCmd),
+ Description: "glob $glob",
+
+ Restat: true,
+ Generator: true,
+ Deps: blueprint.DepsGCC,
+ Depfile: "$out.d",
+ },
+ "glob")
+)
+
+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"
+
+ // Get a globbed file list, and write out fileListFile and depFile
+ files, err := glob.GlobWithDepFile(globPattern, fileListFile, depFile)
+ if err != nil {
+ ctx.ModuleErrorf("glob: %s", err.Error())
+ return []string{globPattern}
+ }
+
+ // 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,
+ },
+ })
+
+ // Phony rule so the cleanup phase doesn't delete the depFile
+ ctx.Build(pctx, blueprint.BuildParams{
+ Rule: blueprint.Phony,
+ Outputs: []string{depFile},
+ })
+
+ // Make build.ninja depend on the fileListFile
+ ctx.AddNinjaFileDeps(fileListFile)
+
+ return files
+}
+
+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
+}