aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-10-17 02:51:58 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-10-17 02:51:58 +0000
commitc0c48de6138d84452870a50547ab521986cf4748 (patch)
treec6db8c6633a1c812caa9bc2fd2a2994c83db8f4c
parent32da00d9648f01cb235e7d4ec5bfc8eac10e2495 (diff)
parent639f241760f0eac593bb31c2df86caddece0c5b0 (diff)
downloadtreble-android14-qpr2-s3-release.tar.gz
Change-Id: I6acaccc6b05a196aca098b2bcb0e1099c12266f0
-rw-r--r--cuttlefish/Android.bp34
-rw-r--r--cuttlefish/build_cf_hybrid_device.py113
-rw-r--r--cuttlefish/build_chd_utils.py78
3 files changed, 225 insertions, 0 deletions
diff --git a/cuttlefish/Android.bp b/cuttlefish/Android.bp
new file mode 100644
index 0000000..1427963
--- /dev/null
+++ b/cuttlefish/Android.bp
@@ -0,0 +1,34 @@
+// Copyright (C) 2023 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.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+python_library_host {
+ name: "build_chd_lib",
+ srcs: [
+ "build_chd_utils.py",
+ ],
+}
+
+python_binary_host {
+ name: "build_cf_hybrid_device",
+ srcs: [
+ "build_cf_hybrid_device.py",
+ ],
+ libs: [
+ "build_chd_lib",
+ ],
+}
diff --git a/cuttlefish/build_cf_hybrid_device.py b/cuttlefish/build_cf_hybrid_device.py
new file mode 100644
index 0000000..a62e919
--- /dev/null
+++ b/cuttlefish/build_cf_hybrid_device.py
@@ -0,0 +1,113 @@
+#!/usr/bin/python3
+#
+# Copyright (C) 2023 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.
+
+import argparse
+import glob
+import os
+import subprocess
+import tempfile
+
+from build_chd_utils import copy_files
+from build_chd_utils import unzip_otatools
+
+"""Test command:
+
+WORKSPACE=out/dist && \
+python3 tools/treble/cuttlefish/build_cf_hybrid_device.py \
+ --build_id 123456 \
+ --otatools_zip $WORKSPACE/otatools.zip \
+ --target chd-target \
+ --output_dir $WORKSPACE \
+ --framework_target_files_zip $WORKSPACE/device-target_files-*.zip \
+ --vendor_target_files_zip $WORKSPACE/cf_arm64_only_phone-target_files-*.zip
+"""
+
+
+def _parse_args():
+ """Parse the arguments for building cuttlefish hybrid devices.
+
+ Returns:
+ An object of argparse.Namespace.
+ """
+ parser = argparse.ArgumentParser()
+
+ parser.add_argument('--build_id', required=True,
+ help='Build id.')
+ parser.add_argument('--target', required=True,
+ help='Target name of the cuttlefish hybrid build.')
+ parser.add_argument('--otatools_zip', required=True,
+ help='Path to the otatools.zip.')
+ parser.add_argument('--output_dir', required=True,
+ help='Path to the output directory of the hybrid build.')
+ parser.add_argument('--framework_target_files_zip', required=True,
+ help='glob pattern of framework target_files zip.')
+ parser.add_argument('--vendor_target_files_zip', required=True,
+ help='glob pattern of vendor target_files zip.')
+ parser.add_argument('--copy_file', action='append', default=[],
+ help='The file to be copied to output directory. '
+ 'The format is <src glob pattern>:<dst path>.')
+ return parser.parse_args()
+
+
+def run(temp_dir):
+ args = _parse_args()
+
+ # unzip otatools
+ otatools = os.path.join(temp_dir, 'otatools')
+ unzip_otatools(args.otatools_zip, otatools)
+
+ # get framework and vendor target files
+ matched_framework_target_files = glob.glob(args.framework_target_files_zip)
+ if not matched_framework_target_files:
+ raise ValueError('framework target files zip '
+ f'{args.framework_target_files_zip} not found.')
+ matched_vendor_target_files = glob.glob(args.vendor_target_files_zip)
+ if not matched_vendor_target_files:
+ raise ValueError('vendor target files zip '
+ f'{args.vendor_target_files_zip} not found.')
+
+ # merge target files
+ framework_target_files = matched_framework_target_files[0]
+ vendor_target_files = matched_vendor_target_files[0]
+ merged_target_files = os.path.join(
+ args.output_dir,
+ f'{args.target}-target_files-{args.build_id}.zip')
+ command = [
+ os.path.join(otatools, 'bin', 'merge_target_files'),
+ '--path', otatools,
+ '--framework-target-files', framework_target_files,
+ '--vendor-target-files', vendor_target_files,
+ '--output-target-files', merged_target_files,
+ '--avb-resolve-rollback-index-location-conflict'
+ ]
+ subprocess.run(command, check=True)
+
+ # create images from the merged target files
+ img_zip_path = os.path.join(args.output_dir,
+ f'{args.target}-img-{args.build_id}.zip')
+ command = [
+ os.path.join(otatools, 'bin', 'img_from_target_files'),
+ merged_target_files,
+ img_zip_path]
+ subprocess.run(command, check=True)
+
+ # copy files
+ copy_files(args.copy_file, args.output_dir)
+
+
+if __name__ == '__main__':
+ with tempfile.TemporaryDirectory() as temp_dir:
+ run(temp_dir)
diff --git a/cuttlefish/build_chd_utils.py b/cuttlefish/build_chd_utils.py
new file mode 100644
index 0000000..77a8eff
--- /dev/null
+++ b/cuttlefish/build_chd_utils.py
@@ -0,0 +1,78 @@
+#!/usr/bin/python3
+#
+# Copyright (C) 2023 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.
+
+import glob
+import os
+import shutil
+import zipfile
+
+
+def unzip_otatools(otatools_zip_path, output_dir):
+ """Unzip otatools to a directory and set the permissions for execution.
+
+ Args:
+ otatools_zip_path: The path to otatools zip archive.
+ output_dir: The root directory of the unzip output.
+ """
+ with zipfile.ZipFile(otatools_zip_path, 'r') as zf:
+ zf.extractall(path=output_dir)
+
+ for f in glob.glob(os.path.join(output_dir, 'bin', '*')):
+ os.chmod(f, 0o777)
+
+
+def _parse_copy_file_pair(copy_file_pair):
+ """Convert a string to a source path and a destination path.
+
+ Args:
+ copy_file_pair: A string in the format of <src glob pattern>:<dst path>.
+
+ Returns:
+ The source path and the destination path.
+
+ Raises:
+ ValueError if the input string is in a wrong format.
+ """
+ split_pair = copy_file_pair.split(':', 1)
+ if len(split_pair) != 2:
+ raise ValueError(f'{copy_file_pair} is not a <src>:<dst> pair.')
+ src_list = glob.glob(split_pair[0])
+ if len(src_list) != 1:
+ raise ValueError(f'{copy_file_pair} has more than one matched src files: '
+ f'{" ".join(src_list)}.')
+ return src_list[0], split_pair[1]
+
+
+def copy_files(copy_files_list, output_dir):
+ """Copy files to the output directory.
+
+ Args:
+ copy_files_list: A list of copy file pairs, where a pair defines the src
+ glob pattern and the dst path.
+ output_dir: The root directory of the copy dst.
+
+ Raises:
+ FileExistsError if the dst file already exists.
+ """
+ for pair in copy_files_list:
+ src, dst = _parse_copy_file_pair(pair)
+ # this line does not change dst if dst is absolute.
+ dst = os.path.join(output_dir, dst)
+ os.makedirs(os.path.dirname(dst), exist_ok=True)
+ print(f'Copying {src} to {dst}')
+ if os.path.exists(dst):
+ raise FileExistsError(dst)
+ shutil.copyfile(src, dst)