Merge "androidbp: Defer local path printing" into master-soong
diff --git a/Android.bp b/Android.bp
index c9480c7..3eb86e5 100644
--- a/Android.bp
+++ b/Android.bp
@@ -80,7 +80,6 @@
     ],
     srcs: [
         "common/arch.go",
-        "common/checkbuild.go",
         "common/config.go",
         "common/defs.go",
         "common/env.go",
diff --git a/bootstrap.bash b/bootstrap.bash
index 551ba72..e9a6436 100755
--- a/bootstrap.bash
+++ b/bootstrap.bash
@@ -2,6 +2,7 @@
 
 export BOOTSTRAP="${BASH_SOURCE[0]}"
 export SRCDIR=$(dirname "${BASH_SOURCE[0]}")
+export TOPNAME="Android.bp"
 export BOOTSTRAP_MANIFEST="${SRCDIR}/build/soong/build.ninja.in"
 
 case $(uname) in
diff --git a/build.ninja.in b/build.ninja.in
index 988b2f9..2af6e08 100644
--- a/build.ninja.in
+++ b/build.ninja.in
@@ -53,7 +53,7 @@
 # Variant:
 # Type:    bootstrap_go_binary
 # Factory: github.com/google/blueprint/bootstrap.newGoBinaryModule
-# Defined: build/soong/Android.bp:186:1
+# Defined: build/soong/Android.bp:185:1
 
 build .bootstrap/androidbp/obj/androidbp.a: g.bootstrap.gc $
         ${g.bootstrap.srcDir}/build/soong/androidbp/cmd/androidbp.go $
@@ -77,7 +77,7 @@
 # Variant:
 # Type:    bootstrap_go_binary
 # Factory: github.com/google/blueprint/bootstrap.newGoBinaryModule
-# Defined: build/soong/Android.bp:162:1
+# Defined: build/soong/Android.bp:161:1
 
 build .bootstrap/androidmk/obj/androidmk.a: g.bootstrap.gc $
         ${g.bootstrap.srcDir}/build/soong/androidmk/cmd/androidmk/android.go $
@@ -103,7 +103,7 @@
 # Variant:
 # Type:    bootstrap_go_package
 # Factory: github.com/google/blueprint/bootstrap.newGoPackageModule
-# Defined: build/soong/Android.bp:175:1
+# Defined: build/soong/Android.bp:174:1
 
 build .bootstrap/androidmk-parser/pkg/android/soong/androidmk/parser.a: $
         g.bootstrap.gc $
@@ -334,7 +334,7 @@
 # Variant:
 # Type:    bootstrap_go_package
 # Factory: github.com/google/blueprint/bootstrap.newGoPackageModule
-# Defined: build/soong/Android.bp:94:1
+# Defined: build/soong/Android.bp:93:1
 
 build .bootstrap/soong-cc/pkg/android/soong/cc.a: g.bootstrap.gc $
         ${g.bootstrap.srcDir}/build/soong/cc/builder.go $
@@ -371,7 +371,6 @@
 
 build .bootstrap/soong-common/pkg/android/soong/common.a: g.bootstrap.gc $
         ${g.bootstrap.srcDir}/build/soong/common/arch.go $
-        ${g.bootstrap.srcDir}/build/soong/common/checkbuild.go $
         ${g.bootstrap.srcDir}/build/soong/common/config.go $
         ${g.bootstrap.srcDir}/build/soong/common/defs.go $
         ${g.bootstrap.srcDir}/build/soong/common/env.go $
@@ -409,7 +408,7 @@
 # Variant:
 # Type:    bootstrap_go_package
 # Factory: github.com/google/blueprint/bootstrap.newGoPackageModule
-# Defined: build/soong/Android.bp:119:1
+# Defined: build/soong/Android.bp:118:1
 
 build .bootstrap/soong-genrule/pkg/android/soong/genrule.a: g.bootstrap.gc $
         ${g.bootstrap.srcDir}/build/soong/genrule/genrule.go | $
@@ -447,7 +446,7 @@
 # Variant:
 # Type:    bootstrap_go_package
 # Factory: github.com/google/blueprint/bootstrap.newGoPackageModule
-# Defined: build/soong/Android.bp:139:1
+# Defined: build/soong/Android.bp:138:1
 
 build .bootstrap/soong-java/pkg/android/soong/java.a: g.bootstrap.gc $
         ${g.bootstrap.srcDir}/build/soong/java/app_builder.go $
@@ -560,7 +559,7 @@
 # Variant:
 # Type:    bootstrap_go_binary
 # Factory: github.com/google/blueprint/bootstrap.newGoBinaryModule
-# Defined: build/soong/Android.bp:132:1
+# Defined: build/soong/Android.bp:131:1
 
 build .bootstrap/soong_jar/obj/soong_jar.a: g.bootstrap.gc $
         ${g.bootstrap.srcDir}/build/soong/cmd/soong_jar/soong_jar.go | $
diff --git a/cc/cc.go b/cc/cc.go
index 61e2568..051bbaf 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -531,7 +531,7 @@
 
 	buildFlags := ccFlagsToBuilderFlags(flags)
 
-	srcFiles = common.ExpandSources(ctx, srcFiles)
+	srcFiles = ctx.ExpandSources(srcFiles)
 	srcFiles, deps := genSources(ctx, srcFiles, buildFlags)
 
 	return TransformSourceToObj(ctx, subdir, srcFiles, buildFlags, deps)
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index b7c4198..9ef0d06 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -81,7 +81,7 @@
 	ctx.RegisterEarlyMutator("test_per_src", cc.TestPerSrcMutator)
 
 	// Singletons
-	ctx.RegisterSingletonType("checkbuild", common.CheckbuildSingleton)
+	ctx.RegisterSingletonType("buildtarget", common.BuildTargetSingleton)
 	ctx.RegisterSingletonType("env", common.EnvSingleton)
 	ctx.RegisterSingletonType("logtags", java.LogtagsSingleton)
 
diff --git a/common/checkbuild.go b/common/checkbuild.go
deleted file mode 100644
index 17ec37f..0000000
--- a/common/checkbuild.go
+++ /dev/null
@@ -1,44 +0,0 @@
-// 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 (
-	"github.com/google/blueprint"
-)
-
-func CheckbuildSingleton() blueprint.Singleton {
-	return &checkbuildSingleton{}
-}
-
-type checkbuildSingleton struct{}
-
-func (c *checkbuildSingleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
-	deps := []string{}
-	ctx.VisitAllModules(func(module blueprint.Module) {
-		if a, ok := module.(AndroidModule); ok {
-			if len(a.base().checkbuildFiles) > 0 {
-				deps = append(deps, ctx.ModuleName(module)+"-checkbuild")
-			}
-		}
-	})
-
-	ctx.Build(pctx, blueprint.BuildParams{
-		Rule:      blueprint.Phony,
-		Outputs:   []string{"checkbuild"},
-		Implicits: deps,
-		// HACK: checkbuild should be an optional build, but force it enabled for now
-		//Optional:  true,
-	})
-}
diff --git a/common/defs.go b/common/defs.go
index 67b93ff..98464fe 100644
--- a/common/defs.go
+++ b/common/defs.go
@@ -15,7 +15,10 @@
 package common
 
 import (
+	"path/filepath"
+
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/bootstrap"
 )
 
 var (
@@ -24,6 +27,13 @@
 	cpPreserveSymlinks = pctx.VariableConfigMethod("cpPreserveSymlinks",
 		Config.CpPreserveSymlinksFlags)
 
+	androidbpCmd = filepath.Join(bootstrap.BinDir, "androidbp")
+	androidbp    = pctx.StaticRule("androidbp",
+		blueprint.RuleParams{
+			Command:     androidbpCmd + " $in $out",
+			Description: "androidbp $out",
+		})
+
 	// A phony rule that is not the built-in Ninja phony rule.  The built-in
 	// phony rule has special behavior that is sometimes not desired.  See the
 	// Ninja docs for more details.
diff --git a/common/glob.go b/common/glob.go
index 669d185..d63f87a 100644
--- a/common/glob.go
+++ b/common/glob.go
@@ -66,41 +66,20 @@
 	return false
 }
 
-func expandGlobs(ctx AndroidModuleContext, in []string) []string {
-	if !hasGlob(in) {
-		return in
-	}
-
-	var excludes []string
-	for _, s := range in {
-		if s[0] == '-' {
-			excludes = append(excludes, s[1:])
-		}
-	}
-
-	out := make([]string, 0, len(in))
-	for _, s := range in {
-		if s[0] == '-' {
-			continue
-		} else if glob.IsGlob(s) {
-			out = append(out, Glob(ctx, s, excludes)...)
-		} else {
-			out = append(out, s)
-		}
-	}
-
-	return out
+// The subset of ModuleContext and SingletonContext needed by Glob
+type globContext interface {
+	Build(pctx *blueprint.PackageContext, params blueprint.BuildParams)
+	AddNinjaFileDeps(deps ...string)
 }
 
-func Glob(ctx AndroidModuleContext, globPattern string, excludes []string) []string {
-	fileListFile := filepath.Join(ModuleOutDir(ctx), "glob", globToString(globPattern))
+func Glob(ctx globContext, outDir string, globPattern string, excludes []string) ([]string, error) {
+	fileListFile := filepath.Join(outDir, "glob", globToString(globPattern))
 	depFile := fileListFile + ".d"
 
 	// 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}
+		return nil, err
 	}
 
 	GlobRule(ctx, globPattern, excludes, fileListFile, depFile)
@@ -108,10 +87,10 @@
 	// Make build.ninja depend on the fileListFile
 	ctx.AddNinjaFileDeps(fileListFile)
 
-	return files
+	return files, nil
 }
 
-func GlobRule(ctx AndroidModuleContext, globPattern string, excludes []string,
+func GlobRule(ctx globContext, globPattern string, excludes []string,
 	fileListFile, depFile string) {
 
 	// Create a rule to rebuild fileListFile if a directory in depFile changes.  fileListFile
diff --git a/common/module.go b/common/module.go
index ef7c63a..bf3c435 100644
--- a/common/module.go
+++ b/common/module.go
@@ -17,6 +17,10 @@
 import (
 	"path/filepath"
 	"runtime"
+	"sort"
+	"strings"
+
+	"android/soong/glob"
 
 	"github.com/google/blueprint"
 )
@@ -49,6 +53,9 @@
 	blueprint.ModuleContext
 	androidBaseContext
 
+	ExpandSources(srcFiles []string) []string
+	Glob(globPattern string, excludes []string) []string
+
 	InstallFile(installPath, srcPath string, deps ...string) string
 	InstallFileName(installPath, name, srcPath string, deps ...string) string
 	CheckbuildFile(srcPath string)
@@ -194,6 +201,12 @@
 	noAddressSanitizer bool
 	installFiles       []string
 	checkbuildFiles    []string
+
+	// Used by buildTargetSingleton to create checkbuild and per-directory build targets
+	// Only set on the final variant of each module
+	installTarget    string
+	checkbuildTarget string
+	blueprintDir     string
 }
 
 func (a *AndroidModuleBase) base() *AndroidModuleBase {
@@ -273,6 +286,7 @@
 			Implicits: allInstalledFiles,
 		})
 		deps = append(deps, name)
+		a.installTarget = name
 	}
 
 	if len(allCheckbuildFiles) > 0 {
@@ -284,6 +298,7 @@
 			Optional:  true,
 		})
 		deps = append(deps, name)
+		a.checkbuildTarget = name
 	}
 
 	if len(deps) > 0 {
@@ -293,6 +308,8 @@
 			Implicits: deps,
 			Optional:  true,
 		})
+
+		a.blueprintDir = ctx.ModuleDir()
 	}
 }
 
@@ -424,6 +441,7 @@
 	})
 
 	a.installFiles = append(a.installFiles, fullInstallPath)
+	a.checkbuildFiles = append(a.checkbuildFiles, srcPath)
 	return fullInstallPath
 }
 
@@ -454,7 +472,7 @@
 	return ok
 }
 
-func ExpandSources(ctx AndroidModuleContext, srcFiles []string) []string {
+func (ctx *androidModuleContext) ExpandSources(srcFiles []string) []string {
 	prefix := ModuleSrcDir(ctx)
 	for i, srcFile := range srcFiles {
 		if srcFile[0] == '-' {
@@ -464,6 +482,131 @@
 		}
 	}
 
-	srcFiles = expandGlobs(ctx, srcFiles)
-	return srcFiles
+	if !hasGlob(srcFiles) {
+		return srcFiles
+	}
+
+	var excludes []string
+	for _, s := range srcFiles {
+		if s[0] == '-' {
+			excludes = append(excludes, s[1:])
+		}
+	}
+
+	globbedSrcFiles := make([]string, 0, len(srcFiles))
+	for _, s := range srcFiles {
+		if s[0] == '-' {
+			continue
+		} else if glob.IsGlob(s) {
+			globbedSrcFiles = append(globbedSrcFiles, ctx.Glob(s, excludes)...)
+		} else {
+			globbedSrcFiles = append(globbedSrcFiles, s)
+		}
+	}
+
+	return globbedSrcFiles
+}
+
+func (ctx *androidModuleContext) Glob(globPattern string, excludes []string) []string {
+	ret, err := Glob(ctx, ModuleOutDir(ctx), globPattern, excludes)
+	if err != nil {
+		ctx.ModuleErrorf("glob: %s", err.Error())
+	}
+	return ret
+}
+
+func BuildTargetSingleton() blueprint.Singleton {
+	return &buildTargetSingleton{}
+}
+
+type buildTargetSingleton struct{}
+
+func (c *buildTargetSingleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
+	checkbuildDeps := []string{}
+
+	dirModules := make(map[string][]string)
+	hasBPFile := make(map[string]bool)
+	bpFiles := []string{}
+
+	ctx.VisitAllModules(func(module blueprint.Module) {
+		if a, ok := module.(AndroidModule); ok {
+			blueprintDir := a.base().blueprintDir
+			installTarget := a.base().installTarget
+			checkbuildTarget := a.base().checkbuildTarget
+			bpFile := ctx.BlueprintFile(module)
+
+			if checkbuildTarget != "" {
+				checkbuildDeps = append(checkbuildDeps, checkbuildTarget)
+				dirModules[blueprintDir] = append(dirModules[blueprintDir], checkbuildTarget)
+			}
+
+			if installTarget != "" {
+				dirModules[blueprintDir] = append(dirModules[blueprintDir], installTarget)
+			}
+
+			if !hasBPFile[bpFile] {
+				hasBPFile[bpFile] = true
+				bpFiles = append(bpFiles, bpFile)
+			}
+		}
+	})
+
+	// Create a top-level checkbuild target that depends on all modules
+	ctx.Build(pctx, blueprint.BuildParams{
+		Rule:      blueprint.Phony,
+		Outputs:   []string{"checkbuild"},
+		Implicits: checkbuildDeps,
+		// HACK: checkbuild should be an optional build, but force it enabled for now
+		//Optional:  true,
+	})
+
+	// Create a mm/<directory> target that depends on all modules in a directory
+	dirs := sortedKeys(dirModules)
+	for _, dir := range dirs {
+		ctx.Build(pctx, blueprint.BuildParams{
+			Rule:      blueprint.Phony,
+			Outputs:   []string{filepath.Join("mm", dir)},
+			Implicits: dirModules[dir],
+			Optional:  true,
+		})
+	}
+
+	// Create Android.bp->mk translation rules
+	androidMks := []string{}
+	srcDir := ctx.Config().(Config).SrcDir()
+	intermediatesDir := filepath.Join(ctx.Config().(Config).IntermediatesDir(), "androidmk")
+	sort.Strings(bpFiles)
+	for _, origBp := range bpFiles {
+		bpFile := filepath.Join(srcDir, origBp)
+		mkFile := filepath.Join(srcDir, filepath.Dir(origBp), "Android.mk")
+
+		files, err := Glob(ctx, intermediatesDir, mkFile, nil)
+		if err != nil {
+			ctx.Errorf("glob: %s", err.Error())
+			continue
+		}
+
+		// Existing Android.mk file, use that instead
+		if len(files) > 0 {
+			continue
+		}
+
+		transMk := filepath.Join("androidmk", "Android_"+strings.Replace(filepath.Dir(origBp), "/", "_", -1)+".mk")
+		ctx.Build(pctx, blueprint.BuildParams{
+			Rule:      androidbp,
+			Outputs:   []string{transMk},
+			Inputs:    []string{bpFile},
+			Implicits: []string{androidbpCmd},
+			Optional:  true,
+		})
+
+		androidMks = append(androidMks, transMk)
+	}
+
+	ctx.Build(pctx, blueprint.BuildParams{
+		Rule:      blueprint.Phony,
+		Outputs:   []string{"androidmk"},
+		Implicits: androidMks,
+		Optional:  true,
+	})
 }
diff --git a/common/paths.go b/common/paths.go
index 070662a..c4bdfc7 100644
--- a/common/paths.go
+++ b/common/paths.go
@@ -15,8 +15,8 @@
 package common
 
 import (
-	"path/filepath"
 	"os"
+	"path/filepath"
 )
 
 // ModuleOutDir returns the path to the module-specific output directory.
diff --git a/common/util.go b/common/util.go
index 599b3b1..0a0ed13 100644
--- a/common/util.go
+++ b/common/util.go
@@ -14,6 +14,8 @@
 
 package common
 
+import "sort"
+
 func JoinWithPrefix(strs []string, prefix string) string {
 	if len(strs) == 0 {
 		return ""
@@ -65,3 +67,12 @@
 	}
 	return string(ret)
 }
+
+func sortedKeys(m map[string][]string) []string {
+	s := make([]string, 0, len(m))
+	for k := range m {
+		s = append(s, k)
+	}
+	sort.Strings(s)
+	return s
+}
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 42ab047..ea30e3d 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -132,7 +132,7 @@
 	properties := &genSrcsProperties{}
 
 	tasks := func(ctx common.AndroidModuleContext) []generateTask {
-		srcFiles := common.ExpandSources(ctx, properties.Srcs)
+		srcFiles := ctx.ExpandSources(properties.Srcs)
 		tasks := make([]generateTask, 0, len(srcFiles))
 		for _, in := range srcFiles {
 			out := pathtools.ReplaceExtension(in, properties.Output_extension)
@@ -159,7 +159,7 @@
 	tasks := func(ctx common.AndroidModuleContext) []generateTask {
 		return []generateTask{
 			{
-				in:  common.ExpandSources(ctx, properties.Srcs),
+				in:  ctx.ExpandSources(properties.Srcs),
 				out: filepath.Join(common.ModuleGenDir(ctx), properties.Out),
 			},
 		}
diff --git a/java/app.go b/java/app.go
index 869cfea..5f153cf 100644
--- a/java/app.go
+++ b/java/app.go
@@ -244,14 +244,14 @@
 	var aaptDeps []string
 	var hasResources bool
 	for _, d := range resourceDirs {
-		newDeps := common.Glob(ctx, filepath.Join(d, "**/*"), aaptIgnoreFilenames)
+		newDeps := ctx.Glob(filepath.Join(d, "**/*"), aaptIgnoreFilenames)
 		aaptDeps = append(aaptDeps, newDeps...)
 		if len(newDeps) > 0 {
 			hasResources = true
 		}
 	}
 	for _, d := range assetDirs {
-		newDeps := common.Glob(ctx, filepath.Join(d, "**/*"), aaptIgnoreFilenames)
+		newDeps := ctx.Glob(filepath.Join(d, "**/*"), aaptIgnoreFilenames)
 		aaptDeps = append(aaptDeps, newDeps...)
 	}
 
diff --git a/java/java.go b/java/java.go
index d22f47a..9787d60 100644
--- a/java/java.go
+++ b/java/java.go
@@ -292,7 +292,7 @@
 		javacDeps = append(javacDeps, classpath...)
 	}
 
-	srcFiles := common.ExpandSources(ctx, j.properties.Srcs)
+	srcFiles := ctx.ExpandSources(j.properties.Srcs)
 
 	srcFiles = j.genSources(ctx, srcFiles, flags)
 
diff --git a/java/resources.go b/java/resources.go
index 7aa909e..f9d7a05 100644
--- a/java/resources.go
+++ b/java/resources.go
@@ -47,7 +47,7 @@
 			continue
 		}
 		resourceDir := filepath.Join(common.ModuleSrcDir(ctx), resourceDir)
-		dirs := common.Glob(ctx, resourceDir, nil)
+		dirs := ctx.Glob(resourceDir, nil)
 		for _, dir := range dirs {
 			fileListFile := filepath.Join(common.ModuleOutDir(ctx), "res", dir, "resources.list")
 			depFile := fileListFile + ".d"