Merge "Soong support for namespaces"
diff --git a/androidmk/cmd/androidmk/android.go b/androidmk/cmd/androidmk/android.go
index df01cf4..73ea4ac 100644
--- a/androidmk/cmd/androidmk/android.go
+++ b/androidmk/cmd/androidmk/android.go
@@ -24,7 +24,8 @@
 )
 
 const (
-	clear_vars = "__android_mk_clear_vars"
+	clear_vars      = "__android_mk_clear_vars"
+	include_ignored = "__android_mk_include_ignored"
 )
 
 type bpVariable struct {
@@ -693,6 +694,10 @@
 	return "**/*.java"
 }
 
+func includeIgnored(args []string) string {
+	return include_ignored
+}
+
 var moduleTypes = map[string]string{
 	"BUILD_SHARED_LIBRARY":        "cc_library_shared",
 	"BUILD_STATIC_LIBRARY":        "cc_library_static",
@@ -729,6 +734,10 @@
 	globalScope.SetFunc("all-java-files-under", allJavaFilesUnder)
 	globalScope.SetFunc("all-proto-files-under", allProtoFilesUnder)
 	globalScope.SetFunc("all-subdir-java-files", allSubdirJavaFiles)
+	globalScope.SetFunc("all-makefiles-under", includeIgnored)
+	globalScope.SetFunc("first-makefiles-under", includeIgnored)
+	globalScope.SetFunc("all-named-subdir-makefiles", includeIgnored)
+	globalScope.SetFunc("all-subdir-makefiles", includeIgnored)
 
 	for k, v := range moduleTypes {
 		globalScope.Set(k, v)
diff --git a/androidmk/cmd/androidmk/androidmk.go b/androidmk/cmd/androidmk/androidmk.go
index 660d9a0..385690c 100644
--- a/androidmk/cmd/androidmk/androidmk.go
+++ b/androidmk/cmd/androidmk/androidmk.go
@@ -177,6 +177,9 @@
 					makeModule(file, val)
 				case val == clear_vars:
 					resetModule(file)
+				case val == include_ignored:
+					// subdirs are already automatically included in Soong
+					continue
 				default:
 					file.errorf(x, "unsupported include")
 					continue
diff --git a/androidmk/cmd/androidmk/androidmk_test.go b/androidmk/cmd/androidmk/androidmk_test.go
index 0b86540..9986889 100644
--- a/androidmk/cmd/androidmk/androidmk_test.go
+++ b/androidmk/cmd/androidmk/androidmk_test.go
@@ -460,6 +460,13 @@
 // endif
 		`,
 	},
+	{
+		desc: "ignore all-makefiles-under",
+		in: `
+include $(call all-makefiles-under,$(LOCAL_PATH))
+`,
+		expected: ``,
+	},
 }
 
 func reformatBlueprint(input string) string {
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index 0619b5c..2ca7ebf 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -114,7 +114,11 @@
 	} else if os.Args[1] == "--dumpvars-mode" {
 		dumpVars(buildCtx, config, os.Args[2:])
 	} else {
-		build.Build(buildCtx, config, build.BuildAll)
+		toBuild := build.BuildAll
+		if config.Checkbuild() {
+			toBuild |= build.RunBuildTests
+		}
+		build.Build(buildCtx, config, toBuild)
 	}
 }
 
diff --git a/ui/build/Android.bp b/ui/build/Android.bp
index d1b4943..5809894 100644
--- a/ui/build/Android.bp
+++ b/ui/build/Android.bp
@@ -36,6 +36,7 @@
         "proc_sync.go",
         "signal.go",
         "soong.go",
+        "test_build.go",
         "util.go",
     ],
     testSrcs: [
diff --git a/ui/build/build.go b/ui/build/build.go
index 0df22b3..78eb6a3 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -68,6 +68,7 @@
 	BuildSoong         = 1 << iota
 	BuildKati          = 1 << iota
 	BuildNinja         = 1 << iota
+	RunBuildTests      = 1 << iota
 	BuildAll           = BuildProductConfig | BuildSoong | BuildKati | BuildNinja
 )
 
@@ -172,14 +173,18 @@
 		}
 	}
 
+	// Write combined ninja file
+	createCombinedBuildNinjaFile(ctx, config)
+
+	if what&RunBuildTests != 0 {
+		testForDanglingRules(ctx, config)
+	}
+
 	if what&BuildNinja != 0 {
 		if !config.SkipMake() {
 			installCleanIfNecessary(ctx, config)
 		}
 
-		// Write combined ninja file
-		createCombinedBuildNinjaFile(ctx, config)
-
 		// Run ninja
 		runNinja(ctx, config)
 	}
diff --git a/ui/build/config.go b/ui/build/config.go
index 6e5559d..df97d80 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -34,11 +34,12 @@
 	environ   *Environment
 
 	// From the arguments
-	parallel  int
-	keepGoing int
-	verbose   bool
-	dist      bool
-	skipMake  bool
+	parallel   int
+	keepGoing  int
+	verbose    bool
+	checkbuild bool
+	dist       bool
+	skipMake   bool
 
 	// From the product config
 	katiArgs     []string
@@ -206,6 +207,8 @@
 		} else {
 			if arg == "dist" {
 				c.dist = true
+			} else if arg == "checkbuild" {
+				c.checkbuild = true
 			}
 			c.arguments = append(c.arguments, arg)
 		}
@@ -313,6 +316,12 @@
 	panic("SetKatiSuffix has not been called")
 }
 
+// Checkbuild returns true if "checkbuild" was one of the build goals, which means that the
+// user is interested in additional checks at the expense of build time.
+func (c *configImpl) Checkbuild() bool {
+	return c.checkbuild
+}
+
 func (c *configImpl) Dist() bool {
 	return c.dist
 }
diff --git a/ui/build/test_build.go b/ui/build/test_build.go
new file mode 100644
index 0000000..940f0c8
--- /dev/null
+++ b/ui/build/test_build.go
@@ -0,0 +1,85 @@
+// Copyright 2017 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 build
+
+import (
+	"bufio"
+	"path/filepath"
+	"runtime"
+	"strings"
+)
+
+// Checks for files in the out directory that have a rule that depends on them but no rule to
+// create them. This catches a common set of build failures where a rule to generate a file is
+// deleted (either by deleting a module in an Android.mk file, or by modifying the build system
+// incorrectly).  These failures are often not caught by a local incremental build because the
+// previously built files are still present in the output directory.
+func testForDanglingRules(ctx Context, config Config) {
+	// Many modules are disabled on mac.  Checking for dangling rules would cause lots of build
+	// breakages, and presubmit wouldn't catch them, so just disable the check.
+	if runtime.GOOS != "linux" {
+		return
+	}
+
+	ctx.BeginTrace("test for dangling rules")
+	defer ctx.EndTrace()
+
+	// Get a list of leaf nodes in the dependency graph from ninja
+	executable := config.PrebuiltBuildTool("ninja")
+
+	args := []string{}
+	args = append(args, config.NinjaArgs()...)
+	args = append(args, "-f", config.CombinedNinjaFile())
+	args = append(args, "-t", "targets", "rule")
+
+	cmd := Command(ctx, config, "ninja", executable, args...)
+	stdout, err := cmd.StdoutPipe()
+	if err != nil {
+		ctx.Fatal(err)
+	}
+
+	cmd.StartOrFatal()
+
+	outDir := config.OutDir()
+	bootstrapDir := filepath.Join(outDir, "soong", ".bootstrap")
+	miniBootstrapDir := filepath.Join(outDir, "soong", ".minibootstrap")
+
+	var danglingRules []string
+
+	scanner := bufio.NewScanner(stdout)
+	for scanner.Scan() {
+		line := scanner.Text()
+		if !strings.HasPrefix(line, outDir) {
+			// Leaf node is not in the out directory.
+			continue
+		}
+		if strings.HasPrefix(line, bootstrapDir) || strings.HasPrefix(line, miniBootstrapDir) {
+			// Leaf node is in one of Soong's bootstrap directories, which do not have
+			// full build rules in the primary build.ninja file.
+			continue
+		}
+		danglingRules = append(danglingRules, line)
+	}
+
+	cmd.WaitOrFatal()
+
+	if len(danglingRules) > 0 {
+		ctx.Println("Dependencies in out found with no rule to create them:")
+		for _, dep := range danglingRules {
+			ctx.Println(dep)
+		}
+		ctx.Fatal("")
+	}
+}