File: repo_diff_android.py

package info (click to toggle)
android-platform-tools 34.0.5-12
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 150,900 kB
  • sloc: cpp: 805,786; java: 293,500; ansic: 128,288; xml: 127,491; python: 41,481; sh: 14,245; javascript: 9,665; cs: 3,846; asm: 2,049; makefile: 1,917; yacc: 440; awk: 368; ruby: 183; sql: 140; perl: 88; lex: 67
file content (211 lines) | stat: -rwxr-xr-x 6,679 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
#!/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()