blob: dfb4c2e74c90d5900811ee7dfb914987d15bf037 [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,
220 runtime_artifacts_bucket_url,
221 build_targets):
David Rileyc0da9d92016-02-01 12:11:01 -0800222 r"""Uprevs the Android ebuild.
223
224 This is the main function that uprevs from a stable candidate
225 to its new version.
226
227 Args:
228 stable_candidate: ebuild that corresponds to the stable ebuild we are
229 revving from. If None, builds the a new ebuild given the version
230 with revision set to 1.
231 unstable_ebuild: ebuild corresponding to the unstable ebuild for Android.
Hidehiko Abe4fd94ae2017-01-24 18:59:55 +0900232 android_package: android package name.
David Rileyc0da9d92016-02-01 12:11:01 -0800233 android_version: The \d+ build id of Android.
David Rileyc0da9d92016-02-01 12:11:01 -0800234 package_dir: Path to the android-container package dir.
David Riley73f00d92016-02-16 18:54:20 -0800235 build_branch: branch of Android builds.
236 arc_bucket_url: URL of the target ARC build gs bucket.
khmel@chromium.orgd3ec3d72020-04-29 15:57:35 -0700237 runtime_artifacts_bucket_url: root of runtime artifacts
Nicolas Norvez4bd854f2017-05-23 10:04:45 -0700238 build_targets: build targets for this particular Android branch.
David Rileyc0da9d92016-02-01 12:11:01 -0800239
240 Returns:
Shao-Chuan Lee301a4192021-02-08 11:53:49 +0900241 Tuple[str, List[str], List[str]] if revved, or None
242 1. Full portage version atom (including rc's, etc) that was revved.
243 2. List of files to be `git add`ed.
244 3. List of files to be `git rm`ed.
David Rileyc0da9d92016-02-01 12:11:01 -0800245 """
246 def IsTheNewEBuildRedundant(new_ebuild, stable_ebuild):
247 """Returns True if the new ebuild is redundant.
248
249 This is True if there if the current stable ebuild is the exact same copy
250 of the new one.
251 """
252 if not stable_ebuild:
253 return False
254
David Riley676f5402016-02-12 17:24:23 -0800255 if stable_candidate.version_no_rev == new_ebuild.version_no_rev:
David Rileyc0da9d92016-02-01 12:11:01 -0800256 return filecmp.cmp(
257 new_ebuild.ebuild_path, stable_ebuild.ebuild_path, shallow=False)
khmel@chromium.orgd3ec3d72020-04-29 15:57:35 -0700258 return False
David Rileyc0da9d92016-02-01 12:11:01 -0800259
260 # Case where we have the last stable candidate with same version just rev.
David Riley676f5402016-02-12 17:24:23 -0800261 if stable_candidate and stable_candidate.version_no_rev == android_version:
David Rileyc0da9d92016-02-01 12:11:01 -0800262 new_ebuild_path = '%s-r%d.ebuild' % (
263 stable_candidate.ebuild_path_no_revision,
264 stable_candidate.current_revision + 1)
265 else:
Hidehiko Abe4fd94ae2017-01-24 18:59:55 +0900266 pf = '%s-%s-r1' % (android_package, android_version)
David Rileyc0da9d92016-02-01 12:11:01 -0800267 new_ebuild_path = os.path.join(package_dir, '%s.ebuild' % pf)
268
David Riley73f00d92016-02-16 18:54:20 -0800269 variables = {'BASE_URL': arc_bucket_url}
Mike Frysinger0bdbc102019-06-13 15:27:29 -0400270 for build, (target, _) in build_targets.items():
David Riley73f00d92016-02-16 18:54:20 -0800271 variables[build + '_TARGET'] = '%s-%s' % (build_branch, target)
David Rileyc0da9d92016-02-01 12:11:01 -0800272
khmel@chromium.orgd3ec3d72020-04-29 15:57:35 -0700273 variables.update(UpdateDataCollectorArtifacts(
Yury Khmelb009aeb2020-08-19 19:40:00 -0700274 android_version, runtime_artifacts_bucket_url, build_branch))
khmel@chromium.orgd3ec3d72020-04-29 15:57:35 -0700275
David Rileyc0da9d92016-02-01 12:11:01 -0800276 portage_util.EBuild.MarkAsStable(
277 unstable_ebuild.ebuild_path, new_ebuild_path,
278 variables, make_stable=True)
279 new_ebuild = portage_util.EBuild(new_ebuild_path)
280
281 # Determine whether this is ebuild is redundant.
282 if IsTheNewEBuildRedundant(new_ebuild, stable_candidate):
283 msg = 'Previous ebuild with same version found and ebuild is redundant.'
284 logging.info(msg)
Shao-Chuan Lee085e3d72020-05-11 16:00:42 +0900285 logging.PrintBuildbotStepText('%s %s not revved'
286 % (stable_candidate.pkgname,
287 stable_candidate.version))
Shao-Chuan Lee301a4192021-02-08 11:53:49 +0900288 osutils.SafeUnlink(new_ebuild_path)
David Rileyc0da9d92016-02-01 12:11:01 -0800289 return None
290
Shao-Chuan Lee085e3d72020-05-11 16:00:42 +0900291 # PFQ runs should always be able to find a stable candidate.
David Rileyc0da9d92016-02-01 12:11:01 -0800292 if stable_candidate:
Shao-Chuan Lee085e3d72020-05-11 16:00:42 +0900293 PrintUprevMetadata(build_branch, stable_candidate, new_ebuild)
David Rileyc0da9d92016-02-01 12:11:01 -0800294
Shao-Chuan Lee301a4192021-02-08 11:53:49 +0900295 files_to_add = [new_ebuild_path]
296 files_to_remove = []
David Rileyc0da9d92016-02-01 12:11:01 -0800297 if stable_candidate and not stable_candidate.IsSticky():
Shao-Chuan Lee301a4192021-02-08 11:53:49 +0900298 osutils.SafeUnlink(stable_candidate.ebuild_path)
299 files_to_remove.append(stable_candidate.ebuild_path)
David Rileyc0da9d92016-02-01 12:11:01 -0800300
301 # Update ebuild manifest and git add it.
302 gen_manifest_cmd = ['ebuild', new_ebuild_path, 'manifest', '--force']
Mike Frysinger45602c72019-09-22 02:15:11 -0400303 cros_build_lib.run(gen_manifest_cmd, extra_env=None, print_cmd=True)
Shao-Chuan Lee301a4192021-02-08 11:53:49 +0900304 files_to_add.append('Manifest')
David Rileyc0da9d92016-02-01 12:11:01 -0800305
Shao-Chuan Lee301a4192021-02-08 11:53:49 +0900306 return (
307 f'{new_ebuild.package}-{new_ebuild.version}',
308 files_to_add,
309 files_to_remove,
310 )
David Rileyc0da9d92016-02-01 12:11:01 -0800311
Shao-Chuan Lee301a4192021-02-08 11:53:49 +0900312
Shao-Chuan Lee007dbe82021-02-09 14:05:39 +0900313def _PrepareGitBranch(overlay_dir):
314 """Prepares a git branch for the uprev commit.
Shao-Chuan Lee301a4192021-02-08 11:53:49 +0900315
Shao-Chuan Lee007dbe82021-02-09 14:05:39 +0900316 If the overlay project is currently on a branch (e.g. patches are being
317 applied), rebase the new branch on top of it.
318
319 Args:
320 overlay_dir: The overlay directory.
321 """
322 existing_branch = git.GetCurrentBranch(overlay_dir)
323 repo_util.Repository.MustFind(overlay_dir).StartBranch(
324 constants.STABLE_EBUILD_BRANCH, projects=['.'], cwd=overlay_dir)
Shao-Chuan Lee301a4192021-02-08 11:53:49 +0900325 if existing_branch:
326 git.RunGit(overlay_dir, ['rebase', existing_branch])
327
328
329def _CommitChange(message, android_package_dir, files_to_add, files_to_remove):
330 """Commit changes to git with list of files to add/remove."""
331 git.RunGit(android_package_dir, ['add', '--'] + files_to_add)
332 git.RunGit(android_package_dir, ['rm', '--'] + files_to_remove)
333
334 portage_util.EBuild.CommitChange(message, android_package_dir)
David Rileyc0da9d92016-02-01 12:11:01 -0800335
336
337def GetParser():
338 """Creates the argument parser."""
339 parser = commandline.ArgumentParser()
340 parser.add_argument('-b', '--boards')
341 parser.add_argument('--android_bucket_url',
David Riley73f00d92016-02-16 18:54:20 -0800342 default=constants.ANDROID_BUCKET_URL,
343 type='gs_path')
David Rileyc0da9d92016-02-01 12:11:01 -0800344 parser.add_argument('--android_build_branch',
Shuhei Takahashi6d02c192017-04-05 14:01:24 +0900345 required=True,
346 help='Android branch to import from. '
347 'Ex: git_mnc-dr-arc-dev')
Hidehiko Abe4fd94ae2017-01-24 18:59:55 +0900348 parser.add_argument('--android_package',
349 default=constants.ANDROID_PACKAGE_NAME)
David Riley73f00d92016-02-16 18:54:20 -0800350 parser.add_argument('--arc_bucket_url',
351 default=constants.ARC_BUCKET_URL,
352 type='gs_path')
David Rileyc0da9d92016-02-01 12:11:01 -0800353 parser.add_argument('-f', '--force_version',
354 help='Android build id to use')
355 parser.add_argument('-s', '--srcroot',
356 default=os.path.join(os.environ['HOME'], 'trunk', 'src'),
357 help='Path to the src directory')
khmel@chromium.orgd3ec3d72020-04-29 15:57:35 -0700358 parser.add_argument('--runtime_artifacts_bucket_url',
359 default=_RUNTIME_ARTIFACTS_BUCKET_URL,
360 type='gs_path')
Shao-Chuan Lee301a4192021-02-08 11:53:49 +0900361 parser.add_argument('--skip_commit',
362 action='store_true',
363 help='Skip commiting uprev changes to git')
David Rileyc0da9d92016-02-01 12:11:01 -0800364 return parser
365
366
367def main(argv):
Hidehiko Abec9ecf262017-07-05 15:17:41 +0900368 logging.EnableBuildbotMarkers()
David Rileyc0da9d92016-02-01 12:11:01 -0800369 parser = GetParser()
370 options = parser.parse_args(argv)
371 options.Freeze()
372
373 overlay_dir = os.path.abspath(_OVERLAY_DIR % {'srcroot': options.srcroot})
Hidehiko Abe4fd94ae2017-01-24 18:59:55 +0900374 android_package_dir = os.path.join(
375 overlay_dir,
376 portage_util.GetFullAndroidPortagePackageName(options.android_package))
David Rileyc0da9d92016-02-01 12:11:01 -0800377 version_to_uprev = None
David Rileyc0da9d92016-02-01 12:11:01 -0800378
379 (unstable_ebuild, stable_ebuilds) = FindAndroidCandidates(android_package_dir)
Shao-Chuan Lee7fd0ca12021-03-19 15:57:40 +0900380 acls = android.MakeAclDict(android_package_dir)
381 build_targets = android.MakeBuildTargetDict(options.android_package,
382 options.android_build_branch)
Hidehiko Abe1ebc25d2016-07-28 02:24:37 +0900383 # Mirror artifacts, i.e., images and some sdk tools (e.g., adb, aapt).
Shao-Chuan Lee7fd0ca12021-03-19 15:57:40 +0900384 version_to_uprev = android.MirrorArtifacts(options.android_bucket_url,
385 options.android_build_branch,
386 options.arc_bucket_url, acls,
387 build_targets,
388 options.force_version)
Hidehiko Abe1ebc25d2016-07-28 02:24:37 +0900389
David Rileyc0da9d92016-02-01 12:11:01 -0800390 stable_candidate = portage_util.BestEBuild(stable_ebuilds)
391
392 if stable_candidate:
Lann Martinffb95162018-08-28 12:02:54 -0600393 logging.info('Stable candidate found %s', stable_candidate.version)
David Rileyc0da9d92016-02-01 12:11:01 -0800394 else:
395 logging.info('No stable candidate found.')
396
Shao-Chuan Lee301a4192021-02-08 11:53:49 +0900397 if not options.skip_commit:
Shao-Chuan Lee007dbe82021-02-09 14:05:39 +0900398 _PrepareGitBranch(overlay_dir)
David Rileyc0da9d92016-02-01 12:11:01 -0800399
Shao-Chuan Lee301a4192021-02-08 11:53:49 +0900400 revved = MarkAndroidEBuildAsStable(
Hidehiko Abe4fd94ae2017-01-24 18:59:55 +0900401 stable_candidate, unstable_ebuild, options.android_package,
David Riley73f00d92016-02-16 18:54:20 -0800402 version_to_uprev, android_package_dir,
khmel@chromium.orgd3ec3d72020-04-29 15:57:35 -0700403 options.android_build_branch, options.arc_bucket_url,
404 options.runtime_artifacts_bucket_url, build_targets)
Shao-Chuan Lee301a4192021-02-08 11:53:49 +0900405
406 if revved:
407 android_version_atom, files_to_add, files_to_remove = revved
408 if not options.skip_commit:
409 _CommitChange(
410 _GIT_COMMIT_MESSAGE % {'android_package': options.android_package,
411 'android_version': version_to_uprev},
412 android_package_dir,
413 files_to_add,
414 files_to_remove,
415 )
David Rileyc0da9d92016-02-01 12:11:01 -0800416 if options.boards:
417 cros_mark_as_stable.CleanStalePackages(options.srcroot,
418 options.boards.split(':'),
419 [android_version_atom])
420
421 # Explicit print to communicate to caller.
422 print('ANDROID_VERSION_ATOM=%s' % android_version_atom)