blob: aa2b3953d7553bcd842d39f37ef7f1297aa6a035 [file] [log] [blame]
Kuang-che Wu708310b2018-03-28 17:24:34 +08001# Copyright 2018 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4"""Android utility.
5
6Terminology used in this module:
7 "product-variant" is sometimes called "target" and sometimes "flavor".
8 I prefer to use "flavor" in the code because
9 - "target" is too general
10 - sometimes, it is not in the form of "product-variant", for example,
11 "cts_arm_64"
12"""
13
14from __future__ import print_function
15import json
16import logging
17import os
18
19from bisect_kit import cli
20from bisect_kit import util
21
22logger = logging.getLogger(__name__)
23
24ANDROID_URL_BASE = ('https://android-build.googleplex.com/'
25 'builds/branch/{branch}')
26BUILD_IDS_BETWEEN_URL_TEMPLATE = (
27 ANDROID_URL_BASE + '/build-ids/between/{end}/{start}')
28BUILD_INFO_URL_TEMPLATE = ANDROID_URL_BASE + '/builds?id={build_id}'
29
30
31def is_android_build_id(s):
32 """Is an Android build id."""
33 # Build ID is always a number
34 return s.isdigit()
35
36
37def argtype_android_build_id(s):
38 if not is_android_build_id(s):
39 msg = 'invalid android build id (%s)' % s
40 raise cli.ArgTypeError(msg, '9876543')
41 return s
42
43
44def fetch_android_build_data(url):
45 """Fetches file from android build server.
46
47 Args:
48 url: URL to fetch
49
50 Returns:
51 file content (str). None if failed.
52 """
53 # Fetching android build data directly will fail without authetication.
54 # Following code is just serving as demo purpose. You should modify or hook
55 # it with your own implementation.
56 logger.warn('fetch_android_build_data need to be hooked')
57 import urllib2
58 try:
59 return urllib2.urlopen(url).read()
60 except urllib2.URLError:
61 logger.exception('failed to fetch "%s"', url)
62 raise
63
64
65def is_good_build(branch, flavor, build_id):
66 """Determine a build_id was succeeded.
67
68 Args:
69 branch: The Android branch from which to retrieve the builds.
70 flavor: Target name of the Android image in question.
71 E.g. "cheets_x86-userdebug" or "cheets_arm-user".
72 build_id: Android build id
73
74 Returns:
75 True if the given build was successful.
76 """
77
78 url = BUILD_INFO_URL_TEMPLATE.format(branch=branch, build_id=build_id)
79 build = json.loads(fetch_android_build_data(url).decode('utf-8'))
80 for target in build[0]['targets']:
81 if target['target']['name'] == flavor and target.get('successful'):
82 return True
83 return False
84
85
86def get_build_ids_between(branch, start, end):
87 """Returns a list of build IDs.
88
89 Args:
90 branch: The Android branch from which to retrieve the builds.
91 start: The starting point build ID. (inclusive)
92 end: The ending point build ID. (inclusive)
93
94 Returns:
95 A list of build IDs. (str)
96 """
97 # TODO(kcwu): remove pagination hack after b/68239878 fixed
98 build_id_set = set()
99 tmp_end = end
100 while True:
101 url = BUILD_IDS_BETWEEN_URL_TEMPLATE.format(
102 branch=branch, start=start, end=tmp_end)
103 query_result = json.loads(fetch_android_build_data(url).decode('utf-8'))
104 found_new = set(map(int, query_result['ids'])) - build_id_set
105 if not found_new:
106 break
107 build_id_set.update(found_new)
108 tmp_end = min(build_id_set)
109
110 logger.debug('Found %d builds in the range.', len(build_id_set))
111
112 return map(str, sorted(build_id_set))
113
114
115def lunch(android_root, flavor, *args, **kwargs):
116 """Helper to run commands with Android lunch env.
117
118 Args:
119 android_root: root path of Android tree
120 flavor: lunch flavor
121 args: command to run
122 kwargs: extra arguments passed to util.Popen
123 """
124 util.check_call('./android_lunch_helper.sh', android_root, flavor, *args,
125 **kwargs)
126
127
128def fetch_artifact(flavor, build_id, filename, dest):
129 """Fetches Android build artifact.
130
131 Args:
132 flavor: Android build flavor
133 build_id: Android build id
134 filename: artifact name
135 dest: local path to store the fetched artifact
136 """
137 util.check_call('/google/data/ro/projects/android/fetch_artifact', '--target',
138 flavor, '--bid', build_id, filename, dest)
139
140
141def fetch_manifest(android_root, flavor, build_id):
142 """Fetches Android repo manifest of given build.
143
144 Args:
145 android_root: root path of Android tree. Fetched manifest file will be
146 stored inside.
147 flavor: Android build flavor
148 build_id: Android build id
149
150 Returns:
151 the local filename of manifest (relative to android_root/.repo/manifests/)
152 """
153 # Assume manifest is flavor independent, thus not encoded into the file name.
154 manifest_name = 'manifest_%s.xml' % build_id
155 manifest_path = os.path.join(android_root, '.repo', 'manifests',
156 manifest_name)
157 if not os.path.exists(manifest_path):
158 fetch_artifact(flavor, build_id, manifest_name, manifest_path)
159 return manifest_name