1*6777b538SAndroid Build Coastguard Worker#!/usr/bin/env python3 2*6777b538SAndroid Build Coastguard Worker 3*6777b538SAndroid Build Coastguard Worker# Copyright 2018 The Chromium Authors 4*6777b538SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be 5*6777b538SAndroid Build Coastguard Worker# found in the LICENSE file. 6*6777b538SAndroid Build Coastguard Worker 7*6777b538SAndroid Build Coastguard Worker""" 8*6777b538SAndroid Build Coastguard WorkerIf should_use_hermetic_xcode.py emits "1", and the current toolchain is out of 9*6777b538SAndroid Build Coastguard Workerdate: 10*6777b538SAndroid Build Coastguard Worker * Downloads the hermetic mac toolchain 11*6777b538SAndroid Build Coastguard Worker * Requires CIPD authentication. Run `cipd auth-login`, use Google account. 12*6777b538SAndroid Build Coastguard Worker * Accepts the license. 13*6777b538SAndroid Build Coastguard Worker * If xcode-select and xcodebuild are not passwordless in sudoers, requires 14*6777b538SAndroid Build Coastguard Worker user interaction. 15*6777b538SAndroid Build Coastguard Worker * Downloads standalone binaries from [a possibly different version of Xcode]. 16*6777b538SAndroid Build Coastguard Worker 17*6777b538SAndroid Build Coastguard WorkerThe toolchain version can be overridden by setting MAC_TOOLCHAIN_REVISION with 18*6777b538SAndroid Build Coastguard Workerthe full revision, e.g. 9A235. 19*6777b538SAndroid Build Coastguard Worker""" 20*6777b538SAndroid Build Coastguard Worker 21*6777b538SAndroid Build Coastguard Workerimport argparse 22*6777b538SAndroid Build Coastguard Workerimport os 23*6777b538SAndroid Build Coastguard Workerimport platform 24*6777b538SAndroid Build Coastguard Workerimport plistlib 25*6777b538SAndroid Build Coastguard Workerimport shutil 26*6777b538SAndroid Build Coastguard Workerimport subprocess 27*6777b538SAndroid Build Coastguard Workerimport sys 28*6777b538SAndroid Build Coastguard Worker 29*6777b538SAndroid Build Coastguard Worker 30*6777b538SAndroid Build Coastguard Workerdef LoadPList(path): 31*6777b538SAndroid Build Coastguard Worker """Loads Plist at |path| and returns it as a dictionary.""" 32*6777b538SAndroid Build Coastguard Worker with open(path, 'rb') as f: 33*6777b538SAndroid Build Coastguard Worker return plistlib.load(f) 34*6777b538SAndroid Build Coastguard Worker 35*6777b538SAndroid Build Coastguard Worker 36*6777b538SAndroid Build Coastguard Worker# This contains binaries from Xcode 15.0 15A240d along with the macOS 14.0 SDK 37*6777b538SAndroid Build Coastguard Worker# (14.0 23A334). To build these packages, see comments in 38*6777b538SAndroid Build Coastguard Worker# build/xcode_binaries.yaml 39*6777b538SAndroid Build Coastguard Worker# To update the version numbers, open Xcode's "About Xcode" for the first number 40*6777b538SAndroid Build Coastguard Worker# and run `xcrun --show-sdk-build-version` for the second. 41*6777b538SAndroid Build Coastguard Worker# To update the _TAG, use the output of the `cipd create` command mentioned in 42*6777b538SAndroid Build Coastguard Worker# xcode_binaries.yaml. 43*6777b538SAndroid Build Coastguard Worker 44*6777b538SAndroid Build Coastguard WorkerMAC_BINARIES_LABEL = 'infra_internal/ios/xcode/xcode_binaries/mac-amd64' 45*6777b538SAndroid Build Coastguard WorkerMAC_BINARIES_TAG = 'dC_BLs9U850OLk8m4V7yxysPhP-ixJ2b5c7hVm8B7tIC' 46*6777b538SAndroid Build Coastguard Worker 47*6777b538SAndroid Build Coastguard Worker# The toolchain will not be downloaded if the minimum OS version is not met. 19 48*6777b538SAndroid Build Coastguard Worker# is the major version number for macOS 10.15. Xcode 15.0 only runs on macOS 49*6777b538SAndroid Build Coastguard Worker# 13.5 and newer, but some bots are still running older OS versions. macOS 50*6777b538SAndroid Build Coastguard Worker# 10.15.4, the OS minimum through Xcode 12.4, still seems to work. 51*6777b538SAndroid Build Coastguard WorkerMAC_MINIMUM_OS_VERSION = [19, 4] 52*6777b538SAndroid Build Coastguard Worker 53*6777b538SAndroid Build Coastguard WorkerBASE_DIR = os.path.abspath(os.path.dirname(__file__)) 54*6777b538SAndroid Build Coastguard WorkerTOOLCHAIN_ROOT = os.path.join(BASE_DIR, 'mac_files') 55*6777b538SAndroid Build Coastguard WorkerTOOLCHAIN_BUILD_DIR = os.path.join(TOOLCHAIN_ROOT, 'Xcode.app') 56*6777b538SAndroid Build Coastguard Worker 57*6777b538SAndroid Build Coastguard Worker# Always integrity-check the entire SDK. Mac SDK packages are complex and often 58*6777b538SAndroid Build Coastguard Worker# hit edge cases in cipd (eg https://crbug.com/1033987, 59*6777b538SAndroid Build Coastguard Worker# https://crbug.com/915278), and generally when this happens it requires manual 60*6777b538SAndroid Build Coastguard Worker# intervention to fix. 61*6777b538SAndroid Build Coastguard Worker# Note the trailing \n! 62*6777b538SAndroid Build Coastguard WorkerPARANOID_MODE = '$ParanoidMode CheckIntegrity\n' 63*6777b538SAndroid Build Coastguard Worker 64*6777b538SAndroid Build Coastguard Worker 65*6777b538SAndroid Build Coastguard Workerdef PlatformMeetsHermeticXcodeRequirements(): 66*6777b538SAndroid Build Coastguard Worker if sys.platform != 'darwin': 67*6777b538SAndroid Build Coastguard Worker return True 68*6777b538SAndroid Build Coastguard Worker needed = MAC_MINIMUM_OS_VERSION 69*6777b538SAndroid Build Coastguard Worker major_version = [int(v) for v in platform.release().split('.')[:len(needed)]] 70*6777b538SAndroid Build Coastguard Worker return major_version >= needed 71*6777b538SAndroid Build Coastguard Worker 72*6777b538SAndroid Build Coastguard Worker 73*6777b538SAndroid Build Coastguard Workerdef _UseHermeticToolchain(): 74*6777b538SAndroid Build Coastguard Worker current_dir = os.path.dirname(os.path.realpath(__file__)) 75*6777b538SAndroid Build Coastguard Worker script_path = os.path.join(current_dir, 'mac/should_use_hermetic_xcode.py') 76*6777b538SAndroid Build Coastguard Worker proc = subprocess.Popen([script_path, 'mac'], stdout=subprocess.PIPE) 77*6777b538SAndroid Build Coastguard Worker return '1' in proc.stdout.readline().decode() 78*6777b538SAndroid Build Coastguard Worker 79*6777b538SAndroid Build Coastguard Worker 80*6777b538SAndroid Build Coastguard Workerdef RequestCipdAuthentication(): 81*6777b538SAndroid Build Coastguard Worker """Requests that the user authenticate to access Xcode CIPD packages.""" 82*6777b538SAndroid Build Coastguard Worker 83*6777b538SAndroid Build Coastguard Worker print('Access to Xcode CIPD package requires authentication.') 84*6777b538SAndroid Build Coastguard Worker print('-----------------------------------------------------------------') 85*6777b538SAndroid Build Coastguard Worker print() 86*6777b538SAndroid Build Coastguard Worker print('You appear to be a Googler.') 87*6777b538SAndroid Build Coastguard Worker print() 88*6777b538SAndroid Build Coastguard Worker print('I\'m sorry for the hassle, but you may need to do a one-time manual') 89*6777b538SAndroid Build Coastguard Worker print('authentication. Please run:') 90*6777b538SAndroid Build Coastguard Worker print() 91*6777b538SAndroid Build Coastguard Worker print(' cipd auth-login') 92*6777b538SAndroid Build Coastguard Worker print() 93*6777b538SAndroid Build Coastguard Worker print('and follow the instructions.') 94*6777b538SAndroid Build Coastguard Worker print() 95*6777b538SAndroid Build Coastguard Worker print('NOTE: Use your google.com credentials, not chromium.org.') 96*6777b538SAndroid Build Coastguard Worker print() 97*6777b538SAndroid Build Coastguard Worker print('-----------------------------------------------------------------') 98*6777b538SAndroid Build Coastguard Worker print() 99*6777b538SAndroid Build Coastguard Worker sys.stdout.flush() 100*6777b538SAndroid Build Coastguard Worker 101*6777b538SAndroid Build Coastguard Worker 102*6777b538SAndroid Build Coastguard Workerdef PrintError(message): 103*6777b538SAndroid Build Coastguard Worker # Flush buffers to ensure correct output ordering. 104*6777b538SAndroid Build Coastguard Worker sys.stdout.flush() 105*6777b538SAndroid Build Coastguard Worker sys.stderr.write(message + '\n') 106*6777b538SAndroid Build Coastguard Worker sys.stderr.flush() 107*6777b538SAndroid Build Coastguard Worker 108*6777b538SAndroid Build Coastguard Worker 109*6777b538SAndroid Build Coastguard Workerdef InstallXcodeBinaries(): 110*6777b538SAndroid Build Coastguard Worker """Installs the Xcode binaries needed to build Chrome and accepts the license. 111*6777b538SAndroid Build Coastguard Worker 112*6777b538SAndroid Build Coastguard Worker This is the replacement for InstallXcode that installs a trimmed down version 113*6777b538SAndroid Build Coastguard Worker of Xcode that is OS-version agnostic. 114*6777b538SAndroid Build Coastguard Worker """ 115*6777b538SAndroid Build Coastguard Worker # First make sure the directory exists. It will serve as the cipd root. This 116*6777b538SAndroid Build Coastguard Worker # also ensures that there will be no conflicts of cipd root. 117*6777b538SAndroid Build Coastguard Worker binaries_root = os.path.join(TOOLCHAIN_ROOT, 'xcode_binaries') 118*6777b538SAndroid Build Coastguard Worker if not os.path.exists(binaries_root): 119*6777b538SAndroid Build Coastguard Worker os.makedirs(binaries_root) 120*6777b538SAndroid Build Coastguard Worker 121*6777b538SAndroid Build Coastguard Worker # 'cipd ensure' is idempotent. 122*6777b538SAndroid Build Coastguard Worker args = ['cipd', 'ensure', '-root', binaries_root, '-ensure-file', '-'] 123*6777b538SAndroid Build Coastguard Worker 124*6777b538SAndroid Build Coastguard Worker p = subprocess.Popen(args, 125*6777b538SAndroid Build Coastguard Worker universal_newlines=True, 126*6777b538SAndroid Build Coastguard Worker stdin=subprocess.PIPE, 127*6777b538SAndroid Build Coastguard Worker stdout=subprocess.PIPE, 128*6777b538SAndroid Build Coastguard Worker stderr=subprocess.PIPE) 129*6777b538SAndroid Build Coastguard Worker stdout, stderr = p.communicate(input=PARANOID_MODE + MAC_BINARIES_LABEL + 130*6777b538SAndroid Build Coastguard Worker ' ' + MAC_BINARIES_TAG) 131*6777b538SAndroid Build Coastguard Worker if p.returncode != 0: 132*6777b538SAndroid Build Coastguard Worker print(stdout) 133*6777b538SAndroid Build Coastguard Worker print(stderr) 134*6777b538SAndroid Build Coastguard Worker RequestCipdAuthentication() 135*6777b538SAndroid Build Coastguard Worker return 1 136*6777b538SAndroid Build Coastguard Worker 137*6777b538SAndroid Build Coastguard Worker if sys.platform != 'darwin': 138*6777b538SAndroid Build Coastguard Worker return 0 139*6777b538SAndroid Build Coastguard Worker 140*6777b538SAndroid Build Coastguard Worker # Accept the license for this version of Xcode if it's newer than the 141*6777b538SAndroid Build Coastguard Worker # currently accepted version. 142*6777b538SAndroid Build Coastguard Worker cipd_xcode_version_plist_path = os.path.join(binaries_root, 143*6777b538SAndroid Build Coastguard Worker 'Contents/version.plist') 144*6777b538SAndroid Build Coastguard Worker cipd_xcode_version_plist = LoadPList(cipd_xcode_version_plist_path) 145*6777b538SAndroid Build Coastguard Worker cipd_xcode_version = cipd_xcode_version_plist['CFBundleShortVersionString'] 146*6777b538SAndroid Build Coastguard Worker 147*6777b538SAndroid Build Coastguard Worker cipd_license_path = os.path.join(binaries_root, 148*6777b538SAndroid Build Coastguard Worker 'Contents/Resources/LicenseInfo.plist') 149*6777b538SAndroid Build Coastguard Worker cipd_license_plist = LoadPList(cipd_license_path) 150*6777b538SAndroid Build Coastguard Worker cipd_license_version = cipd_license_plist['licenseID'] 151*6777b538SAndroid Build Coastguard Worker 152*6777b538SAndroid Build Coastguard Worker should_overwrite_license = True 153*6777b538SAndroid Build Coastguard Worker current_license_path = '/Library/Preferences/com.apple.dt.Xcode.plist' 154*6777b538SAndroid Build Coastguard Worker if os.path.exists(current_license_path): 155*6777b538SAndroid Build Coastguard Worker current_license_plist = LoadPList(current_license_path) 156*6777b538SAndroid Build Coastguard Worker xcode_version = current_license_plist.get( 157*6777b538SAndroid Build Coastguard Worker 'IDEXcodeVersionForAgreedToGMLicense') 158*6777b538SAndroid Build Coastguard Worker if (xcode_version is not None 159*6777b538SAndroid Build Coastguard Worker and xcode_version.split('.') >= cipd_xcode_version.split('.')): 160*6777b538SAndroid Build Coastguard Worker should_overwrite_license = False 161*6777b538SAndroid Build Coastguard Worker 162*6777b538SAndroid Build Coastguard Worker if not should_overwrite_license: 163*6777b538SAndroid Build Coastguard Worker return 0 164*6777b538SAndroid Build Coastguard Worker 165*6777b538SAndroid Build Coastguard Worker # Use puppet's sudoers script to accept the license if its available. 166*6777b538SAndroid Build Coastguard Worker license_accept_script = '/usr/local/bin/xcode_accept_license.sh' 167*6777b538SAndroid Build Coastguard Worker if os.path.exists(license_accept_script): 168*6777b538SAndroid Build Coastguard Worker args = [ 169*6777b538SAndroid Build Coastguard Worker 'sudo', license_accept_script, cipd_xcode_version, cipd_license_version 170*6777b538SAndroid Build Coastguard Worker ] 171*6777b538SAndroid Build Coastguard Worker subprocess.check_call(args) 172*6777b538SAndroid Build Coastguard Worker return 0 173*6777b538SAndroid Build Coastguard Worker 174*6777b538SAndroid Build Coastguard Worker # Otherwise manually accept the license. This will prompt for sudo. 175*6777b538SAndroid Build Coastguard Worker print('Accepting new Xcode license. Requires sudo.') 176*6777b538SAndroid Build Coastguard Worker sys.stdout.flush() 177*6777b538SAndroid Build Coastguard Worker args = [ 178*6777b538SAndroid Build Coastguard Worker 'sudo', 'defaults', 'write', current_license_path, 179*6777b538SAndroid Build Coastguard Worker 'IDEXcodeVersionForAgreedToGMLicense', cipd_xcode_version 180*6777b538SAndroid Build Coastguard Worker ] 181*6777b538SAndroid Build Coastguard Worker subprocess.check_call(args) 182*6777b538SAndroid Build Coastguard Worker args = [ 183*6777b538SAndroid Build Coastguard Worker 'sudo', 'defaults', 'write', current_license_path, 184*6777b538SAndroid Build Coastguard Worker 'IDELastGMLicenseAgreedTo', cipd_license_version 185*6777b538SAndroid Build Coastguard Worker ] 186*6777b538SAndroid Build Coastguard Worker subprocess.check_call(args) 187*6777b538SAndroid Build Coastguard Worker args = ['sudo', 'plutil', '-convert', 'xml1', current_license_path] 188*6777b538SAndroid Build Coastguard Worker subprocess.check_call(args) 189*6777b538SAndroid Build Coastguard Worker 190*6777b538SAndroid Build Coastguard Worker return 0 191*6777b538SAndroid Build Coastguard Worker 192*6777b538SAndroid Build Coastguard Worker 193*6777b538SAndroid Build Coastguard Workerdef main(): 194*6777b538SAndroid Build Coastguard Worker if not _UseHermeticToolchain(): 195*6777b538SAndroid Build Coastguard Worker print('Skipping Mac toolchain installation for mac') 196*6777b538SAndroid Build Coastguard Worker return 0 197*6777b538SAndroid Build Coastguard Worker 198*6777b538SAndroid Build Coastguard Worker parser = argparse.ArgumentParser(description='Download hermetic Xcode.') 199*6777b538SAndroid Build Coastguard Worker args = parser.parse_args() 200*6777b538SAndroid Build Coastguard Worker 201*6777b538SAndroid Build Coastguard Worker if not PlatformMeetsHermeticXcodeRequirements(): 202*6777b538SAndroid Build Coastguard Worker print('OS version does not support toolchain.') 203*6777b538SAndroid Build Coastguard Worker return 0 204*6777b538SAndroid Build Coastguard Worker 205*6777b538SAndroid Build Coastguard Worker return InstallXcodeBinaries() 206*6777b538SAndroid Build Coastguard Worker 207*6777b538SAndroid Build Coastguard Worker 208*6777b538SAndroid Build Coastguard Workerif __name__ == '__main__': 209*6777b538SAndroid Build Coastguard Worker sys.exit(main()) 210