2 # Copyright 2018 The Chromium 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.
7 If should_use_hermetic_xcode.py emits "1", and the current toolchain is out of
9 * Downloads the hermetic mac toolchain
10 * Requires CIPD authentication. Run `cipd auth-login`, use Google account.
11 * Accepts the license.
12 * If xcode-select and xcodebuild are not passwordless in sudoers, requires
15 The toolchain version can be overridden by setting MAC_TOOLCHAIN_REVISION with
16 the full revision, e.g. 9A235.
26 # This can be changed after running:
27 # mac_toolchain upload -xcode-path path/to/Xcode.app
28 MAC_TOOLCHAIN_VERSION = '8E2002'
30 # The toolchain will not be downloaded if the minimum OS version is not met.
31 # 16 is the major version number for macOS 10.12.
32 MAC_MINIMUM_OS_VERSION = 16
34 # The toolchain will not be downloaded if the maximum OS version is exceeded.
35 # 17 is the major version number for macOS 10.13. Xcode 8 does not run on macOS
37 # TODO(https://crbug.com/780980): Once we build with 10.13 SDK, Xcode 9, we
38 # should be able to remove this upper bound.
39 MAC_MAXIMUM_OS_VERSION = 17
41 MAC_TOOLCHAIN_INSTALLER = 'mac_toolchain'
43 # Absolute path to src/ directory.
44 REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
46 # Absolute path to a file with gclient solutions.
47 GCLIENT_CONFIG = os.path.join(os.path.dirname(REPO_ROOT), '.gclient')
49 BASE_DIR = os.path.abspath(os.path.dirname(__file__))
50 TOOLCHAIN_ROOT = os.path.join(BASE_DIR, 'mac_files')
51 TOOLCHAIN_BUILD_DIR = os.path.join(TOOLCHAIN_ROOT, 'Xcode.app')
52 STAMP_FILE = os.path.join(TOOLCHAIN_ROOT, 'toolchain_build_revision')
55 def PlatformMeetsHermeticXcodeRequirements():
56 major_version = int(platform.release().split('.')[0])
57 return (major_version >= MAC_MINIMUM_OS_VERSION and
58 major_version <= MAC_MAXIMUM_OS_VERSION)
61 def _UseHermeticToolchain():
62 current_dir = os.path.dirname(os.path.realpath(__file__))
63 script_path = os.path.join(current_dir, 'mac/should_use_hermetic_xcode.py')
64 proc = subprocess.Popen([script_path, 'mac'], stdout=subprocess.PIPE)
65 return '1' in proc.stdout.readline()
68 def RequestCipdAuthentication():
69 """Requests that the user authenticate to access Xcode CIPD packages."""
71 print 'Access to Xcode CIPD package requires authentication.'
72 print '-----------------------------------------------------------------'
74 print 'You appear to be a Googler.'
76 print 'I\'m sorry for the hassle, but you may need to do a one-time manual'
77 print 'authentication. Please run:'
79 print ' cipd auth-login'
81 print 'and follow the instructions.'
83 print 'NOTE: Use your google.com credentials, not chromium.org.'
85 print '-----------------------------------------------------------------'
90 def PrintError(message):
91 # Flush buffers to ensure correct output ordering.
93 sys.stderr.write(message + '\n')
97 def InstallXcode(xcode_build_version, installer_cmd, xcode_app_path):
98 """Installs the requested Xcode build version.
101 xcode_build_version: (string) Xcode build version to install.
102 installer_cmd: (string) Path to mac_toolchain command to install Xcode.
103 See https://chromium.googlesource.com/infra/infra/+/master/go/src/infra/cmd/mac_toolchain/
104 xcode_app_path: (string) Path to install the contents of Xcode.app.
107 True if installation was successful. False otherwise.
110 installer_cmd, 'install',
112 '-xcode-version', xcode_build_version.lower(),
113 '-output-dir', xcode_app_path,
116 # Buildbot slaves need to use explicit credentials. LUCI bots should NOT set
118 creds = os.environ.get('MAC_TOOLCHAIN_CREDS')
120 args.extend(['--service-account-json', creds])
123 subprocess.check_call(args)
124 except subprocess.CalledProcessError as e:
125 PrintError('Xcode build version %s failed to install: %s\n' % (
126 xcode_build_version, e))
127 RequestCipdAuthentication()
130 PrintError(('Xcode installer "%s" failed to execute'
131 ' (not on PATH or not installed).') % installer_cmd)
138 if sys.platform != 'darwin':
141 if not _UseHermeticToolchain():
142 print 'Skipping Mac toolchain installation for mac'
145 if not PlatformMeetsHermeticXcodeRequirements():
146 print 'OS version does not support toolchain.'
149 toolchain_version = os.environ.get('MAC_TOOLCHAIN_REVISION',
150 MAC_TOOLCHAIN_VERSION)
152 # On developer machines, mac_toolchain tool is provided by
153 # depot_tools. On the bots, the recipe is responsible for installing
154 # it and providing the path to the executable.
155 installer_cmd = os.environ.get('MAC_TOOLCHAIN_INSTALLER',
156 MAC_TOOLCHAIN_INSTALLER)
158 toolchain_root = TOOLCHAIN_ROOT
159 xcode_app_path = TOOLCHAIN_BUILD_DIR
160 stamp_file = STAMP_FILE
162 # Delete the old "hermetic" installation if detected.
163 # TODO(crbug.com/797051): remove this once the old "hermetic" solution is no
165 if os.path.exists(stamp_file):
166 print 'Detected old hermetic installation at %s. Deleting.' % (
168 shutil.rmtree(toolchain_root)
170 success = InstallXcode(toolchain_version, installer_cmd, xcode_app_path)
177 if __name__ == '__main__':