summaryrefslogtreecommitdiff
path: root/chromium/build/util
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2019-05-24 11:40:17 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2019-05-24 12:42:11 +0000
commit5d87695f37678f96492b258bbab36486c59866b4 (patch)
treebe9783bbaf04fb930c4d74ca9c00b5e7954c8bc6 /chromium/build/util
parent6c11fb357ec39bf087b8b632e2b1e375aef1b38b (diff)
downloadqtwebengine-chromium-5d87695f37678f96492b258bbab36486c59866b4.tar.gz
BASELINE: Update Chromium to 75.0.3770.56
Change-Id: I86d2007fd27a45d5797eee06f4c9369b8b50ac4f Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
Diffstat (limited to 'chromium/build/util')
-rw-r--r--chromium/build/util/LASTCHANGE2
-rw-r--r--chromium/build/util/LASTCHANGE.committime2
-rw-r--r--chromium/build/util/PRESUBMIT.py58
-rw-r--r--chromium/build/util/android_chrome_version.py167
-rw-r--r--chromium/build/util/android_chrome_version_test.py293
-rw-r--r--chromium/build/util/generate_wrapper.gni98
-rwxr-xr-xchromium/build/util/generate_wrapper.py136
-rwxr-xr-xchromium/build/util/lastchange.py201
-rw-r--r--chromium/build/util/version.gni123
-rwxr-xr-xchromium/build/util/version.py157
-rw-r--r--chromium/build/util/version_test.py174
11 files changed, 1295 insertions, 116 deletions
diff --git a/chromium/build/util/LASTCHANGE b/chromium/build/util/LASTCHANGE
index d553b21db54..803868098e1 100644
--- a/chromium/build/util/LASTCHANGE
+++ b/chromium/build/util/LASTCHANGE
@@ -1 +1 @@
-LASTCHANGE=7e9e689503f506e8e943b05c4f50c0c3e6597794-refs/branch-heads/3729@{#1001}
+LASTCHANGE=75fcf24dd8773d5959a7177f492969e627d83c01-refs/branch-heads/3770@{#819}
diff --git a/chromium/build/util/LASTCHANGE.committime b/chromium/build/util/LASTCHANGE.committime
index 7d9b0e21a75..f3a9396894b 100644
--- a/chromium/build/util/LASTCHANGE.committime
+++ b/chromium/build/util/LASTCHANGE.committime
@@ -1 +1 @@
-1557879296 \ No newline at end of file
+1558658817 \ No newline at end of file
diff --git a/chromium/build/util/PRESUBMIT.py b/chromium/build/util/PRESUBMIT.py
new file mode 100644
index 00000000000..271afbbb625
--- /dev/null
+++ b/chromium/build/util/PRESUBMIT.py
@@ -0,0 +1,58 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import re
+"""Presubmit for build/util"""
+
+
+def _GetBlacklist(input_api):
+ blacklist = []
+ affected_files = input_api.change.AffectedFiles()
+ version_script_change = next(
+ (f for f in affected_files
+ if re.search('\\/version\\.py$|\\/version_test\\.py$', f.LocalPath())),
+ None)
+
+ if version_script_change is None:
+ blacklist.append('version_test\\.py$')
+
+ android_chrome_version_script_change = next(
+ (f for f in affected_files if re.search(
+ '\\/android_chrome_version\\.py$|'
+ '\\/android_chrome_version_test\\.py$', f.LocalPath())), None)
+
+ if android_chrome_version_script_change is None:
+ blacklist.append('android_chrome_version_test\\.py$')
+
+ return blacklist
+
+
+def _GetPythonUnitTests(input_api, output_api):
+ # No need to test if files are unchanged
+ blacklist = _GetBlacklist(input_api)
+
+ return input_api.canned_checks.GetUnitTestsRecursively(
+ input_api,
+ output_api,
+ input_api.PresubmitLocalPath(),
+ whitelist=['.*_test\\.py$'],
+ blacklist=blacklist)
+
+
+def CommonChecks(input_api, output_api):
+ """Presubmit checks run on both upload and commit.
+ """
+ checks = []
+ checks.extend(_GetPythonUnitTests(input_api, output_api))
+ return input_api.RunTests(checks, False)
+
+
+def CheckChangeOnUpload(input_api, output_api):
+ """Presubmit checks on CL upload."""
+ return CommonChecks(input_api, output_api)
+
+
+def CheckChangeOnCommit(input_api, output_api):
+ """Presubmit checks on commit."""
+ return CommonChecks(input_api, output_api)
diff --git a/chromium/build/util/android_chrome_version.py b/chromium/build/util/android_chrome_version.py
new file mode 100644
index 00000000000..eb58b2f97e9
--- /dev/null
+++ b/chromium/build/util/android_chrome_version.py
@@ -0,0 +1,167 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""Different build variants of chrome for android have different version codes.
+Reason: for targets that have the same package name (e.g. chrome, chome
+modern, monochrome, trichrome), Play Store considers them the same app
+and will push the supported app with the highest version code to devices.
+(note Play Store does not support hosting two different apps with same
+version code and package name)
+
+Each key in this dict represents a unique version code that will be used for
+one or more android chrome apks.
+
+Webview channels must have unique version codes for a couple reasons:
+a) Play Store does not support having the same version code for different
+ versions of a package. Without unique codes, promoting a beta apk to stable
+ would require first removing the beta version.
+b) Firebase project support (used by official builders) requires unique
+ [version code + package name].
+ We cannot add new webview package names for new channels because webview
+ packages are whitelisted by Android as webview providers.
+
+WEBVIEW_STABLE, WEBVIEW_BETA, WEBVIEW_DEV are all used for standalone webview,
+whereas the others are used for various chrome apks.
+
+Note that a final digit of '3' for webview is reserved for Trichrome Webview.
+The same versionCode is used for both Trichrome Chrome and Trichrome Webview.
+"""
+ANDROID_CHROME_APK_VERSION_CODE_DIFFS = {
+ 'CHROME': 0,
+ 'CHROME_MODERN': 1,
+ 'MONOCHROME': 2,
+ 'TRICHROME': 3,
+ 'NOTOUCH_CHROME': 4,
+ 'WEBVIEW_STABLE': 0,
+ 'WEBVIEW_BETA': 1,
+ 'WEBVIEW_DEV': 2,
+}
+
+"""The architecture preference is encoded into the version_code for devices
+that support multiple architectures. (exploiting play store logic that pushes
+apk with highest version code)
+
+Detail:
+Many Android devices support multiple architectures, and can run applications
+built for any of them; the Play Store considers all of the supported
+architectures compatible and does not, itself, have any preference for which
+is "better". The common cases here:
+
+- All production arm64 devices can also run arm
+- All production x64 devices can also run x86
+- Pretty much all production x86/x64 devices can also run arm (via a binary
+ translator)
+
+Since the Play Store has no particular preferences, you have to encode your own
+preferences into the ordering of the version codes. There's a few relevant
+things here:
+
+- For any android app, it's theoretically preferable to ship a 64-bit version to
+ 64-bit devices if it exists, because the 64-bit architectures are supposed to
+ be "better" than their 32-bit predecessors (unfortunately this is not always
+ true due to the effect on memory usage, but we currently deal with this by
+ simply not shipping a 64-bit version *at all* on the configurations where we
+ want the 32-bit version to be used).
+- For any android app, it's definitely preferable to ship an x86 version to x86
+ devices if it exists instead of an arm version, because running things through
+ the binary translator is a performance hit.
+- For WebView, Monochrome, and Trichrome specifically, they are a special class
+ of APK called "multiarch" which means that they actually need to *use* more
+ than one architecture at runtime (rather than simply being compatible with
+ more than one). The 64-bit builds of these multiarch APKs contain both 32-bit
+ and 64-bit code, so that Webview is available for both ABIs. If you're
+ multiarch you *must* have a version that supports both 32-bit and 64-bit
+ version on a 64-bit device, otherwise it won't work properly. So, the 64-bit
+ version needs to be a higher versionCode, as otherwise a 64-bit device would
+ prefer the 32-bit version that does not include any 64-bit code, and fail.
+- The relative order of mips isn't important, but it needs to be a *distinct*
+ value to the other architectures because all builds need unique version codes.
+"""
+ARCH_VERSION_CODE_DIFF = {
+ 'arm': 0,
+ 'x86': 10,
+ 'mipsel': 20,
+ 'arm64': 30,
+ 'x64': 60
+}
+ARCH_CHOICES = ARCH_VERSION_CODE_DIFF.keys()
+
+""" "Next" builds get +5 last version code digit.
+
+We choose 5 because it won't conflict with values in
+ANDROID_CHROME_APK_VERSION_CODE_DIFFS
+"""
+NEXT_BUILD_VERSION_CODE_DIFF = 5
+
+"""For 64-bit architectures, some packages have multiple targets with version
+codes that differ by the second-to-last digit (the architecture digit). This is
+for various combinations of 32-bit vs 64-bit chrome and webview. The
+default/traditional configuration is 32-bit chrome with 64-bit webview, but we
+are adding:
++ 64-bit chrome with 32-bit webview
++ 64-bit combined Chrome and Webview (only one library)
++ (maybe someday 32-bit chrome with 32-bit webview)
+
+The naming scheme followed here is <chrome>_<webview>,
+e.g. 64_32 is 64-bit chrome with 32-bit webview.
+"""
+ARCH64_APK_VARIANTS = {
+ '64_32': {
+ 'PACKAGES': frozenset(['MONOCHROME', 'TRICHROME']),
+ 'MODIFIER': 10
+ },
+ '64': {
+ 'PACKAGES': frozenset(['MONOCHROME', 'TRICHROME']),
+ 'MODIFIER': 20
+ }
+}
+
+
+def GenerateVersionCodes(version_values, arch, is_next_build):
+ """Get dict of version codes for chrome-for-android-related targets
+
+ e.g.
+ {
+ 'CHROME_VERSION_CODE': '378100010',
+ 'MONOCHROME_VERSION_CODE': '378100013',
+ ...
+ }
+
+ versionCode values are built like this:
+ {full BUILD int}{3 digits for PATCH}{1 digit for architecture}{final digit}.
+
+ MAJOR and MINOR values are not used for generating versionCode.
+ - MINOR is always 0. It was used for something long ago in Chrome's history
+ but has not been used since, and has never been nonzero on Android.
+ - MAJOR is cosmetic and controlled by the release managers. MAJOR and BUILD
+ always have reasonable sort ordering: for two version codes A and B, it's
+ always the case that (A.MAJOR < B.MAJOR) implies (A.BUILD < B.BUILD), and
+ that (A.MAJOR > B.MAJOR) implies (A.BUILD > B.BUILD). This property is just
+ maintained by the humans who set MAJOR.
+
+ Thus, this method is responsible for the final two digits of versionCode.
+ """
+
+ base_version_code = '%s%03d00' % (version_values['BUILD'],
+ int(version_values['PATCH']))
+ new_version_code = int(base_version_code)
+
+ new_version_code += ARCH_VERSION_CODE_DIFF[arch]
+ if is_next_build:
+ new_version_code += NEXT_BUILD_VERSION_CODE_DIFF
+
+ version_codes = {}
+ for apk, diff in ANDROID_CHROME_APK_VERSION_CODE_DIFFS.iteritems():
+ version_code_name = apk + '_VERSION_CODE'
+ version_code_val = new_version_code + diff
+ version_codes[version_code_name] = str(version_code_val)
+
+ if arch == 'arm64' or arch == 'x64':
+ for variant, config in ARCH64_APK_VARIANTS.iteritems():
+ if apk in config['PACKAGES']:
+ variant_name = apk + '_' + variant + '_VERSION_CODE'
+ variant_val = version_code_val + config['MODIFIER']
+ version_codes[variant_name] = str(variant_val)
+
+
+ return version_codes
diff --git a/chromium/build/util/android_chrome_version_test.py b/chromium/build/util/android_chrome_version_test.py
new file mode 100644
index 00000000000..529ccd33aa1
--- /dev/null
+++ b/chromium/build/util/android_chrome_version_test.py
@@ -0,0 +1,293 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from android_chrome_version import GenerateVersionCodes
+
+
+class _VersionTest(unittest.TestCase):
+ """Unittests for the android_chrome_version module.
+ """
+
+ EXAMPLE_VERSION_VALUES = {
+ 'MAJOR': '74',
+ 'MINOR': '0',
+ 'BUILD': '3720',
+ 'PATCH': '0',
+ }
+
+ def testGenerateVersionCodesAndroidChrome(self):
+ """Assert it gives correct values for standard/example inputs"""
+ output = GenerateVersionCodes(
+ self.EXAMPLE_VERSION_VALUES, arch='arm', is_next_build=False)
+
+ chrome_version_code = output['CHROME_VERSION_CODE']
+
+ self.assertEqual(chrome_version_code, '372000000')
+
+ def testGenerateVersionCodesAndroidChromeModern(self):
+ """Assert it gives correct values for standard/example inputs"""
+ output = GenerateVersionCodes(
+ self.EXAMPLE_VERSION_VALUES, arch='arm', is_next_build=False)
+
+ chrome_modern_version_code = output['CHROME_MODERN_VERSION_CODE']
+
+ self.assertEqual(chrome_modern_version_code, '372000001')
+
+ def testGenerateVersionCodesAndroidMonochrome(self):
+ """Assert it gives correct values for standard/example inputs"""
+ output = GenerateVersionCodes(
+ self.EXAMPLE_VERSION_VALUES, arch='arm', is_next_build=False)
+
+ monochrome_version_code = output['MONOCHROME_VERSION_CODE']
+
+ self.assertEqual(monochrome_version_code, '372000002')
+
+ def testGenerateVersionCodesAndroidTrichrome(self):
+ """Assert it gives correct values for standard/example inputs"""
+ output = GenerateVersionCodes(
+ self.EXAMPLE_VERSION_VALUES, arch='arm', is_next_build=False)
+
+ trichrome_version_code = output['TRICHROME_VERSION_CODE']
+
+ self.assertEqual(trichrome_version_code, '372000003')
+
+ def testGenerateVersionCodesAndroidNoTouch(self):
+ """Assert it gives correct values for standard/example inputs"""
+ output = GenerateVersionCodes(
+ self.EXAMPLE_VERSION_VALUES, arch='arm', is_next_build=False)
+
+ notouch_chrome_version_code = output['NOTOUCH_CHROME_VERSION_CODE']
+
+ self.assertEqual(notouch_chrome_version_code, '372000004')
+
+ def testGenerateVersionCodesAndroidWebviewStable(self):
+ """Assert it gives correct values for standard/example inputs"""
+ output = GenerateVersionCodes(
+ self.EXAMPLE_VERSION_VALUES, arch='arm', is_next_build=False)
+
+ webview_stable_version_code = output['WEBVIEW_STABLE_VERSION_CODE']
+
+ self.assertEqual(webview_stable_version_code, '372000000')
+
+ def testGenerateVersionCodesAndroidWebviewBeta(self):
+ """Assert it gives correct values for standard/example inputs"""
+ output = GenerateVersionCodes(
+ self.EXAMPLE_VERSION_VALUES, arch='arm', is_next_build=False)
+
+ webview_beta_version_code = output['WEBVIEW_BETA_VERSION_CODE']
+
+ self.assertEqual(webview_beta_version_code, '372000001')
+
+ def testGenerateVersionCodesAndroidWebviewDev(self):
+ """Assert it gives correct values for standard/example inputs"""
+ output = GenerateVersionCodes(
+ self.EXAMPLE_VERSION_VALUES, arch='arm', is_next_build=False)
+
+ webview_dev_version_code = output['WEBVIEW_DEV_VERSION_CODE']
+
+ self.assertEqual(webview_dev_version_code, '372000002')
+
+ def testGenerateVersionCodesAndroidNextBuild(self):
+ """Assert it handles "next" builds correctly"""
+ output = GenerateVersionCodes(
+ self.EXAMPLE_VERSION_VALUES, arch='arm', is_next_build=True)
+
+ # Get just a sample of values
+ chrome_version_code = output['CHROME_VERSION_CODE']
+ monochrome_version_code = output['MONOCHROME_VERSION_CODE']
+ webview_stable_version_code = output['WEBVIEW_STABLE_VERSION_CODE']
+ webview_beta_version_code = output['WEBVIEW_BETA_VERSION_CODE']
+
+ self.assertEqual(chrome_version_code, '372000005')
+ self.assertEqual(monochrome_version_code, '372000007')
+ self.assertEqual(webview_stable_version_code, '372000005')
+ self.assertEqual(webview_beta_version_code, '372000006')
+
+ def testGenerateVersionCodesAndroidArchArm(self):
+ """Assert it handles different architectures correctly.
+
+ Version codes for different builds need to be distinct and maintain a
+ certain ordering.
+ See docstring on android_chrome_version.ARCH_VERSION_CODE_DIFF for
+ reasoning.
+ """
+ output = GenerateVersionCodes(
+ self.EXAMPLE_VERSION_VALUES, arch='arm', is_next_build=False)
+ arch_chrome_version_code = output['CHROME_VERSION_CODE']
+
+ self.assertEqual(arch_chrome_version_code, '372000000')
+
+ def testGenerateVersionCodesAndroidArchX86(self):
+ """Assert it handles different architectures correctly.
+
+ Version codes for different builds need to be distinct and maintain a
+ certain ordering.
+ See docstring on android_chrome_version.ARCH_VERSION_CODE_DIFF for
+ reasoning.
+ """
+ output = GenerateVersionCodes(
+ self.EXAMPLE_VERSION_VALUES, arch='x86', is_next_build=False)
+ arch_chrome_version_code = output['CHROME_VERSION_CODE']
+
+ self.assertEqual(arch_chrome_version_code, '372000010')
+
+ def testGenerateVersionCodesAndroidArchMips(self):
+ """Assert it handles different architectures correctly.
+
+ Version codes for different builds need to be distinct and maintain a
+ certain ordering.
+ See docstring on android_chrome_version.ARCH_VERSION_CODE_DIFF for
+ reasoning.
+ """
+ output = GenerateVersionCodes(
+ self.EXAMPLE_VERSION_VALUES, arch='mipsel', is_next_build=False)
+ arch_chrome_version_code = output['CHROME_VERSION_CODE']
+
+ self.assertEqual(arch_chrome_version_code, '372000020')
+
+ def testGenerateVersionCodesAndroidArchArm64(self):
+ """Assert it handles different architectures correctly.
+
+ Version codes for different builds need to be distinct and maintain a
+ certain ordering.
+ See docstring on android_chrome_version.ARCH_VERSION_CODE_DIFF for
+ reasoning.
+ """
+ output = GenerateVersionCodes(
+ self.EXAMPLE_VERSION_VALUES, arch='arm64', is_next_build=False)
+ arch_chrome_version_code = output['CHROME_VERSION_CODE']
+
+ self.assertEqual(arch_chrome_version_code, '372000030')
+
+ def testGenerateVersionCodesAndroidArchArm64Variants(self):
+ """Assert it handles 64-bit-specific additional version codes correctly.
+
+ Some additional version codes are generated for 64-bit architectures.
+ See docstring on android_chrome_version.ARCH64_APK_VARIANTS for more info.
+ """
+ output = GenerateVersionCodes(
+ self.EXAMPLE_VERSION_VALUES, arch='arm64', is_next_build=False)
+ arch_monochrome_64_32_version_code = output['MONOCHROME_64_32_VERSION_CODE']
+ arch_monochrome_64_version_code = output['MONOCHROME_64_VERSION_CODE']
+ arch_trichrome_64_32_version_code = output['TRICHROME_64_32_VERSION_CODE']
+ arch_trichrome_64_version_code = output['TRICHROME_64_VERSION_CODE']
+
+ self.assertEqual(arch_monochrome_64_32_version_code, '372000042')
+ self.assertEqual(arch_monochrome_64_version_code, '372000052')
+ self.assertEqual(arch_trichrome_64_32_version_code, '372000043')
+ self.assertEqual(arch_trichrome_64_version_code, '372000053')
+
+ def testGenerateVersionCodesAndroidArchX64(self):
+ """Assert it handles different architectures correctly.
+
+ Version codes for different builds need to be distinct and maintain a
+ certain ordering.
+ See docstring on android_chrome_version.ARCH_VERSION_CODE_DIFF for
+ reasoning.
+ """
+ output = GenerateVersionCodes(
+ self.EXAMPLE_VERSION_VALUES, arch='x64', is_next_build=False)
+ arch_chrome_version_code = output['CHROME_VERSION_CODE']
+
+ self.assertEqual(arch_chrome_version_code, '372000060')
+
+ def testGenerateVersionCodesAndroidArchX64Variants(self):
+ """Assert it handles 64-bit-specific additional version codes correctly.
+
+ Some additional version codes are generated for 64-bit architectures.
+ See docstring on android_chrome_version.ARCH64_APK_VARIANTS for more info.
+ """
+ output = GenerateVersionCodes(
+ self.EXAMPLE_VERSION_VALUES, arch='x64', is_next_build=False)
+ arch_monochrome_64_32_version_code = output['MONOCHROME_64_32_VERSION_CODE']
+ arch_monochrome_64_version_code = output['MONOCHROME_64_VERSION_CODE']
+ arch_trichrome_64_32_version_code = output['TRICHROME_64_32_VERSION_CODE']
+ arch_trichrome_64_version_code = output['TRICHROME_64_VERSION_CODE']
+
+ self.assertEqual(arch_monochrome_64_32_version_code, '372000072')
+ self.assertEqual(arch_monochrome_64_version_code, '372000082')
+ self.assertEqual(arch_trichrome_64_32_version_code, '372000073')
+ self.assertEqual(arch_trichrome_64_version_code, '372000083')
+
+ def testGenerateVersionCodesAndroidArchOrderArm(self):
+ """Assert it handles different architectures correctly.
+
+ Version codes for different builds need to be distinct and maintain a
+ certain ordering.
+ See docstring on android_chrome_version.ARCH_VERSION_CODE_DIFF for
+ reasoning.
+
+ Test arm-related values.
+ """
+ arm_output = GenerateVersionCodes(
+ self.EXAMPLE_VERSION_VALUES, arch='arm', is_next_build=False)
+ arm64_output = GenerateVersionCodes(
+ self.EXAMPLE_VERSION_VALUES, arch='arm64', is_next_build=False)
+
+ arm_chrome_version_code = arm_output['CHROME_VERSION_CODE']
+ arm64_chrome_version_code = arm64_output['CHROME_VERSION_CODE']
+
+ self.assertLess(arm_chrome_version_code, arm64_chrome_version_code)
+
+ def testGenerateVersionCodesAndroidArchOrderX86(self):
+ """Assert it handles different architectures correctly.
+
+ Version codes for different builds need to be distinct and maintain a
+ certain ordering.
+ See docstring on android_chrome_version.ARCH_VERSION_CODE_DIFF for
+ reasoning.
+
+ Test x86-related values.
+ """
+ x86_output = GenerateVersionCodes(
+ self.EXAMPLE_VERSION_VALUES, arch='x86', is_next_build=False)
+ x64_output = GenerateVersionCodes(
+ self.EXAMPLE_VERSION_VALUES, arch='x64', is_next_build=False)
+
+ x86_chrome_version_code = x86_output['CHROME_VERSION_CODE']
+ x64_chrome_version_code = x64_output['CHROME_VERSION_CODE']
+
+ self.assertLess(x86_chrome_version_code, x64_chrome_version_code)
+
+ def testGenerateVersionCodesAndroidWebviewChannelOrderBeta(self):
+ """Assert webview beta channel is higher than stable.
+
+ The channel-specific version codes for standalone webview needs to follow
+ the order stable < beta < dev.
+
+ This allows that if a user opts into beta track, they will always have the
+ beta apk, including any finch experiments targeted at beta users, even when
+ beta and stable channels are otherwise on the same version.
+ """
+ output = GenerateVersionCodes(
+ self.EXAMPLE_VERSION_VALUES, arch='arm', is_next_build=False)
+
+ webview_stable_version_code = output['WEBVIEW_STABLE_VERSION_CODE']
+ webview_beta_version_code = output['WEBVIEW_BETA_VERSION_CODE']
+
+ self.assertGreater(webview_beta_version_code, webview_stable_version_code)
+
+ def testGenerateVersionCodesAndroidWebviewChannelOrderDev(self):
+ """Assert webview dev channel is higher than beta.
+
+ The channel-specific version codes for standalone webview needs to follow
+ the order stable < beta < dev.
+
+ This allows that if a user opts into dev track, they will always have the
+ dev apk, including any finch experiments targeted at dev users, even when
+ dev and beta channels are otherwise on the same version.
+ """
+ output = GenerateVersionCodes(
+ self.EXAMPLE_VERSION_VALUES, arch='arm', is_next_build=False)
+
+ webview_beta_version_code = output['WEBVIEW_BETA_VERSION_CODE']
+ webview_dev_version_code = output['WEBVIEW_DEV_VERSION_CODE']
+
+ self.assertGreater(webview_dev_version_code, webview_beta_version_code)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/chromium/build/util/generate_wrapper.gni b/chromium/build/util/generate_wrapper.gni
new file mode 100644
index 00000000000..74d94330da0
--- /dev/null
+++ b/chromium/build/util/generate_wrapper.gni
@@ -0,0 +1,98 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Wraps a target and any of its arguments to an executable script.
+#
+# Many executable targets have build-time-constant arguments. This
+# template allows those to be wrapped into a single, user- or bot-friendly
+# script at build time.
+#
+# Paths to be wrapped should be relative to root_build_dir and should be
+# wrapped in "@WrappedPath(...)"; see Example below.
+#
+# Variables:
+# generator_script: Path to the script to use to perform the wrapping.
+# Defaults to //build/util/generate_wrapper.py. Generally should only
+# be set by other templates.
+# wrapper_script: Output path.
+# executable: Path to the executable to wrap. Can be a script or a
+# build product. Paths can be relative to the containing gn file
+# or source-absolute.
+# executable_args: List of arguments to write into the wrapper.
+#
+# Example wrapping a checked-in script:
+# generate_wrapper("sample_wrapper") {
+# executable = "//for/bar/sample.py"
+# wrapper_script = "$root_build_dir/bin/run_sample"
+#
+# _sample_argument_path = "//sample/$target_cpu/lib/sample_lib.so"
+# _rebased_sample_argument_path = rebase_path(
+# _sample_argument_path,
+# root_build_dir)
+# executable_args = [
+# "--sample-lib", "@WrappedPath(${_rebased_sample_argument_path})",
+# ]
+# }
+#
+# Example wrapping a build product:
+# generate_wrapper("sample_wrapper") {
+# executable = "$root_build_dir/sample_build_product"
+# wrapper_script = "$root_build_dir/bin/run_sample_build_product"
+# }
+template("generate_wrapper") {
+ _generator_script = "//build/util/generate_wrapper.py"
+ if (defined(invoker.generator_script)) {
+ _generator_script = invoker.generator_script
+ }
+ _executable_to_wrap = invoker.executable
+ _wrapper_script = invoker.wrapper_script
+ if (is_win) {
+ _wrapper_script += ".bat"
+ }
+ if (defined(invoker.executable_args)) {
+ _wrapped_arguments = invoker.executable_args
+ } else {
+ _wrapped_arguments = []
+ }
+
+ action(target_name) {
+ forward_variables_from(invoker,
+ [
+ "data",
+ "data_deps",
+ "deps",
+ "sources",
+ "testonly",
+ ])
+ script = _generator_script
+ if (!defined(data)) {
+ data = []
+ }
+ data += [ _wrapper_script ]
+ outputs = [
+ _wrapper_script,
+ ]
+
+ _rebased_executable_to_wrap =
+ rebase_path(_executable_to_wrap, root_build_dir)
+ _rebased_wrapper_script = rebase_path(_wrapper_script, root_build_dir)
+ if (is_win) {
+ _script_language = "batch"
+ } else {
+ _script_language = "bash"
+ }
+ args = [
+ "--executable",
+ "@WrappedPath(${_rebased_executable_to_wrap})",
+ "--wrapper-script",
+ _rebased_wrapper_script,
+ "--output-directory",
+ rebase_path(root_build_dir, root_build_dir),
+ "--script-language",
+ _script_language,
+ "--",
+ ]
+ args += _wrapped_arguments
+ }
+}
diff --git a/chromium/build/util/generate_wrapper.py b/chromium/build/util/generate_wrapper.py
new file mode 100755
index 00000000000..5373e1ea2e0
--- /dev/null
+++ b/chromium/build/util/generate_wrapper.py
@@ -0,0 +1,136 @@
+#!/usr/bin/env vpython
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Wraps an executable and any provided arguments into an executable script."""
+
+import argparse
+import os
+import sys
+import textwrap
+
+
+# The bash template passes the python script into vpython via stdin.
+# The interpreter doesn't know about the script, so we have bash
+# inject the script location.
+BASH_TEMPLATE = textwrap.dedent(
+ """\
+ #!/usr/bin/env vpython
+ _SCRIPT_LOCATION = __file__
+ {script}
+ """)
+
+
+# The batch template reruns the batch script with vpython, with the -x
+# flag instructing the interpreter to ignore the first line. The interpreter
+# knows about the (batch) script in this case, so it can get the file location
+# directly.
+BATCH_TEMPLATE = textwrap.dedent(
+ """\
+ @SETLOCAL ENABLEDELAYEDEXPANSION \
+ & vpython.bat -x "%~f0" %* \
+ & EXIT /B !ERRORLEVEL!
+ _SCRIPT_LOCATION = __file__
+ {script}
+ """)
+
+
+SCRIPT_TEMPLATES = {
+ 'bash': BASH_TEMPLATE,
+ 'batch': BATCH_TEMPLATE,
+}
+
+
+PY_TEMPLATE = textwrap.dedent(
+ """\
+ import os
+ import re
+ import subprocess
+ import sys
+
+ _WRAPPED_PATH_RE = re.compile(r'@WrappedPath\(([^)]+)\)')
+ _PATH_TO_OUTPUT_DIR = '{path_to_output_dir}'
+ _SCRIPT_DIR = os.path.dirname(os.path.realpath(_SCRIPT_LOCATION))
+
+
+ def ExpandWrappedPath(arg):
+ m = _WRAPPED_PATH_RE.match(arg)
+ if m:
+ return os.path.join(
+ os.path.relpath(_SCRIPT_DIR), _PATH_TO_OUTPUT_DIR, m.group(1))
+ return arg
+
+
+ def ExpandWrappedPaths(args):
+ for i, arg in enumerate(args):
+ args[i] = ExpandWrappedPath(arg)
+ return args
+
+
+ def main(raw_args):
+ executable_path = ExpandWrappedPath('{executable_path}')
+ executable_args = ExpandWrappedPaths({executable_args})
+
+ return subprocess.call([executable_path] + executable_args + raw_args)
+
+
+ if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
+ """)
+
+
+def Wrap(args):
+ """Writes a wrapped script according to the provided arguments.
+
+ Arguments:
+ args: an argparse.Namespace object containing command-line arguments
+ as parsed by a parser returned by CreateArgumentParser.
+ """
+ path_to_output_dir = os.path.relpath(
+ args.output_directory,
+ os.path.dirname(args.wrapper_script))
+
+ with open(args.wrapper_script, 'w') as wrapper_script:
+ py_contents = PY_TEMPLATE.format(
+ path_to_output_dir=path_to_output_dir,
+ executable_path=str(args.executable),
+ executable_args=str(args.executable_args))
+ template = SCRIPT_TEMPLATES[args.script_language]
+ wrapper_script.write(template.format(
+ script=py_contents))
+ os.chmod(args.wrapper_script, 0750)
+
+ return 0
+
+
+def CreateArgumentParser():
+ """Creates an argparse.ArgumentParser instance."""
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ '--executable',
+ help='Executable to wrap.')
+ parser.add_argument(
+ '--wrapper-script',
+ help='Path to which the wrapper script will be written.')
+ parser.add_argument(
+ '--output-directory',
+ help='Path to the output directory.')
+ parser.add_argument(
+ '--script-language',
+ choices=SCRIPT_TEMPLATES.keys(),
+ help='Language in which the warpper script will be written.')
+ parser.add_argument(
+ 'executable_args', nargs='*',
+ help='Arguments to wrap into the executable.')
+ return parser
+
+
+def main(raw_args):
+ parser = CreateArgumentParser()
+ args = parser.parse_args(raw_args)
+ return Wrap(args)
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
diff --git a/chromium/build/util/lastchange.py b/chromium/build/util/lastchange.py
index 81c74312366..6d704b7afa4 100755
--- a/chromium/build/util/lastchange.py
+++ b/chromium/build/util/lastchange.py
@@ -6,21 +6,25 @@
"""
lastchange.py -- Chromium revision fetching utility.
"""
+from __future__ import print_function
-import re
-import logging
import argparse
+import collections
+import logging
import os
import subprocess
import sys
-class VersionInfo(object):
- def __init__(self, revision_id, full_revision_string, timestamp):
- self.revision_id = revision_id
- self.revision = full_revision_string
- self.timestamp = timestamp
+VersionInfo = collections.namedtuple("VersionInfo",
+ ("revision_id", "revision", "timestamp"))
+class GitError(Exception):
+ pass
+# This function exists for compatibility with logic outside this
+# repository that uses this file as a library.
+# TODO(eliribble) remove this function after it has been ported into
+# the repositories that depend on it
def RunGitCommand(directory, command):
"""
Launches git subcommand.
@@ -49,53 +53,95 @@ def RunGitCommand(directory, command):
return None
-def FetchGitRevision(directory, git_log_filter):
+def _RunGitCommand(directory, command):
+ """Launches git subcommand.
+
+ Returns:
+ The stripped stdout of the git command.
+ Raises:
+ GitError on failure, including a nonzero return code.
"""
- Fetch the Git hash (and Cr-Commit-Position if any) for a given directory.
+ command = ['git'] + command
+ # Force shell usage under cygwin. This is a workaround for
+ # mysterious loss of cwd while invoking cygwin's git.
+ # We can't just pass shell=True to Popen, as under win32 this will
+ # cause CMD to be used, while we explicitly want a cygwin shell.
+ if sys.platform == 'cygwin':
+ command = ['sh', '-c', ' '.join(command)]
+ try:
+ logging.info("Executing '%s' in %s", ' '.join(command), directory)
+ proc = subprocess.Popen(command,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ cwd=directory,
+ shell=(sys.platform=='win32'))
+ stdout, stderr = proc.communicate()
+ stdout = stdout.strip()
+ logging.debug("returncode: %d", proc.returncode)
+ logging.debug("stdout: %s", stdout)
+ logging.debug("stderr: %s", stderr)
+ if proc.returncode != 0 or not stdout:
+ raise GitError((
+ "Git command '{}' in {} failed: "
+ "rc={}, stdout='{}' stderr='{}'").format(
+ " ".join(command), directory, proc.returncode, stdout, stderr))
+ return stdout
+ except OSError as e:
+ raise GitError("Git command 'git {}' in {} failed: {}".format(
+ " ".join(command), directory, e))
- Errors are swallowed.
- Args:
- git_log_filter: a string to be used for filtering git log result.
+def GetMergeBase(directory, ref):
+ """
+ Return the merge-base of HEAD and ref.
+ Args:
+ directory: The directory containing the .git directory.
+ ref: The ref to use to find the merge base.
Returns:
- A VersionInfo object or None on error.
+ The git commit SHA of the merge-base as a string.
"""
- hsh = ''
- git_args = ['log', '-1', '--format=%H %ct']
- if git_log_filter is not None:
- git_args.append('--grep=' + git_log_filter)
- proc = RunGitCommand(directory, git_args)
- if proc:
- output = proc.communicate()[0].strip()
- if proc.returncode == 0 and output:
- hsh, ct = output.split()
- else:
- logging.error('Git error: rc=%d, output=%r' %
- (proc.returncode, output))
- if not hsh:
- return None
- pos = ''
- proc = RunGitCommand(directory, ['cat-file', 'commit', hsh])
- if proc:
- output = proc.communicate()[0]
- if proc.returncode == 0 and output:
- for line in reversed(output.splitlines()):
- if line.startswith('Cr-Commit-Position:'):
- pos = line.rsplit()[-1].strip()
- break
- return VersionInfo(hsh, '%s-%s' % (hsh, pos), int(ct))
-
-
-def FetchVersionInfo(directory=None, git_log_filter=None):
+ logging.debug("Calculating merge base between HEAD and %s in %s",
+ ref, directory)
+ command = ['merge-base', 'HEAD', ref]
+ return _RunGitCommand(directory, command)
+
+
+def FetchGitRevision(directory, commit_filter, start_commit="HEAD"):
"""
- Returns the last change (as a VersionInfo object)
- from some appropriate revision control system.
+ Fetch the Git hash (and Cr-Commit-Position if any) for a given directory.
+
+ Args:
+ directory: The directory containing the .git directory.
+ commit_filter: A filter to supply to grep to filter commits
+ start_commit: A commit identifier. The result of this function
+ will be limited to only consider commits before the provided
+ commit.
+ Returns:
+ A VersionInfo object. On error all values will be 0.
"""
- version_info = FetchGitRevision(directory, git_log_filter)
- if not version_info:
- version_info = VersionInfo('0', '0', 0)
- return version_info
+ hash_ = ''
+
+ git_args = ['log', '-1', '--format=%H %ct']
+ if commit_filter is not None:
+ git_args.append('--grep=' + commit_filter)
+
+ git_args.append(start_commit)
+
+ output = _RunGitCommand(directory, git_args)
+ hash_, commit_timestamp = output.split()
+ if not hash_:
+ return VersionInfo('0', '0', 0)
+
+ revision = hash_
+ output = _RunGitCommand(directory, ['cat-file', 'commit', hash_])
+ for line in reversed(output.splitlines()):
+ if line.startswith('Cr-Commit-Position:'):
+ pos = line.rsplit()[-1].strip()
+ logging.debug("Found Cr-Commit-Position '%s'", pos)
+ revision = "{}-{}".format(hash_, pos)
+ break
+ return VersionInfo(hash_, revision, int(commit_timestamp))
def GetHeaderGuard(path):
@@ -136,6 +182,17 @@ def GetHeaderContents(path, define, version):
return header_contents
+def GetGitTopDirectory(source_dir):
+ """Get the top git directory - the directory that contains the .git directory.
+
+ Args:
+ source_dir: The directory to search.
+ Returns:
+ The output of "git rev-parse --show-toplevel" as a string
+ """
+ return _RunGitCommand(source_dir, ['rev-parse', '--show-toplevel'])
+
+
def WriteIfChanged(file_name, contents):
"""
Writes the specified contents to the specified file_name
@@ -160,20 +217,23 @@ def main(argv=None):
parser = argparse.ArgumentParser(usage="lastchange.py [options]")
parser.add_argument("-m", "--version-macro",
- help="Name of C #define when using --header. Defaults to " +
- "LAST_CHANGE.",
- default="LAST_CHANGE")
+ help=("Name of C #define when using --header. Defaults to "
+ "LAST_CHANGE."))
parser.add_argument("-o", "--output", metavar="FILE",
- help="Write last change to FILE. " +
- "Can be combined with --header to write both files.")
+ help=("Write last change to FILE. "
+ "Can be combined with --header to write both files."))
parser.add_argument("--header", metavar="FILE",
help=("Write last change to FILE as a C/C++ header. "
"Can be combined with --output to write both files."))
+ parser.add_argument("--merge-base-ref",
+ default=None,
+ help=("Only consider changes since the merge "
+ "base between HEAD and the provided ref"))
parser.add_argument("--revision-id-only", action='store_true',
help=("Output the revision as a VCS revision ID only (in "
"Git, a 40-character commit hash, excluding the "
"Cr-Commit-Position)."))
- parser.add_argument("--print-only", action='store_true',
+ parser.add_argument("--print-only", action="store_true",
help=("Just print the revision string. Overrides any "
"file-output-related options."))
parser.add_argument("-s", "--source-dir", metavar="DIR",
@@ -183,13 +243,14 @@ def main(argv=None):
"matches the supplied filter regex. Defaults to "
"'^Change-Id:' to suppress local commits."),
default='^Change-Id:')
+
args, extras = parser.parse_known_args(argv[1:])
logging.basicConfig(level=logging.WARNING)
out_file = args.output
header = args.header
- git_log_filter=args.filter
+ commit_filter=args.filter
while len(extras) and out_file is None:
if out_file is None:
@@ -199,19 +260,41 @@ def main(argv=None):
parser.print_help()
sys.exit(2)
- if args.source_dir:
- src_dir = args.source_dir
+ source_dir = args.source_dir or os.path.dirname(os.path.abspath(__file__))
+ try:
+ git_top_dir = GetGitTopDirectory(source_dir)
+ except GitError as e:
+ logging.error("Failed to get git top directory from '%s': %s",
+ source_dir, e)
+ return 2
+
+ if args.merge_base_ref:
+ try:
+ merge_base_sha = GetMergeBase(git_top_dir, args.merge_base_ref)
+ except GitError as e:
+ logging.error("You requested a --merge-base-ref value of '%s' but no "
+ "merge base could be found between it and HEAD. Git "
+ "reports: %s", args.merge_base_ref, e)
+ return 3
else:
- src_dir = os.path.dirname(os.path.abspath(__file__))
+ merge_base_sha = 'HEAD'
+
+ try:
+ version_info = FetchGitRevision(git_top_dir, commit_filter, merge_base_sha)
+ except GitError as e:
+ logging.error("Failed to get version info: %s", e)
+ logging.info(("Falling back to a version of 0.0.0 to allow script to "
+ "finish. This is normal if you are bootstrapping a new environment "
+ "or do not have a git repository for any other reason. If not, this "
+ "could represent a serious error."))
+ version_info = VersionInfo('0', '0', 0)
- version_info = FetchVersionInfo(directory=src_dir,
- git_log_filter=git_log_filter)
revision_string = version_info.revision
if args.revision_id_only:
revision_string = version_info.revision_id
if args.print_only:
- print revision_string
+ print(revision_string)
else:
contents = "LASTCHANGE=%s\n" % revision_string
if not out_file and not args.header:
diff --git a/chromium/build/util/version.gni b/chromium/build/util/version.gni
index 189d109f1fd..ef52bf6ce64 100644
--- a/chromium/build/util/version.gni
+++ b/chromium/build/util/version.gni
@@ -3,6 +3,8 @@
# found in the LICENSE file.
# This exposes the Chrome version as GN variables for use in build files.
+# This also generates the various version codes used for builds of chrome for
+# android.
#
# PREFER NOT TO USE THESE. The GYP build uses this kind of thing extensively.
# However, it is far better to write an action (or use the process_version
@@ -15,28 +17,66 @@
# Give version.py a pattern that will expand to a GN scope consisting of
# all values we need at once.
-_version_dictionary_template =
- "full = \"@MAJOR@.@MINOR@.@BUILD@.@PATCH@\" " +
- "major = \"@MAJOR@\" minor = \"@MINOR@\" " +
- "build = \"@BUILD@\" patch = \"@PATCH@\" " + "version_id = @VERSION_ID@ " +
- "patch_hi = @PATCH_HI@ " + "patch_lo = @PATCH_LO@ "
+_version_dictionary_template = "full = \"@MAJOR@.@MINOR@.@BUILD@.@PATCH@\" " +
+ "major = \"@MAJOR@\" minor = \"@MINOR@\" " +
+ "build = \"@BUILD@\" patch = \"@PATCH@\" "
# The file containing the Chrome version number.
chrome_version_file = "//chrome/VERSION"
+_script_arguments = []
+
+if (target_os == "mac") {
+ _version_dictionary_template += "patch_hi = @PATCH_HI@ patch_lo = @PATCH_LO@ "
+
+ _script_arguments += [
+ "-e",
+ "PATCH_HI=int(PATCH)//256",
+ "-e",
+ "PATCH_LO=int(PATCH)%256",
+ ]
+} else if (target_os == "android") {
+ import("//build/config/android/config.gni")
+
+ _version_dictionary_template +=
+ "chrome_version_code = " + "\"@CHROME_VERSION_CODE@\" " +
+ "chrome_modern_version_code = \"@CHROME_MODERN_VERSION_CODE@\" " +
+ "monochrome_version_code = \"@MONOCHROME_VERSION_CODE@\" " +
+ "trichrome_version_code = \"@TRICHROME_VERSION_CODE@\" " +
+ "notouch_chrome_version_code = \"@NOTOUCH_CHROME_VERSION_CODE@\" " +
+ "webview_stable_version_code = \"@WEBVIEW_STABLE_VERSION_CODE@\" " +
+ "webview_beta_version_code = \"@WEBVIEW_BETA_VERSION_CODE@\" " +
+ "webview_dev_version_code = \"@WEBVIEW_DEV_VERSION_CODE@\" "
+
+ if (target_cpu == "arm64" || target_cpu == "x64") {
+ _version_dictionary_template +=
+ "monochrome_64_32_version_code = \"@MONOCHROME_64_32_VERSION_CODE@\" " +
+ "monochrome_64_version_code = \"@MONOCHROME_64_VERSION_CODE@\" " +
+ "trichrome_64_32_version_code = \"@TRICHROME_64_32_VERSION_CODE@\" " +
+ "trichrome_64_version_code = \"@TRICHROME_64_VERSION_CODE@\" "
+ }
+
+ _script_arguments += [
+ "-a",
+ target_cpu,
+ ]
+
+ if (!public_android_sdk) {
+ _script_arguments += [ "--next" ]
+ }
+}
+
+_script_arguments += [
+ "-f",
+ rebase_path(chrome_version_file, root_build_dir),
+ "-t",
+ _version_dictionary_template,
+ "--os",
+ target_os,
+]
+
_result = exec_script("version.py",
- [
- "-f",
- rebase_path(chrome_version_file, root_build_dir),
- "-t",
- _version_dictionary_template,
- "-e",
- "VERSION_ID='%s%03d00' % (BUILD, int(PATCH))",
- "-e",
- "PATCH_HI=int(PATCH)/256",
- "-e",
- "PATCH_LO=int(PATCH)%256",
- ],
+ _script_arguments,
"scope",
[ chrome_version_file ])
@@ -48,11 +88,52 @@ chrome_version_major = _result.major
chrome_version_minor = _result.minor
chrome_version_build = _result.build
chrome_version_patch = _result.patch
-chrome_version_id = _result.version_id
-chrome_version_patch_hi = _result.patch_hi
-chrome_version_patch_lo = _result.patch_lo
-if (is_mac) {
+if (target_os == "mac") {
+ chrome_version_patch_hi = _result.patch_hi
+ chrome_version_patch_lo = _result.patch_lo
+
chrome_dylib_version = "$chrome_version_build.$chrome_version_patch_hi" +
".$chrome_version_patch_lo"
+} else if (target_os == "android") {
+ forward_variables_from(_result,
+ [
+ "chrome_modern_version_code",
+ "chrome_version_code",
+ "monochrome_64_32_version_code",
+ "monochrome_64_version_code",
+ "monochrome_version_code",
+ "notouch_chrome_version_code",
+ "trichrome_64_32_version_code",
+ "trichrome_64_version_code",
+ "trichrome_version_code",
+ "webview_beta_version_code",
+ "webview_dev_version_code",
+ "webview_stable_version_code",
+ ])
+
+ chrome_version_name = chrome_version_full
+
+ lines_to_write = [
+ "VersionName: $chrome_version_name",
+ "Chrome: $chrome_version_code",
+ "ChromeModern: $chrome_modern_version_code",
+ "Monochrome: $monochrome_version_code",
+ "TrichromeChrome: $trichrome_version_code",
+ "MonochromeFP: $notouch_chrome_version_code",
+ "AndroidWebviewStable: $webview_stable_version_code",
+ "AndroidWebviewBeta: $webview_beta_version_code",
+ "AndroidWebviewDev: $webview_dev_version_code",
+ ]
+
+ if (target_cpu == "arm64" || target_cpu == "x64") {
+ lines_to_write += [
+ "Monochrome6432: $monochrome_64_32_version_code",
+ "Monochrome64: $monochrome_64_version_code",
+ "TrichromeChrome6432: $trichrome_64_32_version_code",
+ "TrichromeChrome64: $trichrome_64_version_code",
+ ]
+ }
+
+ write_file("$root_out_dir/android_chrome_versions.txt", lines_to_write)
}
diff --git a/chromium/build/util/version.py b/chromium/build/util/version.py
index 767412e93d1..4f440c4ee7b 100755
--- a/chromium/build/util/version.py
+++ b/chromium/build/util/version.py
@@ -7,12 +7,16 @@
version.py -- Chromium version string substitution utility.
"""
+from __future__ import print_function
+
import argparse
import os
import sys
+import android_chrome_version
+
-def fetch_values_from_file(values_dict, file_name):
+def FetchValuesFromFile(values_dict, file_name):
"""
Fetches KEYWORD=VALUE settings from the specified file.
@@ -27,10 +31,12 @@ def fetch_values_from_file(values_dict, file_name):
values_dict[key] = val
-def fetch_values(file_list, is_official_build=None):
+def FetchValues(file_list, is_official_build=None):
"""
- Returns a dictionary of values to be used for substitution, populating
- the dictionary with KEYWORD=VALUE settings from the files in 'file_list'.
+ Returns a dictionary of values to be used for substitution.
+
+ Populates the dictionary with KEYWORD=VALUE settings from the files in
+ 'file_list'.
Explicitly adds the following value from internal calculations:
@@ -47,12 +53,12 @@ def fetch_values(file_list, is_official_build=None):
)
for file_name in file_list:
- fetch_values_from_file(values, file_name)
+ FetchValuesFromFile(values, file_name)
return values
-def subst_template(contents, values):
+def SubstTemplate(contents, values):
"""
Returns the template with substituted values from the specified dictionary.
@@ -64,29 +70,31 @@ def subst_template(contents, values):
contains any @KEYWORD@ strings expecting them to be recursively
substituted, okay?
"""
- for key, val in values.iteritems():
+ for key, val in values.items():
try:
contents = contents.replace('@' + key + '@', val)
except TypeError:
- print repr(key), repr(val)
+ print(repr(key), repr(val))
return contents
-def subst_file(file_name, values):
+def SubstFile(file_name, values):
"""
- Returns the contents of the specified file_name with substituted
- values from the specified dictionary.
+ Returns the contents of the specified file_name with substituted values.
- This is like subst_template, except it operates on a file.
+ Substituted values come from the specified dictionary.
+
+ This is like SubstTemplate, except it operates on a file.
"""
template = open(file_name, 'r').read()
- return subst_template(template, values);
+ return SubstTemplate(template, values)
-def write_if_changed(file_name, contents):
+def WriteIfChanged(file_name, contents):
"""
- Writes the specified contents to the specified file_name
- iff the contents are different than the current contents.
+ Writes the specified contents to the specified file_name.
+
+ Does nothing if the contents aren't different than the current contents.
"""
try:
old_contents = open(file_name, 'r').read()
@@ -99,7 +107,8 @@ def write_if_changed(file_name, contents):
open(file_name, 'w').write(contents)
-def main():
+def BuildParser():
+ """Build argparse parser, with added arguments."""
parser = argparse.ArgumentParser()
parser.add_argument('-f', '--file', action='append', default=[],
help='Read variables from FILE.')
@@ -109,30 +118,55 @@ def main():
help='Write substituted strings to FILE.')
parser.add_argument('-t', '--template', default=None,
help='Use TEMPLATE as the strings to substitute.')
- parser.add_argument('-e', '--eval', action='append', default=[],
- help='Evaluate VAL after reading variables. Can be used '
- 'to synthesize variables. e.g. -e \'PATCH_HI=int('
- 'PATCH)/256.')
+ parser.add_argument(
+ '-e',
+ '--eval',
+ action='append',
+ default=[],
+ help='Evaluate VAL after reading variables. Can be used '
+ 'to synthesize variables. e.g. -e \'PATCH_HI=int('
+ 'PATCH)//256.')
+ parser.add_argument(
+ '-a',
+ '--arch',
+ default=None,
+ choices=android_chrome_version.ARCH_CHOICES,
+ help='Set which cpu architecture the build is for.')
+ parser.add_argument('--os', default=None, help='Set the target os.')
parser.add_argument('--official', action='store_true',
help='Whether the current build should be an official '
'build, used in addition to the environment '
'variable.')
+ parser.add_argument(
+ '--next',
+ action='store_true',
+ help='Whether the current build should be a "next" '
+ 'build, which targets pre-release versions of '
+ 'Android')
parser.add_argument('args', nargs=argparse.REMAINDER,
help='For compatibility: INPUT and OUTPUT can be '
'passed as positional arguments.')
- options = parser.parse_args()
+ return parser
+
+def BuildEvals(options, parser):
+ """Construct a dict of passed '-e' arguments for evaluating."""
evals = {}
for expression in options.eval:
try:
evals.update(dict([expression.split('=', 1)]))
except ValueError:
parser.error('-e requires VAR=VAL')
+ return evals
- # Compatibility with old versions that considered the first two positional
- # arguments shorthands for --input and --output.
- while len(options.args) and (options.input is None or \
- options.output is None):
+
+def ModifyOptionsCompat(options, parser):
+ """Support compatibility with old versions.
+
+ Specifically, for old versions that considered the first two
+ positional arguments shorthands for --input and --output.
+ """
+ while len(options.args) and (options.input is None or options.output is None):
if options.input is None:
options.input = options.args.pop(0)
elif options.output is None:
@@ -140,17 +174,48 @@ def main():
if options.args:
parser.error('Unexpected arguments: %r' % options.args)
- values = fetch_values(options.file, options.official)
- for key, val in evals.iteritems():
+
+def GenerateValues(options, evals):
+ """Construct a dict of raw values used to generate output.
+
+ e.g. this could return a dict like
+ {
+ 'BUILD': 74,
+ }
+
+ which would be used to resolve a template like
+ 'build = "@BUILD@"' into 'build = "74"'
+
+ """
+ values = FetchValues(options.file, options.official)
+
+ for key, val in evals.items():
values[key] = str(eval(val, globals(), values))
+ if options.os == 'android':
+ android_chrome_version_codes = android_chrome_version.GenerateVersionCodes(
+ values, options.arch, options.next)
+ values.update(android_chrome_version_codes)
+
+ return values
+
+
+def GenerateOutputContents(options, values):
+ """Construct output string (e.g. from template).
+
+ Arguments:
+ options -- argparse parsed arguments
+ values -- dict with raw values used to resolve the keywords in a template
+ string
+ """
+
if options.template is not None:
- contents = subst_template(options.template, values)
+ return SubstTemplate(options.template, values)
elif options.input:
- contents = subst_file(options.input, values)
+ return SubstFile(options.input, values)
else:
# Generate a default set of version information.
- contents = """MAJOR=%(MAJOR)s
+ return """MAJOR=%(MAJOR)s
MINOR=%(MINOR)s
BUILD=%(BUILD)s
PATCH=%(PATCH)s
@@ -158,10 +223,34 @@ LASTCHANGE=%(LASTCHANGE)s
OFFICIAL_BUILD=%(OFFICIAL_BUILD)s
""" % values
- if options.output is not None:
- write_if_changed(options.output, contents)
+
+def BuildOutput(args):
+ """Gets all input and output values needed for writing output."""
+ # Build argparse parser with arguments
+ parser = BuildParser()
+ options = parser.parse_args(args)
+
+ # Get dict of passed '-e' arguments for evaluating
+ evals = BuildEvals(options, parser)
+ # For compatibility with interface that considered first two positional
+ # arguments shorthands for --input and --output.
+ ModifyOptionsCompat(options, parser)
+
+ # Get the raw values that will be used the generate the output
+ values = GenerateValues(options, evals)
+ # Get the output string
+ contents = GenerateOutputContents(options, values)
+
+ return {'options': options, 'contents': contents}
+
+
+def main():
+ output = BuildOutput(sys.argv[1:])
+
+ if output['options'].output is not None:
+ WriteIfChanged(output['options'].output, output['contents'])
else:
- print contents
+ print(output['contents'])
return 0
diff --git a/chromium/build/util/version_test.py b/chromium/build/util/version_test.py
new file mode 100644
index 00000000000..2a65ddc7163
--- /dev/null
+++ b/chromium/build/util/version_test.py
@@ -0,0 +1,174 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import unittest
+
+import mock
+import version
+
+
+def _ReplaceArgs(args, *replacements):
+ new_args = args[:]
+ for flag, val in replacements:
+ flag_index = args.index(flag)
+ new_args[flag_index + 1] = val
+ return new_args
+
+
+class _VersionTest(unittest.TestCase):
+ """Unittests for the version module.
+ """
+
+ _CHROME_VERSION_FILE = os.path.join(
+ os.path.dirname(__file__), os.pardir, os.pardir, 'chrome', 'VERSION')
+
+ _SCRIPT = os.path.join(os.path.dirname(__file__), 'version.py')
+
+ _EXAMPLE_VERSION = {
+ 'MAJOR': '74',
+ 'MINOR': '0',
+ 'BUILD': '3720',
+ 'PATCH': '0',
+ }
+
+ _EXAMPLE_TEMPLATE = (
+ 'full = "@MAJOR@.@MINOR@.@BUILD@.@PATCH@" '
+ 'major = "@MAJOR@" minor = "@MINOR@" '
+ 'build = "@BUILD@" patch = "@PATCH@" version_id = @VERSION_ID@ ')
+
+ _ANDROID_CHROME_VARS = [
+ 'chrome_version_code',
+ 'chrome_modern_version_code',
+ 'monochrome_version_code',
+ 'trichrome_version_code',
+ 'webview_stable_version_code',
+ 'webview_beta_version_code',
+ 'webview_dev_version_code',
+ ]
+
+ _EXAMPLE_ANDROID_TEMPLATE = (
+ _EXAMPLE_TEMPLATE + ''.join(
+ ['%s = "@%s@" ' % (el, el.upper()) for el in _ANDROID_CHROME_VARS]))
+
+ _EXAMPLE_ARGS = [
+ '-f',
+ _CHROME_VERSION_FILE,
+ '-t',
+ _EXAMPLE_TEMPLATE,
+ ]
+
+ _EXAMPLE_ANDROID_ARGS = _ReplaceArgs(_EXAMPLE_ARGS,
+ ['-t', _EXAMPLE_ANDROID_TEMPLATE]) + [
+ '-a',
+ 'arm',
+ '--os',
+ 'android',
+ ]
+
+ @staticmethod
+ def _RunBuildOutput(new_version_values={},
+ get_new_args=lambda old_args: old_args):
+ """Parameterized helper method for running the main testable method in
+ version.py.
+
+ Keyword arguments:
+ new_version_values -- dict used to update _EXAMPLE_VERSION
+ get_new_args -- lambda for updating _EXAMPLE_ANDROID_ARGS
+ """
+
+ with mock.patch('version.FetchValuesFromFile') as \
+ fetch_values_from_file_mock:
+
+ fetch_values_from_file_mock.side_effect = (lambda values, file :
+ values.update(
+ dict(_VersionTest._EXAMPLE_VERSION, **new_version_values)))
+
+ new_args = get_new_args(_VersionTest._EXAMPLE_ARGS)
+ return version.BuildOutput(new_args)
+
+ def testFetchValuesFromFile(self):
+ """It returns a dict in correct format - { <str>: <str> }, to verify
+ assumption of other tests that mock this function
+ """
+ result = {}
+ version.FetchValuesFromFile(result, self._CHROME_VERSION_FILE)
+
+ for key, val in result.iteritems():
+ self.assertIsInstance(key, str)
+ self.assertIsInstance(val, str)
+
+ def testBuildOutputAndroid(self):
+ """Assert it gives includes assignments of expected variables"""
+ output = self._RunBuildOutput(
+ get_new_args=lambda args: self._EXAMPLE_ANDROID_ARGS)
+ contents = output['contents']
+
+ self.assertRegexpMatches(contents, r'\bchrome_version_code = "\d+"\s')
+ self.assertRegexpMatches(contents,
+ r'\bchrome_modern_version_code = "\d+"\s')
+ self.assertRegexpMatches(contents, r'\bmonochrome_version_code = "\d+"\s')
+ self.assertRegexpMatches(contents, r'\btrichrome_version_code = "\d+"\s')
+ self.assertRegexpMatches(contents,
+ r'\bwebview_stable_version_code = "\d+"\s')
+ self.assertRegexpMatches(contents, r'\bwebview_beta_version_code = "\d+"\s')
+ self.assertRegexpMatches(contents, r'\bwebview_dev_version_code = "\d+"\s')
+
+ def testBuildOutputAndroidArchVariantsArm64(self):
+ """Assert 64-bit-specific version codes"""
+ new_template = (
+ self._EXAMPLE_ANDROID_TEMPLATE +
+ "monochrome_64_32_version_code = \"@MONOCHROME_64_32_VERSION_CODE@\" "
+ "monochrome_64_version_code = \"@MONOCHROME_64_VERSION_CODE@\" "
+ "trichrome_64_32_version_code = \"@TRICHROME_64_32_VERSION_CODE@\" "
+ "trichrome_64_version_code = \"@TRICHROME_64_VERSION_CODE@\" ")
+ args_with_template = _ReplaceArgs(self._EXAMPLE_ANDROID_ARGS,
+ ['-t', new_template])
+ new_args = _ReplaceArgs(args_with_template, ['-a', 'arm64'])
+ output = self._RunBuildOutput(get_new_args=lambda args: new_args)
+ contents = output['contents']
+
+ self.assertRegexpMatches(contents,
+ r'\bmonochrome_64_32_version_code = "\d+"\s')
+ self.assertRegexpMatches(contents,
+ r'\bmonochrome_64_version_code = "\d+"\s')
+ self.assertRegexpMatches(contents,
+ r'\btrichrome_64_32_version_code = "\d+"\s')
+ self.assertRegexpMatches(contents,
+ r'\btrichrome_64_version_code = "\d+"\s')
+
+ def testBuildOutputAndroidArchVariantsX64(self):
+ """Assert 64-bit-specific version codes"""
+ new_template = (
+ self._EXAMPLE_ANDROID_TEMPLATE +
+ "monochrome_64_32_version_code = \"@MONOCHROME_64_32_VERSION_CODE@\" "
+ "monochrome_64_version_code = \"@MONOCHROME_64_VERSION_CODE@\" "
+ "trichrome_64_32_version_code = \"@TRICHROME_64_32_VERSION_CODE@\" "
+ "trichrome_64_version_code = \"@TRICHROME_64_VERSION_CODE@\" ")
+ args_with_template = _ReplaceArgs(self._EXAMPLE_ANDROID_ARGS,
+ ['-t', new_template])
+ new_args = _ReplaceArgs(args_with_template, ['-a', 'x64'])
+ output = self._RunBuildOutput(get_new_args=lambda args: new_args)
+ contents = output['contents']
+
+ self.assertRegexpMatches(contents,
+ r'\bmonochrome_64_32_version_code = "\d+"\s')
+ self.assertRegexpMatches(contents,
+ r'\bmonochrome_64_version_code = "\d+"\s')
+ self.assertRegexpMatches(contents,
+ r'\btrichrome_64_32_version_code = "\d+"\s')
+ self.assertRegexpMatches(contents,
+ r'\btrichrome_64_version_code = "\d+"\s')
+
+ def testBuildOutputAndroidChromeArchInput(self):
+ """Assert it raises an exception when using an invalid architecture input"""
+ new_args = _ReplaceArgs(self._EXAMPLE_ANDROID_ARGS, ['-a', 'foobar'])
+ with self.assertRaises(SystemExit) as cm:
+ self._RunBuildOutput(get_new_args=lambda args: new_args)
+
+ self.assertEqual(cm.exception.code, 2)
+
+
+if __name__ == '__main__':
+ unittest.main()