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. |
Justin Cohen | 170c409b7 | 2017-09-13 02:37:02 | [diff] [blame] | 14 | |
Sergey Berezin | 5654182d | 2018-04-30 21:24:49 | [diff] [blame] | 15 | The toolchain version can be overridden by setting MAC_TOOLCHAIN_REVISION with |
| 16 | the full revision, e.g. 9A235. |
justincohen | 6a03a3d | 2016-03-26 21:44:38 | [diff] [blame] | 17 | """ |
| 18 | |
Raul Tambre | 9e24293b | 2019-05-12 06:11:07 | [diff] [blame^] | 19 | from __future__ import print_function |
| 20 | |
justincohen | 6a03a3d | 2016-03-26 21:44:38 | [diff] [blame] | 21 | import os |
erikchen | d5dfcdb0 | 2017-06-29 00:22:53 | [diff] [blame] | 22 | import platform |
justincohen | 6a03a3d | 2016-03-26 21:44:38 | [diff] [blame] | 23 | import shutil |
| 24 | import subprocess |
| 25 | import sys |
justincohen | 6a03a3d | 2016-03-26 21:44:38 | [diff] [blame] | 26 | |
Sergey Berezin | 5654182d | 2018-04-30 21:24:49 | [diff] [blame] | 27 | |
| 28 | # This can be changed after running: |
| 29 | # mac_toolchain upload -xcode-path path/to/Xcode.app |
Elly Fong-Jones | 6998987 | 2019-03-06 18:34:11 | [diff] [blame] | 30 | MAC_TOOLCHAIN_VERSION = '9E501' |
Sergey Berezin | 5654182d | 2018-04-30 21:24:49 | [diff] [blame] | 31 | |
erikchen | d5dfcdb0 | 2017-06-29 00:22:53 | [diff] [blame] | 32 | # 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] | 33 | # 17 is the major version number for macOS 10.13. |
| 34 | # 9E145 (Xcode 9.3) only runs on 10.13.2 and newer. |
| 35 | MAC_MINIMUM_OS_VERSION = 17 |
Erik Chen | a537a905 | 2018-11-13 01:44:43 | [diff] [blame] | 36 | |
Sergey Berezin | 5654182d | 2018-04-30 21:24:49 | [diff] [blame] | 37 | MAC_TOOLCHAIN_INSTALLER = 'mac_toolchain' |
justincohen | abbd93dc | 2016-11-30 20:11:28 | [diff] [blame] | 38 | |
| 39 | # Absolute path to src/ directory. |
| 40 | REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) |
| 41 | |
| 42 | # Absolute path to a file with gclient solutions. |
| 43 | GCLIENT_CONFIG = os.path.join(os.path.dirname(REPO_ROOT), '.gclient') |
justincohen | 6a03a3d | 2016-03-26 21:44:38 | [diff] [blame] | 44 | |
| 45 | BASE_DIR = os.path.abspath(os.path.dirname(__file__)) |
Sergey Berezin | 5654182d | 2018-04-30 21:24:49 | [diff] [blame] | 46 | TOOLCHAIN_ROOT = os.path.join(BASE_DIR, 'mac_files') |
| 47 | TOOLCHAIN_BUILD_DIR = os.path.join(TOOLCHAIN_ROOT, 'Xcode.app') |
| 48 | STAMP_FILE = os.path.join(TOOLCHAIN_ROOT, 'toolchain_build_revision') |
justincohen | 6a03a3d | 2016-03-26 21:44:38 | [diff] [blame] | 49 | |
erikchen | d5dfcdb0 | 2017-06-29 00:22:53 | [diff] [blame] | 50 | |
Sergey Berezin | 5654182d | 2018-04-30 21:24:49 | [diff] [blame] | 51 | def PlatformMeetsHermeticXcodeRequirements(): |
Erik Chen | a537a905 | 2018-11-13 01:44:43 | [diff] [blame] | 52 | major_version = int(platform.release().split('.')[0]) |
Elly Fong-Jones | 6998987 | 2019-03-06 18:34:11 | [diff] [blame] | 53 | return major_version >= MAC_MINIMUM_OS_VERSION |
erikchen | d5dfcdb0 | 2017-06-29 00:22:53 | [diff] [blame] | 54 | |
| 55 | |
Sergey Berezin | 5654182d | 2018-04-30 21:24:49 | [diff] [blame] | 56 | def _UseHermeticToolchain(): |
erikchen | da01626 | 2016-11-09 04:32:13 | [diff] [blame] | 57 | current_dir = os.path.dirname(os.path.realpath(__file__)) |
| 58 | script_path = os.path.join(current_dir, 'mac/should_use_hermetic_xcode.py') |
Sergey Berezin | 5654182d | 2018-04-30 21:24:49 | [diff] [blame] | 59 | proc = subprocess.Popen([script_path, 'mac'], stdout=subprocess.PIPE) |
erikchen | da01626 | 2016-11-09 04:32:13 | [diff] [blame] | 60 | return '1' in proc.stdout.readline() |
justincohen | 6a03a3d | 2016-03-26 21:44:38 | [diff] [blame] | 61 | |
erikchen | da01626 | 2016-11-09 04:32:13 | [diff] [blame] | 62 | |
Sergey Berezin | 5654182d | 2018-04-30 21:24:49 | [diff] [blame] | 63 | def RequestCipdAuthentication(): |
| 64 | """Requests that the user authenticate to access Xcode CIPD packages.""" |
| 65 | |
Raul Tambre | 9e24293b | 2019-05-12 06:11:07 | [diff] [blame^] | 66 | print('Access to Xcode CIPD package requires authentication.') |
| 67 | print('-----------------------------------------------------------------') |
| 68 | print() |
| 69 | print('You appear to be a Googler.') |
| 70 | print() |
| 71 | print('I\'m sorry for the hassle, but you may need to do a one-time manual') |
| 72 | print('authentication. Please run:') |
| 73 | print() |
| 74 | print(' cipd auth-login') |
| 75 | print() |
| 76 | print('and follow the instructions.') |
| 77 | print() |
| 78 | print('NOTE: Use your google.com credentials, not chromium.org.') |
| 79 | print() |
| 80 | print('-----------------------------------------------------------------') |
| 81 | print() |
erikchen | da01626 | 2016-11-09 04:32:13 | [diff] [blame] | 82 | sys.stdout.flush() |
justincohen | 6a03a3d | 2016-03-26 21:44:38 | [diff] [blame] | 83 | |
| 84 | |
Sergey Berezin | 5654182d | 2018-04-30 21:24:49 | [diff] [blame] | 85 | def PrintError(message): |
| 86 | # Flush buffers to ensure correct output ordering. |
| 87 | sys.stdout.flush() |
| 88 | sys.stderr.write(message + '\n') |
| 89 | sys.stderr.flush() |
justincohen | 6a03a3d | 2016-03-26 21:44:38 | [diff] [blame] | 90 | |
justincohen | 6a03a3d | 2016-03-26 21:44:38 | [diff] [blame] | 91 | |
Sergey Berezin | 5654182d | 2018-04-30 21:24:49 | [diff] [blame] | 92 | def InstallXcode(xcode_build_version, installer_cmd, xcode_app_path): |
| 93 | """Installs the requested Xcode build version. |
justincohen | 6a03a3d | 2016-03-26 21:44:38 | [diff] [blame] | 94 | |
Sergey Berezin | 5654182d | 2018-04-30 21:24:49 | [diff] [blame] | 95 | Args: |
| 96 | xcode_build_version: (string) Xcode build version to install. |
| 97 | installer_cmd: (string) Path to mac_toolchain command to install Xcode. |
| 98 | See https://chromium.googlesource.com/infra/infra/+/master/go/src/infra/cmd/mac_toolchain/ |
| 99 | xcode_app_path: (string) Path to install the contents of Xcode.app. |
justincohen | 6a03a3d | 2016-03-26 21:44:38 | [diff] [blame] | 100 | |
Sergey Berezin | 5654182d | 2018-04-30 21:24:49 | [diff] [blame] | 101 | Returns: |
| 102 | True if installation was successful. False otherwise. |
| 103 | """ |
| 104 | args = [ |
| 105 | installer_cmd, 'install', |
| 106 | '-kind', 'mac', |
| 107 | '-xcode-version', xcode_build_version.lower(), |
| 108 | '-output-dir', xcode_app_path, |
| 109 | ] |
justincohen | 6a03a3d | 2016-03-26 21:44:38 | [diff] [blame] | 110 | |
Sergey Berezin | 5654182d | 2018-04-30 21:24:49 | [diff] [blame] | 111 | # Buildbot slaves need to use explicit credentials. LUCI bots should NOT set |
| 112 | # this variable. |
| 113 | creds = os.environ.get('MAC_TOOLCHAIN_CREDS') |
| 114 | if creds: |
| 115 | args.extend(['--service-account-json', creds]) |
| 116 | |
justincohen | 6a03a3d | 2016-03-26 21:44:38 | [diff] [blame] | 117 | try: |
Sergey Berezin | 5654182d | 2018-04-30 21:24:49 | [diff] [blame] | 118 | subprocess.check_call(args) |
| 119 | except subprocess.CalledProcessError as e: |
| 120 | PrintError('Xcode build version %s failed to install: %s\n' % ( |
| 121 | xcode_build_version, e)) |
| 122 | RequestCipdAuthentication() |
| 123 | return False |
| 124 | except OSError as e: |
| 125 | PrintError(('Xcode installer "%s" failed to execute' |
| 126 | ' (not on PATH or not installed).') % installer_cmd) |
| 127 | return False |
justincohen | 6a03a3d | 2016-03-26 21:44:38 | [diff] [blame] | 128 | |
Sergey Berezin | 5654182d | 2018-04-30 21:24:49 | [diff] [blame] | 129 | return True |
justincohen | 6a03a3d | 2016-03-26 21:44:38 | [diff] [blame] | 130 | |
sdefresne | aacc2845 | 2017-01-12 10:33:37 | [diff] [blame] | 131 | |
| 132 | def main(): |
| 133 | if sys.platform != 'darwin': |
| 134 | return 0 |
| 135 | |
Sergey Berezin | 5654182d | 2018-04-30 21:24:49 | [diff] [blame] | 136 | if not _UseHermeticToolchain(): |
Raul Tambre | 9e24293b | 2019-05-12 06:11:07 | [diff] [blame^] | 137 | print('Skipping Mac toolchain installation for mac') |
Sergey Berezin | 5654182d | 2018-04-30 21:24:49 | [diff] [blame] | 138 | return 0 |
erikchen | d5dfcdb0 | 2017-06-29 00:22:53 | [diff] [blame] | 139 | |
Sergey Berezin | 5654182d | 2018-04-30 21:24:49 | [diff] [blame] | 140 | if not PlatformMeetsHermeticXcodeRequirements(): |
Raul Tambre | 9e24293b | 2019-05-12 06:11:07 | [diff] [blame^] | 141 | print('OS version does not support toolchain.') |
Sergey Berezin | 5654182d | 2018-04-30 21:24:49 | [diff] [blame] | 142 | return 0 |
sdefresne | aacc2845 | 2017-01-12 10:33:37 | [diff] [blame] | 143 | |
Sergey Berezin | 5654182d | 2018-04-30 21:24:49 | [diff] [blame] | 144 | toolchain_version = os.environ.get('MAC_TOOLCHAIN_REVISION', |
| 145 | MAC_TOOLCHAIN_VERSION) |
| 146 | |
| 147 | # On developer machines, mac_toolchain tool is provided by |
| 148 | # depot_tools. On the bots, the recipe is responsible for installing |
| 149 | # it and providing the path to the executable. |
| 150 | installer_cmd = os.environ.get('MAC_TOOLCHAIN_INSTALLER', |
| 151 | MAC_TOOLCHAIN_INSTALLER) |
| 152 | |
| 153 | toolchain_root = TOOLCHAIN_ROOT |
| 154 | xcode_app_path = TOOLCHAIN_BUILD_DIR |
| 155 | stamp_file = STAMP_FILE |
| 156 | |
| 157 | # Delete the old "hermetic" installation if detected. |
| 158 | # TODO(crbug.com/797051): remove this once the old "hermetic" solution is no |
| 159 | # longer in use. |
| 160 | if os.path.exists(stamp_file): |
Raul Tambre | 9e24293b | 2019-05-12 06:11:07 | [diff] [blame^] | 161 | print( |
| 162 | 'Detected old hermetic installation at %s. Deleting.' % toolchain_root) |
Sergey Berezin | 5654182d | 2018-04-30 21:24:49 | [diff] [blame] | 163 | shutil.rmtree(toolchain_root) |
| 164 | |
| 165 | success = InstallXcode(toolchain_version, installer_cmd, xcode_app_path) |
| 166 | if not success: |
| 167 | return 1 |
sdefresne | aacc2845 | 2017-01-12 10:33:37 | [diff] [blame] | 168 | |
| 169 | return 0 |
| 170 | |
| 171 | |
justincohen | 6a03a3d | 2016-03-26 21:44:38 | [diff] [blame] | 172 | if __name__ == '__main__': |
| 173 | sys.exit(main()) |