bisect-kit: migrate android bisector to use codechange module
After this CL, android localbuild bisector can handle following issues:
- add and remove repo projects
- git history incomplete due to <project clone-depth="1">
- manifest snapshot racing
The setup step of android checkout is changed as well. Now you have to
make a repo mirror and sync the tree from the mirror. The exact steps
are:
1. cd $ANDROID_REPO_MIRROR_DIR
repo init ...<original flags>... --mirror
repo sync -c
2. cd $ANDROID_ROOT
repo init ...<original flags>... --reference=$ANDROID_REPO_MIRROR_DIR
repo sync -c
3. specify --android_repo_mirror_dir $ANDROID_REPO_MIRROR_DIR when you
use bisect_android_repo.py and switch_arc_localbuild.py
BUG=None
TEST=unit test and following commands
$ ./bisect_android_repo.py init \
--old 4851505 --new 4852106 --dut $DUT \
--android_root $ANDROID_ROOT \
--android_repo_mirror_dir $ANDROID_REPO_MIRROR_DIR
$ ./bisect_android_repo.py view
$ ./switch_arc_localbuild.py \
--android_root $ANDROID_ROOT \
--android_repo_mirror_dir $ANDROID_REPO_MIRROR_DIR \
$DUT 4851505~4852106/1
Change-Id: I2708c119e328ec294a02a45bb3a7710ef1a603c5
Reviewed-on: https://chromium-review.googlesource.com/1126182
Commit-Ready: Kuang-che Wu <kcwu@chromium.org>
Tested-by: Kuang-che Wu <kcwu@chromium.org>
Reviewed-by: Chung-yih Wang <cywang@chromium.org>
Reviewed-by: Chi-Ngai Wan <cnwan@google.com>
diff --git a/bisect_kit/android_util.py b/bisect_kit/android_util.py
index e42f5a2..524a4d9 100644
--- a/bisect_kit/android_util.py
+++ b/bisect_kit/android_util.py
@@ -16,8 +16,11 @@
import json
import logging
import os
+import tempfile
from bisect_kit import cli
+from bisect_kit import codechange
+from bisect_kit import git_util
from bisect_kit import repo_util
from bisect_kit import util
@@ -161,30 +164,90 @@
return manifest_name
-class AndroidManifestManager(repo_util.ManifestManager):
- """Manifest operations for Android repo"""
+def lookup_build_timestamp(flavor, build_id):
+ """Lookup timestamp of Android prebuilt.
+
+ Args:
+ flavor: Android build flavor
+ build_id: Android build id
+
+ Returns:
+ timestamp
+ """
+ tmp_fn = tempfile.mktemp()
+ try:
+ fetch_artifact(flavor, build_id, 'BUILD_INFO', tmp_fn)
+ data = json.load(open(tmp_fn))
+ return int(data['sync_start_time'])
+ finally:
+ if os.path.exists(tmp_fn):
+ os.unlink(tmp_fn)
+
+
+class AndroidSpecManager(codechange.SpecManager):
+ """Repo manifest related operations.
+
+ This class fetches and enumerates android manifest files, parses them,
+ and sync to disk state according to them.
+ """
def __init__(self, config):
self.config = config
- self.flavor = config['flavor']
+ self.manifest_dir = os.path.join(self.config['android_root'], '.repo',
+ 'manifests')
+
+ def collect_float_spec(self, old, new):
+ result = []
+ path = 'default.xml'
+
+ commits = []
+ old_timestamp = lookup_build_timestamp(self.config['flavor'], old)
+ new_timestamp = lookup_build_timestamp(self.config['flavor'], new)
+ for timestamp, git_rev in git_util.get_history(self.manifest_dir, path):
+ if timestamp < old_timestamp:
+ commits = []
+ commits.append((timestamp, git_rev))
+ if timestamp > new_timestamp:
+ break
+
+ for timestamp, git_rev in commits:
+ result.append(
+ codechange.Spec(codechange.SPEC_FLOAT, git_rev, timestamp, path))
+ return result
+
+ def collect_fixed_spec(self, old, new):
+ result = []
+ revlist = get_build_ids_between(self.config['branch'], int(old), int(new))
+ for rev in revlist:
+ manifest_name = fetch_manifest(self.config['android_root'],
+ self.config['flavor'], rev)
+ path = os.path.join(self.manifest_dir, manifest_name)
+ timestamp = lookup_build_timestamp(self.config['flavor'], rev)
+ result.append(
+ codechange.Spec(codechange.SPEC_FIXED, rev, timestamp, path))
+ return result
+
+ def _load_manifest_content(self, spec):
+ if spec.spec_type == codechange.SPEC_FIXED:
+ manifest_name = fetch_manifest(self.config['branch'],
+ self.config['flavor'], spec.name)
+ return open(os.path.join(self.manifest_dir, manifest_name)).read()
+ else:
+ return git_util.get_file_from_revision(self.manifest_dir, spec.name,
+ spec.path)
+
+ def parse_spec(self, spec):
+ logging.debug('parse_spec %s', spec.name)
+ manifest_content = self._load_manifest_content(spec)
+ manifest_url = repo_util.get_manifest_url(self.config['android_root'])
+ spec.entries = repo_util.parse_manifest(manifest_content, manifest_url)
+ if spec.spec_type == codechange.SPEC_FIXED:
+ assert spec.is_static()
def sync_disk_state(self, rev):
- manifest_name = self.fetch_manifest(rev)
+ manifest_name = fetch_manifest(self.config['android_root'],
+ self.config['flavor'], rev)
repo_util.sync(
self.config['android_root'],
manifest_name=manifest_name,
current_branch=True)
-
- def fetch_git_repos(self, rev):
- # TODO(kcwu): fetch git history but don't switch current disk state.
- self.sync_disk_state(rev)
-
- def enumerate_manifest(self, old, new):
- revlist = get_build_ids_between(self.config['branch'], int(old), int(new))
-
- assert revlist[0] == old
- assert revlist[-1] == new
- return revlist
-
- def fetch_manifest(self, rev):
- return fetch_manifest(self.config['android_root'], self.flavor, rev)