justincohen | 6a03a3d | 2016-03-26 21:44:38 | [diff] [blame] | 1 | #!/usr/bin/env python |
Sergey Berezin | 5654182d | 2018-04-30 21:24:49 | [diff] [blame] | 2 | # Copyright 2018 The Chromium Authors. All rights reserved. |
justincohen | 6a03a3d | 2016-03-26 21:44:38 | [diff] [blame] | 3 | # Use of this source code is governed by a BSD-style license that can be |
| 4 | # found in the LICENSE file. |
| 5 | |
erikchen | da01626 | 2016-11-09 04:32:13 | [diff] [blame] | 6 | """ |
| 7 | If should_use_hermetic_xcode.py emits "1", and the current toolchain is out of |
| 8 | date: |
| 9 | * Downloads the hermetic mac toolchain |
Sergey Berezin | 5654182d | 2018-04-30 21:24:49 | [diff] [blame] | 10 | * Requires CIPD authentication. Run `cipd auth-login`, use Google account. |
erikchen | da01626 | 2016-11-09 04:32:13 | [diff] [blame] | 11 | * Accepts the license. |
| 12 | * If xcode-select and xcodebuild are not passwordless in sudoers, requires |
| 13 | user interaction. |
Erik Chen | bc7409485 | 2019-07-17 21:47:00 | [diff] [blame] | 14 | * Downloads standalone binaries from [a possibly different version of Xcode]. |
Justin Cohen | 170c409b7 | 2017-09-13 02:37:02 | [diff] [blame] | 15 | |
Sergey Berezin | 5654182d | 2018-04-30 21:24:49 | [diff] [blame] | 16 | The toolchain version can be overridden by setting MAC_TOOLCHAIN_REVISION with |
| 17 | the full revision, e.g. 9A235. |
justincohen | 6a03a3d | 2016-03-26 21:44:38 | [diff] [blame] | 18 | """ |
| 19 | |
Raul Tambre | 9e24293b | 2019-05-12 06:11:07 | [diff] [blame] | 20 | from __future__ import print_function |
| 21 | |
justincohen | 6a03a3d | 2016-03-26 21:44:38 | [diff] [blame] | 22 | import os |
Erik Chen | bc7409485 | 2019-07-17 21:47:00 | [diff] [blame] | 23 | import pkg_resources |
erikchen | d5dfcdb0 | 2017-06-29 00:22:53 | [diff] [blame] | 24 | import platform |
Erik Chen | bc7409485 | 2019-07-17 21:47:00 | [diff] [blame] | 25 | import plistlib |
justincohen | 6a03a3d | 2016-03-26 21:44:38 | [diff] [blame] | 26 | import shutil |
| 27 | import subprocess |
| 28 | import sys |
justincohen | 6a03a3d | 2016-03-26 21:44:38 | [diff] [blame] | 29 | |
Sergey Berezin | 5654182d | 2018-04-30 21:24:49 | [diff] [blame] | 30 | |
Elly Fong-Jones | 5fed7d8 | 2020-01-28 17:41:54 | [diff] [blame] | 31 | # This contains binaries from Xcode 11.2.1, along with the 10.15 SDKs (aka |
| 32 | # 11B53). To build this package, see comments in build/xcode_binaries.yaml |
Erik Chen | bc7409485 | 2019-07-17 21:47:00 | [diff] [blame] | 33 | MAC_BINARIES_LABEL = 'infra_internal/ios/xcode/xcode_binaries/mac-amd64' |
Elly Fong-Jones | 5fed7d8 | 2020-01-28 17:41:54 | [diff] [blame] | 34 | MAC_BINARIES_TAG = 'X5ZbqG_UKa-N64_XSBkAwShWPtzskeXhQRfpzc_1KUYC' |
Erik Chen | bc7409485 | 2019-07-17 21:47:00 | [diff] [blame] | 35 | |
erikchen | d5dfcdb0 | 2017-06-29 00:22:53 | [diff] [blame] | 36 | # The toolchain will not be downloaded if the minimum OS version is not met. |
Elly Fong-Jones | 6998987 | 2019-03-06 18:34:11 | [diff] [blame] | 37 | # 17 is the major version number for macOS 10.13. |
| 38 | # 9E145 (Xcode 9.3) only runs on 10.13.2 and newer. |
| 39 | MAC_MINIMUM_OS_VERSION = 17 |
Erik Chen | a537a905 | 2018-11-13 01:44:43 | [diff] [blame] | 40 | |
justincohen | 6a03a3d | 2016-03-26 21:44:38 | [diff] [blame] | 41 | BASE_DIR = os.path.abspath(os.path.dirname(__file__)) |
Sergey Berezin | 5654182d | 2018-04-30 21:24:49 | [diff] [blame] | 42 | TOOLCHAIN_ROOT = os.path.join(BASE_DIR, 'mac_files') |
| 43 | TOOLCHAIN_BUILD_DIR = os.path.join(TOOLCHAIN_ROOT, 'Xcode.app') |
justincohen | 6a03a3d | 2016-03-26 21:44:38 | [diff] [blame] | 44 | |
Elly Fong-Jones | 33187c6e | 2019-12-13 20:50:11 | [diff] [blame] | 45 | # Always integrity-check the entire SDK. Mac SDK packages are complex and often |
| 46 | # hit edge cases in cipd (eg https://crbug.com/1033987, |
| 47 | # https://crbug.com/915278), and generally when this happens it requires manual |
| 48 | # intervention to fix. |
| 49 | # Note the trailing \n! |
| 50 | PARANOID_MODE = '$ParanoidMode CheckIntegrity\n' |
erikchen | d5dfcdb0 | 2017-06-29 00:22:53 | [diff] [blame] | 51 | |
Sergey Berezin | 5654182d | 2018-04-30 21:24:49 | [diff] [blame] | 52 | def PlatformMeetsHermeticXcodeRequirements(): |
Erik Chen | a537a905 | 2018-11-13 01:44:43 | [diff] [blame] | 53 | major_version = int(platform.release().split('.')[0]) |
Elly Fong-Jones | 6998987 | 2019-03-06 18:34:11 | [diff] [blame] | 54 | return major_version >= MAC_MINIMUM_OS_VERSION |
erikchen | d5dfcdb0 | 2017-06-29 00:22:53 | [diff] [blame] | 55 | |
| 56 | |
Sergey Berezin | 5654182d | 2018-04-30 21:24:49 | [diff] [blame] | 57 | def _UseHermeticToolchain(): |
erikchen | da01626 | 2016-11-09 04:32:13 | [diff] [blame] | 58 | current_dir = os.path.dirname(os.path.realpath(__file__)) |
| 59 | script_path = os.path.join(current_dir, 'mac/should_use_hermetic_xcode.py') |
Sergey Berezin | 5654182d | 2018-04-30 21:24:49 | [diff] [blame] | 60 | proc = subprocess.Popen([script_path, 'mac'], stdout=subprocess.PIPE) |
erikchen | da01626 | 2016-11-09 04:32:13 | [diff] [blame] | 61 | return '1' in proc.stdout.readline() |
justincohen | 6a03a3d | 2016-03-26 21:44:38 | [diff] [blame] | 62 | |
erikchen | da01626 | 2016-11-09 04:32:13 | [diff] [blame] | 63 | |
Sergey Berezin | 5654182d | 2018-04-30 21:24:49 | [diff] [blame] | 64 | def RequestCipdAuthentication(): |
| 65 | """Requests that the user authenticate to access Xcode CIPD packages.""" |
| 66 | |
Raul Tambre | 9e24293b | 2019-05-12 06:11:07 | [diff] [blame] | 67 | print('Access to Xcode CIPD package requires authentication.') |
| 68 | print('-----------------------------------------------------------------') |
| 69 | print() |
| 70 | print('You appear to be a Googler.') |
| 71 | print() |
| 72 | print('I\'m sorry for the hassle, but you may need to do a one-time manual') |
| 73 | print('authentication. Please run:') |
| 74 | print() |
| 75 | print(' cipd auth-login') |
| 76 | print() |
| 77 | print('and follow the instructions.') |
| 78 | print() |
| 79 | print('NOTE: Use your google.com credentials, not chromium.org.') |
| 80 | print() |
| 81 | print('-----------------------------------------------------------------') |
| 82 | print() |
erikchen | da01626 | 2016-11-09 04:32:13 | [diff] [blame] | 83 | sys.stdout.flush() |
justincohen | 6a03a3d | 2016-03-26 21:44:38 | [diff] [blame] | 84 | |
| 85 | |
Sergey Berezin | 5654182d | 2018-04-30 21:24:49 | [diff] [blame] | 86 | def PrintError(message): |
| 87 | # Flush buffers to ensure correct output ordering. |
| 88 | sys.stdout.flush() |
| 89 | sys.stderr.write(message + '\n') |
| 90 | sys.stderr.flush() |
justincohen | 6a03a3d | 2016-03-26 21:44:38 | [diff] [blame] | 91 | |
justincohen | 6a03a3d | 2016-03-26 21:44:38 | [diff] [blame] | 92 | |
Erik Chen | bc7409485 | 2019-07-17 21:47:00 | [diff] [blame] | 93 | def InstallXcodeBinaries(): |
| 94 | """Installs the Xcode binaries needed to build Chrome and accepts the license. |
| 95 | |
| 96 | This is the replacement for InstallXcode that installs a trimmed down version |
| 97 | of Xcode that is OS-version agnostic. |
| 98 | """ |
| 99 | # First make sure the directory exists. It will serve as the cipd root. This |
| 100 | # also ensures that there will be no conflicts of cipd root. |
| 101 | binaries_root = os.path.join(TOOLCHAIN_ROOT, 'xcode_binaries') |
| 102 | if not os.path.exists(binaries_root): |
Nico Weber | a3d706d | 2019-08-09 23:04:38 | [diff] [blame] | 103 | os.makedirs(binaries_root) |
Erik Chen | bc7409485 | 2019-07-17 21:47:00 | [diff] [blame] | 104 | |
| 105 | # 'cipd ensure' is idempotent. |
| 106 | args = [ |
| 107 | 'cipd', 'ensure', '-root', binaries_root, '-ensure-file', '-' |
| 108 | ] |
Erik Chen | dea65134 | 2019-07-23 05:02:47 | [diff] [blame] | 109 | |
Erik Chen | 890362ea | 2019-07-22 22:43:48 | [diff] [blame] | 110 | p = subprocess.Popen( |
| 111 | args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, |
| 112 | stderr=subprocess.PIPE) |
| 113 | stdout, stderr = p.communicate( |
Elly Fong-Jones | 33187c6e | 2019-12-13 20:50:11 | [diff] [blame] | 114 | input=PARANOID_MODE + MAC_BINARIES_LABEL + ' ' + MAC_BINARIES_TAG) |
Erik Chen | 890362ea | 2019-07-22 22:43:48 | [diff] [blame] | 115 | if p.returncode != 0: |
| 116 | print(stdout) |
| 117 | print(stderr) |
| 118 | RequestCipdAuthentication() |
| 119 | return 1 |
Erik Chen | bc7409485 | 2019-07-17 21:47:00 | [diff] [blame] | 120 | |
| 121 | # Accept the license for this version of Xcode if it's newer than the |
| 122 | # currently accepted version. |
| 123 | cipd_xcode_version_plist_path = os.path.join( |
| 124 | binaries_root, 'Contents/version.plist') |
| 125 | cipd_xcode_version_plist = plistlib.readPlist(cipd_xcode_version_plist_path) |
| 126 | cipd_xcode_version = cipd_xcode_version_plist['CFBundleShortVersionString'] |
| 127 | |
| 128 | cipd_license_path = os.path.join( |
| 129 | binaries_root, 'Contents/Resources/LicenseInfo.plist') |
| 130 | cipd_license_plist = plistlib.readPlist(cipd_license_path) |
| 131 | cipd_license_version = cipd_license_plist['licenseID'] |
| 132 | |
| 133 | should_overwrite_license = True |
| 134 | current_license_path = '/Library/Preferences/com.apple.dt.Xcode.plist' |
| 135 | if os.path.exists(current_license_path): |
| 136 | current_license_plist = plistlib.readPlist(current_license_path) |
| 137 | xcode_version = current_license_plist['IDEXcodeVersionForAgreedToGMLicense'] |
| 138 | if (pkg_resources.parse_version(xcode_version) >= |
| 139 | pkg_resources.parse_version(cipd_xcode_version)): |
| 140 | should_overwrite_license = False |
| 141 | |
| 142 | if not should_overwrite_license: |
Erik Chen | 890362ea | 2019-07-22 22:43:48 | [diff] [blame] | 143 | return 0 |
Erik Chen | bc7409485 | 2019-07-17 21:47:00 | [diff] [blame] | 144 | |
| 145 | # Use puppet's sudoers script to accept the license if its available. |
| 146 | license_accept_script = '/usr/local/bin/xcode_accept_license.py' |
| 147 | if os.path.exists(license_accept_script): |
Erik Chen | 687a936 | 2019-07-23 03:47:06 | [diff] [blame] | 148 | args = ['sudo', license_accept_script, '--xcode-version', |
Erik Chen | bc7409485 | 2019-07-17 21:47:00 | [diff] [blame] | 149 | cipd_xcode_version, '--license-version', cipd_license_version] |
Erik Chen | 687a936 | 2019-07-23 03:47:06 | [diff] [blame] | 150 | subprocess.check_call(args) |
Erik Chen | 890362ea | 2019-07-22 22:43:48 | [diff] [blame] | 151 | return 0 |
Erik Chen | bc7409485 | 2019-07-17 21:47:00 | [diff] [blame] | 152 | |
| 153 | # Otherwise manually accept the license. This will prompt for sudo. |
Erik Chen | ffc4275 | 2019-07-20 01:26:26 | [diff] [blame] | 154 | print('Accepting new Xcode license. Requires sudo.') |
| 155 | sys.stdout.flush() |
Erik Chen | bc7409485 | 2019-07-17 21:47:00 | [diff] [blame] | 156 | args = ['sudo', 'defaults', 'write', current_license_path, |
| 157 | 'IDEXcodeVersionForAgreedToGMLicense', cipd_xcode_version] |
Erik Chen | 687a936 | 2019-07-23 03:47:06 | [diff] [blame] | 158 | subprocess.check_call(args) |
Erik Chen | bc7409485 | 2019-07-17 21:47:00 | [diff] [blame] | 159 | args = ['sudo', 'defaults', 'write', current_license_path, |
| 160 | 'IDELastGMLicenseAgreedTo', cipd_license_version] |
Erik Chen | 687a936 | 2019-07-23 03:47:06 | [diff] [blame] | 161 | subprocess.check_call(args) |
Erik Chen | bc7409485 | 2019-07-17 21:47:00 | [diff] [blame] | 162 | args = ['sudo', 'plutil', '-convert', 'xml1', current_license_path] |
Erik Chen | 687a936 | 2019-07-23 03:47:06 | [diff] [blame] | 163 | subprocess.check_call(args) |
Erik Chen | bc7409485 | 2019-07-17 21:47:00 | [diff] [blame] | 164 | |
Erik Chen | 890362ea | 2019-07-22 22:43:48 | [diff] [blame] | 165 | return 0 |
| 166 | |
Erik Chen | bc7409485 | 2019-07-17 21:47:00 | [diff] [blame] | 167 | |
sdefresne | aacc2845 | 2017-01-12 10:33:37 | [diff] [blame] | 168 | def main(): |
| 169 | if sys.platform != 'darwin': |
| 170 | return 0 |
| 171 | |
Sergey Berezin | 5654182d | 2018-04-30 21:24:49 | [diff] [blame] | 172 | if not _UseHermeticToolchain(): |
Raul Tambre | 9e24293b | 2019-05-12 06:11:07 | [diff] [blame] | 173 | print('Skipping Mac toolchain installation for mac') |
Sergey Berezin | 5654182d | 2018-04-30 21:24:49 | [diff] [blame] | 174 | return 0 |
erikchen | d5dfcdb0 | 2017-06-29 00:22:53 | [diff] [blame] | 175 | |
Sergey Berezin | 5654182d | 2018-04-30 21:24:49 | [diff] [blame] | 176 | if not PlatformMeetsHermeticXcodeRequirements(): |
Raul Tambre | 9e24293b | 2019-05-12 06:11:07 | [diff] [blame] | 177 | print('OS version does not support toolchain.') |
Sergey Berezin | 5654182d | 2018-04-30 21:24:49 | [diff] [blame] | 178 | return 0 |
sdefresne | aacc2845 | 2017-01-12 10:33:37 | [diff] [blame] | 179 | |
Nico Weber | 6b9763fb | 2019-08-09 18:03:10 | [diff] [blame] | 180 | # Delete obsolete hermetic full Xcode folder, the build now uses |
| 181 | # build/mac_files/xcode_binaries instead. |
| 182 | if os.path.exists(TOOLCHAIN_BUILD_DIR): |
| 183 | # TODO(thakis): Remove this after it's been here for a few months. |
| 184 | print('Deleting obsolete build/mac_files/Xcode.app...', end='') |
| 185 | sys.stdout.flush() |
| 186 | shutil.rmtree(TOOLCHAIN_BUILD_DIR) |
| 187 | print('done') |
sdefresne | aacc2845 | 2017-01-12 10:33:37 | [diff] [blame] | 188 | |
Erik Chen | 890362ea | 2019-07-22 22:43:48 | [diff] [blame] | 189 | return InstallXcodeBinaries() |
sdefresne | aacc2845 | 2017-01-12 10:33:37 | [diff] [blame] | 190 | |
| 191 | |
justincohen | 6a03a3d | 2016-03-26 21:44:38 | [diff] [blame] | 192 | if __name__ == '__main__': |
| 193 | sys.exit(main()) |