OLD | NEW |
(Empty) | |
| 1 #!/usr/bin/env python |
| 2 # Copyright 2016 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. |
| 5 |
| 6 """Download necessary mac toolchain files under certain conditions. If |
| 7 xcode-select is already set and points to an external folder |
| 8 (e.g. /Application/Xcode.app), this script only runs if the GYP_DEFINE |
| 9 |force_mac_toolchain| is set. To override the values in |
| 10 |TOOLCHAIN_REVISION|-|TOOLCHAIN_SUB_REVISION| below, GYP_DEFINE |
| 11 mac_toolchain_revision can be used instead. |
| 12 |
| 13 This script will only run on machines if /usr/bin/xcodebuild has been added to |
| 14 the sudoers list so the license can be accepted. |
| 15 |
| 16 Otherwise, user input would be required to complete the script. Perhaps future |
| 17 versions can be modified to allow for user input on developer machines. |
| 18 """ |
| 19 |
| 20 import os |
| 21 import shutil |
| 22 import subprocess |
| 23 import sys |
| 24 import tarfile |
| 25 import time |
| 26 import tempfile |
| 27 import urllib2 |
| 28 |
| 29 # This can be changed after running /build/package_mac_toolchain.py. |
| 30 TOOLCHAIN_REVISION = '5B1008' |
| 31 TOOLCHAIN_SUB_REVISION = 2 |
| 32 TOOLCHAIN_VERSION = '%s-%s' % (TOOLCHAIN_REVISION, TOOLCHAIN_SUB_REVISION) |
| 33 |
| 34 BASE_DIR = os.path.abspath(os.path.dirname(__file__)) |
| 35 TOOLCHAIN_BUILD_DIR = os.path.join(BASE_DIR, 'mac_files', 'Xcode.app') |
| 36 STAMP_FILE = os.path.join(BASE_DIR, 'mac_files', 'toolchain_build_revision') |
| 37 TOOLCHAIN_URL = 'gs://chrome-mac-sdk/' |
| 38 |
| 39 |
| 40 def GetToolchainDirectory(): |
| 41 if sys.platform == 'darwin' and not UseLocalMacSDK(): |
| 42 return TOOLCHAIN_BUILD_DIR |
| 43 |
| 44 |
| 45 def ReadStampFile(): |
| 46 """Return the contents of the stamp file, or '' if it doesn't exist.""" |
| 47 try: |
| 48 with open(STAMP_FILE, 'r') as f: |
| 49 return f.read().rstrip() |
| 50 except IOError: |
| 51 return '' |
| 52 |
| 53 |
| 54 def WriteStampFile(s): |
| 55 """Write s to the stamp file.""" |
| 56 EnsureDirExists(os.path.dirname(STAMP_FILE)) |
| 57 with open(STAMP_FILE, 'w') as f: |
| 58 f.write(s) |
| 59 f.write('\n') |
| 60 |
| 61 |
| 62 def EnsureDirExists(path): |
| 63 if not os.path.exists(path): |
| 64 os.makedirs(path) |
| 65 |
| 66 |
| 67 def DownloadAndUnpack(url, output_dir): |
| 68 """Decompresses |url| into a cleared |output_dir|.""" |
| 69 temp_name = tempfile.mktemp(prefix='mac_toolchain') |
| 70 try: |
| 71 print 'Downloading new toolchain.' |
| 72 subprocess.check_call(['gsutil.py', 'cp', url, temp_name]) |
| 73 if os.path.exists(output_dir): |
| 74 print 'Deleting old toolchain.' |
| 75 shutil.rmtree(output_dir) |
| 76 EnsureDirExists(output_dir) |
| 77 print 'Unpacking new toolchain.' |
| 78 tarfile.open(mode='r:gz', name=temp_name).extractall(path=output_dir) |
| 79 finally: |
| 80 if os.path.exists(temp_name): |
| 81 os.unlink(temp_name) |
| 82 |
| 83 |
| 84 def CanAccessToolchainBucket(): |
| 85 """Checks whether the user has access to |TOOLCHAIN_URL|.""" |
| 86 proc = subprocess.Popen(['gsutil.py', 'ls', TOOLCHAIN_URL], |
| 87 stdout=subprocess.PIPE) |
| 88 proc.communicate() |
| 89 return proc.returncode == 0 |
| 90 |
| 91 |
| 92 def AcceptLicense(directory): |
| 93 """Use xcodebuild to accept new toolchain license. This only |
| 94 works if xcodebuild is in sudoers.""" |
| 95 xcodebuild = os.path.join(TOOLCHAIN_BUILD_DIR, |
| 96 'Contents/Developer/usr/bin/xcodebuild') |
| 97 subprocess.check_call(['sudo', xcodebuild, '-license', 'accept']) |
| 98 |
| 99 |
| 100 def UseLocalMacSDK(): |
| 101 force_pull = os.environ.has_key('FORCE_MAC_TOOLCHAIN') |
| 102 |
| 103 # Don't update the toolchain if there's already one installed outside of the |
| 104 # expected location for a Chromium mac toolchain, unless |force_pull| is set. |
| 105 proc = subprocess.Popen(['xcode-select', '-p'], stdout=subprocess.PIPE) |
| 106 xcode_select_dir = proc.communicate()[0] |
| 107 rc = proc.returncode |
| 108 return (not force_pull and rc == 0 and |
| 109 TOOLCHAIN_BUILD_DIR not in xcode_select_dir) |
| 110 |
| 111 |
| 112 def main(): |
| 113 if sys.platform != 'darwin': |
| 114 return 0 |
| 115 |
| 116 # TODO(justincohen): Add support for GN per crbug.com/570091 |
| 117 if UseLocalMacSDK(): |
| 118 print 'Using local toolchain.' |
| 119 return 0 |
| 120 |
| 121 toolchain_revision = os.environ.get('MAC_TOOLCHAIN_REVISION', |
| 122 TOOLCHAIN_VERSION) |
| 123 if ReadStampFile() == toolchain_revision: |
| 124 print 'Toolchain (%s) is already up to date.' % toolchain_revision |
| 125 AcceptLicense(TOOLCHAIN_BUILD_DIR) |
| 126 return 0 |
| 127 |
| 128 if not CanAccessToolchainBucket(): |
| 129 print 'Cannot access toolchain bucket.' |
| 130 return 0 |
| 131 |
| 132 # Reset the stamp file in case the build is unsuccessful. |
| 133 WriteStampFile('') |
| 134 |
| 135 toolchain_file = '%s.tgz' % toolchain_revision |
| 136 toolchain_full_url = TOOLCHAIN_URL + toolchain_file |
| 137 |
| 138 print 'Updating toolchain to %s...' % toolchain_revision |
| 139 try: |
| 140 toolchain_file = 'toolchain-%s.tgz' % toolchain_revision |
| 141 toolchain_full_url = TOOLCHAIN_URL + toolchain_file |
| 142 DownloadAndUnpack(toolchain_full_url, TOOLCHAIN_BUILD_DIR) |
| 143 AcceptLicense(TOOLCHAIN_BUILD_DIR) |
| 144 |
| 145 print 'Toolchain %s unpacked.' % toolchain_revision |
| 146 WriteStampFile(toolchain_revision) |
| 147 return 0 |
| 148 except: |
| 149 print 'Failed to download toolchain %s.' % toolchain_file |
| 150 print 'Exiting.' |
| 151 return 1 |
| 152 |
| 153 if __name__ == '__main__': |
| 154 sys.exit(main()) |
OLD | NEW |