justincohen | 6a03a3d | 2016-03-26 21:44:38 | [diff] [blame] | 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 | |
justincohen | aa615f07 | 2016-11-30 18:46:41 | [diff] [blame] | 6 | """Compress and upload Mac toolchain files. |
| 7 | |
| 8 | Stored in in https://pantheon.corp.google.com/storage/browser/chrome-mac-sdk/. |
| 9 | """ |
justincohen | 6a03a3d | 2016-03-26 21:44:38 | [diff] [blame] | 10 | |
| 11 | import argparse |
| 12 | import glob |
| 13 | import os |
| 14 | import plistlib |
| 15 | import re |
| 16 | import subprocess |
| 17 | import sys |
| 18 | import tarfile |
| 19 | import tempfile |
| 20 | |
| 21 | |
| 22 | TOOLCHAIN_URL = "gs://chrome-mac-sdk" |
| 23 | |
| 24 | # It's important to at least remove unused Platform folders to cut down on the |
| 25 | # size of the toolchain folder. There are other various unused folders that |
| 26 | # have been removed through trial and error. If future versions of Xcode become |
| 27 | # problematic it's possible this list is incorrect, and can be reduced to just |
| 28 | # the unused platforms. On the flip side, it's likely more directories can be |
| 29 | # excluded. |
justincohen | aa615f07 | 2016-11-30 18:46:41 | [diff] [blame] | 30 | DEFAULT_EXCLUDE_FOLDERS = [ |
justincohen | 6a03a3d | 2016-03-26 21:44:38 | [diff] [blame] | 31 | 'Contents/Applications', |
| 32 | 'Contents/Developer/Documentation', |
justincohen | aa615f07 | 2016-11-30 18:46:41 | [diff] [blame] | 33 | 'Contents/Developer/Library/Xcode/Templates', |
justincohen | 6a03a3d | 2016-03-26 21:44:38 | [diff] [blame] | 34 | 'Contents/Developer/Platforms/AppleTVOS.platform', |
| 35 | 'Contents/Developer/Platforms/AppleTVSimulator.platform', |
justincohen | aa615f07 | 2016-11-30 18:46:41 | [diff] [blame] | 36 | 'Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/' |
| 37 | 'usr/share/man/', |
justincohen | 6a03a3d | 2016-03-26 21:44:38 | [diff] [blame] | 38 | 'Contents/Developer/Platforms/WatchOS.platform', |
| 39 | 'Contents/Developer/Platforms/WatchSimulator.platform', |
justincohen | aa615f07 | 2016-11-30 18:46:41 | [diff] [blame] | 40 | 'Contents/Developer/Toolchains/Swift*', |
justincohen | 6a03a3d | 2016-03-26 21:44:38 | [diff] [blame] | 41 | 'Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift', |
justincohen | aa615f07 | 2016-11-30 18:46:41 | [diff] [blame] | 42 | 'Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-migrator', |
| 43 | 'Contents/Resources/Packages/MobileDevice.pkg', |
justincohen | 6a03a3d | 2016-03-26 21:44:38 | [diff] [blame] | 44 | ] |
| 45 | |
justincohen | aa615f07 | 2016-11-30 18:46:41 | [diff] [blame] | 46 | MAC_EXCLUDE_FOLDERS = [ |
justincohen | ccbbfa2 | 2017-02-13 20:51:43 | [diff] [blame] | 47 | # The only thing we need in iPhoneOS.platform on mac is: |
| 48 | # \Developer\Library\Xcode\PrivatePlugins |
| 49 | # \Info.Plist. |
| 50 | # This is the cleanest way to get these. |
| 51 | 'Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Frameworks', |
| 52 | 'Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/GPUTools', |
| 53 | 'Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/' |
| 54 | 'GPUToolsPlatform', |
| 55 | 'Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/' |
| 56 | 'PrivateFrameworks', |
| 57 | 'Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr', |
| 58 | 'Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs', |
| 59 | 'Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport', |
| 60 | 'Contents/Developer/Platforms/iPhoneOS.platform/Library', |
| 61 | 'Contents/Developer/Platforms/iPhoneOS.platform/usr', |
| 62 | |
| 63 | # iPhoneSimulator has a similar requirement, but the bulk of the binary size is |
| 64 | # in \Developer\SDKs, so only excluding that here. |
justincohen | 62d09b76 | 2017-02-08 00:38:20 | [diff] [blame] | 65 | 'Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs', |
justincohen | aa615f07 | 2016-11-30 18:46:41 | [diff] [blame] | 66 | ] |
| 67 | |
| 68 | IOS_EXCLUDE_FOLDERS = [ |
| 69 | 'Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/' |
| 70 | 'Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/' |
| 71 | 'iPhoneSimulator.sdk/Applications/', |
| 72 | 'Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/' |
| 73 | 'iPhoneSimulator.sdk/System/Library/AccessibilityBundles/', |
| 74 | 'Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/' |
| 75 | 'iPhoneSimulator.sdk/System/Library/CoreServices/', |
| 76 | 'Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/' |
| 77 | 'iPhoneSimulator.sdk/System/Library/LinguisticData/', |
| 78 | ] |
justincohen | 6a03a3d | 2016-03-26 21:44:38 | [diff] [blame] | 79 | |
| 80 | def main(): |
| 81 | """Compress |target_dir| and upload to |TOOLCHAIN_URL|""" |
| 82 | parser = argparse.ArgumentParser() |
| 83 | parser.add_argument('target_dir', |
| 84 | help="Xcode installation directory.") |
justincohen | aa615f07 | 2016-11-30 18:46:41 | [diff] [blame] | 85 | parser.add_argument('platform', choices=['ios', 'mac'], |
| 86 | help="Target platform for bundle.") |
| 87 | parser_args = parser.parse_args() |
justincohen | 6a03a3d | 2016-03-26 21:44:38 | [diff] [blame] | 88 | |
| 89 | # Verify this looks like an Xcode directory. |
justincohen | aa615f07 | 2016-11-30 18:46:41 | [diff] [blame] | 90 | contents_dir = os.path.join(parser_args.target_dir, 'Contents') |
justincohen | 6a03a3d | 2016-03-26 21:44:38 | [diff] [blame] | 91 | plist_file = os.path.join(contents_dir, 'version.plist') |
| 92 | try: |
| 93 | info = plistlib.readPlist(plist_file) |
| 94 | except: |
| 95 | print "Invalid Xcode dir." |
| 96 | return 0 |
| 97 | build_version = info['ProductBuildVersion'] |
| 98 | |
| 99 | # Look for previous toolchain tgz files with the same |build_version|. |
justincohen | aa615f07 | 2016-11-30 18:46:41 | [diff] [blame] | 100 | fname = 'toolchain' |
| 101 | if parser_args.platform == 'ios': |
| 102 | fname = 'ios-' + fname |
| 103 | wildcard_filename = '%s/%s-%s-*.tgz' % (TOOLCHAIN_URL, fname, build_version) |
justincohen | 6a03a3d | 2016-03-26 21:44:38 | [diff] [blame] | 104 | p = subprocess.Popen(['gsutil.py', 'ls', wildcard_filename], |
| 105 | stdout=subprocess.PIPE, |
| 106 | stderr=subprocess.PIPE) |
| 107 | output = p.communicate()[0] |
| 108 | next_count = 1 |
| 109 | if p.returncode == 0: |
| 110 | next_count = len(output.split('\n')) |
| 111 | sys.stdout.write("%s already exists (%s). " |
| 112 | "Do you want to create another? [y/n] " |
| 113 | % (build_version, next_count - 1)) |
| 114 | |
| 115 | if raw_input().lower() not in set(['yes','y', 'ye']): |
| 116 | print "Skipping duplicate upload." |
| 117 | return 0 |
| 118 | |
justincohen | aa615f07 | 2016-11-30 18:46:41 | [diff] [blame] | 119 | os.chdir(parser_args.target_dir) |
| 120 | toolchain_file_name = "%s-%s-%s" % (fname, build_version, next_count) |
justincohen | 6a03a3d | 2016-03-26 21:44:38 | [diff] [blame] | 121 | toolchain_name = tempfile.mktemp(suffix='toolchain.tgz') |
| 122 | |
| 123 | print "Creating %s (%s)." % (toolchain_file_name, toolchain_name) |
| 124 | os.environ["COPYFILE_DISABLE"] = "1" |
justincohen | aa615f07 | 2016-11-30 18:46:41 | [diff] [blame] | 125 | os.environ["GZ_OPT"] = "-8" |
justincohen | 6a03a3d | 2016-03-26 21:44:38 | [diff] [blame] | 126 | args = ['tar', '-cvzf', toolchain_name] |
justincohen | aa615f07 | 2016-11-30 18:46:41 | [diff] [blame] | 127 | exclude_folders = DEFAULT_EXCLUDE_FOLDERS |
| 128 | if parser_args.platform == 'mac': |
| 129 | exclude_folders += MAC_EXCLUDE_FOLDERS |
| 130 | else: |
| 131 | exclude_folders += IOS_EXCLUDE_FOLDERS |
| 132 | args.extend(map('--exclude={0}'.format, exclude_folders)) |
justincohen | 6a03a3d | 2016-03-26 21:44:38 | [diff] [blame] | 133 | args.extend(['.']) |
| 134 | subprocess.check_call(args) |
| 135 | |
| 136 | print "Uploading %s toolchain." % toolchain_file_name |
| 137 | destination_path = '%s/%s.tgz' % (TOOLCHAIN_URL, toolchain_file_name) |
justincohen | a139a156 | 2016-11-16 18:52:24 | [diff] [blame] | 138 | subprocess.check_call(['gsutil.py', 'cp', '-n', toolchain_name, |
| 139 | destination_path]) |
justincohen | 6a03a3d | 2016-03-26 21:44:38 | [diff] [blame] | 140 | |
| 141 | print "Done with %s upload." % toolchain_file_name |
| 142 | return 0 |
| 143 | |
| 144 | if __name__ == '__main__': |
| 145 | sys.exit(main()) |