blob: e1430f816035f466d543d50dc3036916226a6a85 [file] [log] [blame]
Mike Frysingere58c0e22017-10-04 15:43:30 -04001# -*- coding: utf-8 -*-
David Rileyc0da9d92016-02-01 12:11:01 -08002# Copyright 2016 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""This module uprevs Android for cbuildbot.
7
8After calling, it prints outs ANDROID_VERSION_ATOM=(version atom string). A
9caller could then use this atom with emerge to build the newly uprevved version
10of Android e.g.
11
Shuhei Takahashi6d02c192017-04-05 14:01:24 +090012./cros_mark_android_as_stable \
Shao-Chuan Lee9c39e0c2020-04-24 11:40:34 +090013 --android_build_branch=git_pi-arc \
14 --android_package=android-container-pi
Shuhei Takahashi6d02c192017-04-05 14:01:24 +090015
Shao-Chuan Lee9c39e0c2020-04-24 11:40:34 +090016Returns chromeos-base/android-container-pi-6417892-r1
David Rileyc0da9d92016-02-01 12:11:01 -080017
Shao-Chuan Lee9c39e0c2020-04-24 11:40:34 +090018emerge-eve =chromeos-base/android-container-pi-6417892-r1
David Rileyc0da9d92016-02-01 12:11:01 -080019"""
20
21from __future__ import print_function
22
23import filecmp
24import glob
Junichi Uekawad21f94d2020-07-27 15:50:05 +090025import json
David Rileyc0da9d92016-02-01 12:11:01 -080026import os
Mike Frysinger00a02292020-04-19 06:28:03 -040027import sys
David Rileyc0da9d92016-02-01 12:11:01 -080028
Aviv Keshetb7519e12016-10-04 00:50:00 -070029from chromite.lib import constants
David Rileyc0da9d92016-02-01 12:11:01 -080030from chromite.lib import commandline
31from chromite.lib import cros_build_lib
32from chromite.lib import cros_logging as logging
33from chromite.lib import git
34from chromite.lib import gs
Shao-Chuan Lee301a4192021-02-08 11:53:49 +090035from chromite.lib import osutils
David Rileyc0da9d92016-02-01 12:11:01 -080036from chromite.lib import portage_util
Shao-Chuan Lee007dbe82021-02-09 14:05:39 +090037from chromite.lib import repo_util
David Rileyc0da9d92016-02-01 12:11:01 -080038from chromite.scripts import cros_mark_as_stable
Shao-Chuan Lee7fd0ca12021-03-19 15:57:40 +090039from chromite.service import android
David Rileyc0da9d92016-02-01 12:11:01 -080040
41
Mike Frysinger00a02292020-04-19 06:28:03 -040042assert sys.version_info >= (3, 6), 'This module requires Python 3.6+'
43
44
David Rileyc0da9d92016-02-01 12:11:01 -080045# Dir where all the action happens.
46_OVERLAY_DIR = '%(srcroot)s/private-overlays/project-cheets-private/'
47
Junichi Uekawa6d61ab02020-04-15 14:52:28 +090048_GIT_COMMIT_MESSAGE = """Marking latest for %(android_package)s ebuild with \
49version %(android_version)s as stable.
50
51BUG=None
52TEST=CQ
53"""
David Rileyc0da9d92016-02-01 12:11:01 -080054
khmel@chromium.orgd3ec3d72020-04-29 15:57:35 -070055_RUNTIME_ARTIFACTS_BUCKET_URL = 'gs://chromeos-arc-images/runtime_artifacts'
David Rileyc0da9d92016-02-01 12:11:01 -080056
David Rileyc0da9d92016-02-01 12:11:01 -080057
58def FindAndroidCandidates(package_dir):
59 """Return a tuple of Android's unstable ebuild and stable ebuilds.
60
61 Args:
62 package_dir: The path to where the package ebuild is stored.
63
64 Returns:
65 Tuple [unstable_ebuild, stable_ebuilds].
66
67 Raises:
68 Exception: if no unstable ebuild exists for Android.
69 """
70 stable_ebuilds = []
71 unstable_ebuilds = []
72 for path in glob.glob(os.path.join(package_dir, '*.ebuild')):
73 ebuild = portage_util.EBuild(path)
74 if ebuild.version == '9999':
75 unstable_ebuilds.append(ebuild)
76 else:
77 stable_ebuilds.append(ebuild)
78
79 # Apply some sanity checks.
80 if not unstable_ebuilds:
81 raise Exception('Missing 9999 ebuild for %s' % package_dir)
82 if not stable_ebuilds:
Lann Martinffb95162018-08-28 12:02:54 -060083 logging.warning('Missing stable ebuild for %s', package_dir)
David Rileyc0da9d92016-02-01 12:11:01 -080084
85 return portage_util.BestEBuild(unstable_ebuilds), stable_ebuilds
86
87
Shao-Chuan Lee085e3d72020-05-11 16:00:42 +090088def PrintUprevMetadata(build_branch, stable_candidate, new_ebuild):
89 """Shows metadata on buildbot page at UprevAndroid step.
David Rileyc0da9d92016-02-01 12:11:01 -080090
91 Args:
Shao-Chuan Lee085e3d72020-05-11 16:00:42 +090092 build_branch: The branch of Android builds.
93 stable_candidate: The existing stable ebuild.
94 new_ebuild: The newly written ebuild.
David Rileyc0da9d92016-02-01 12:11:01 -080095 """
Shao-Chuan Lee085e3d72020-05-11 16:00:42 +090096 # Examples:
97 # "android-container-pi revved 6461825-r1 -> 6468247-r1"
98 # "android-container-pi revved 6461825-r1 -> 6461825-r2 (ebuild update only)"
99 msg = '%s revved %s -> %s' % (stable_candidate.pkgname,
100 stable_candidate.version,
101 new_ebuild.version)
102
103 old_android = stable_candidate.version_no_rev
104 new_android = new_ebuild.version_no_rev
105
106 if old_android == new_android:
107 msg += ' (ebuild update only)'
108 else:
109 ab_link = ('https://android-build.googleplex.com'
110 '/builds/%s/branches/%s/cls?end=%s'
111 % (new_android, build_branch, old_android))
112 logging.PrintBuildbotLink('Android changelog', ab_link)
113
114 logging.PrintBuildbotStepText(msg)
Junichi Uekawad21f94d2020-07-27 15:50:05 +0900115 logging.PrintKitchenSetBuildProperty('android_uprev', json.dumps({
116 'branch': build_branch,
117 'new': new_ebuild.version,
118 'old': stable_candidate.version,
119 'pkgname': stable_candidate.pkgname,
120 }))
David Rileyc0da9d92016-02-01 12:11:01 -0800121
122
Yury Khmelb009aeb2020-08-19 19:40:00 -0700123def FindDataCollectorArtifacts(gs_context,
124 android_version,
125 runtime_artifacts_bucket_url,
126 version_reference):
khmel@chromium.orgd3ec3d72020-04-29 15:57:35 -0700127 r"""Finds and includes into variables artifacts from arc.DataCollector.
128
Yury Khmelb009aeb2020-08-19 19:40:00 -0700129 This is used from UpdateDataCollectorArtifacts in order to check the
130 particular version.
131
khmel@chromium.orgd3ec3d72020-04-29 15:57:35 -0700132 Args:
Yury Khmelb009aeb2020-08-19 19:40:00 -0700133 gs_context: context to execute gsutil
khmel@chromium.orgd3ec3d72020-04-29 15:57:35 -0700134 android_version: The \d+ build id of Android.
135 runtime_artifacts_bucket_url: root of runtime artifacts
Yury Khmelb009aeb2020-08-19 19:40:00 -0700136 build_branch: build branch. Used to determine the pinned version if exists.
137 version_reference: which version to use as a reference. Could be '${PV}' in
138 case version of data collector artifacts matches the
139 Android version or direct version in case of override.
khmel@chromium.orgd3ec3d72020-04-29 15:57:35 -0700140
141 Returns:
Yury Khmelb009aeb2020-08-19 19:40:00 -0700142 dictionary with filled ebuild variables. This dictionary is empty in case
143 no artificats are found.
khmel@chromium.orgd3ec3d72020-04-29 15:57:35 -0700144 """
khmel@chromium.orgd3ec3d72020-04-29 15:57:35 -0700145 variables = {}
Yury Khmelb009aeb2020-08-19 19:40:00 -0700146
khmel@chromium.orgd3ec3d72020-04-29 15:57:35 -0700147 buckets = ['ureadahead_pack', 'gms_core_cache']
148 archs = ['arm', 'arm64', 'x86', 'x86_64']
149 build_types = ['user', 'userdebug']
150
khmel@chromium.orgd3ec3d72020-04-29 15:57:35 -0700151 for bucket in buckets:
152 for arch in archs:
153 for build_type in build_types:
154 path = (f'{runtime_artifacts_bucket_url}/{bucket}_{arch}_{build_type}_'
Yury Khmele1b74402020-05-18 08:41:35 -0700155 f'{android_version}.tar')
khmel@chromium.orgd3ec3d72020-04-29 15:57:35 -0700156 if gs_context.Exists(path):
157 variables[(f'{arch}_{build_type}_{bucket}').upper()] = (
158 f'{runtime_artifacts_bucket_url}/{bucket}_{arch}_{build_type}_'
159 f'{version_reference}.tar')
160
161 return variables
162
163
Yury Khmelb009aeb2020-08-19 19:40:00 -0700164def UpdateDataCollectorArtifacts(android_version,
165 runtime_artifacts_bucket_url,
166 build_branch):
167 r"""Finds and includes into variables artifacts from arc.DataCollector.
168
169 This verifies default android version. In case artificts are not found for
170 default Android version it tries to find artifacts for pinned version. If
171 pinned version is provided, it is required artifacts exist for the pinned
172 version.
173
174 Args:
175 android_version: The \d+ build id of Android.
176 runtime_artifacts_bucket_url: root of runtime artifacts
177 build_branch: build branch. Used to determine the pinned version if exists.
178
179 Returns:
180 dictionary with filled ebuild variables.
181 """
182
183 gs_context = gs.GSContext()
184 # Check the existing version. If we find any artifacts, use them.
185 variables = FindDataCollectorArtifacts(gs_context,
186 android_version,
187 runtime_artifacts_bucket_url,
188 '${PV}')
189 if variables:
190 # Data artificts were found.
191 return variables
192
193 # Check pinned version for the current branch.
194 pin_path = (f'{runtime_artifacts_bucket_url}/{build_branch}_pin_version')
195 if not gs_context.Exists(pin_path):
196 # No pinned version.
197 logging.warning(
198 'No data collector artifacts were found for %s',
199 android_version)
200 return variables
201
202 pin_version = gs_context.Cat(pin_path, encoding='utf-8').rstrip()
203 logging.info('Pinned version %s overrides %s',
204 pin_version, android_version)
205 variables = FindDataCollectorArtifacts(gs_context,
206 pin_version,
207 runtime_artifacts_bucket_url,
208 pin_version)
209 if not variables:
210 # If pin version set it must contain data.
211 raise Exception('Pinned version %s:%s does not contain artificats' % (
212 build_branch, pin_version))
213
214 return variables
215
216
Hidehiko Abe4fd94ae2017-01-24 18:59:55 +0900217def MarkAndroidEBuildAsStable(stable_candidate, unstable_ebuild,
218 android_package, android_version, package_dir,
khmel@chromium.orgd3ec3d72020-04-29 15:57:35 -0700219 build_branch, arc_bucket_url,
Shao-Chuan Lee7e9eacb2021-03-19 16:45:21 +0900220 runtime_artifacts_bucket_url):
David Rileyc0da9d92016-02-01 12:11:01 -0800221 r"""Uprevs the Android ebuild.
222
223 This is the main function that uprevs from a stable candidate
224 to its new version.
225
226 Args:
227 stable_candidate: ebuild that corresponds to the stable ebuild we are
228 revving from. If None, builds the a new ebuild given the version
229 with revision set to 1.
230 unstable_ebuild: ebuild corresponding to the unstable ebuild for Android.
Hidehiko Abe4fd94ae2017-01-24 18:59:55 +0900231 android_package: android package name.
David Rileyc0da9d92016-02-01 12:11:01 -0800232 android_version: The \d+ build id of Android.
David Rileyc0da9d92016-02-01 12:11:01 -0800233 package_dir: Path to the android-container package dir.
David Riley73f00d92016-02-16 18:54:20 -0800234 build_branch: branch of Android builds.
235 arc_bucket_url: URL of the target ARC build gs bucket.
khmel@chromium.orgd3ec3d72020-04-29 15:57:35 -0700236 runtime_artifacts_bucket_url: root of runtime artifacts
David Rileyc0da9d92016-02-01 12:11:01 -0800237
238 Returns:
Shao-Chuan Lee301a4192021-02-08 11:53:49 +0900239 Tuple[str, List[str], List[str]] if revved, or None
240 1. Full portage version atom (including rc's, etc) that was revved.
241 2. List of files to be `git add`ed.
242 3. List of files to be `git rm`ed.
David Rileyc0da9d92016-02-01 12:11:01 -0800243 """
244 def IsTheNewEBuildRedundant(new_ebuild, stable_ebuild):
245 """Returns True if the new ebuild is redundant.
246
247 This is True if there if the current stable ebuild is the exact same copy
248 of the new one.
249 """
250 if not stable_ebuild:
251 return False
252
David Riley676f5402016-02-12 17:24:23 -0800253 if stable_candidate.version_no_rev == new_ebuild.version_no_rev:
David Rileyc0da9d92016-02-01 12:11:01 -0800254 return filecmp.cmp(
255 new_ebuild.ebuild_path, stable_ebuild.ebuild_path, shallow=False)
khmel@chromium.orgd3ec3d72020-04-29 15:57:35 -0700256 return False
David Rileyc0da9d92016-02-01 12:11:01 -0800257
258 # Case where we have the last stable candidate with same version just rev.
David Riley676f5402016-02-12 17:24:23 -0800259 if stable_candidate and stable_candidate.version_no_rev == android_version:
David Rileyc0da9d92016-02-01 12:11:01 -0800260 new_ebuild_path = '%s-r%d.ebuild' % (
261 stable_candidate.ebuild_path_no_revision,
262 stable_candidate.current_revision + 1)
263 else:
Hidehiko Abe4fd94ae2017-01-24 18:59:55 +0900264 pf = '%s-%s-r1' % (android_package, android_version)
David Rileyc0da9d92016-02-01 12:11:01 -0800265 new_ebuild_path = os.path.join(package_dir, '%s.ebuild' % pf)
266
Shao-Chuan Lee7e9eacb2021-03-19 16:45:21 +0900267 build_targets = constants.ANDROID_BRANCH_TO_BUILD_TARGETS[build_branch]
David Riley73f00d92016-02-16 18:54:20 -0800268 variables = {'BASE_URL': arc_bucket_url}
Mike Frysinger0bdbc102019-06-13 15:27:29 -0400269 for build, (target, _) in build_targets.items():
David Riley73f00d92016-02-16 18:54:20 -0800270 variables[build + '_TARGET'] = '%s-%s' % (build_branch, target)
David Rileyc0da9d92016-02-01 12:11:01 -0800271
khmel@chromium.orgd3ec3d72020-04-29 15:57:35 -0700272 variables.update(UpdateDataCollectorArtifacts(
Yury Khmelb009aeb2020-08-19 19:40:00 -0700273 android_version, runtime_artifacts_bucket_url, build_branch))
khmel@chromium.orgd3ec3d72020-04-29 15:57:35 -0700274
David Rileyc0da9d92016-02-01 12:11:01 -0800275 portage_util.EBuild.MarkAsStable(
276 unstable_ebuild.ebuild_path, new_ebuild_path,
277 variables, make_stable=True)
278 new_ebuild = portage_util.EBuild(new_ebuild_path)
279
280 # Determine whether this is ebuild is redundant.
281 if IsTheNewEBuildRedundant(new_ebuild, stable_candidate):
282 msg = 'Previous ebuild with same version found and ebuild is redundant.'
283 logging.info(msg)
Shao-Chuan Lee085e3d72020-05-11 16:00:42 +0900284 logging.PrintBuildbotStepText('%s %s not revved'
285 % (stable_candidate.pkgname,
286 stable_candidate.version))
Shao-Chuan Lee301a4192021-02-08 11:53:49 +0900287 osutils.SafeUnlink(new_ebuild_path)
David Rileyc0da9d92016-02-01 12:11:01 -0800288 return None
289
Shao-Chuan Lee085e3d72020-05-11 16:00:42 +0900290 # PFQ runs should always be able to find a stable candidate.
David Rileyc0da9d92016-02-01 12:11:01 -0800291 if stable_candidate:
Shao-Chuan Lee085e3d72020-05-11 16:00:42 +0900292 PrintUprevMetadata(build_branch, stable_candidate, new_ebuild)
David Rileyc0da9d92016-02-01 12:11:01 -0800293
Shao-Chuan Lee301a4192021-02-08 11:53:49 +0900294 files_to_add = [new_ebuild_path]
295 files_to_remove = []
David Rileyc0da9d92016-02-01 12:11:01 -0800296 if stable_candidate and not stable_candidate.IsSticky():
Shao-Chuan Lee301a4192021-02-08 11:53:49 +0900297 osutils.SafeUnlink(stable_candidate.ebuild_path)
298 files_to_remove.append(stable_candidate.ebuild_path)
David Rileyc0da9d92016-02-01 12:11:01 -0800299
300 # Update ebuild manifest and git add it.
301 gen_manifest_cmd = ['ebuild', new_ebuild_path, 'manifest', '--force']
Mike Frysinger45602c72019-09-22 02:15:11 -0400302 cros_build_lib.run(gen_manifest_cmd, extra_env=None, print_cmd=True)
Shao-Chuan Lee301a4192021-02-08 11:53:49 +0900303 files_to_add.append('Manifest')
David Rileyc0da9d92016-02-01 12:11:01 -0800304
Shao-Chuan Lee301a4192021-02-08 11:53:49 +0900305 return (
306 f'{new_ebuild.package}-{new_ebuild.version}',
307 files_to_add,
308 files_to_remove,
309 )
David Rileyc0da9d92016-02-01 12:11:01 -0800310
Shao-Chuan Lee301a4192021-02-08 11:53:49 +0900311
Shao-Chuan Lee007dbe82021-02-09 14:05:39 +0900312def _PrepareGitBranch(overlay_dir):
313 """Prepares a git branch for the uprev commit.
Shao-Chuan Lee301a4192021-02-08 11:53:49 +0900314
Shao-Chuan Lee007dbe82021-02-09 14:05:39 +0900315 If the overlay project is currently on a branch (e.g. patches are being
316 applied), rebase the new branch on top of it.
317
318 Args:
319 overlay_dir: The overlay directory.
320 """
321 existing_branch = git.GetCurrentBranch(overlay_dir)
322 repo_util.Repository.MustFind(overlay_dir).StartBranch(
323 constants.STABLE_EBUILD_BRANCH, projects=['.'], cwd=overlay_dir)
Shao-Chuan Lee301a4192021-02-08 11:53:49 +0900324 if existing_branch:
325 git.RunGit(overlay_dir, ['rebase', existing_branch])
326
327
328def _CommitChange(message, android_package_dir, files_to_add, files_to_remove):
329 """Commit changes to git with list of files to add/remove."""
330 git.RunGit(android_package_dir, ['add', '--'] + files_to_add)
331 git.RunGit(android_package_dir, ['rm', '--'] + files_to_remove)
332
333 portage_util.EBuild.CommitChange(message, android_package_dir)
David Rileyc0da9d92016-02-01 12:11:01 -0800334
335
336def GetParser():
337 """Creates the argument parser."""
338 parser = commandline.ArgumentParser()
339 parser.add_argument('-b', '--boards')
340 parser.add_argument('--android_bucket_url',
David Riley73f00d92016-02-16 18:54:20 -0800341 default=constants.ANDROID_BUCKET_URL,
342 type='gs_path')
David Rileyc0da9d92016-02-01 12:11:01 -0800343 parser.add_argument('--android_build_branch',
Shuhei Takahashi6d02c192017-04-05 14:01:24 +0900344 required=True,
Shao-Chuan Lee7e9eacb2021-03-19 16:45:21 +0900345 choices=constants.ANDROID_BRANCH_TO_BUILD_TARGETS,
346 help='Android branch to import from')
Hidehiko Abe4fd94ae2017-01-24 18:59:55 +0900347 parser.add_argument('--android_package',
348 default=constants.ANDROID_PACKAGE_NAME)
David Riley73f00d92016-02-16 18:54:20 -0800349 parser.add_argument('--arc_bucket_url',
350 default=constants.ARC_BUCKET_URL,
351 type='gs_path')
David Rileyc0da9d92016-02-01 12:11:01 -0800352 parser.add_argument('-f', '--force_version',
353 help='Android build id to use')
354 parser.add_argument('-s', '--srcroot',
355 default=os.path.join(os.environ['HOME'], 'trunk', 'src'),
356 help='Path to the src directory')
khmel@chromium.orgd3ec3d72020-04-29 15:57:35 -0700357 parser.add_argument('--runtime_artifacts_bucket_url',
358 default=_RUNTIME_ARTIFACTS_BUCKET_URL,
359 type='gs_path')
Shao-Chuan Lee301a4192021-02-08 11:53:49 +0900360 parser.add_argument('--skip_commit',
361 action='store_true',
362 help='Skip commiting uprev changes to git')
David Rileyc0da9d92016-02-01 12:11:01 -0800363 return parser
364
365
366def main(argv):
Hidehiko Abec9ecf262017-07-05 15:17:41 +0900367 logging.EnableBuildbotMarkers()
David Rileyc0da9d92016-02-01 12:11:01 -0800368 parser = GetParser()
369 options = parser.parse_args(argv)
370 options.Freeze()
371
372 overlay_dir = os.path.abspath(_OVERLAY_DIR % {'srcroot': options.srcroot})
Hidehiko Abe4fd94ae2017-01-24 18:59:55 +0900373 android_package_dir = os.path.join(
374 overlay_dir,
375 portage_util.GetFullAndroidPortagePackageName(options.android_package))
David Rileyc0da9d92016-02-01 12:11:01 -0800376
377 (unstable_ebuild, stable_ebuilds) = FindAndroidCandidates(android_package_dir)
Shao-Chuan Lee7fd0ca12021-03-19 15:57:40 +0900378 acls = android.MakeAclDict(android_package_dir)
Hidehiko Abe1ebc25d2016-07-28 02:24:37 +0900379 # Mirror artifacts, i.e., images and some sdk tools (e.g., adb, aapt).
Shao-Chuan Lee7fd0ca12021-03-19 15:57:40 +0900380 version_to_uprev = android.MirrorArtifacts(options.android_bucket_url,
381 options.android_build_branch,
382 options.arc_bucket_url, acls,
Shao-Chuan Lee7fd0ca12021-03-19 15:57:40 +0900383 options.force_version)
Hidehiko Abe1ebc25d2016-07-28 02:24:37 +0900384
David Rileyc0da9d92016-02-01 12:11:01 -0800385 stable_candidate = portage_util.BestEBuild(stable_ebuilds)
386
387 if stable_candidate:
Lann Martinffb95162018-08-28 12:02:54 -0600388 logging.info('Stable candidate found %s', stable_candidate.version)
David Rileyc0da9d92016-02-01 12:11:01 -0800389 else:
390 logging.info('No stable candidate found.')
391
Shao-Chuan Lee301a4192021-02-08 11:53:49 +0900392 if not options.skip_commit:
Shao-Chuan Lee007dbe82021-02-09 14:05:39 +0900393 _PrepareGitBranch(overlay_dir)
David Rileyc0da9d92016-02-01 12:11:01 -0800394
Shao-Chuan Lee301a4192021-02-08 11:53:49 +0900395 revved = MarkAndroidEBuildAsStable(
Hidehiko Abe4fd94ae2017-01-24 18:59:55 +0900396 stable_candidate, unstable_ebuild, options.android_package,
David Riley73f00d92016-02-16 18:54:20 -0800397 version_to_uprev, android_package_dir,
khmel@chromium.orgd3ec3d72020-04-29 15:57:35 -0700398 options.android_build_branch, options.arc_bucket_url,
Shao-Chuan Lee7e9eacb2021-03-19 16:45:21 +0900399 options.runtime_artifacts_bucket_url)
Shao-Chuan Lee301a4192021-02-08 11:53:49 +0900400
401 if revved:
402 android_version_atom, files_to_add, files_to_remove = revved
403 if not options.skip_commit:
404 _CommitChange(
405 _GIT_COMMIT_MESSAGE % {'android_package': options.android_package,
406 'android_version': version_to_uprev},
407 android_package_dir,
408 files_to_add,
409 files_to_remove,
410 )
David Rileyc0da9d92016-02-01 12:11:01 -0800411 if options.boards:
412 cros_mark_as_stable.CleanStalePackages(options.srcroot,
413 options.boards.split(':'),
414 [android_version_atom])
415
416 # Explicit print to communicate to caller.
417 print('ANDROID_VERSION_ATOM=%s' % android_version_atom)