Merge "Changed droiddoc.go to compile Metalava based android.jar"
diff --git a/android/paths.go b/android/paths.go
index 8cc3182..af2f956 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -950,7 +950,8 @@
if ctx.InstallInData() {
partition = "data"
} else if ctx.InstallInRecovery() {
- partition = "recovery/root"
+ // the layout of recovery partion is the same as that of system partition
+ partition = "recovery/root/system"
} else if ctx.SocSpecific() {
partition = ctx.DeviceConfig().VendorPath()
} else if ctx.DeviceSpecific() {
diff --git a/cc/binary.go b/cc/binary.go
index 04b912a..4a6eb93 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -357,11 +357,6 @@
}
func (binary *binaryDecorator) install(ctx ModuleContext, file android.Path) {
- // <recovery>/bin is a symlink to /system/bin. Recovery binaries are all in /sbin.
- if ctx.inRecovery() {
- binary.baseInstaller.dir = "sbin"
- }
-
binary.baseInstaller.install(ctx, file)
for _, symlink := range binary.Properties.Symlinks {
binary.symlinks = append(binary.symlinks,
diff --git a/cc/compiler.go b/cc/compiler.go
index 10cec8c..8d034c9 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -332,6 +332,10 @@
"-D__ANDROID_API__="+version, "-D__ANDROID_VNDK__")
}
+ if ctx.inRecovery() {
+ flags.GlobalFlags = append(flags.GlobalFlags, "-D__ANDROID_RECOVERY__")
+ }
+
instructionSet := String(compiler.Properties.Instruction_set)
if flags.RequiredInstructionSet != "" {
instructionSet = flags.RequiredInstructionSet
diff --git a/cc/config/arm64_device.go b/cc/config/arm64_device.go
index 73cee5c..172784a 100644
--- a/cc/config/arm64_device.go
+++ b/cc/config/arm64_device.go
@@ -53,15 +53,12 @@
"-mcpu=cortex-a53",
},
"cortex-a55": []string{
- // The cortex-a55 target is not yet supported,
- // so use cortex-a53.
- "-mcpu=cortex-a53",
+ "-mcpu=cortex-a55",
},
"cortex-a75": []string{
- // Use the cortex-a53 since it is similar to the little
+ // Use the cortex-a55 since it is similar to the little
// core (cortex-a55) and is sensitive to ordering.
- // The cortex-a55 target is not yet supported.
- "-mcpu=cortex-a53",
+ "-mcpu=cortex-a55",
},
"kryo": []string{
// Use the cortex-a57 cpu since some compilers
diff --git a/cc/config/arm_device.go b/cc/config/arm_device.go
index 398df90..4719fb7 100644
--- a/cc/config/arm_device.go
+++ b/cc/config/arm_device.go
@@ -100,7 +100,7 @@
"-D__ARM_FEATURE_LPAE=1",
},
"cortex-a55": []string{
- "-mcpu=cortex-a53",
+ "-mcpu=cortex-a55",
"-mfpu=neon-fp-armv8",
// Fake an ARM compiler flag as these processors support LPAE which GCC/clang
// don't advertise.
@@ -109,7 +109,7 @@
"-D__ARM_FEATURE_LPAE=1",
},
"cortex-a75": []string{
- "-mcpu=cortex-a53",
+ "-mcpu=cortex-a55",
"-mfpu=neon-fp-armv8",
// Fake an ARM compiler flag as these processors support LPAE which GCC/clang
// don't advertise.
diff --git a/cc/sabi.go b/cc/sabi.go
index f5a7c77..42b2f35 100644
--- a/cc/sabi.go
+++ b/cc/sabi.go
@@ -74,8 +74,13 @@
// RSClang does not support recent mcpu option likes exynos-m2.
// So we need overriding mcpu option when we want to use it.
- if ctx.Arch().CpuVariant == "exynos-m2" {
- flags.ToolingCFlags = append(flags.ToolingCFlags, "-mcpu=cortex-a53")
+ mappedArch := map[string]string{
+ "exynos-m2": "cortex-a53",
+ "cortex-a55": "cortex-a53",
+ "cortex-a75": "cortex-a57",
+ }
+ if arch, ok := mappedArch[ctx.Arch().CpuVariant]; ok {
+ flags.ToolingCFlags = append(flags.ToolingCFlags, "-mcpu="+arch)
}
return flags
diff --git a/java/config/config.go b/java/config/config.go
index c6555f1..2fa48cb 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -132,4 +132,6 @@
}
hostBinToolVariableWithPrebuilt("Aapt2Cmd", "prebuilts/sdk/tools", "aapt2")
+
+ pctx.SourcePathVariable("ManifestFixerCmd", "build/soong/scripts/manifest_fixer.py")
}
diff --git a/java/config/makevars.go b/java/config/makevars.go
index 4dffa02..0e62274 100644
--- a/java/config/makevars.go
+++ b/java/config/makevars.go
@@ -72,4 +72,6 @@
ctx.Strict("DEFAULT_JACOCO_EXCLUDE_FILTER", strings.Join(DefaultJacocoExcludeFilter, ","))
ctx.Strict("EXTRACT_JAR_PACKAGES", "${ExtractJarPackagesCmd}")
+
+ ctx.Strict("MANIFEST_FIXER", "${ManifestFixerCmd}")
}
diff --git a/java/prebuilt_apis.go b/java/prebuilt_apis.go
index 50318bb..3f4b076 100644
--- a/java/prebuilt_apis.go
+++ b/java/prebuilt_apis.go
@@ -50,6 +50,20 @@
// no need to implement
}
+func parseJarPath(ctx android.BaseModuleContext, path string) (module string, apiver string, scope string) {
+ elements := strings.Split(path, "/")
+
+ apiver = elements[0]
+ scope = elements[1]
+ if scope != "public" && scope != "system" && scope != "test" && scope != "core" {
+ // scope must be public, system or test
+ return
+ }
+
+ module = strings.TrimSuffix(elements[2], ".jar")
+ return
+}
+
func parseApiFilePath(ctx android.BaseModuleContext, path string) (module string, apiver int, scope string) {
elements := strings.Split(path, "/")
ver, err := strconv.Atoi(elements[0])
@@ -70,6 +84,22 @@
return
}
+func createImport(mctx android.TopDownMutatorContext, module string, scope string, apiver string, path string) {
+ props := struct {
+ Name *string
+ Jars []string
+ Sdk_version *string
+ Installable *bool
+ }{}
+ props.Name = proptools.StringPtr("sdk_" + scope + "_" + apiver + "_" + module)
+ props.Jars = append(props.Jars, path)
+ // TODO(hansson): change to scope after migration is done.
+ props.Sdk_version = proptools.StringPtr("current")
+ props.Installable = proptools.BoolPtr(false)
+
+ mctx.CreateModule(android.ModuleFactoryAdaptor(ImportFactory), &props)
+}
+
func createFilegroup(mctx android.TopDownMutatorContext, module string, scope string, apiver string, path string) {
fgName := module + ".api." + scope + "." + apiver
filegroupProps := struct {
@@ -81,55 +111,82 @@
mctx.CreateModule(android.ModuleFactoryAdaptor(android.FileGroupFactory), &filegroupProps)
}
+func prebuiltSdkStubs(mctx android.TopDownMutatorContext) {
+ mydir := mctx.ModuleDir() + "/"
+ // <apiver>/<scope>/<module>.jar
+ files, err := mctx.GlobWithDeps(mydir+"*/*/*.jar", nil)
+ if err != nil {
+ mctx.ModuleErrorf("failed to glob jar files under %q: %s", mydir, err)
+ }
+ if len(files) == 0 {
+ mctx.ModuleErrorf("no jar file found under %q", mydir)
+ }
+
+ for _, f := range files {
+ // create a Import module for each jar file
+ localPath := strings.TrimPrefix(f, mydir)
+ module, apiver, scope := parseJarPath(mctx, localPath)
+
+ if len(module) != 0 {
+ createImport(mctx, module, scope, apiver, localPath)
+ }
+ }
+}
+
+func prebuiltApiFiles(mctx android.TopDownMutatorContext) {
+ mydir := mctx.ModuleDir() + "/"
+ // <apiver>/<scope>/api/<module>.txt
+ files, err := mctx.GlobWithDeps(mydir+"*/*/api/*.txt", nil)
+ if err != nil {
+ mctx.ModuleErrorf("failed to glob api txt files under %q: %s", mydir, err)
+ }
+ if len(files) == 0 {
+ mctx.ModuleErrorf("no api file found under %q", mydir)
+ }
+
+ // construct a map to find out the latest api file path
+ // for each (<module>, <scope>) pair.
+ type latestApiInfo struct {
+ module string
+ scope string
+ apiver int
+ path string
+ }
+ m := make(map[string]latestApiInfo)
+
+ for _, f := range files {
+ // create a filegroup for each api txt file
+ localPath := strings.TrimPrefix(f, mydir)
+ module, apiver, scope := parseApiFilePath(mctx, localPath)
+ createFilegroup(mctx, module, scope, strconv.Itoa(apiver), localPath)
+
+ // find the latest apiver
+ key := module + "." + scope
+ info, ok := m[key]
+ if !ok {
+ m[key] = latestApiInfo{module, scope, apiver, localPath}
+ } else if apiver > info.apiver {
+ info.apiver = apiver
+ info.path = localPath
+ }
+ }
+ // create filegroups for the latest version of (<module>, <scope>) pairs
+ // sort the keys in order to make build.ninja stable
+ keys := make([]string, 0, len(m))
+ for k := range m {
+ keys = append(keys, k)
+ }
+ sort.Strings(keys)
+ for _, k := range keys {
+ info := m[k]
+ createFilegroup(mctx, info.module, info.scope, "latest", info.path)
+ }
+}
+
func prebuiltApisMutator(mctx android.TopDownMutatorContext) {
if _, ok := mctx.Module().(*prebuiltApis); ok {
- mydir := mctx.ModuleDir() + "/"
- // <apiver>/<scope>/api/<module>.txt
- files, err := mctx.GlobWithDeps(mydir+"*/*/api/*.txt", nil)
- if err != nil {
- mctx.ModuleErrorf("failed to glob api txt files under %q: %s", mydir, err)
- }
- if len(files) == 0 {
- mctx.ModuleErrorf("no api file found under %q", mydir)
- }
-
- // construct a map to find out the latest api file path
- // for each (<module>, <scope>) pair.
- type latestApiInfo struct {
- module string
- scope string
- apiver int
- path string
- }
- m := make(map[string]latestApiInfo)
-
- for _, f := range files {
- // create a filegroup for each api txt file
- localPath := strings.TrimPrefix(f, mydir)
- module, apiver, scope := parseApiFilePath(mctx, localPath)
- createFilegroup(mctx, module, scope, strconv.Itoa(apiver), localPath)
-
- // find the latest apiver
- key := module + "." + scope
- info, ok := m[key]
- if !ok {
- m[key] = latestApiInfo{module, scope, apiver, localPath}
- } else if apiver > info.apiver {
- info.apiver = apiver
- info.path = localPath
- }
- }
- // create filegroups for the latest version of (<module>, <scope>) pairs
- // sort the keys in order to make build.ninja stable
- keys := make([]string, 0, len(m))
- for k := range m {
- keys = append(keys, k)
- }
- sort.Strings(keys)
- for _, k := range keys {
- info := m[k]
- createFilegroup(mctx, info.module, info.scope, "latest", info.path)
- }
+ prebuiltApiFiles(mctx)
+ prebuiltSdkStubs(mctx)
}
}
diff --git a/java/sdk_library.go b/java/sdk_library.go
index abd2dc2..aee528f 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -106,6 +106,11 @@
// list of package names that must be hidden from the API
Hidden_api_packages []string
+ Errorprone struct {
+ // List of javac flags that should only be used when running errorprone.
+ Javacflags []string
+ }
+
// TODO: determines whether to create HTML doc or not
//Html_doc *bool
}
@@ -445,6 +450,9 @@
Device_specific *bool
Product_specific *bool
Required []string
+ Errorprone struct {
+ Javacflags []string
+ }
}{}
props.Name = proptools.StringPtr(module.implName())
@@ -453,6 +461,7 @@
props.Static_libs = module.properties.Static_libs
// XML file is installed along with the impl lib
props.Required = []string{module.xmlFileName()}
+ props.Errorprone.Javacflags = module.properties.Errorprone.Javacflags
if module.SocSpecific() {
props.Soc_specific = proptools.BoolPtr(true)
diff --git a/scripts/manifest_fixer.py b/scripts/manifest_fixer.py
new file mode 100755
index 0000000..f34f6c3
--- /dev/null
+++ b/scripts/manifest_fixer.py
@@ -0,0 +1,180 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2018 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 for inserting values from the build system into a manifest."""
+
+from __future__ import print_function
+import argparse
+import sys
+from xml.dom import minidom
+
+
+android_ns = 'http://schemas.android.com/apk/res/android'
+
+
+def get_children_with_tag(parent, tag_name):
+ children = []
+ for child in parent.childNodes:
+ if child.nodeType == minidom.Node.ELEMENT_NODE and \
+ child.tagName == tag_name:
+ children.append(child)
+ return children
+
+
+def parse_args():
+ """Parse commandline arguments."""
+
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--minSdkVersion', default='', dest='min_sdk_version',
+ help='specify minSdkVersion used by the build system')
+ parser.add_argument('input', help='input AndroidManifest.xml file')
+ parser.add_argument('output', help='input AndroidManifest.xml file')
+ return parser.parse_args()
+
+
+def parse_manifest(doc):
+ """Get the manifest element."""
+
+ manifest = doc.documentElement
+ if manifest.tagName != 'manifest':
+ raise RuntimeError('expected manifest tag at root')
+ return manifest
+
+
+def ensure_manifest_android_ns(doc):
+ """Make sure the manifest tag defines the android namespace."""
+
+ manifest = parse_manifest(doc)
+
+ ns = manifest.getAttributeNodeNS(minidom.XMLNS_NAMESPACE, 'android')
+ if ns is None:
+ attr = doc.createAttributeNS(minidom.XMLNS_NAMESPACE, 'xmlns:android')
+ attr.value = android_ns
+ manifest.setAttributeNode(attr)
+ elif ns.value != android_ns:
+ raise RuntimeError('manifest tag has incorrect android namespace ' +
+ ns.value)
+
+
+def as_int(s):
+ try:
+ i = int(s)
+ except ValueError:
+ return s, False
+ return i, True
+
+
+def compare_version_gt(a, b):
+ """Compare two SDK versions.
+
+ Compares a and b, treating codenames like 'Q' as higher
+ than numerical versions like '28'.
+
+ Returns True if a > b
+
+ Args:
+ a: value to compare
+ b: value to compare
+ Returns:
+ True if a is a higher version than b
+ """
+
+ a, a_is_int = as_int(a.upper())
+ b, b_is_int = as_int(b.upper())
+
+ if a_is_int == b_is_int:
+ # Both are codenames or both are versions, compare directly
+ return a > b
+ else:
+ # One is a codename, the other is not. Return true if
+ # b is an integer version
+ return b_is_int
+
+
+def raise_min_sdk_version(doc, requested):
+ """Ensure the manifest contains a <uses-sdk> tag with a minSdkVersion.
+
+ Args:
+ doc: The XML document. May be modified by this function.
+ requested: The requested minSdkVersion attribute.
+ Raises:
+ RuntimeError: invalid manifest
+ """
+
+ manifest = parse_manifest(doc)
+
+ # Get or insert the uses-sdk element
+ uses_sdk = get_children_with_tag(manifest, 'uses-sdk')
+ if len(uses_sdk) > 1:
+ raise RuntimeError('found multiple uses-sdk elements')
+ elif len(uses_sdk) == 1:
+ element = uses_sdk[0]
+ else:
+ element = doc.createElement('uses-sdk')
+ indent = ''
+ first = manifest.firstChild
+ if first is not None and first.nodeType == minidom.Node.TEXT_NODE:
+ text = first.nodeValue
+ indent = text[:len(text)-len(text.lstrip())]
+ if not indent or indent == '\n':
+ indent = '\n '
+
+ manifest.insertBefore(element, manifest.firstChild)
+
+ # Insert an indent before uses-sdk to line it up with the indentation of the
+ # other children of the <manifest> tag.
+ manifest.insertBefore(doc.createTextNode(indent), manifest.firstChild)
+
+ # Get or insert the minSdkVersion attribute
+ min_attr = element.getAttributeNodeNS(android_ns, 'minSdkVersion')
+ if min_attr is None:
+ min_attr = doc.createAttributeNS(android_ns, 'android:minSdkVersion')
+ min_attr.value = '1'
+ element.setAttributeNode(min_attr)
+
+ # Update the value of the minSdkVersion attribute if necessary
+ if compare_version_gt(requested, min_attr.value):
+ min_attr.value = requested
+
+
+def write_xml(f, doc):
+ f.write('<?xml version="1.0" encoding="utf-8"?>\n')
+ for node in doc.childNodes:
+ f.write(node.toxml(encoding='utf-8') + '\n')
+
+
+def main():
+ """Program entry point."""
+ try:
+ args = parse_args()
+
+ doc = minidom.parse(args.input)
+
+ ensure_manifest_android_ns(doc)
+
+ if args.min_sdk_version:
+ raise_min_sdk_version(doc, args.min_sdk_version)
+
+ with open(args.output, 'wb') as f:
+ write_xml(f, doc)
+
+ # pylint: disable=broad-except
+ except Exception as err:
+ print('error: ' + str(err), file=sys.stderr)
+ sys.exit(-1)
+
+if __name__ == '__main__':
+ main()
diff --git a/scripts/manifest_fixer_test.py b/scripts/manifest_fixer_test.py
new file mode 100755
index 0000000..ccfa8fb
--- /dev/null
+++ b/scripts/manifest_fixer_test.py
@@ -0,0 +1,162 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2018 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.
+#
+"""Unit tests for manifest_fixer_test.py."""
+
+import StringIO
+import sys
+import unittest
+from xml.dom import minidom
+
+import manifest_fixer
+
+sys.dont_write_bytecode = True
+
+
+class CompareVersionGtTest(unittest.TestCase):
+ """Unit tests for compare_version_gt function."""
+
+ def test_sdk(self):
+ """Test comparing sdk versions."""
+ self.assertTrue(manifest_fixer.compare_version_gt('28', '27'))
+ self.assertFalse(manifest_fixer.compare_version_gt('27', '28'))
+ self.assertFalse(manifest_fixer.compare_version_gt('28', '28'))
+
+ def test_codename(self):
+ """Test comparing codenames."""
+ self.assertTrue(manifest_fixer.compare_version_gt('Q', 'P'))
+ self.assertFalse(manifest_fixer.compare_version_gt('P', 'Q'))
+ self.assertFalse(manifest_fixer.compare_version_gt('Q', 'Q'))
+
+ def test_sdk_codename(self):
+ """Test comparing sdk versions with codenames."""
+ self.assertTrue(manifest_fixer.compare_version_gt('Q', '28'))
+ self.assertFalse(manifest_fixer.compare_version_gt('28', 'Q'))
+
+ def test_compare_numeric(self):
+ """Test that numbers are compared in numeric and not lexicographic order."""
+ self.assertTrue(manifest_fixer.compare_version_gt('18', '8'))
+
+
+class RaiseMinSdkVersionTest(unittest.TestCase):
+ """Unit tests for raise_min_sdk_version function."""
+
+ def raise_min_sdk_version_test(self, input_manifest, min_sdk_version):
+ doc = minidom.parseString(input_manifest)
+ manifest_fixer.raise_min_sdk_version(doc, min_sdk_version)
+ output = StringIO.StringIO()
+ manifest_fixer.write_xml(output, doc)
+ return output.getvalue()
+
+ manifest_tmpl = (
+ '<?xml version="1.0" encoding="utf-8"?>\n'
+ '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n'
+ '%s'
+ '</manifest>\n')
+
+ def uses_sdk(self, v, extra=''):
+ if extra:
+ extra = ' ' + extra
+ return ' <uses-sdk android:minSdkVersion="%s"%s/>\n' % (v, extra)
+
+ def test_no_uses_sdk(self):
+ """Tests inserting a uses-sdk element into a manifest."""
+
+ manifest_input = self.manifest_tmpl % ''
+ expected = self.manifest_tmpl % self.uses_sdk('28')
+ output = self.raise_min_sdk_version_test(manifest_input, '28')
+ self.assertEqual(output, expected)
+
+ def test_no_min(self):
+ """Tests inserting a minSdkVersion attribute into a uses-sdk element."""
+
+ manifest_input = self.manifest_tmpl % ' <uses-sdk extra="foo"/>\n'
+ expected = self.manifest_tmpl % self.uses_sdk('28', 'extra="foo"')
+ output = self.raise_min_sdk_version_test(manifest_input, '28')
+ self.assertEqual(output, expected)
+
+ def test_raise_min(self):
+ """Tests inserting a minSdkVersion attribute into a uses-sdk element."""
+
+ manifest_input = self.manifest_tmpl % self.uses_sdk('27')
+ expected = self.manifest_tmpl % self.uses_sdk('28')
+ output = self.raise_min_sdk_version_test(manifest_input, '28')
+ self.assertEqual(output, expected)
+
+ def test_raise(self):
+ """Tests raising a minSdkVersion attribute."""
+
+ manifest_input = self.manifest_tmpl % self.uses_sdk('27')
+ expected = self.manifest_tmpl % self.uses_sdk('28')
+ output = self.raise_min_sdk_version_test(manifest_input, '28')
+ self.assertEqual(output, expected)
+
+ def test_no_raise_min(self):
+ """Tests a minSdkVersion that doesn't need raising."""
+
+ manifest_input = self.manifest_tmpl % self.uses_sdk('28')
+ expected = manifest_input
+ output = self.raise_min_sdk_version_test(manifest_input, '27')
+ self.assertEqual(output, expected)
+
+ def test_raise_codename(self):
+ """Tests raising a minSdkVersion attribute to a codename."""
+
+ manifest_input = self.manifest_tmpl % self.uses_sdk('28')
+ expected = self.manifest_tmpl % self.uses_sdk('P')
+ output = self.raise_min_sdk_version_test(manifest_input, 'P')
+ self.assertEqual(output, expected)
+
+ def test_no_raise_codename(self):
+ """Tests a minSdkVersion codename that doesn't need raising."""
+
+ manifest_input = self.manifest_tmpl % self.uses_sdk('P')
+ expected = manifest_input
+ output = self.raise_min_sdk_version_test(manifest_input, '28')
+ self.assertEqual(output, expected)
+
+ def test_extra(self):
+ """Tests that extra attributes and elements are maintained."""
+
+ manifest_input = self.manifest_tmpl % (
+ ' <!-- comment -->\n'
+ ' <uses-sdk android:minSdkVersion="27" extra="foo"/>\n'
+ ' <application/>\n')
+
+ expected = self.manifest_tmpl % (
+ ' <!-- comment -->\n'
+ ' <uses-sdk android:minSdkVersion="28" extra="foo"/>\n'
+ ' <application/>\n')
+
+ output = self.raise_min_sdk_version_test(manifest_input, '28')
+
+ self.assertEqual(output, expected)
+
+ def test_indent(self):
+ """Tests that an inserted element copies the existing indentation."""
+
+ manifest_input = self.manifest_tmpl % ' <!-- comment -->\n'
+
+ expected = self.manifest_tmpl % (
+ ' <uses-sdk android:minSdkVersion="28"/>\n'
+ ' <!-- comment -->\n')
+
+ output = self.raise_min_sdk_version_test(manifest_input, '28')
+
+ self.assertEqual(output, expected)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/ui/build/kati.go b/ui/build/kati.go
index 81edd32..743da4a 100644
--- a/ui/build/kati.go
+++ b/ui/build/kati.go
@@ -77,6 +77,8 @@
"--color_warnings",
"--gen_all_targets",
"--werror_find_emulator",
+ "--no_builtin_rules",
+ "--werror_suffix_rules",
"--kati_stats",
"-f", "build/make/core/main.mk",
}