Merge "Handle `test_per_src` test variations as dependencies of APEX modules."
diff --git a/android/neverallow.go b/android/neverallow.go
index 1893e8b..23b6454 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -48,6 +48,7 @@
 var neverallows = []Rule{}
 
 func init() {
+	AddNeverAllowRules(createIncludeDirsRules()...)
 	AddNeverAllowRules(createTrebleRules()...)
 	AddNeverAllowRules(createLibcoreRules()...)
 	AddNeverAllowRules(createJavaDeviceForHostRules()...)
@@ -58,6 +59,42 @@
 	neverallows = append(neverallows, rules...)
 }
 
+func createIncludeDirsRules() []Rule {
+	// The list of paths that cannot be referenced using include_dirs
+	paths := []string{
+		"art",
+		"libcore",
+		"libnativehelper",
+		"external/apache-harmony",
+		"external/apache-xml",
+		"external/boringssl",
+		"external/bouncycastle",
+		"external/conscrypt",
+		"external/icu",
+		"external/okhttp",
+		"external/vixl",
+		"external/wycheproof",
+		"system/core/libnativebridge",
+		"system/core/libnativehelper",
+	}
+
+	// Create a composite matcher that will match if the value starts with any of the restricted
+	// paths. A / is appended to the prefix to ensure that restricting path X does not affect paths
+	// XY.
+	rules := make([]Rule, 0, len(paths))
+	for _, path := range paths {
+		rule :=
+			NeverAllow().
+				WithMatcher("include_dirs", StartsWith(path+"/")).
+				Because("include_dirs is deprecated, all usages of '" + path + "' have been migrated" +
+					" to use alternate mechanisms and so can no longer be used.")
+
+		rules = append(rules, rule)
+	}
+
+	return rules
+}
+
 func createTrebleRules() []Rule {
 	return []Rule{
 		NeverAllow().
@@ -156,9 +193,51 @@
 	}
 }
 
+type ValueMatcher interface {
+	test(string) bool
+	String() string
+}
+
+type equalMatcher struct {
+	expected string
+}
+
+func (m *equalMatcher) test(value string) bool {
+	return m.expected == value
+}
+
+func (m *equalMatcher) String() string {
+	return "=" + m.expected
+}
+
+type anyMatcher struct {
+}
+
+func (m *anyMatcher) test(value string) bool {
+	return true
+}
+
+func (m *anyMatcher) String() string {
+	return "=*"
+}
+
+var anyMatcherInstance = &anyMatcher{}
+
+type startsWithMatcher struct {
+	prefix string
+}
+
+func (m *startsWithMatcher) test(value string) bool {
+	return strings.HasPrefix(value, m.prefix)
+}
+
+func (m *startsWithMatcher) String() string {
+	return ".starts-with(" + m.prefix + ")"
+}
+
 type ruleProperty struct {
-	fields []string // e.x.: Vndk.Enabled
-	value  string   // e.x.: true
+	fields  []string // e.x.: Vndk.Enabled
+	matcher ValueMatcher
 }
 
 // A NeverAllow rule.
@@ -173,8 +252,12 @@
 
 	With(properties, value string) Rule
 
+	WithMatcher(properties string, matcher ValueMatcher) Rule
+
 	Without(properties, value string) Rule
 
+	WithoutMatcher(properties string, matcher ValueMatcher) Rule
+
 	Because(reason string) Rule
 }
 
@@ -218,21 +301,36 @@
 }
 
 func (r *rule) With(properties, value string) Rule {
+	return r.WithMatcher(properties, selectMatcher(value))
+}
+
+func (r *rule) WithMatcher(properties string, matcher ValueMatcher) Rule {
 	r.props = append(r.props, ruleProperty{
-		fields: fieldNamesForProperties(properties),
-		value:  value,
+		fields:  fieldNamesForProperties(properties),
+		matcher: matcher,
 	})
 	return r
 }
 
 func (r *rule) Without(properties, value string) Rule {
+	return r.WithoutMatcher(properties, selectMatcher(value))
+}
+
+func (r *rule) WithoutMatcher(properties string, matcher ValueMatcher) Rule {
 	r.unlessProps = append(r.unlessProps, ruleProperty{
-		fields: fieldNamesForProperties(properties),
-		value:  value,
+		fields:  fieldNamesForProperties(properties),
+		matcher: matcher,
 	})
 	return r
 }
 
+func selectMatcher(expected string) ValueMatcher {
+	if expected == "*" {
+		return anyMatcherInstance
+	}
+	return &equalMatcher{expected: expected}
+}
+
 func (r *rule) Because(reason string) Rule {
 	r.reason = reason
 	return r
@@ -253,10 +351,10 @@
 		s += " -type:" + v
 	}
 	for _, v := range r.props {
-		s += " " + strings.Join(v.fields, ".") + "=" + v.value
+		s += " " + strings.Join(v.fields, ".") + v.matcher.String()
 	}
 	for _, v := range r.unlessProps {
-		s += " -" + strings.Join(v.fields, ".") + "=" + v.value
+		s += " -" + strings.Join(v.fields, ".") + v.matcher.String()
 	}
 	if len(r.reason) != 0 {
 		s += " which is restricted because " + r.reason
@@ -280,6 +378,10 @@
 	return includeProps && !excludeProps
 }
 
+func StartsWith(prefix string) ValueMatcher {
+	return &startsWithMatcher{prefix}
+}
+
 // assorted utils
 
 func cleanPaths(paths []string) []string {
@@ -338,8 +440,8 @@
 			continue
 		}
 
-		check := func(v string) bool {
-			return prop.value == "*" || prop.value == v
+		check := func(value string) bool {
+			return prop.matcher.test(value)
 		}
 
 		if matchValue(propertiesValue, check) {
diff --git a/android/neverallow_test.go b/android/neverallow_test.go
index ee3c94f..02b4362 100644
--- a/android/neverallow_test.go
+++ b/android/neverallow_test.go
@@ -23,6 +23,29 @@
 	fs            map[string][]byte
 	expectedError string
 }{
+	// include_dir rule tests
+	{
+		name: "include_dir not allowed to reference art",
+		fs: map[string][]byte{
+			"other/Blueprints": []byte(`
+				cc_library {
+					name: "libother",
+					include_dirs: ["art/libdexfile/include"],
+				}`),
+		},
+		expectedError: "all usages of 'art' have been migrated",
+	},
+	{
+		name: "include_dir can reference another location",
+		fs: map[string][]byte{
+			"other/Blueprints": []byte(`
+				cc_library {
+					name: "libother",
+					include_dirs: ["another/include"],
+				}`),
+		},
+	},
+	// Treble rule tests
 	{
 		name: "no vndk.enabled under vendor directory",
 		fs: map[string][]byte{
@@ -217,6 +240,7 @@
 }
 
 type mockCcLibraryProperties struct {
+	Include_dirs     []string
 	Vendor_available *bool
 
 	Vndk struct {
diff --git a/cc/cc.go b/cc/cc.go
index b12aab1..2cee807 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -179,6 +179,9 @@
 }
 
 type ObjectLinkerProperties struct {
+	// list of modules that should only provide headers for this module.
+	Header_libs []string `android:"arch_variant,variant_prepend"`
+
 	// names of other cc_object modules to link into this module using partial linking
 	Objs []string `android:"arch_variant"`
 
@@ -2114,6 +2117,7 @@
 		&VendorProperties{},
 		&BaseCompilerProperties{},
 		&BaseLinkerProperties{},
+		&ObjectLinkerProperties{},
 		&LibraryProperties{},
 		&FlagExporterProperties{},
 		&BinaryLinkerProperties{},
diff --git a/cc/object.go b/cc/object.go
index 50ecc38..9fa0ac9 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -66,6 +66,7 @@
 		deps.LateSharedLibs = append(deps.LateSharedLibs, "libc")
 	}
 
+	deps.HeaderLibs = append(deps.HeaderLibs, object.Properties.Header_libs...)
 	deps.ObjFiles = append(deps.ObjFiles, object.Properties.Objs...)
 	return deps
 }
diff --git a/scripts/strip.sh b/scripts/strip.sh
index bd62619..4a1ed3f 100755
--- a/scripts/strip.sh
+++ b/scripts/strip.sh
@@ -59,7 +59,7 @@
     # ${CROSS_COMPILE}strip --strip-all does not strip .ARM.attributes,
     # so we tell llvm-strip to keep it too.
     if [ -z "${use_gnu_strip}" ]; then
-        "${CLANG_BIN}/llvm-strip" --strip-all -keep-section=.ARM.attributes "${infile}" -o "${outfile}.tmp"
+        "${CLANG_BIN}/llvm-strip" --strip-all --keep-section=.ARM.attributes "${infile}" -o "${outfile}.tmp"
     else
         "${CROSS_COMPILE}strip" --strip-all "${infile}" -o "${outfile}.tmp"
     fi
@@ -101,7 +101,7 @@
     rm -f "${outfile}.dynsyms" "${outfile}.funcsyms" "${outfile}.keep_symbols" "${outfile}.debug" "${outfile}.mini_debuginfo" "${outfile}.mini_debuginfo.xz"
     local fail=
     if [ -z "${use_gnu_strip}" ]; then
-        "${CLANG_BIN}/llvm-strip" --strip-all -keep-section=.ARM.attributes -remove-section=.comment "${infile}" -o "${outfile}.tmp" || fail=true
+        "${CLANG_BIN}/llvm-strip" --strip-all --keep-section=.ARM.attributes --remove-section=.comment "${infile}" -o "${outfile}.tmp" || fail=true
     else
         "${CROSS_COMPILE}strip" --strip-all -R .comment "${infile}" -o "${outfile}.tmp" || fail=true
     fi