| 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 13 matching lines...) Expand all Loading... |
| 94 os.unlink(temp_name) | 94 os.unlink(temp_name) |
| 95 | 95 |
| 96 | 96 |
| 97 def CanAccessToolchainBucket(): | 97 def CanAccessToolchainBucket(): |
| 98 """Checks whether the user has access to |TOOLCHAIN_URL|.""" | 98 """Checks whether the user has access to |TOOLCHAIN_URL|.""" |
| 99 proc = subprocess.Popen(['gsutil.py', 'ls', TOOLCHAIN_URL], | 99 proc = subprocess.Popen(['gsutil.py', 'ls', TOOLCHAIN_URL], |
| 100 stdout=subprocess.PIPE) | 100 stdout=subprocess.PIPE) |
| 101 proc.communicate() | 101 proc.communicate() |
| 102 return proc.returncode == 0 | 102 return proc.returncode == 0 |
| 103 | 103 |
| 104 |
| 104 def LoadPlist(path): | 105 def LoadPlist(path): |
| 105 """Loads Plist at |path| and returns it as a dictionary.""" | 106 """Loads Plist at |path| and returns it as a dictionary.""" |
| 106 fd, name = tempfile.mkstemp() | 107 fd, name = tempfile.mkstemp() |
| 107 try: | 108 try: |
| 108 subprocess.check_call(['plutil', '-convert', 'xml1', '-o', name, path]) | 109 subprocess.check_call(['plutil', '-convert', 'xml1', '-o', name, path]) |
| 109 with os.fdopen(fd, 'r') as f: | 110 with os.fdopen(fd, 'r') as f: |
| 110 return plistlib.readPlist(f) | 111 return plistlib.readPlist(f) |
| 111 finally: | 112 finally: |
| 112 os.unlink(name) | 113 os.unlink(name) |
| 113 | 114 |
| 114 | 115 |
| 115 def AcceptLicense(): | 116 def AcceptLicense(target_os): |
| 116 """Use xcodebuild to accept new toolchain license if necessary. Don't accept | 117 """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 | 118 the license if a newer license has already been accepted. This only works if |
| 118 xcodebuild and xcode-select are passwordless in sudoers.""" | 119 xcodebuild and xcode-select are passwordless in sudoers.""" |
| 119 | 120 |
| 120 # Check old license | 121 # Check old license |
| 121 try: | 122 try: |
| 122 target_license_plist_path = \ | 123 target_license_plist_path = \ |
| 123 os.path.join(TOOLCHAIN_BUILD_DIR, | 124 os.path.join(TOOLCHAIN_BUILD_DIR % target_os, |
| 124 *['Contents','Resources','LicenseInfo.plist']) | 125 *['Contents','Resources','LicenseInfo.plist']) |
| 125 target_license_plist = LoadPlist(target_license_plist_path) | 126 target_license_plist = LoadPlist(target_license_plist_path) |
| 126 build_type = target_license_plist['licenseType'] | 127 build_type = target_license_plist['licenseType'] |
| 127 build_version = target_license_plist['licenseID'] | 128 build_version = target_license_plist['licenseID'] |
| 128 | 129 |
| 129 accepted_license_plist = LoadPlist( | 130 accepted_license_plist = LoadPlist( |
| 130 '/Library/Preferences/com.apple.dt.Xcode.plist') | 131 '/Library/Preferences/com.apple.dt.Xcode.plist') |
| 131 agreed_to_key = 'IDELast%sLicenseAgreedTo' % build_type | 132 agreed_to_key = 'IDELast%sLicenseAgreedTo' % build_type |
| 132 last_license_agreed_to = accepted_license_plist[agreed_to_key] | 133 last_license_agreed_to = accepted_license_plist[agreed_to_key] |
| 133 | 134 |
| 134 # Historically all Xcode build numbers have been in the format of AANNNN, so | 135 # 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 | 136 # a simple string compare works. If Xcode's build numbers change this may |
| 136 # need a more complex compare. | 137 # need a more complex compare. |
| 137 if build_version <= last_license_agreed_to: | 138 if build_version <= last_license_agreed_to: |
| 138 # Don't accept the license of older toolchain builds, this will break the | 139 # Don't accept the license of older toolchain builds, this will break the |
| 139 # license of newer builds. | 140 # license of newer builds. |
| 140 return | 141 return |
| 141 except (subprocess.CalledProcessError, KeyError): | 142 except (subprocess.CalledProcessError, KeyError): |
| 142 # If there's never been a license of type |build_type| accepted, | 143 # If there's never been a license of type |build_type| accepted, |
| 143 # |target_license_plist_path| or |agreed_to_key| may not exist. | 144 # |target_license_plist_path| or |agreed_to_key| may not exist. |
| 144 pass | 145 pass |
| 145 | 146 |
| 146 print "Accepting license." | 147 print "Accepting license." |
| 147 old_path = subprocess.Popen(['/usr/bin/xcode-select', '-p'], | 148 old_path = subprocess.Popen(['/usr/bin/xcode-select', '-p'], |
| 148 stdout=subprocess.PIPE).communicate()[0].strip() | 149 stdout=subprocess.PIPE).communicate()[0].strip() |
| 149 try: | 150 try: |
| 150 build_dir = os.path.join(TOOLCHAIN_BUILD_DIR, 'Contents/Developer') | 151 build_dir = os.path.join( |
| 152 TOOLCHAIN_BUILD_DIR % target_os, 'Contents/Developer') |
| 151 subprocess.check_call(['sudo', '/usr/bin/xcode-select', '-s', build_dir]) | 153 subprocess.check_call(['sudo', '/usr/bin/xcode-select', '-s', build_dir]) |
| 152 subprocess.check_call(['sudo', '/usr/bin/xcodebuild', '-license', 'accept']) | 154 subprocess.check_call(['sudo', '/usr/bin/xcodebuild', '-license', 'accept']) |
| 153 finally: | 155 finally: |
| 154 subprocess.check_call(['sudo', '/usr/bin/xcode-select', '-s', old_path]) | 156 subprocess.check_call(['sudo', '/usr/bin/xcode-select', '-s', old_path]) |
| 155 | 157 |
| 156 | 158 |
| 157 def _UseHermeticToolchain(): | 159 def _UseHermeticToolchain(target_os): |
| 158 current_dir = os.path.dirname(os.path.realpath(__file__)) | 160 current_dir = os.path.dirname(os.path.realpath(__file__)) |
| 159 script_path = os.path.join(current_dir, 'mac/should_use_hermetic_xcode.py') | 161 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) | 162 proc = subprocess.Popen([script_path, target_os], stdout=subprocess.PIPE) |
| 162 return '1' in proc.stdout.readline() | 163 return '1' in proc.stdout.readline() |
| 163 | 164 |
| 164 | 165 |
| 165 def RequestGsAuthentication(): | 166 def RequestGsAuthentication(): |
| 166 """Requests that the user authenticate to be able to access gs://. | 167 """Requests that the user authenticate to be able to access gs://. |
| 167 """ | 168 """ |
| 168 print 'Access to ' + TOOLCHAIN_URL + ' not configured.' | 169 print 'Access to ' + TOOLCHAIN_URL + ' not configured.' |
| 169 print '-----------------------------------------------------------------' | 170 print '-----------------------------------------------------------------' |
| 170 print | 171 print |
| 171 print 'You appear to be a Googler.' | 172 print 'You appear to be a Googler.' |
| 172 print | 173 print |
| 173 print 'I\'m sorry for the hassle, but you need to do a one-time manual' | 174 print 'I\'m sorry for the hassle, but you need to do a one-time manual' |
| 174 print 'authentication. Please run:' | 175 print 'authentication. Please run:' |
| 175 print | 176 print |
| 176 print ' download_from_google_storage --config' | 177 print ' download_from_google_storage --config' |
| 177 print | 178 print |
| 178 print 'and follow the instructions.' | 179 print 'and follow the instructions.' |
| 179 print | 180 print |
| 180 print 'NOTE 1: Use your google.com credentials, not chromium.org.' | 181 print 'NOTE 1: Use your google.com credentials, not chromium.org.' |
| 181 print 'NOTE 2: Enter 0 when asked for a "project-id".' | 182 print 'NOTE 2: Enter 0 when asked for a "project-id".' |
| 182 print | 183 print |
| 183 print '-----------------------------------------------------------------' | 184 print '-----------------------------------------------------------------' |
| 184 print | 185 print |
| 185 sys.stdout.flush() | 186 sys.stdout.flush() |
| 186 sys.exit(1) | 187 sys.exit(1) |
| 187 | 188 |
| 188 | 189 |
| 189 def main(): | 190 def DownloadHermeticBuild(target_os, default_version, toolchain_filename): |
| 190 if sys.platform != 'darwin': | 191 if not _UseHermeticToolchain(target_os): |
| 192 print 'Using local toolchain for %s.' % target_os |
| 191 return 0 | 193 return 0 |
| 192 | 194 |
| 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', | 195 toolchain_version = os.environ.get('MAC_TOOLCHAIN_REVISION', |
| 205 default_version) | 196 default_version) |
| 206 | 197 |
| 207 if ReadStampFile() == toolchain_version: | 198 if ReadStampFile(target_os) == toolchain_version: |
| 208 print 'Toolchain (%s) is already up to date.' % toolchain_version | 199 print 'Toolchain (%s) is already up to date.' % toolchain_version |
| 209 AcceptLicense() | 200 AcceptLicense(target_os) |
| 210 return 0 | 201 return 0 |
| 211 | 202 |
| 212 if not CanAccessToolchainBucket(): | 203 if not CanAccessToolchainBucket(): |
| 213 RequestGsAuthentication() | 204 RequestGsAuthentication() |
| 214 return 1 | 205 return 1 |
| 215 | 206 |
| 216 # Reset the stamp file in case the build is unsuccessful. | 207 # Reset the stamp file in case the build is unsuccessful. |
| 217 WriteStampFile('') | 208 WriteStampFile(target_os, '') |
| 218 | 209 |
| 219 toolchain_file = '%s.tgz' % toolchain_version | 210 toolchain_file = '%s.tgz' % toolchain_version |
| 220 toolchain_full_url = TOOLCHAIN_URL + toolchain_file | 211 toolchain_full_url = TOOLCHAIN_URL + toolchain_file |
| 221 | 212 |
| 222 print 'Updating toolchain to %s...' % toolchain_version | 213 print 'Updating toolchain to %s...' % toolchain_version |
| 223 try: | 214 try: |
| 224 toolchain_file = toolchain_filename % toolchain_version | 215 toolchain_file = toolchain_filename % toolchain_version |
| 225 toolchain_full_url = TOOLCHAIN_URL + toolchain_file | 216 toolchain_full_url = TOOLCHAIN_URL + toolchain_file |
| 226 DownloadAndUnpack(toolchain_full_url, TOOLCHAIN_BUILD_DIR) | 217 DownloadAndUnpack(toolchain_full_url, TOOLCHAIN_BUILD_DIR % target_os) |
| 227 AcceptLicense() | 218 AcceptLicense(target_os) |
| 228 | 219 |
| 229 print 'Toolchain %s unpacked.' % toolchain_version | 220 print 'Toolchain %s unpacked.' % toolchain_version |
| 230 WriteStampFile(toolchain_version) | 221 WriteStampFile(target_os, toolchain_version) |
| 231 return 0 | 222 return 0 |
| 232 except Exception as e: | 223 except Exception as e: |
| 233 print 'Failed to download toolchain %s.' % toolchain_file | 224 print 'Failed to download toolchain %s.' % toolchain_file |
| 234 print 'Exception %s' % e | 225 print 'Exception %s' % e |
| 235 print 'Exiting.' | 226 print 'Exiting.' |
| 236 return 1 | 227 return 1 |
| 237 | 228 |
| 229 |
| 230 def main(): |
| 231 if sys.platform != 'darwin': |
| 232 return 0 |
| 233 |
| 234 for target_os in GetPlatforms(): |
| 235 if target_os == 'ios': |
| 236 default_version = IOS_TOOLCHAIN_VERSION |
| 237 toolchain_filename = 'ios-toolchain-%s.tgz' |
| 238 else: |
| 239 default_version = MAC_TOOLCHAIN_VERSION |
| 240 toolchain_filename = 'toolchain-%s.tgz' |
| 241 |
| 242 return_value = DownloadHermeticBuild( |
| 243 target_os, default_version, toolchain_filename) |
| 244 if return_value: |
| 245 return return_value |
| 246 |
| 247 return 0 |
| 248 |
| 249 |
| 238 if __name__ == '__main__': | 250 if __name__ == '__main__': |
| 239 sys.exit(main()) | 251 sys.exit(main()) |
| OLD | NEW |