xref: /aosp_15_r20/external/cronet/build/mac_toolchain.py (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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