Snap for 6338078 from dba92766b742da430fb6534e35b6bd7959e8514a to rvc-release
Change-Id: I045d487cb5159bb2543273863159ee52eb8ea5f5
diff --git a/split/manifest_split.py b/split/manifest_split.py
index 3e4698e..89f8dc5 100644
--- a/split/manifest_split.py
+++ b/split/manifest_split.py
@@ -39,11 +39,18 @@
--ninja-build <path>
Optional path to the combined-<target>.ninja file found in an out dir.
If not provided, the default file is used based on the lunch environment.
+ --ninja-binary <path>
+ Optional path to the ninja binary. Uses the standard binary by default.
--module-info <path>
Optional path to the module-info.json file found in an out dir.
If not provided, the default file is used based on the lunch environment.
- --ninja-binary <path>
- Optional path to the ninja binary. Uses the standard binary by default.
+ --kati-stamp <path>
+ Optional path to the .kati_stamp file found in an out dir.
+ If not provided, the default file is used based on the lunch environment.
+ --overlay <path>
+ Optional path(s) to treat as overlays when parsing the kati stamp file
+ and scanning for makefiles. See the tools/treble/build/sandbox directory
+ for more info about overlays. This flag can be passed more than once.
--debug
Print debug messages.
-h (--help)
@@ -172,6 +179,83 @@
return {path.strip() for path in inputs}
+def get_kati_makefiles(kati_stamp_file, overlays):
+ """Returns the set of makefile paths from the kati stamp file.
+
+ Uses the ckati_stamp_dump prebuilt binary.
+ Also includes symlink sources in the resulting set for any
+ makefiles that are symlinks.
+
+ Args:
+ kati_stamp_file: The path to a .kati_stamp file from a build.
+ overlays: A list of paths to treat as overlays when parsing the kati stamp
+ file.
+ """
+ # Get a set of all makefiles that were parsed by Kati during the build.
+ makefiles = set(
+ subprocess.check_output([
+ "prebuilts/build-tools/linux-x86/bin/ckati_stamp_dump",
+ "--files",
+ kati_stamp_file,
+ ]).decode().strip("\n").split("\n"))
+
+ def is_product_makefile(makefile):
+ """Returns True if the makefile path meets certain criteria."""
+ banned_prefixes = [
+ "out/",
+ # Ignore product makefiles for sample AOSP boards.
+ "device/amlogic",
+ "device/generic",
+ "device/google",
+ "device/linaro",
+ "device/sample",
+ ]
+ banned_suffixes = [
+ # All Android.mk files in the source are always parsed by Kati,
+ # so including them here would bring in lots of unnecessary projects.
+ "Android.mk",
+ # The ckati stamp file always includes a line for the ckati bin at
+ # the beginnning.
+ "bin/ckati",
+ ]
+ return (all([not makefile.startswith(p) for p in banned_prefixes]) and
+ all([not makefile.endswith(s) for s in banned_suffixes]))
+
+ # Limit the makefiles to only product makefiles.
+ product_makefiles = {
+ os.path.normpath(path) for path in makefiles if is_product_makefile(path)
+ }
+
+ def strip_overlay(makefile):
+ """Remove any overlays from a makefile path."""
+ for overlay in overlays:
+ if makefile.startswith(overlay):
+ return makefile[len(overlay):]
+ return makefile
+
+ makefiles_and_symlinks = set()
+ for makefile in product_makefiles:
+ # Search for the makefile, possibly scanning overlays as well.
+ for overlay in [""] + overlays:
+ makefile_with_overlay = os.path.join(overlay, makefile)
+ if os.path.exists(makefile_with_overlay):
+ makefile = makefile_with_overlay
+ break
+
+ if not os.path.exists(makefile):
+ logger.warning("Unknown kati makefile: %s" % makefile)
+ continue
+
+ # Ensure the project that contains the makefile is included, as well as
+ # the project that any makefile symlinks point to.
+ makefiles_and_symlinks.add(strip_overlay(makefile))
+ if os.path.islink(makefile):
+ makefiles_and_symlinks.add(
+ strip_overlay(os.path.relpath(os.path.realpath(makefile))))
+
+ return makefiles_and_symlinks
+
+
def scan_repo_projects(repo_projects, input_path):
"""Returns the project path of the given input path if it exists.
@@ -253,7 +337,8 @@
def create_split_manifest(targets, manifest_file, split_manifest_file,
config_files, repo_list_file, ninja_build_file,
- module_info_file, ninja_binary):
+ ninja_binary, module_info_file, kati_stamp_file,
+ overlays):
"""Creates and writes a split manifest by inspecting build inputs.
Args:
@@ -266,8 +351,11 @@
repo_list_file: Path to the output of the 'repo list' command.
ninja_build_file: Path to the combined-<target>.ninja file found in an out
dir.
- module_info_file: Path to the module-info.json file found in an out dir.
ninja_binary: Path to the ninja binary.
+ module_info_file: Path to the module-info.json file found in an out dir.
+ kati_stamp_file: The path to a .kati_stamp file from a build.
+ overlays: A list of paths to treat as overlays when parsing the kati stamp
+ file.
"""
remove_projects = set()
add_projects = set()
@@ -287,6 +375,15 @@
logger.info("%s projects needed for targets \"%s\"", len(input_projects),
" ".join(targets))
+ kati_makefiles = get_kati_makefiles(kati_stamp_file, overlays)
+ kati_makefiles_projects = get_input_projects(repo_projects, kati_makefiles)
+ if logger.isEnabledFor(logging.DEBUG):
+ for project in sorted(kati_makefiles_projects.difference(input_projects)):
+ logger.debug("Kati makefile dependency: %s", project)
+ input_projects = input_projects.union(kati_makefiles_projects)
+ logger.info("%s projects after including Kati makefiles projects.",
+ len(input_projects))
+
if logger.isEnabledFor(logging.DEBUG):
manual_projects = add_projects.difference(input_projects)
for project in sorted(manual_projects):
@@ -295,6 +392,11 @@
logger.info("%s projects after including manual additions.",
len(input_projects))
+ # Remove projects from our set of input projects before adding adjacent
+ # modules, so that no project is added only because of an adjacent
+ # dependency in a to-be-removed project.
+ input_projects = input_projects.difference(remove_projects)
+
# While we still have projects whose modules we haven't checked yet,
checked_projects = set()
projects_to_check = input_projects.difference(checked_projects)
@@ -339,8 +441,10 @@
"config=",
"repo-list=",
"ninja-build=",
- "module-info=",
"ninja-binary=",
+ "module-info=",
+ "kati-stamp=",
+ "overlay=",
])
except getopt.GetoptError as err:
print(__doc__, file=sys.stderr)
@@ -354,6 +458,8 @@
ninja_build_file = None
module_info_file = None
ninja_binary = "ninja"
+ kati_stamp_file = None
+ overlays = []
for o, a in opts:
if o in ("-h", "--help"):
@@ -371,10 +477,14 @@
repo_list_file = a
elif o in ("--ninja-build"):
ninja_build_file = a
- elif o in ("--module-info"):
- module_info_file = a
elif o in ("--ninja-binary"):
ninja_binary = a
+ elif o in ("--module-info"):
+ module_info_file = a
+ elif o in ("--kati-stamp"):
+ kati_stamp_file = a
+ elif o in ("--overlay"):
+ overlays.append(a)
else:
assert False, "unknown option \"%s\"" % o
@@ -393,6 +503,10 @@
if not module_info_file:
module_info_file = os.path.join(os.environ["ANDROID_PRODUCT_OUT"],
"module-info.json")
+ if not kati_stamp_file:
+ kati_stamp_file = os.path.join(
+ os.environ["ANDROID_BUILD_TOP"], "out",
+ ".kati_stamp-%s" % os.environ["TARGET_PRODUCT"])
if not ninja_build_file:
ninja_build_file = os.path.join(
os.environ["ANDROID_BUILD_TOP"], "out",
@@ -405,8 +519,10 @@
config_files=config_files,
repo_list_file=repo_list_file,
ninja_build_file=ninja_build_file,
+ ninja_binary=ninja_binary,
module_info_file=module_info_file,
- ninja_binary=ninja_binary)
+ kati_stamp_file=kati_stamp_file,
+ overlays=overlays)
if __name__ == "__main__":
diff --git a/split/manifest_split_test.py b/split/manifest_split_test.py
index 11b1607..2fdb9bb 100644
--- a/split/manifest_split_test.py
+++ b/split/manifest_split_test.py
@@ -15,6 +15,7 @@
import hashlib
import mock
+import os
import subprocess
import tempfile
import unittest
@@ -85,6 +86,52 @@
'Unknown module path for module target1'):
manifest_split.get_module_info(module_info_file.name, repo_projects)
+ @mock.patch.object(subprocess, 'check_output', autospec=True)
+ def test_get_kati_makefiles(self, mock_check_output):
+ with tempfile.TemporaryDirectory() as temp_dir:
+ os.chdir(temp_dir)
+
+ makefiles = [
+ 'device/oem1/product1.mk',
+ 'device/oem2/product2.mk',
+ 'device/google/google_product.mk',
+ 'overlays/oem_overlay/device/oem3/product3.mk',
+ 'packages/apps/Camera/Android.mk',
+ ]
+ for makefile in makefiles:
+ os.makedirs(os.path.dirname(makefile))
+ os.mknod(makefile)
+
+ symlink_src = os.path.join(temp_dir, 'vendor/oem4/symlink_src.mk')
+ os.makedirs(os.path.dirname(symlink_src))
+ os.mknod(symlink_src)
+ symlink_dest = 'device/oem4/symlink_dest.mk'
+ os.makedirs(os.path.dirname(symlink_dest))
+ os.symlink(symlink_src, symlink_dest)
+ # Only append the symlink destination, not where the symlink points to.
+ # (The Kati stamp file does not resolve symlink sources.)
+ makefiles.append(symlink_dest)
+
+ # Mock the output of ckati_stamp_dump:
+ mock_check_output.side_effect = [
+ '\n'.join(makefiles).encode(),
+ ]
+
+ kati_makefiles = manifest_split.get_kati_makefiles(
+ 'stamp-file', ['overlays/oem_overlay/'])
+ self.assertEqual(
+ kati_makefiles,
+ set([
+ # Regular product makefiles
+ 'device/oem1/product1.mk',
+ 'device/oem2/product2.mk',
+ # Product makefile remapped from an overlay
+ 'device/oem3/product3.mk',
+ # Product makefile symlink and its source
+ 'device/oem4/symlink_dest.mk',
+ 'vendor/oem4/symlink_src.mk',
+ ]))
+
def test_scan_repo_projects(self):
repo_projects = {
'system/project1': 'platform/project1',
@@ -208,6 +255,7 @@
mock_check_output.side_effect = [
ninja_inputs_droid,
+ b'', # Unused kati makefiles. This is tested in its own method.
ninja_inputs_target_b,
ninja_inputs_target_c,
]
@@ -219,12 +267,11 @@
</config>""")
config_file.flush()
- manifest_split.create_split_manifest(['droid'], manifest_file.name,
- split_manifest_file.name,
- [config_file.name],
- repo_list_file.name,
- 'build-target.ninja',
- module_info_file.name, 'ninja')
+ manifest_split.create_split_manifest(
+ ['droid'], manifest_file.name, split_manifest_file.name,
+ [config_file.name], repo_list_file.name, 'build-target.ninja',
+ 'ninja', module_info_file.name, 'unused kati stamp',
+ ['unused overlay'])
split_manifest = ET.parse(split_manifest_file.name)
split_manifest_projects = [
child.attrib['name']