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 with the following programs added to a | |
14 sudoers list: | |
15 /usr/bin/xcode-select | |
16 /usr/bin/xcodebuild | |
17 | |
18 Otherwise user input would be required to complete the script. Perhaps future | |
19 versions can be modified to allow for user input on developer machines. | |
20 """ | |
21 | |
22 import os | |
23 import shutil | |
24 import subprocess | |
25 import sys | |
26 import tarfile | |
27 import time | |
28 import tempfile | |
29 import urllib2 | |
30 | |
31 # Update sys.path to import gyp. | |
32 script_dir = os.path.dirname(os.path.realpath(__file__)) | |
33 chrome_src = os.path.abspath(os.path.join(script_dir, os.pardir)) | |
34 sys.path.insert(0, os.path.join(chrome_src, 'tools', 'gyp', 'pylib')) | |
35 import gyp | |
36 | |
37 # This can be changed after running /build/package_mac_toolchain.py. | |
38 TOOLCHAIN_REVISION = '7C68' | |
39 TOOLCHAIN_SUB_REVISION = 1 | |
40 TOOLCHAIN_VERSION = "%s-%s" % (TOOLCHAIN_REVISION, TOOLCHAIN_SUB_REVISION) | |
41 | |
42 BASE_DIR = os.path.abspath(os.path.dirname(__file__)) | |
43 TOOLCHAIN_BUILD_DIR = os.path.join(BASE_DIR, 'mac_files', 'Xcode.app') | |
44 STAMP_FILE = os.path.join(BASE_DIR, 'mac_files', 'toolchain_build_revision') | |
45 TOOLCHAIN_URL = 'gs://chrome-mac-sdk/' | |
46 | |
47 def ReadStampFile(): | |
48 """Return the contents of the stamp file, or '' if it doesn't exist.""" | |
49 try: | |
50 with open(STAMP_FILE, 'r') as f: | |
51 return f.read().rstrip() | |
52 except IOError: | |
53 return '' | |
54 | |
55 | |
56 def WriteStampFile(s): | |
57 """Write s to the stamp file.""" | |
58 EnsureDirExists(os.path.dirname(STAMP_FILE)) | |
59 with open(STAMP_FILE, 'w') as f: | |
60 f.write(s) | |
61 f.write('\n') | |
62 | |
63 | |
64 def EnsureDirExists(path): | |
65 if not os.path.exists(path): | |
66 os.makedirs(path) | |
67 | |
68 | |
69 def DownloadAndUnpack(url, output_dir): | |
70 """Decompresses |url| into a cleared |output_dir|.""" | |
71 try: | |
72 temp_name = tempfile.mktemp(prefix='mac_toolchain') | |
73 print 'Downloading new toolchain.' | |
74 subprocess.check_call(['gsutil.py', 'cp', url, temp_name]) | |
75 if os.path.exists(output_dir): | |
76 print 'Deleting old toolchain.' | |
77 shutil.rmtree(output_dir) | |
78 EnsureDirExists(output_dir) | |
79 print 'Unpacking new toolchain.' | |
80 tarfile.open(mode='r:gz', name=temp_name).extractall(path=output_dir) | |
81 finally: | |
82 if os.path.exists(temp_name): | |
83 os.unlink(temp_name) | |
84 | |
85 | |
86 def CanAccessToolchainBucket(): | |
87 """Checks whether the user has access to |TOOLCHAIN_URL|.""" | |
88 proc = subprocess.Popen(['gsutil.py', 'ls', TOOLCHAIN_URL], | |
89 stdout=subprocess.PIPE) | |
90 proc.communicate() | |
91 return proc.returncode == 0 | |
92 | |
93 | |
94 def SwitchToolchain(directory): | |
95 """Use xcode-select to select new toolchain and accept license. This only | |
96 works if both xcode-select and xcodebuild are in sudoers.""" | |
97 print 'Setting xcode-select to %s.' % directory | |
98 subprocess.check_call(['sudo', '/usr/bin/xcode-select', '-s', directory]) | |
Nico
2016/03/08 18:48:46
won't this require an auth password usually?
justincohen
2016/03/08 19:53:27
Correct. We check below that (sudo -l) to make su
| |
99 subprocess.check_call(['sudo', '/usr/bin/xcodebuild', '-license', 'accept']) | |
100 | |
101 | |
102 def main(): | |
103 gyp_defines = gyp.NameValueListToDict(gyp.ShlexEnv('GYP_DEFINES')) | |
Nico
2016/03/08 18:48:46
(https://bugs.chromium.org/p/chromium/issues/detai
justincohen
2016/03/08 19:53:27
Added a TODO
| |
104 force_pull = 'force_mac_toolchain' in gyp_defines | |
105 toolchain_revision = gyp_defines.get('mac_toolchain_revision', | |
106 TOOLCHAIN_VERSION) | |
107 | |
108 # Don't update the toolchain if there's already one installed outside of the | |
109 # expected location for a Chromium mac toolchain, unless |force_pull| is set. | |
110 proc = subprocess.Popen(['xcode-select', '-p'], stdout=subprocess.PIPE) | |
111 xcode_select_dir = proc.communicate()[0] | |
112 rc = proc.returncode | |
113 if not force_pull and rc == 0 and TOOLCHAIN_BUILD_DIR not in xcode_select_dir: | |
Nico
2016/03/08 18:48:46
should this check that the local toolchain has the
justincohen
2016/03/08 19:53:27
I wasn't sure what the right functionality should
| |
114 print 'Using local toolchain.' | |
115 return 0 | |
116 | |
117 if ReadStampFile() == toolchain_revision: | |
118 print 'Toolchain (%s) is already up to date.' % toolchain_revision | |
119 SwitchToolchain(TOOLCHAIN_BUILD_DIR) | |
120 return 0 | |
121 | |
122 # This script cannot complete if xcode-select and xcodebuild are not in | |
123 # sudoers. | |
124 proc = subprocess.Popen(['sudo', '-l'], stdout=subprocess.PIPE) | |
125 output = proc.communicate()[0] | |
126 cmds = ['xcode-select', 'xcodebuild'] | |
127 if not all(x in output for x in cmds): | |
128 print 'xcode-select and xcodebuild must be present in sudoers.' | |
justincohen
2016/03/08 19:53:27
Missed a return 0 here.
| |
129 | |
130 if not CanAccessToolchainBucket(): | |
131 print 'Cannot access toolchain bucket.' | |
132 return 0 | |
133 | |
134 # Reset the stamp file in case the build is unsuccessful. | |
135 WriteStampFile('') | |
136 | |
137 toolchain_file = "%s.tgz" % toolchain_revision | |
138 toolchain_full_url = TOOLCHAIN_URL + toolchain_file | |
139 | |
140 print 'Updating toolchain to %s...' % toolchain_revision | |
141 try: | |
142 toolchain_file = "toolchain-%s.tgz" % toolchain_revision | |
143 toolchain_full_url = TOOLCHAIN_URL + toolchain_file | |
144 DownloadAndUnpack(toolchain_full_url, TOOLCHAIN_BUILD_DIR) | |
145 SwitchToolchain(TOOLCHAIN_BUILD_DIR) | |
146 | |
147 print 'Toolchain %s unpacked.' % toolchain_revision | |
148 WriteStampFile(toolchain_revision) | |
149 | |
150 return 0 | |
151 except: | |
152 print 'Failed to download toolchain %s.' % toolchain_file | |
153 print 'Exiting.' | |
154 return 1 | |
155 | |
156 if __name__ == '__main__': | |
157 sys.exit(main()) | |
OLD | NEW |