You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
212 lines
6.5 KiB
212 lines
6.5 KiB
#!/usr/bin/python
|
|
"""Diff a repo (downstream) and its upstream.
|
|
|
|
This script:
|
|
1. Downloads a repo source tree with specified manifest URL, branch
|
|
and release tag.
|
|
2. Retrieves the BUILD_ID from $downstream/build/core/build_id.mk.
|
|
3. Downloads the upstream using the BUILD_ID.
|
|
4. Diffs each project in these two repos.
|
|
"""
|
|
|
|
import argparse
|
|
import datetime
|
|
import os
|
|
import subprocess
|
|
import repo_diff_trees
|
|
|
|
HELP_MSG = "Diff a repo (downstream) and its upstream"
|
|
|
|
DOWNSTREAM_WORKSPACE = "downstream"
|
|
UPSTREAM_WORKSPACE = "upstream"
|
|
|
|
DEFAULT_MANIFEST_URL = "https://android.googlesource.com/platform/manifest"
|
|
DEFAULT_MANIFEST_BRANCH = "android-8.0.0_r10"
|
|
DEFAULT_UPSTREAM_MANIFEST_URL = "https://android.googlesource.com/platform/manifest"
|
|
DEFAULT_UPSTREAM_MANIFEST_BRANCH = "android-8.0.0_r1"
|
|
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
DEFAULT_EXCLUSIONS_FILE = os.path.join(SCRIPT_DIR, "android_exclusions.txt")
|
|
|
|
|
|
def parse_args():
|
|
"""Parse args."""
|
|
|
|
parser = argparse.ArgumentParser(description=HELP_MSG)
|
|
|
|
parser.add_argument("-u", "--manifest-url",
|
|
help="manifest url",
|
|
default=DEFAULT_MANIFEST_URL)
|
|
parser.add_argument("-b", "--manifest-branch",
|
|
help="manifest branch",
|
|
default=DEFAULT_MANIFEST_BRANCH)
|
|
parser.add_argument("-r", "--upstream-manifest-url",
|
|
help="upstream manifest url",
|
|
default=DEFAULT_UPSTREAM_MANIFEST_URL)
|
|
parser.add_argument("-a", "--upstream-manifest-branch",
|
|
help="upstream manifest branch",
|
|
default=DEFAULT_UPSTREAM_MANIFEST_BRANCH)
|
|
parser.add_argument("-e", "--exclusions-file",
|
|
help="exclusions file",
|
|
default=DEFAULT_EXCLUSIONS_FILE)
|
|
parser.add_argument("-t", "--tag",
|
|
help="release tag (optional). If not set then will "
|
|
"sync the latest in the branch.")
|
|
parser.add_argument("-i", "--ignore_error_during_sync",
|
|
action="store_true",
|
|
help="repo sync might fail due to varios reasons. "
|
|
"Ignore these errors and move on. Use with caution.")
|
|
|
|
return parser.parse_args()
|
|
|
|
|
|
def repo_init(url, rev, workspace):
|
|
"""Repo init with specific url and rev.
|
|
|
|
Args:
|
|
url: manifest url
|
|
rev: manifest branch, or rev
|
|
workspace: the folder to init and sync code
|
|
"""
|
|
|
|
try:
|
|
subprocess.check_output("repo", stderr=subprocess.PIPE,
|
|
cwd=os.path.dirname(workspace), shell=True)
|
|
except subprocess.CalledProcessError:
|
|
pass
|
|
else:
|
|
raise ValueError("cannot repo-init workspace (%s), workspace is within an "
|
|
"existing tree" % workspace)
|
|
|
|
print("repo init:\n url: %s\n rev: %s\n workspace: %s" %
|
|
(url, rev, workspace))
|
|
|
|
subprocess.check_output("repo init --manifest-url=%s --manifest-branch=%s" %
|
|
(url, rev), cwd=workspace, shell=True)
|
|
|
|
|
|
def repo_sync(workspace, ignore_error, retry=5):
|
|
"""Repo sync."""
|
|
|
|
count = 0
|
|
while count < retry:
|
|
count += 1
|
|
print("repo sync (retry=%d/%d):\n workspace: %s" %
|
|
(count, retry, workspace))
|
|
|
|
try:
|
|
command = "repo sync --jobs=24 --current-branch --quiet"
|
|
command += " --no-tags --no-clone-bundle"
|
|
if ignore_error:
|
|
command += " --force-broken"
|
|
subprocess.check_output(command, cwd=workspace, shell=True)
|
|
except subprocess.CalledProcessError as e:
|
|
print "Error: %s" % e.output
|
|
if count == retry and not ignore_error:
|
|
raise e
|
|
# Stop retrying if the repo sync was successful
|
|
else:
|
|
break
|
|
|
|
|
|
def get_commit_with_keyword(project_path, keyword):
|
|
"""Get the latest commit in $project_path with the specific keyword."""
|
|
|
|
return subprocess.check_output(("git -C %s "
|
|
"rev-list --max-count=1 --grep=\"%s\" "
|
|
"HEAD") %
|
|
(project_path, keyword), shell=True).rstrip()
|
|
|
|
|
|
def get_build_id(workspace):
|
|
"""Get BUILD_ID defined in $workspace/build/core/build_id.mk."""
|
|
|
|
path = os.path.join(workspace, "build", "core", "build_id.mk")
|
|
return subprocess.check_output("source %s && echo $BUILD_ID" % path,
|
|
shell=True).rstrip()
|
|
|
|
|
|
def repo_sync_specific_release(url, branch, tag, workspace, ignore_error):
|
|
"""Repo sync source with the specific release tag."""
|
|
|
|
if not os.path.exists(workspace):
|
|
os.makedirs(workspace)
|
|
|
|
manifest_path = os.path.join(workspace, ".repo", "manifests")
|
|
|
|
repo_init(url, branch, workspace)
|
|
|
|
if tag:
|
|
rev = get_commit_with_keyword(manifest_path, tag)
|
|
if not rev:
|
|
raise(ValueError("could not find a manifest revision for tag " + tag))
|
|
repo_init(url, rev, workspace)
|
|
|
|
repo_sync(workspace, ignore_error)
|
|
|
|
|
|
def diff(manifest_url, manifest_branch, tag,
|
|
upstream_manifest_url, upstream_manifest_branch,
|
|
exclusions_file, ignore_error_during_sync):
|
|
"""Syncs and diffs an Android workspace against an upstream workspace."""
|
|
|
|
workspace = os.path.abspath(DOWNSTREAM_WORKSPACE)
|
|
upstream_workspace = os.path.abspath(UPSTREAM_WORKSPACE)
|
|
# repo sync downstream source tree
|
|
repo_sync_specific_release(
|
|
manifest_url,
|
|
manifest_branch,
|
|
tag,
|
|
workspace,
|
|
ignore_error_during_sync)
|
|
|
|
build_id = None
|
|
|
|
if tag:
|
|
# get the build_id so that we know which rev of upstream we need
|
|
build_id = get_build_id(workspace)
|
|
if not build_id:
|
|
raise(ValueError("Error: could not find the Build ID of " + workspace))
|
|
|
|
# repo sync upstream source tree
|
|
repo_sync_specific_release(
|
|
upstream_manifest_url,
|
|
upstream_manifest_branch,
|
|
build_id,
|
|
upstream_workspace,
|
|
ignore_error_during_sync)
|
|
|
|
|
|
# make output folder
|
|
if tag:
|
|
output_folder = os.path.abspath(tag.replace(" ", "_"))
|
|
else:
|
|
current_time = datetime.datetime.today().strftime('%Y%m%d_%H%M%S')
|
|
output_folder = os.path.abspath(current_time)
|
|
|
|
if not os.path.exists(output_folder):
|
|
os.makedirs(output_folder)
|
|
|
|
# do the comparison
|
|
repo_diff_trees.diff(
|
|
upstream_workspace,
|
|
workspace,
|
|
os.path.join(output_folder, "project.csv"),
|
|
os.path.join(output_folder, "commit.csv"),
|
|
os.path.abspath(exclusions_file),
|
|
)
|
|
|
|
|
|
def main():
|
|
args = parse_args()
|
|
|
|
diff(args.manifest_url,
|
|
args.manifest_branch,
|
|
args.tag,
|
|
args.upstream_manifest_url,
|
|
args.upstream_manifest_branch,
|
|
args.exclusions_file,
|
|
args.ignore_error_during_sync)
|
|
|
|
if __name__ == "__main__":
|
|
main()
|