Chromium Code Reviews| Index: build/mac_toolchain.py |
| diff --git a/build/mac_toolchain.py b/build/mac_toolchain.py |
| new file mode 100755 |
| index 0000000000000000000000000000000000000000..bfbcd752bb43dd5a13828132f2a108aa42bc13d4 |
| --- /dev/null |
| +++ b/build/mac_toolchain.py |
| @@ -0,0 +1,162 @@ |
| +#!/usr/bin/env python |
| +# Copyright 2016 The Chromium Authors. All rights reserved. |
| +# Use of this source code is governed by a BSD-style license that can be |
| +# found in the LICENSE file. |
| + |
| +"""Download necessary mac toolchain files under certain conditions. If |
| +xcode-select is already set and points to an external folder |
| +(e.g. /Application/Xcode.app), this script only runs if the GYP_DEFINE |
| +|force_mac_toolchain| is set. To override the values in |
| +|TOOLCHAIN_REVISION|-|TOOLCHAIN_SUB_REVISION| below, GYP_DEFINE |
| +mac_toolchain_revision can be used instead. |
| + |
| +This script will only run on machines if /usr/bin/xcodebuild has been added to |
| +the sudoers list so the license can be accepted. |
| + |
| +Otherwise, user input would be required to complete the script. Perhaps future |
| +versions can be modified to allow for user input on developer machines. |
| +""" |
| + |
| +import os |
| +import shutil |
| +import subprocess |
| +import sys |
| +import tarfile |
| +import time |
| +import tempfile |
| +import urllib2 |
| + |
| +# Update sys.path to import gyp. |
| +script_dir = os.path.dirname(os.path.realpath(__file__)) |
| +chrome_src = os.path.abspath(os.path.join(script_dir, os.pardir)) |
| +sys.path.insert(0, os.path.join(chrome_src, 'tools', 'gyp', 'pylib')) |
| +import gyp |
| + |
| +# This can be changed after running /build/package_mac_toolchain.py. |
| +TOOLCHAIN_REVISION = '7D175' |
|
erikchen
2016/03/22 17:51:39
What version of Xcode is this? How did you get thi
|
| +TOOLCHAIN_SUB_REVISION = 1 |
| +TOOLCHAIN_VERSION = '%s-%s' % (TOOLCHAIN_REVISION, TOOLCHAIN_SUB_REVISION) |
| + |
| +BASE_DIR = os.path.abspath(os.path.dirname(__file__)) |
| +TOOLCHAIN_BUILD_DIR = os.path.join(BASE_DIR, 'mac_files', 'Xcode.app') |
| +STAMP_FILE = os.path.join(BASE_DIR, 'mac_files', 'toolchain_build_revision') |
| +TOOLCHAIN_URL = 'gs://chrome-mac-sdk/' |
| + |
| + |
| +def SetEnvironment(): |
| + if sys.platform == 'darwin' and not UseLocalMacSDK(): |
| + os.environ['DEVELOPER_DIR'] = TOOLCHAIN_BUILD_DIR |
| + |
| + |
| +def ReadStampFile(): |
| + """Return the contents of the stamp file, or '' if it doesn't exist.""" |
| + try: |
| + with open(STAMP_FILE, 'r') as f: |
| + return f.read().rstrip() |
| + except IOError: |
| + return '' |
| + |
| + |
| +def WriteStampFile(s): |
| + """Write s to the stamp file.""" |
| + EnsureDirExists(os.path.dirname(STAMP_FILE)) |
| + with open(STAMP_FILE, 'w') as f: |
| + f.write(s) |
| + f.write('\n') |
| + |
| + |
| +def EnsureDirExists(path): |
| + if not os.path.exists(path): |
| + os.makedirs(path) |
| + |
| + |
| +def DownloadAndUnpack(url, output_dir): |
| + """Decompresses |url| into a cleared |output_dir|.""" |
| + temp_name = tempfile.mktemp(prefix='mac_toolchain') |
| + try: |
| + print 'Downloading new toolchain.' |
| + subprocess.check_call(['gsutil.py', 'cp', url, temp_name]) |
| + if os.path.exists(output_dir): |
| + print 'Deleting old toolchain.' |
| + shutil.rmtree(output_dir) |
| + EnsureDirExists(output_dir) |
| + print 'Unpacking new toolchain.' |
| + tarfile.open(mode='r:gz', name=temp_name).extractall(path=output_dir) |
| + finally: |
| + if os.path.exists(temp_name): |
| + os.unlink(temp_name) |
| + |
| + |
| +def CanAccessToolchainBucket(): |
| + """Checks whether the user has access to |TOOLCHAIN_URL|.""" |
| + proc = subprocess.Popen(['gsutil.py', 'ls', TOOLCHAIN_URL], |
| + stdout=subprocess.PIPE) |
| + proc.communicate() |
| + return proc.returncode == 0 |
| + |
| + |
| +def SwitchToolchain(directory): |
| + """Use xcodebuild to accept new toolchain license. This only |
| + works if xcodebuild is in sudoers.""" |
| + xcodebuild = os.path.join(TOOLCHAIN_BUILD_DIR, |
| + 'Contents/Developer/usr/bin/xcodebuild') |
| + subprocess.check_call(['sudo', xcodebuild, '-license', 'accept']) |
| + |
| + |
| +def UseLocalMacSDK(): |
| + gyp_defines = gyp.NameValueListToDict(gyp.ShlexEnv('GYP_DEFINES')) |
| + force_pull = 'force_mac_toolchain' in gyp_defines |
| + |
| + # Don't update the toolchain if there's already one installed outside of the |
| + # expected location for a Chromium mac toolchain, unless |force_pull| is set. |
| + proc = subprocess.Popen(['xcode-select', '-p'], stdout=subprocess.PIPE) |
| + xcode_select_dir = proc.communicate()[0] |
| + rc = proc.returncode |
| + return (not force_pull and rc == 0 and |
| + TOOLCHAIN_BUILD_DIR not in xcode_select_dir) |
| + |
| + |
| +def main(): |
| + if sys.platform != 'darwin': |
| + return 0 |
| + |
| + # TODO(justincohen): Add support for GN per crbug.com/570091 |
| + if UseLocalMacSDK(): |
| + print 'Using local toolchain.' |
| + return 0 |
| + |
| + gyp_defines = gyp.NameValueListToDict(gyp.ShlexEnv('GYP_DEFINES')) |
| + toolchain_revision = gyp_defines.get('mac_toolchain_revision', |
| + TOOLCHAIN_VERSION) |
| + if ReadStampFile() == toolchain_revision: |
| + print 'Toolchain (%s) is already up to date.' % toolchain_revision |
| + SwitchToolchain(TOOLCHAIN_BUILD_DIR) |
| + return 0 |
| + |
| + if not CanAccessToolchainBucket(): |
| + print 'Cannot access toolchain bucket.' |
| + return 0 |
| + |
| + # Reset the stamp file in case the build is unsuccessful. |
| + WriteStampFile('') |
| + |
| + toolchain_file = '%s.tgz' % toolchain_revision |
| + toolchain_full_url = TOOLCHAIN_URL + toolchain_file |
| + |
| + print 'Updating toolchain to %s...' % toolchain_revision |
| + try: |
| + toolchain_file = 'toolchain-%s.tgz' % toolchain_revision |
| + toolchain_full_url = TOOLCHAIN_URL + toolchain_file |
| + DownloadAndUnpack(toolchain_full_url, TOOLCHAIN_BUILD_DIR) |
| + SwitchToolchain(TOOLCHAIN_BUILD_DIR) |
| + |
| + print 'Toolchain %s unpacked.' % toolchain_revision |
| + WriteStampFile(toolchain_revision) |
| + return 0 |
| + except: |
| + print 'Failed to download toolchain %s.' % toolchain_file |
| + print 'Exiting.' |
| + return 1 |
| + |
| +if __name__ == '__main__': |
| + sys.exit(main()) |