OLD | NEW |
---|---|
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright 2016 The Chromium Authors. All rights reserved. | 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 | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 """ | 6 """ |
7 If should_use_hermetic_xcode.py emits "1", and the current toolchain is out of | 7 If should_use_hermetic_xcode.py emits "1", and the current toolchain is out of |
8 date: | 8 date: |
9 * Downloads the hermetic mac toolchain | 9 * Downloads the hermetic mac toolchain |
10 * Requires gsutil to be configured. | 10 * Requires gsutil to be configured. |
(...skipping 22 matching lines...) Expand all Loading... | |
33 IOS_TOOLCHAIN_VERSION = '%s-%s' % (IOS_TOOLCHAIN_VERSION, | 33 IOS_TOOLCHAIN_VERSION = '%s-%s' % (IOS_TOOLCHAIN_VERSION, |
34 IOS_TOOLCHAIN_SUB_REVISION) | 34 IOS_TOOLCHAIN_SUB_REVISION) |
35 | 35 |
36 # Absolute path to src/ directory. | 36 # Absolute path to src/ directory. |
37 REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | 37 REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) |
38 | 38 |
39 # Absolute path to a file with gclient solutions. | 39 # Absolute path to a file with gclient solutions. |
40 GCLIENT_CONFIG = os.path.join(os.path.dirname(REPO_ROOT), '.gclient') | 40 GCLIENT_CONFIG = os.path.join(os.path.dirname(REPO_ROOT), '.gclient') |
41 | 41 |
42 BASE_DIR = os.path.abspath(os.path.dirname(__file__)) | 42 BASE_DIR = os.path.abspath(os.path.dirname(__file__)) |
43 TOOLCHAIN_BUILD_DIR = os.path.join(BASE_DIR, 'mac_files', 'Xcode.app') | 43 TOOLCHAIN_BUILD_DIR = os.path.join(BASE_DIR, '%s_files', 'Xcode.app') |
44 STAMP_FILE = os.path.join(BASE_DIR, 'mac_files', 'toolchain_build_revision') | 44 STAMP_FILE = os.path.join(BASE_DIR, '%s_files', 'toolchain_build_revision') |
45 TOOLCHAIN_URL = 'gs://chrome-mac-sdk/' | 45 TOOLCHAIN_URL = 'gs://chrome-mac-sdk/' |
46 | 46 |
47 def IsIOSPlatform(): | 47 def GetPlatforms(): |
48 default_target_os = ["mac"] | |
48 try: | 49 try: |
49 env = {} | 50 env = {} |
50 execfile(GCLIENT_CONFIG, env, env) | 51 execfile(GCLIENT_CONFIG, env, env) |
51 if 'ios' in env.get('target_os', []): | 52 return env.get('target_os', default_target_os) |
52 return True | |
53 except: | 53 except: |
54 pass | 54 pass |
55 return False | 55 return default_target_os |
56 | 56 |
57 | 57 |
58 def ReadStampFile(): | 58 def ReadStampFile(target_os): |
59 """Return the contents of the stamp file, or '' if it doesn't exist.""" | 59 """Return the contents of the stamp file, or '' if it doesn't exist.""" |
60 try: | 60 try: |
61 with open(STAMP_FILE, 'r') as f: | 61 with open(STAMP_FILE % target_os, 'r') as f: |
62 return f.read().rstrip() | 62 return f.read().rstrip() |
63 except IOError: | 63 except IOError: |
64 return '' | 64 return '' |
65 | 65 |
66 | 66 |
67 def WriteStampFile(s): | 67 def WriteStampFile(target_os, s): |
68 """Write s to the stamp file.""" | 68 """Write s to the stamp file.""" |
69 EnsureDirExists(os.path.dirname(STAMP_FILE)) | 69 EnsureDirExists(os.path.dirname(STAMP_FILE % target_os)) |
70 with open(STAMP_FILE, 'w') as f: | 70 with open(STAMP_FILE % target_os, 'w') as f: |
71 f.write(s) | 71 f.write(s) |
72 f.write('\n') | 72 f.write('\n') |
73 | 73 |
74 | 74 |
75 def EnsureDirExists(path): | 75 def EnsureDirExists(path): |
76 if not os.path.exists(path): | 76 if not os.path.exists(path): |
77 os.makedirs(path) | 77 os.makedirs(path) |
78 | 78 |
79 | 79 |
80 def DownloadAndUnpack(url, output_dir): | 80 def DownloadAndUnpack(url, output_dir): |
(...skipping 24 matching lines...) Expand all Loading... | |
105 """Loads Plist at |path| and returns it as a dictionary.""" | 105 """Loads Plist at |path| and returns it as a dictionary.""" |
106 fd, name = tempfile.mkstemp() | 106 fd, name = tempfile.mkstemp() |
107 try: | 107 try: |
108 subprocess.check_call(['plutil', '-convert', 'xml1', '-o', name, path]) | 108 subprocess.check_call(['plutil', '-convert', 'xml1', '-o', name, path]) |
109 with os.fdopen(fd, 'r') as f: | 109 with os.fdopen(fd, 'r') as f: |
110 return plistlib.readPlist(f) | 110 return plistlib.readPlist(f) |
111 finally: | 111 finally: |
112 os.unlink(name) | 112 os.unlink(name) |
113 | 113 |
114 | 114 |
115 def AcceptLicense(): | 115 def AcceptLicense(target_os): |
116 """Use xcodebuild to accept new toolchain license if necessary. Don't accept | 116 """Use xcodebuild to accept new toolchain license if necessary. Don't accept |
117 the license if a newer license has already been accepted. This only works if | 117 the license if a newer license has already been accepted. This only works if |
118 xcodebuild and xcode-select are passwordless in sudoers.""" | 118 xcodebuild and xcode-select are passwordless in sudoers.""" |
119 | 119 |
120 # Check old license | 120 # Check old license |
121 try: | 121 try: |
122 target_license_plist_path = \ | 122 target_license_plist_path = \ |
123 os.path.join(TOOLCHAIN_BUILD_DIR, | 123 os.path.join(TOOLCHAIN_BUILD_DIR % target_os, |
124 *['Contents','Resources','LicenseInfo.plist']) | 124 *['Contents','Resources','LicenseInfo.plist']) |
125 target_license_plist = LoadPlist(target_license_plist_path) | 125 target_license_plist = LoadPlist(target_license_plist_path) |
126 build_type = target_license_plist['licenseType'] | 126 build_type = target_license_plist['licenseType'] |
127 build_version = target_license_plist['licenseID'] | 127 build_version = target_license_plist['licenseID'] |
128 | 128 |
129 accepted_license_plist = LoadPlist( | 129 accepted_license_plist = LoadPlist( |
130 '/Library/Preferences/com.apple.dt.Xcode.plist') | 130 '/Library/Preferences/com.apple.dt.Xcode.plist') |
131 agreed_to_key = 'IDELast%sLicenseAgreedTo' % build_type | 131 agreed_to_key = 'IDELast%sLicenseAgreedTo' % build_type |
132 last_license_agreed_to = accepted_license_plist[agreed_to_key] | 132 last_license_agreed_to = accepted_license_plist[agreed_to_key] |
133 | 133 |
134 # Historically all Xcode build numbers have been in the format of AANNNN, so | 134 # Historically all Xcode build numbers have been in the format of AANNNN, so |
135 # a simple string compare works. If Xcode's build numbers change this may | 135 # a simple string compare works. If Xcode's build numbers change this may |
136 # need a more complex compare. | 136 # need a more complex compare. |
137 if build_version <= last_license_agreed_to: | 137 if build_version <= last_license_agreed_to: |
138 # Don't accept the license of older toolchain builds, this will break the | 138 # Don't accept the license of older toolchain builds, this will break the |
139 # license of newer builds. | 139 # license of newer builds. |
140 return | 140 return |
141 except (subprocess.CalledProcessError, KeyError): | 141 except (subprocess.CalledProcessError, KeyError): |
142 # If there's never been a license of type |build_type| accepted, | 142 # If there's never been a license of type |build_type| accepted, |
143 # |target_license_plist_path| or |agreed_to_key| may not exist. | 143 # |target_license_plist_path| or |agreed_to_key| may not exist. |
144 pass | 144 pass |
145 | 145 |
146 print "Accepting license." | 146 print "Accepting license." |
147 old_path = subprocess.Popen(['/usr/bin/xcode-select', '-p'], | 147 old_path = subprocess.Popen(['/usr/bin/xcode-select', '-p'], |
148 stdout=subprocess.PIPE).communicate()[0].strip() | 148 stdout=subprocess.PIPE).communicate()[0].strip() |
149 try: | 149 try: |
150 build_dir = os.path.join(TOOLCHAIN_BUILD_DIR, 'Contents/Developer') | 150 build_dir = os.path.join( |
151 TOOLCHAIN_BUILD_DIR % target_os, 'Contents/Developer') | |
151 subprocess.check_call(['sudo', '/usr/bin/xcode-select', '-s', build_dir]) | 152 subprocess.check_call(['sudo', '/usr/bin/xcode-select', '-s', build_dir]) |
152 subprocess.check_call(['sudo', '/usr/bin/xcodebuild', '-license', 'accept']) | 153 subprocess.check_call(['sudo', '/usr/bin/xcodebuild', '-license', 'accept']) |
153 finally: | 154 finally: |
154 subprocess.check_call(['sudo', '/usr/bin/xcode-select', '-s', old_path]) | 155 subprocess.check_call(['sudo', '/usr/bin/xcode-select', '-s', old_path]) |
155 | 156 |
156 | 157 |
157 def _UseHermeticToolchain(): | 158 def _UseHermeticToolchain(target_os): |
158 current_dir = os.path.dirname(os.path.realpath(__file__)) | 159 current_dir = os.path.dirname(os.path.realpath(__file__)) |
159 script_path = os.path.join(current_dir, 'mac/should_use_hermetic_xcode.py') | 160 script_path = os.path.join(current_dir, 'mac/should_use_hermetic_xcode.py') |
160 target_os = 'ios' if IsIOSPlatform() else 'mac' | |
161 proc = subprocess.Popen([script_path, target_os], stdout=subprocess.PIPE) | 161 proc = subprocess.Popen([script_path, target_os], stdout=subprocess.PIPE) |
162 return '1' in proc.stdout.readline() | 162 return '1' in proc.stdout.readline() |
163 | 163 |
164 | 164 |
165 def RequestGsAuthentication(): | 165 def RequestGsAuthentication(): |
166 """Requests that the user authenticate to be able to access gs://. | 166 """Requests that the user authenticate to be able to access gs://. |
167 """ | 167 """ |
168 print 'Access to ' + TOOLCHAIN_URL + ' not configured.' | 168 print 'Access to ' + TOOLCHAIN_URL + ' not configured.' |
169 print '-----------------------------------------------------------------' | 169 print '-----------------------------------------------------------------' |
170 print | 170 print |
171 print 'You appear to be a Googler.' | 171 print 'You appear to be a Googler.' |
172 print | 172 print |
173 print 'I\'m sorry for the hassle, but you need to do a one-time manual' | 173 print 'I\'m sorry for the hassle, but you need to do a one-time manual' |
174 print 'authentication. Please run:' | 174 print 'authentication. Please run:' |
175 print | 175 print |
176 print ' download_from_google_storage --config' | 176 print ' download_from_google_storage --config' |
177 print | 177 print |
178 print 'and follow the instructions.' | 178 print 'and follow the instructions.' |
179 print | 179 print |
180 print 'NOTE 1: Use your google.com credentials, not chromium.org.' | 180 print 'NOTE 1: Use your google.com credentials, not chromium.org.' |
181 print 'NOTE 2: Enter 0 when asked for a "project-id".' | 181 print 'NOTE 2: Enter 0 when asked for a "project-id".' |
182 print | 182 print |
183 print '-----------------------------------------------------------------' | 183 print '-----------------------------------------------------------------' |
184 print | 184 print |
185 sys.stdout.flush() | 185 sys.stdout.flush() |
186 sys.exit(1) | 186 sys.exit(1) |
187 | 187 |
188 | 188 |
189 def main(): | 189 def DownloadHermeticBuild(target_os, default_version, toolchain_filename): |
190 if sys.platform != 'darwin': | |
191 return 0 | |
192 | |
193 if not _UseHermeticToolchain(): | |
194 print 'Using local toolchain.' | |
195 return 0 | |
196 | |
197 if IsIOSPlatform(): | |
198 default_version = IOS_TOOLCHAIN_VERSION | |
199 toolchain_filename = 'ios-toolchain-%s.tgz' | |
200 else: | |
201 default_version = MAC_TOOLCHAIN_VERSION | |
202 toolchain_filename = 'toolchain-%s.tgz' | |
203 | |
204 toolchain_version = os.environ.get('MAC_TOOLCHAIN_REVISION', | 190 toolchain_version = os.environ.get('MAC_TOOLCHAIN_REVISION', |
205 default_version) | 191 default_version) |
206 | 192 |
207 if ReadStampFile() == toolchain_version: | 193 if ReadStampFile(target_os) == toolchain_version: |
208 print 'Toolchain (%s) is already up to date.' % toolchain_version | 194 print 'Toolchain (%s) is already up to date.' % toolchain_version |
209 AcceptLicense() | 195 AcceptLicense(target_os) |
210 return 0 | 196 return 0 |
211 | 197 |
212 if not CanAccessToolchainBucket(): | 198 if not CanAccessToolchainBucket(): |
213 RequestGsAuthentication() | 199 RequestGsAuthentication() |
214 return 1 | 200 return 1 |
215 | 201 |
216 # Reset the stamp file in case the build is unsuccessful. | 202 # Reset the stamp file in case the build is unsuccessful. |
217 WriteStampFile('') | 203 WriteStampFile(target_os, '') |
218 | 204 |
219 toolchain_file = '%s.tgz' % toolchain_version | 205 toolchain_file = '%s.tgz' % toolchain_version |
220 toolchain_full_url = TOOLCHAIN_URL + toolchain_file | 206 toolchain_full_url = TOOLCHAIN_URL + toolchain_file |
221 | 207 |
222 print 'Updating toolchain to %s...' % toolchain_version | 208 print 'Updating toolchain to %s...' % toolchain_version |
223 try: | 209 try: |
224 toolchain_file = toolchain_filename % toolchain_version | 210 toolchain_file = toolchain_filename % toolchain_version |
225 toolchain_full_url = TOOLCHAIN_URL + toolchain_file | 211 toolchain_full_url = TOOLCHAIN_URL + toolchain_file |
226 DownloadAndUnpack(toolchain_full_url, TOOLCHAIN_BUILD_DIR) | 212 DownloadAndUnpack(toolchain_full_url, TOOLCHAIN_BUILD_DIR % target_os) |
227 AcceptLicense() | 213 AcceptLicense(target_os) |
228 | 214 |
229 print 'Toolchain %s unpacked.' % toolchain_version | 215 print 'Toolchain %s unpacked.' % toolchain_version |
230 WriteStampFile(toolchain_version) | 216 WriteStampFile(target_os, toolchain_version) |
231 return 0 | 217 return 0 |
232 except Exception as e: | 218 except Exception as e: |
233 print 'Failed to download toolchain %s.' % toolchain_file | 219 print 'Failed to download toolchain %s.' % toolchain_file |
234 print 'Exception %s' % e | 220 print 'Exception %s' % e |
235 print 'Exiting.' | 221 print 'Exiting.' |
236 return 1 | 222 return 1 |
237 | 223 |
224 | |
225 def main(): | |
226 if sys.platform != 'darwin': | |
227 return 0 | |
228 | |
229 for target_os in GetPlatforms(): | |
230 if target_os == "ios": | |
231 default_version = IOS_TOOLCHAIN_VERSION | |
232 toolchain_filename = 'ios-toolchain-%s.tgz' | |
233 else: | |
234 default_version = MAC_TOOLCHAIN_VERSION | |
235 toolchain_filename = 'toolchain-%s.tgz' | |
236 | |
237 if not _UseHermeticToolchain(target_os): | |
238 print 'Using local toolchain for %s.' % target_os | |
justincohen
2017/01/11 14:25:22
no return value here
sdefresne
2017/01/11 14:34:15
Yes, we want to continue iterating on the next tar
| |
239 elif DownloadHermeticBuild(target_os, default_version, toolchain_filename): | |
240 return 1 | |
justincohen
2017/01/11 14:25:22
don't you want to return the result of DownloadHer
sdefresne
2017/01/11 14:34:15
Done.
| |
241 | |
242 | |
238 if __name__ == '__main__': | 243 if __name__ == '__main__': |
239 sys.exit(main()) | 244 sys.exit(main()) |
OLD | NEW |