Snap for 9550355 from c5baf11a2d863555db3a0054cc8f0782f86237bd to sdk-release

Change-Id: If6784b2328c8299e71d1550ca013b1073f3ff0d7
diff --git a/README.md b/README.md
index 961bc64..22525bf 100644
--- a/README.md
+++ b/README.md
@@ -1,25 +1,6 @@
 Blueprint Build System
 ======================
 
-Blueprint is being archived on 2021 May 3.
+Blueprint is part of Soong.
 
-On 2021 May 3, we will be archiving the Blueprint project. This means it will
-not be possible to file new issues or open new pull requests for this GitHub
-project. As the project is being archived, patches -- including security
-patches -- will not be applied after May 3. The source tree will remain
-available, but changes to Blueprint in AOSP will not be merged here and
-Blueprint's source tree in AOSP will eventually stop being usable outside of
-Android.
-
-Whereas there are no meta-build systems one can use as a drop-in replacement for
-Blueprint, there are a number of build systems that can be used:
-
-* [Bazel](https://bazel.build), Google's multi-language build tool to build and
-  test software of any size, quickly and reliably
-* [Soong](https://source.android.com/setup/build), for building the Android
-  operating system itself
-* [CMake](https://cmake.org), an open-source, cross-platform family of tools
-  designed to build, test and package software
-* [Buck](https://buck.build), a fast build system that encourages the creation
-  of small, reusable modules over a variety of platforms and languages
-* The venerable [GNU Make](https://www.gnu.org/software/make/)
+For more information, see `build/soong/README.md` .
diff --git a/bootstrap/bootstrap.go b/bootstrap/bootstrap.go
index ceeee19..e0a6e06 100644
--- a/bootstrap/bootstrap.go
+++ b/bootstrap/bootstrap.go
@@ -149,7 +149,7 @@
 		},
 		"depfile")
 
-	_ = pctx.VariableFunc("ToolDir", func(config interface{}) (string, error) {
+	_ = pctx.VariableFunc("ToolDir", func(ctx blueprint.VariableFuncContext, config interface{}) (string, error) {
 		return config.(BootstrapConfig).HostToolDir(), nil
 	})
 )
@@ -693,9 +693,10 @@
 
 		// Build the main build.ninja
 		ctx.Build(pctx, blueprint.BuildParams{
-			Rule:    generateBuildNinja,
-			Outputs: i.Outputs,
-			Inputs:  i.Inputs,
+			Rule:      generateBuildNinja,
+			Outputs:   i.Outputs,
+			Inputs:    i.Inputs,
+			OrderOnly: i.OrderOnlyInputs,
 			Args: map[string]string{
 				"builder": primaryBuilderFile,
 				"env":     envAssignments,
diff --git a/bootstrap/command.go b/bootstrap/command.go
index 3342bbe..0ddd110 100644
--- a/bootstrap/command.go
+++ b/bootstrap/command.go
@@ -18,7 +18,6 @@
 	"bufio"
 	"fmt"
 	"io"
-	"io/ioutil"
 	"os"
 	"path/filepath"
 	"runtime"
@@ -41,9 +40,10 @@
 	TraceFile  string
 }
 
-// Returns the list of dependencies the emitted Ninja files has. These can be
-// written to the .d file for the output so that it is correctly rebuilt when
-// needed in case Blueprint is itself invoked from Ninja
+// RunBlueprint emits `args.OutFile` (a Ninja file) and returns the list of
+// its dependencies. These can be written to a `${args.OutFile}.d` file
+// so that it is correctly rebuilt when needed in case Blueprint is itself
+// invoked from Ninja
 func RunBlueprint(args Args, stopBefore StopBefore, ctx *blueprint.Context, config interface{}) []string {
 	runtime.GOMAXPROCS(runtime.NumCPU())
 
@@ -71,60 +71,58 @@
 		defer trace.Stop()
 	}
 
-	srcDir := "."
-
-	ninjaDeps := make([]string, 0)
-
-	if args.ModuleListFile != "" {
-		ctx.SetModuleListFile(args.ModuleListFile)
-		ninjaDeps = append(ninjaDeps, args.ModuleListFile)
-	} else {
+	if args.ModuleListFile == "" {
 		fatalf("-l <moduleListFile> is required and must be nonempty")
 	}
+	ctx.SetModuleListFile(args.ModuleListFile)
+
+	var ninjaDeps []string
+	ninjaDeps = append(ninjaDeps, args.ModuleListFile)
+
 	ctx.BeginEvent("list_modules")
-	filesToParse, err := ctx.ListModulePaths(srcDir)
-	ctx.EndEvent("list_modules")
-	if err != nil {
+	var filesToParse []string
+	if f, err := ctx.ListModulePaths("."); err != nil {
 		fatalf("could not enumerate files: %v\n", err.Error())
+	} else {
+		filesToParse = f
 	}
+	ctx.EndEvent("list_modules")
 
 	ctx.RegisterBottomUpMutator("bootstrap_plugin_deps", pluginDeps)
 	ctx.RegisterModuleType("bootstrap_go_package", newGoPackageModuleFactory())
 	ctx.RegisterModuleType("blueprint_go_binary", newGoBinaryModuleFactory())
 	ctx.RegisterSingletonType("bootstrap", newSingletonFactory())
+	blueprint.RegisterPackageIncludesModuleType(ctx)
 
 	ctx.BeginEvent("parse_bp")
-	blueprintFiles, errs := ctx.ParseFileList(".", filesToParse, config)
-	if len(errs) > 0 {
+	if blueprintFiles, errs := ctx.ParseFileList(".", filesToParse, config); len(errs) > 0 {
 		fatalErrors(errs)
+	} else {
+		ctx.EndEvent("parse_bp")
+		ninjaDeps = append(ninjaDeps, blueprintFiles...)
 	}
-	ctx.EndEvent("parse_bp")
 
-	// Add extra ninja file dependencies
-	ninjaDeps = append(ninjaDeps, blueprintFiles...)
-
-	extraDeps, errs := ctx.ResolveDependencies(config)
-	if len(errs) > 0 {
+	if resolvedDeps, errs := ctx.ResolveDependencies(config); len(errs) > 0 {
 		fatalErrors(errs)
+	} else {
+		ninjaDeps = append(ninjaDeps, resolvedDeps...)
 	}
-	ninjaDeps = append(ninjaDeps, extraDeps...)
 
 	if stopBefore == StopBeforePrepareBuildActions {
 		return ninjaDeps
 	}
 
 	if ctx.BeforePrepareBuildActionsHook != nil {
-		err := ctx.BeforePrepareBuildActionsHook()
-		if err != nil {
+		if err := ctx.BeforePrepareBuildActionsHook(); err != nil {
 			fatalErrors([]error{err})
 		}
 	}
 
-	extraDeps, errs = ctx.PrepareBuildActions(config)
-	if len(errs) > 0 {
+	if buildActionsDeps, errs := ctx.PrepareBuildActions(config); len(errs) > 0 {
 		fatalErrors(errs)
+	} else {
+		ninjaDeps = append(ninjaDeps, buildActionsDeps...)
 	}
-	ninjaDeps = append(ninjaDeps, extraDeps...)
 
 	if stopBefore == StopBeforeWriteNinja {
 		return ninjaDeps
@@ -138,37 +136,34 @@
 	ctx.BeginEvent("write_files")
 	defer ctx.EndEvent("write_files")
 	if args.EmptyNinjaFile {
-		if err := ioutil.WriteFile(joinPath(ctx.SrcDir(), args.OutFile), []byte(nil), outFilePermissions); err != nil {
+		if err := os.WriteFile(joinPath(ctx.SrcDir(), args.OutFile), []byte(nil), outFilePermissions); err != nil {
 			fatalf("error writing empty Ninja file: %s", err)
 		}
 	}
 
 	if !args.EmptyNinjaFile {
-		f, err = os.OpenFile(joinPath(ctx.SrcDir(), args.OutFile), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, outFilePermissions)
+		f, err := os.OpenFile(joinPath(ctx.SrcDir(), args.OutFile), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, outFilePermissions)
 		if err != nil {
 			fatalf("error opening Ninja file: %s", err)
 		}
 		buf = bufio.NewWriterSize(f, 16*1024*1024)
 		out = buf
 	} else {
-		out = ioutil.Discard.(io.StringWriter)
+		out = io.Discard.(io.StringWriter)
 	}
 
-	err = ctx.WriteBuildFile(out)
-	if err != nil {
+	if err := ctx.WriteBuildFile(out); err != nil {
 		fatalf("error writing Ninja file contents: %s", err)
 	}
 
 	if buf != nil {
-		err = buf.Flush()
-		if err != nil {
+		if err := buf.Flush(); err != nil {
 			fatalf("error flushing Ninja file contents: %s", err)
 		}
 	}
 
 	if f != nil {
-		err = f.Close()
-		if err != nil {
+		if err := f.Close(); err != nil {
 			fatalf("error closing Ninja file: %s", err)
 		}
 	}
diff --git a/bootstrap/config.go b/bootstrap/config.go
index 9972b5d..1d256ba 100644
--- a/bootstrap/config.go
+++ b/bootstrap/config.go
@@ -25,7 +25,7 @@
 )
 
 func bootstrapVariable(name string, value func(BootstrapConfig) string) blueprint.Variable {
-	return pctx.VariableFunc(name, func(config interface{}) (string, error) {
+	return pctx.VariableFunc(name, func(ctx blueprint.VariableFuncContext, config interface{}) (string, error) {
 		c, ok := config.(BootstrapConfig)
 		if !ok {
 			panic(fmt.Sprintf("Bootstrap rules were passed a configuration that does not include theirs, config=%q",
@@ -105,10 +105,11 @@
 )
 
 type PrimaryBuilderInvocation struct {
-	Inputs      []string
-	Outputs     []string
-	Args        []string
-	Console     bool
-	Description string
-	Env         map[string]string
+	Inputs          []string
+	OrderOnlyInputs []string
+	Outputs         []string
+	Args            []string
+	Console         bool
+	Description     string
+	Env             map[string]string
 }
diff --git a/bootstrap/glob.go b/bootstrap/glob.go
index 70495dc..a766676 100644
--- a/bootstrap/glob.go
+++ b/bootstrap/glob.go
@@ -44,7 +44,7 @@
 // in a build failure with a "missing and no known rule to make it" error.
 
 var (
-	_ = pctx.VariableFunc("globCmd", func(config interface{}) (string, error) {
+	_ = pctx.VariableFunc("globCmd", func(ctx blueprint.VariableFuncContext, config interface{}) (string, error) {
 		return filepath.Join(config.(BootstrapConfig).SoongOutDir(), "bpglob"), nil
 	})
 
@@ -237,6 +237,8 @@
 		return nil, errs
 	}
 
+	// PrepareBuildActions() will write $OUTDIR/soong/globs/$m/$i files
+	// where $m=bp2build|build and $i=0..numGlobBuckets
 	extraDeps, errs = ctx.PrepareBuildActions(config)
 	if len(extraDeps) > 0 {
 		return nil, []error{fmt.Errorf("shouldn't have extra deps")}
diff --git a/bpmodify/bpmodify.go b/bpmodify/bpmodify.go
index 42de70e..1df808e 100644
--- a/bpmodify/bpmodify.go
+++ b/bpmodify/bpmodify.go
@@ -8,7 +8,6 @@
 	"bytes"
 	"flag"
 	"fmt"
-	"github.com/google/blueprint/parser"
 	"io"
 	"io/ioutil"
 	"os"
@@ -17,6 +16,8 @@
 	"strings"
 	"syscall"
 	"unicode"
+
+	"github.com/google/blueprint/parser"
 )
 
 var (
@@ -34,6 +35,7 @@
 	newLocation        string
 	setString          *string
 	addLiteral         *string
+	setBool            *string
 	replaceProperty    = new(replacements)
 )
 
@@ -43,10 +45,11 @@
 	flag.StringVar(&newLocation, "new-location", "", " use with moveProperty to move contents of -property into a property with name -new-location ")
 	flag.Var(targetedProperties, "property", "comma-separated list of fully qualified `name`s of properties to modify (default \"deps\")")
 	flag.Var(addIdents, "a", "comma or whitespace separated list of identifiers to add")
-	flag.Var(stringPtrFlag{&addLiteral}, "add-literal", "a literal to add")
+	flag.Var(stringPtrFlag{&addLiteral}, "add-literal", "a literal to add to a list")
 	flag.Var(removeIdents, "r", "comma or whitespace separated list of identifiers to remove")
 	flag.Var(stringPtrFlag{&setString}, "str", "set a string property")
-	flag.Var(replaceProperty, "replace-property", "property names to be replaced, in the form of oldName1:newName1,oldName2:newName2")
+	flag.Var(replaceProperty, "replace-property", "property names to be replaced, in the form of oldName1=newName1,oldName2=newName2")
+	flag.Var(stringPtrFlag{&setBool}, "set-bool", "a boolean value to set a property with (not a list)")
 	flag.Usage = usage
 }
 
@@ -154,6 +157,9 @@
 		} else if setString != nil {
 			// We setting a non-existent string property, so we need to create it first.
 			prop, modified, err = createRecursiveProperty(module, property.name(), property.prefixes(), &parser.String{})
+		} else if setBool != nil {
+			// We are setting a non-existent property, so we need to create it first.
+			prop, modified, err = createRecursiveProperty(module, property.name(), property.prefixes(), &parser.Bool{})
 		} else {
 			// We cannot find an existing prop, and we aren't adding anything to the prop,
 			// which means we must be removing something from a non-existing prop,
@@ -278,6 +284,21 @@
 		}
 		list.Values = append(list.Values, value)
 		modified = true
+	} else if setBool != nil {
+		res, ok := value.(*parser.Bool)
+		if !ok {
+			return false, []error{fmt.Errorf("expected parameter %s in module %s to be bool, found %s",
+				paramName, moduleName, value.Type().String())}
+		}
+		if *setBool == "true" {
+			res.Value = true
+		} else if *setBool == "false" {
+			res.Value = false
+		} else {
+			return false, []error{fmt.Errorf("expected parameter %s to be true or false, found %s",
+				paramName, *setBool)}
+		}
+		modified = true
 	} else if setString != nil {
 		str, ok := value.(*parser.String)
 		if !ok {
@@ -301,7 +322,8 @@
 	return false
 }
 func visitFile(path string, f os.FileInfo, err error) error {
-	if err == nil && f.Name() == "Blueprints" {
+	//TODO(dacek): figure out a better way to target intended .bp files without parsing errors
+	if err == nil && (f.Name() == "Blueprints" || strings.HasSuffix(f.Name(), ".bp")) {
 		err = processFile(path, nil, os.Stdout)
 	}
 	if err != nil {
@@ -344,7 +366,7 @@
 		return
 	}
 
-	if len(addIdents.idents) == 0 && len(removeIdents.idents) == 0 && setString == nil && addLiteral == nil && !*removeProperty && !*moveProperty && (*replaceProperty).size() == 0 {
+	if len(addIdents.idents) == 0 && len(removeIdents.idents) == 0 && setString == nil && addLiteral == nil && !*removeProperty && !*moveProperty && (*replaceProperty).size() == 0 && setBool == nil {
 		report(fmt.Errorf("-a, -add-literal, -r, -remove-property, -move-property, replace-property or -str parameter is required"))
 		return
 	}
@@ -374,6 +396,7 @@
 		}
 	}
 }
+
 func diff(b1, b2 []byte) (data []byte, err error) {
 	f1, err := ioutil.TempFile("", "bpfmt")
 	if err != nil {
@@ -438,7 +461,7 @@
 	m.oldNameToNewName = make(map[string]string)
 	for i := 0; i < length; i++ {
 
-		pair := strings.SplitN(pairs[i], ":", 2)
+		pair := strings.SplitN(pairs[i], "=", 2)
 		if len(pair) != 2 {
 			return fmt.Errorf("Invalid replacement pair %s", pairs[i])
 		}
diff --git a/bpmodify/bpmodify_test.go b/bpmodify/bpmodify_test.go
index 4f37251..7bd8b57 100644
--- a/bpmodify/bpmodify_test.go
+++ b/bpmodify/bpmodify_test.go
@@ -14,10 +14,11 @@
 package main
 
 import (
-	"github.com/google/blueprint/parser"
-	"github.com/google/blueprint/proptools"
 	"strings"
 	"testing"
+
+	"github.com/google/blueprint/parser"
+	"github.com/google/blueprint/proptools"
 )
 
 var testCases = []struct {
@@ -29,6 +30,7 @@
 	removeSet       string
 	addLiteral      *string
 	setString       *string
+	setBool         *string
 	removeProperty  bool
 	replaceProperty string
 	moveProperty    bool
@@ -307,6 +309,39 @@
 		setString: proptools.StringPtr("bar"),
 	},
 	{
+		name: "set bool",
+		input: `
+			cc_foo {
+				name: "foo",
+			}
+		`,
+		output: `
+			cc_foo {
+				name: "foo",
+				foo: true,
+			}
+		`,
+		property: "foo",
+		setBool:  proptools.StringPtr("true"),
+	},
+	{
+		name: "set existing bool",
+		input: `
+			cc_foo {
+				name: "foo",
+				foo: true,
+			}
+		`,
+		output: `
+			cc_foo {
+				name: "foo",
+				foo: false,
+			}
+		`,
+		property: "foo",
+		setBool:  proptools.StringPtr("false"),
+	},
+	{
 		name: "remove existing property",
 		input: `
 			cc_foo {
@@ -373,7 +408,7 @@
 				],
 			}
 		`,
-		replaceProperty: "baz:baz_lib,foobar:foobar_lib",
+		replaceProperty: "baz=baz_lib,foobar=foobar_lib",
 	}, {
 		name:     "replace property multiple modules",
 		property: "deps,required",
@@ -396,7 +431,7 @@
 				required: ["foobar_lib"],
 			}
 		`,
-		replaceProperty: "baz:baz_lib,foobar:foobar_lib",
+		replaceProperty: "baz=baz_lib,foobar=foobar_lib",
 	}, {
 		name:     "replace property string value",
 		property: "name",
@@ -416,7 +451,7 @@
 				required: ["foobar"],
 			}
 		`,
-		replaceProperty: "foo:foo_lib",
+		replaceProperty: "foo=foo_lib",
 	}, {
 		name:     "replace property string and list values",
 		property: "name,deps",
@@ -436,7 +471,7 @@
 				required: ["foobar"],
 			}
 		`,
-		replaceProperty: "foo:foo_lib,baz:baz_lib",
+		replaceProperty: "foo=foo_lib,baz=baz_lib",
 	}, {
 		name: "move contents of property into non-existing property",
 		input: `
@@ -476,6 +511,26 @@
 		property:     "bar",
 		moveProperty: true,
 		newLocation:  "baz",
+	}, {
+		name: "replace nested",
+		input: `
+		cc_foo {
+			name: "foo",
+			foo: {
+				bar: "baz",
+			},
+		}
+	`,
+		output: `
+		cc_foo {
+			name: "foo",
+			foo: {
+				bar: "baz2",
+			},
+		}
+	`,
+		property:        "foo.bar",
+		replaceProperty: "baz=baz2",
 	},
 }
 
@@ -496,6 +551,7 @@
 			moveProperty = &testCase.moveProperty
 			newLocation = testCase.newLocation
 			setString = testCase.setString
+			setBool = testCase.setBool
 			addLiteral = testCase.addLiteral
 			replaceProperty.Set(testCase.replaceProperty)
 
@@ -536,7 +592,7 @@
 }
 
 func TestReplacementsCycleError(t *testing.T) {
-	cycleString := "old1:new1,new1:old1"
+	cycleString := "old1=new1,new1=old1"
 	err := replaceProperty.Set(cycleString)
 
 	if err.Error() != "Duplicated replacement name new1" {
@@ -550,7 +606,7 @@
 }
 
 func TestReplacementsDuplicatedError(t *testing.T) {
-	cycleString := "a:b,a:c"
+	cycleString := "a=b,a=c"
 	err := replaceProperty.Set(cycleString)
 
 	if err.Error() != "Duplicated replacement name a" {
@@ -564,7 +620,7 @@
 }
 
 func TestReplacementsMultipleReplacedToSame(t *testing.T) {
-	cycleString := "a:c,d:c"
+	cycleString := "a=c,d=c"
 	err := replaceProperty.Set(cycleString)
 
 	if err.Error() != "Duplicated replacement name c" {
diff --git a/bpmodify/rename_module_and_deps.py b/bpmodify/rename_module_and_deps.py
new file mode 100755
index 0000000..b1e6db9
--- /dev/null
+++ b/bpmodify/rename_module_and_deps.py
@@ -0,0 +1,58 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# 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.
+"""A tool to run bpmodify for a given module in order to rename it and all references to it"""
+import os
+import subprocess
+import sys
+
+
+def main():
+  if len(sys.argv) < 2:
+    print("Usage: rename_module_and_deps <pathToModule1,pathToModule2,...>")
+    return
+
+  modulePaths = sys.argv[1].split(",")
+  replacementsList = []
+  colonReplacementsList = []
+
+  for modulePath in modulePaths:
+    moduleName = modulePath.split("/")[-1]
+    replacementsList.append(moduleName + "=" + moduleName + "_lib")
+    # add in the colon replacement
+    colonReplaceString = ":" + moduleName + "=" + ":" + moduleName + "_lib"
+    replacementsList.append(colonReplaceString)
+    colonReplacementsList.append(colonReplaceString)
+
+  replacementsString = ",".join(replacementsList)
+  colonReplacementsString = ",".join(colonReplacementsList)
+  buildTop = os.getenv("ANDROID_BUILD_TOP")
+
+  if not buildTop:
+    raise Exception(
+        "$ANDROID_BUILD_TOP not found in environment. Have you run lunch?")
+
+  rename_deps_cmd = f"{buildTop}/prebuilts/go/linux-x86/bin/go run bpmodify.go -w -m=* -property=static_libs,deps,required,test_suites,name,host,libs,data_bins,data_native_bins,tools,shared_libs,file_contexts,target.not_windows.required,target.android.required,target.platform.required -replace-property={replacementsString} {buildTop}"
+  print(rename_deps_cmd)
+  subprocess.check_output(rename_deps_cmd, shell=True)
+
+  # Some properties (for example,  data ), refer to files. Such properties may also refer to a filegroup module by prefixing it with a colon. Replacing these module references must thus be done separately.
+  colon_rename_deps_cmd = f"{buildTop}/prebuilts/go/linux-x86/bin/go run bpmodify.go -w -m=* -property=data -replace-property={colonReplacementsString} {buildTop}"
+  print(colon_rename_deps_cmd)
+  subprocess.check_output(colon_rename_deps_cmd, shell=True)
+
+
+if __name__ == "__main__":
+  main()
diff --git a/context.go b/context.go
index 5448f1e..a7f1013 100644
--- a/context.go
+++ b/context.go
@@ -137,6 +137,31 @@
 
 	// Can be set by tests to avoid invalidating Module values after mutators.
 	skipCloneModulesAfterMutators bool
+
+	// String values that can be used to gate build graph traversal
+	includeTags *IncludeTags
+}
+
+// A container for String keys. The keys can be used to gate build graph traversal
+type IncludeTags map[string]bool
+
+func (tags *IncludeTags) Add(names ...string) {
+	for _, name := range names {
+		(*tags)[name] = true
+	}
+}
+
+func (tags *IncludeTags) Contains(tag string) bool {
+	_, exists := (*tags)[tag]
+	return exists
+}
+
+func (c *Context) AddIncludeTags(names ...string) {
+	c.includeTags.Add(names...)
+}
+
+func (c *Context) ContainsIncludeTag(name string) bool {
+	return c.includeTags.Contains(name)
 }
 
 // An Error describes a problem that was encountered that is related to a
@@ -399,6 +424,7 @@
 		globs:              make(map[globKey]pathtools.GlobResult),
 		fs:                 pathtools.OsFs,
 		finishedMutators:   make(map[*mutatorInfo]bool),
+		includeTags: &IncludeTags{},
 		outDir:             nil,
 		requiredNinjaMajor: 1,
 		requiredNinjaMinor: 7,
@@ -940,6 +966,33 @@
 	return c.ParseFileList(baseDir, pathsToParse, config)
 }
 
+// Returns a boolean for whether this file should be analyzed
+// Evaluates to true if the file either
+// 1. does not contain a blueprint_package_includes
+// 2. contains a blueprint_package_includes and all requested tags are set
+// This should be processed before adding any modules to the build graph
+func shouldVisitFile(c *Context, file *parser.File) (bool, []error) {
+	for _, def := range file.Defs {
+		switch def := def.(type) {
+		case *parser.Module:
+			if def.Type != "blueprint_package_includes" {
+				continue
+			}
+			module, errs := processModuleDef(def, file.Name, c.moduleFactories, nil, c.ignoreUnknownModuleTypes)
+			if len(errs) > 0 {
+				// This file contains errors in blueprint_package_includes
+				// Visit anyways so that we can report errors on other modules in the file
+				return true, errs
+			}
+			logicModule, _ := c.cloneLogicModule(module)
+			pi := logicModule.(*PackageIncludes)
+			return pi.MatchesIncludeTags(c), []error{}
+		}
+	}
+	return true, []error{}
+}
+
+
 func (c *Context) ParseFileList(rootDir string, filePaths []string,
 	config interface{}) (deps []string, errs []error) {
 
@@ -992,6 +1045,15 @@
 			}
 			return nil
 		}
+		shouldVisit, errs := shouldVisitFile(c, file)
+		if len(errs) > 0 {
+			atomic.AddUint32(&numErrs, uint32(len(errs)))
+			errsCh <- errs
+		}
+		if !shouldVisit {
+			// TODO: Write a file that lists the skipped bp files
+			return
+		}
 
 		for _, def := range file.Defs {
 			switch def := def.(type) {
@@ -1798,7 +1860,7 @@
 	pprof.Do(ctx, pprof.Labels("blueprint", "ResolveDependencies"), func(ctx context.Context) {
 		c.initProviders()
 
-		c.liveGlobals = newLiveTracker(config)
+		c.liveGlobals = newLiveTracker(c, config)
 
 		deps, errs = c.generateSingletonBuildActions(config, c.preSingletonInfo, c.liveGlobals)
 		if len(errs) > 0 {
@@ -4413,7 +4475,7 @@
 		// A localVariable doesn't need the package names or config to
 		// determine its name or value.
 		name := v.fullName(nil)
-		value, err := v.value(nil)
+		value, err := v.value(nil, nil)
 		if err != nil {
 			panic(err)
 		}
@@ -4540,3 +4602,49 @@
 Singleton: {{.name}}
 Factory:   {{.goFactory}}
 `
+
+// Blueprint module type that can be used to gate blueprint files beneath this directory
+type PackageIncludes struct {
+	properties struct {
+		// Package will be included if all include tags in this list are set
+		Match_all []string
+	}
+	name *string `blueprint:"mutated"`
+}
+
+func (pi *PackageIncludes) Name() string {
+	return proptools.String(pi.name)
+}
+
+// This module type does not have any build actions
+func (pi *PackageIncludes) GenerateBuildActions(ctx ModuleContext) {
+}
+
+func newPackageIncludesFactory() (Module, []interface{}) {
+	module := &PackageIncludes{}
+	AddLoadHook(module, func(ctx LoadHookContext) {
+		module.name = proptools.StringPtr(ctx.ModuleDir() + "_includes") // Generate a synthetic name
+	})
+	return module, []interface{}{&module.properties}
+}
+
+func RegisterPackageIncludesModuleType(ctx *Context) {
+	ctx.RegisterModuleType("blueprint_package_includes", newPackageIncludesFactory)
+}
+
+func (pi *PackageIncludes) MatchAll() []string {
+	return pi.properties.Match_all
+}
+
+// Returns true if all requested include tags are set in the Context object
+func (pi *PackageIncludes) MatchesIncludeTags(ctx *Context) bool {
+	if len(pi.MatchAll()) == 0 {
+		ctx.ModuleErrorf(pi, "Match_all must be a non-empty list")
+	}
+	for _, includeTag := range pi.MatchAll() {
+		if !ctx.ContainsIncludeTag(includeTag) {
+			return false
+		}
+	}
+	return true
+}
diff --git a/context_test.go b/context_test.go
index 6b9b599..777cfda 100644
--- a/context_test.go
+++ b/context_test.go
@@ -18,6 +18,7 @@
 	"bytes"
 	"errors"
 	"fmt"
+	"path/filepath"
 	"reflect"
 	"strings"
 	"sync"
@@ -1084,3 +1085,73 @@
 		}
 	})
 }
+
+func TestPackageIncludes(t *testing.T) {
+	dir1_foo_bp := `
+	blueprint_package_includes {
+		match_all: ["use_dir1"],
+	}
+	foo_module {
+		name: "foo",
+	}
+	`
+	dir2_foo_bp := `
+	blueprint_package_includes {
+		match_all: ["use_dir2"],
+	}
+	foo_module {
+		name: "foo",
+	}
+	`
+	mockFs := map[string][]byte{
+		"dir1/Android.bp": []byte(dir1_foo_bp),
+		"dir2/Android.bp": []byte(dir2_foo_bp),
+	}
+	testCases := []struct{
+		desc string
+		includeTags []string
+		expectedDir string
+		expectedErr string
+	}{
+		{
+			desc: "use_dir1 is set, use dir1 foo",
+			includeTags: []string{"use_dir1"},
+			expectedDir: "dir1",
+		},
+		{
+			desc: "use_dir2 is set, use dir2 foo",
+			includeTags: []string{"use_dir2"},
+			expectedDir: "dir2",
+		},
+		{
+			desc: "duplicate module error if both use_dir1 and use_dir2 are set",
+			includeTags: []string{"use_dir1", "use_dir2"},
+			expectedDir: "",
+			expectedErr: `module "foo" already defined`,
+		},
+	}
+	for _, tc := range testCases {
+		ctx := NewContext()
+		// Register mock FS
+		ctx.MockFileSystem(mockFs)
+		// Register module types
+		ctx.RegisterModuleType("foo_module", newFooModule)
+		RegisterPackageIncludesModuleType(ctx)
+		// Add include tags for test case
+		ctx.AddIncludeTags(tc.includeTags...)
+		// Run test
+		_, actualErrs := ctx.ParseFileList(".", []string{"dir1/Android.bp", "dir2/Android.bp"}, nil)
+		// Evaluate
+		if !strings.Contains(fmt.Sprintf("%s", actualErrs), fmt.Sprintf("%s", tc.expectedErr)) {
+			t.Errorf("Expected errors: %s, got errors: %s\n", tc.expectedErr, actualErrs)
+		}
+		if tc.expectedErr != "" {
+			continue // expectedDir check not necessary
+		}
+		actualBpFile := ctx.moduleGroupFromName("foo", nil).modules.firstModule().relBlueprintsFile
+		if tc.expectedDir != filepath.Dir(actualBpFile) {
+			t.Errorf("Expected foo from %s, got %s\n", tc.expectedDir, filepath.Dir(actualBpFile))
+		}
+	}
+
+}
diff --git a/live_tracker.go b/live_tracker.go
index 1d48e58..ef9c8a9 100644
--- a/live_tracker.go
+++ b/live_tracker.go
@@ -23,14 +23,16 @@
 type liveTracker struct {
 	sync.Mutex
 	config interface{} // Used to evaluate variable, rule, and pool values.
+	ctx    *Context    // Used to evaluate globs
 
 	variables map[Variable]ninjaString
 	pools     map[Pool]*poolDef
 	rules     map[Rule]*ruleDef
 }
 
-func newLiveTracker(config interface{}) *liveTracker {
+func newLiveTracker(ctx *Context, config interface{}) *liveTracker {
 	return &liveTracker{
+		ctx:       ctx,
 		config:    config,
 		variables: make(map[Variable]ninjaString),
 		pools:     make(map[Pool]*poolDef),
@@ -153,7 +155,9 @@
 func (l *liveTracker) addVariable(v Variable) error {
 	_, ok := l.variables[v]
 	if !ok {
-		value, err := v.value(l.config)
+		ctx := &variableFuncContext{l.ctx}
+
+		value, err := v.value(ctx, l.config)
 		if err == errVariableIsArg {
 			// This variable is a placeholder for an argument that can be passed
 			// to a rule.  It has no value and thus doesn't reference any other
diff --git a/metrics/event_handler.go b/metrics/event_handler.go
index f6bcaf1..3fd0f37 100644
--- a/metrics/event_handler.go
+++ b/metrics/event_handler.go
@@ -31,10 +31,8 @@
 	scopeStartTimes []time.Time
 }
 
-// _now wraps the time.Now() function. _now is declared for unit testing purpose.
-var _now = func() time.Time {
-	return time.Now()
-}
+// _now simply delegates to time.Now() function. _now is declared for unit testing purpose.
+var _now = time.Now
 
 // Event holds the performance metrics data of a single build event.
 type Event struct {
@@ -100,12 +98,12 @@
 			h.scopeIds))
 	}
 	// Validate no two events have the same full id.
-	ids := map[string]bool{}
+	ids := map[string]struct{}{}
 	for _, event := range h.completedEvents {
 		if _, containsId := ids[event.Id]; containsId {
 			panic(fmt.Errorf("Duplicate event registered: %s", event.Id))
 		}
-		ids[event.Id] = true
+		ids[event.Id] = struct{}{}
 	}
 	return h.completedEvents
 }
diff --git a/ninja_strings.go b/ninja_strings.go
index 51a167d..0b783c3 100644
--- a/ninja_strings.go
+++ b/ninja_strings.go
@@ -118,7 +118,7 @@
 		r := rune(str[i])
 		state, err = state(parseState, i, r)
 		if err != nil {
-			return nil, err
+			return nil, fmt.Errorf("error parsing ninja string %q: %s", str, err)
 		}
 	}
 
diff --git a/ninja_strings_test.go b/ninja_strings_test.go
index c1e05f7..f400074 100644
--- a/ninja_strings_test.go
+++ b/ninja_strings_test.go
@@ -87,7 +87,7 @@
 		strs:  []string{"$ ", " "},
 	}, {
 		input: "foo $ bar",
-		err:   "invalid character after '$' at byte offset 5",
+		err:   `error parsing ninja string "foo $ bar": invalid character after '$' at byte offset 5`,
 	},
 	{
 		input: "foo $",
@@ -95,11 +95,11 @@
 	},
 	{
 		input: "foo ${} bar",
-		err:   "empty variable name at byte offset 6",
+		err:   `error parsing ninja string "foo ${} bar": empty variable name at byte offset 6`,
 	},
 	{
 		input: "foo ${abc!} bar",
-		err:   "invalid character in variable name at byte offset 9",
+		err:   `error parsing ninja string "foo ${abc!} bar": invalid character in variable name at byte offset 9`,
 	},
 	{
 		input: "foo ${abc",
diff --git a/package_ctx.go b/package_ctx.go
index 1eafdb9..07b7a9c 100644
--- a/package_ctx.go
+++ b/package_ctx.go
@@ -59,7 +59,7 @@
 	ImportAs(as, pkgPath string)
 
 	StaticVariable(name, value string) Variable
-	VariableFunc(name string, f func(config interface{}) (string, error)) Variable
+	VariableFunc(name string, f func(ctx VariableFuncContext, config interface{}) (string, error)) Variable
 	VariableConfigMethod(name string, method interface{}) Variable
 
 	StaticPool(name string, params PoolParams) Pool
@@ -304,7 +304,7 @@
 	v.fullName_ = v.fullName(pkgNames)
 }
 
-func (v *staticVariable) value(interface{}) (ninjaString, error) {
+func (v *staticVariable) value(VariableFuncContext, interface{}) (ninjaString, error) {
 	ninjaStr, err := parseNinjaString(v.pctx.scope, v.value_)
 	if err != nil {
 		err = fmt.Errorf("error parsing variable %s value: %s", v, err)
@@ -320,10 +320,30 @@
 type variableFunc struct {
 	pctx      *packageContext
 	name_     string
-	value_    func(interface{}) (string, error)
+	value_    func(VariableFuncContext, interface{}) (string, error)
 	fullName_ string
 }
 
+// VariableFuncContext is passed to VariableFunc functions.
+type VariableFuncContext interface {
+	// GlobWithDeps returns a list of files and directories that match the
+	// specified pattern but do not match any of the patterns in excludes.
+	// Any directories will have a '/' suffix.  It also adds efficient
+	// dependencies to rerun the primary builder whenever a file matching
+	// the pattern as added or removed, without rerunning if a file that
+	// does not match the pattern is added to a searched directory.
+	GlobWithDeps(globPattern string, excludes []string) ([]string, error)
+}
+
+type variableFuncContext struct {
+	context *Context
+}
+
+func (v *variableFuncContext) GlobWithDeps(pattern string,
+	excludes []string) ([]string, error) {
+	return v.context.glob(pattern, excludes)
+}
+
 // VariableFunc returns a Variable whose value is determined by a function that
 // takes a config object as input and returns either the variable value or an
 // error.  It may only be called during a Go package's initialization - either
@@ -336,7 +356,7 @@
 // reference other Ninja variables that are visible within the calling Go
 // package.
 func (p *packageContext) VariableFunc(name string,
-	f func(config interface{}) (string, error)) Variable {
+	f func(ctx VariableFuncContext, config interface{}) (string, error)) Variable {
 
 	checkCalledFromInit()
 
@@ -382,7 +402,7 @@
 	methodValue := reflect.ValueOf(method)
 	validateVariableMethod(name, methodValue)
 
-	fun := func(config interface{}) (string, error) {
+	fun := func(ctx VariableFuncContext, config interface{}) (string, error) {
 		result := methodValue.Call([]reflect.Value{reflect.ValueOf(config)})
 		resultStr := result[0].Interface().(string)
 		return resultStr, nil
@@ -420,8 +440,8 @@
 	v.fullName_ = v.fullName(pkgNames)
 }
 
-func (v *variableFunc) value(config interface{}) (ninjaString, error) {
-	value, err := v.value_(config)
+func (v *variableFunc) value(ctx VariableFuncContext, config interface{}) (ninjaString, error) {
+	value, err := v.value_(ctx, config)
 	if err != nil {
 		return nil, err
 	}
@@ -484,7 +504,7 @@
 	// Nothing to do, full name is known at initialization.
 }
 
-func (v *argVariable) value(config interface{}) (ninjaString, error) {
+func (v *argVariable) value(ctx VariableFuncContext, config interface{}) (ninjaString, error) {
 	return nil, errVariableIsArg
 }
 
diff --git a/parser/parser.go b/parser/parser.go
index df694c5..63a6ac1 100644
--- a/parser/parser.go
+++ b/parser/parser.go
@@ -70,6 +70,7 @@
 		}
 	}()
 
+	p.next()
 	defs := p.parseDefinitions()
 	p.accept(scanner.EOF)
 	errs = p.errors
@@ -100,6 +101,7 @@
 
 func ParseExpression(r io.Reader) (value Expression, errs []error) {
 	p := newParser(r, NewScope(nil))
+	p.next()
 	value = p.parseExpression()
 	p.accept(scanner.EOF)
 	errs = p.errors
@@ -124,7 +126,6 @@
 	}
 	p.scanner.Mode = scanner.ScanIdents | scanner.ScanInts | scanner.ScanStrings |
 		scanner.ScanRawStrings | scanner.ScanComments
-	p.next()
 	return p
 }
 
diff --git a/parser/parser_test.go b/parser/parser_test.go
index 4cc4e1b..b393792 100644
--- a/parser/parser_test.go
+++ b/parser/parser_test.go
@@ -1139,7 +1139,7 @@
 				Comments: []*Comment{
 					&Comment{
 						Comment: []string{"/* comment3", "		   comment4 */"},
-						Slash: mkpos(32, 5, 3),
+						Slash:   mkpos(32, 5, 3),
 					},
 					&Comment{
 						Comment: []string{"// comment5"},
@@ -1214,7 +1214,36 @@
 	}
 }
 
-// TODO: Test error strings
+func TestParserError(t *testing.T) {
+	testcases := []struct {
+		name  string
+		input string
+		err   string
+	}{
+		{
+			name:  "invalid first token",
+			input: "\x00",
+			err:   "invalid character NUL",
+		},
+		// TODO: test more parser errors
+	}
+
+	for _, tt := range testcases {
+		t.Run(tt.name, func(t *testing.T) {
+			r := bytes.NewBufferString(tt.input)
+			_, errs := ParseAndEval("", r, NewScope(nil))
+			if len(errs) == 0 {
+				t.Fatalf("missing expected error")
+			}
+			if g, w := errs[0], tt.err; !strings.Contains(g.Error(), w) {
+				t.Errorf("expected error %q, got %q", w, g)
+			}
+			for _, err := range errs[1:] {
+				t.Errorf("got unexpected extra error %q", err)
+			}
+		})
+	}
+}
 
 func TestParserEndPos(t *testing.T) {
 	in := `
diff --git a/scope.go b/scope.go
index 3f39eb7..9585559 100644
--- a/scope.go
+++ b/scope.go
@@ -29,7 +29,7 @@
 	name() string                                        // "foo"
 	fullName(pkgNames map[*packageContext]string) string // "pkg.foo" or "path.to.pkg.foo"
 	memoizeFullName(pkgNames map[*packageContext]string) // precompute fullName if desired
-	value(config interface{}) (ninjaString, error)
+	value(ctx VariableFuncContext, config interface{}) (ninjaString, error)
 	String() string
 }
 
@@ -373,7 +373,7 @@
 	// Nothing to do, full name is known at initialization.
 }
 
-func (l *localVariable) value(interface{}) (ninjaString, error) {
+func (l *localVariable) value(VariableFuncContext, interface{}) (ninjaString, error) {
 	return l.value_, nil
 }