Alan Viverette | 95f6d36 | 2017-04-06 09:40:50 -0400 | [diff] [blame] | 1 | #!/usr/bin/python |
| 2 | |
| 3 | import os, sys, getopt, zipfile, re |
Alan Viverette | d4527e6 | 2017-05-11 15:03:25 -0400 | [diff] [blame^] | 4 | import argparse |
| 5 | import subprocess |
Alan Viverette | 95f6d36 | 2017-04-06 09:40:50 -0400 | [diff] [blame] | 6 | from shutil import copyfile, rmtree |
Alan Viverette | d4527e6 | 2017-05-11 15:03:25 -0400 | [diff] [blame^] | 7 | from distutils.version import LooseVersion |
Alan Viverette | 95f6d36 | 2017-04-06 09:40:50 -0400 | [diff] [blame] | 8 | |
| 9 | output_path = 'current/support' |
| 10 | |
Alan Viverette | d4527e6 | 2017-05-11 15:03:25 -0400 | [diff] [blame^] | 11 | # See go/fetch_artifact |
| 12 | FETCH_ARTIFACT = '/google/data/ro/projects/android/fetch_artifact' |
| 13 | |
Alan Viverette | 95f6d36 | 2017-04-06 09:40:50 -0400 | [diff] [blame] | 14 | # Does not import support-v4, which is handled as a separate Android.mk (../support-v4) to |
| 15 | # statically include dependencies. Similarly, the support-v13 module is imported as |
| 16 | # support-v13-nodeps and then handled as a separate Android.mk (../support-v13) to statically |
| 17 | # include dependencies. |
| 18 | maven_to_make = { |
| 19 | 'animated-vector-drawable': ['android-support-animatedvectordrawable', 'graphics/drawable'], |
| 20 | 'appcompat-v7': ['android-support-v7-appcompat', 'v7/appcompat'], |
| 21 | 'cardview-v7': ['android-support-v7-cardview', 'v7/cardview'], |
| 22 | 'customtabs': ['android-support-customtabs', 'customtabs'], |
| 23 | 'design': ['android-support-design', 'design'], |
| 24 | 'exifinterface': ['android-support-exifinterface', 'exifinterface'], |
| 25 | 'gridlayout-v7': ['android-support-v7-gridlayout', 'v7/gridlayout'], |
| 26 | 'leanback-v17': ['android-support-v17-leanback', 'v17/leanback'], |
| 27 | 'mediarouter-v7': ['android-support-v7-mediarouter', 'v7/mediarouter'], |
| 28 | 'multidex': ['android-support-multidex', 'multidex/library'], |
| 29 | 'multidex-instrumentation': ['android-support-multidex-instrumentation', 'multidex/instrumentation'], |
| 30 | 'palette-v7': ['android-support-v7-palette', 'v7/palette'], |
| 31 | 'percent': ['android-support-percent', 'percent'], |
| 32 | 'preference-leanback-v17': ['android-support-v17-preference-leanback', 'v17/preference-leanback'], |
| 33 | 'preference-v14': ['android-support-v14-preference', 'v14/preference'], |
| 34 | 'preference-v7': ['android-support-v7-preference', 'v7/preference'], |
| 35 | 'recommendation': ['android-support-recommendation', 'recommendation'], |
| 36 | 'recyclerview-v7': ['android-support-v7-recyclerview', 'v7/recyclerview'], |
| 37 | 'support-annotations': ['android-support-annotations', 'annotations'], |
| 38 | 'support-compat': ['android-support-compat', 'compat'], |
| 39 | 'support-core-ui': ['android-support-core-ui', 'core-ui'], |
| 40 | 'support-core-utils': ['android-support-core-utils', 'core-utils'], |
| 41 | 'support-dynamic-animation': ['android-support-dynamic-animation', 'dynamic-animation'], |
| 42 | 'support-emoji': ['android-support-emoji', 'emoji'], |
| 43 | 'support-emoji-appcompat': ['android-support-emoji-appcompat', 'emoji-appcompat'], |
| 44 | 'support-emoji-bundled': ['android-support-emoji-bundled', 'emoji-bundled'], |
| 45 | 'support-fragment': ['android-support-fragment', 'fragment'], |
| 46 | 'support-media-compat': ['android-support-media-compat', 'media-compat'], |
| 47 | 'support-tv-provider': ['android-support-tv-provider', 'tv-provider'], |
| 48 | 'support-v13': ['android-support-v13-nodeps', 'v13-nodeps'], |
| 49 | 'support-vector-drawable': ['android-support-vectordrawable', 'graphics/drawable'], |
| 50 | 'transition': ['android-support-transition', 'transition'], |
| 51 | 'wear': ['android-support-wear', 'wear'] |
| 52 | } |
| 53 | |
| 54 | # Always remove these files. |
| 55 | blacklist_files = [ |
| 56 | 'annotations.zip', |
| 57 | 'public.txt', |
| 58 | 'R.txt', |
| 59 | 'AndroidManifest.xml' |
| 60 | ] |
| 61 | |
Alan Viverette | d4527e6 | 2017-05-11 15:03:25 -0400 | [diff] [blame^] | 62 | artifact_pattern = re.compile(r"^(.+?)-(\d+\.\d+\.\d+(?:-\w+\d*)?)\.(jar|aar)$") |
Alan Viverette | 95f6d36 | 2017-04-06 09:40:50 -0400 | [diff] [blame] | 63 | |
| 64 | |
| 65 | def touch(fname, times=None): |
| 66 | with open(fname, 'a'): |
| 67 | os.utime(fname, times) |
| 68 | |
| 69 | |
Alan Viverette | d4527e6 | 2017-05-11 15:03:25 -0400 | [diff] [blame^] | 70 | def transform_support(repoDir): |
Alan Viverette | 95f6d36 | 2017-04-06 09:40:50 -0400 | [diff] [blame] | 71 | cwd = os.getcwd() |
| 72 | |
Alan Viverette | 95f6d36 | 2017-04-06 09:40:50 -0400 | [diff] [blame] | 73 | # Use a temporary working directory. |
| 74 | working_dir = os.path.join(cwd, 'support_tmp') |
| 75 | if os.path.exists(working_dir): |
| 76 | rmtree(working_dir) |
| 77 | os.mkdir(working_dir) |
| 78 | |
Alan Viverette | d4527e6 | 2017-05-11 15:03:25 -0400 | [diff] [blame^] | 79 | maven_lib_info = {} |
| 80 | |
| 81 | for root, dirs, files in os.walk(repoDir): |
Alan Viverette | 95f6d36 | 2017-04-06 09:40:50 -0400 | [diff] [blame] | 82 | for file in files: |
| 83 | matcher = artifact_pattern.match(file) |
| 84 | if matcher: |
| 85 | maven_lib_name = matcher.group(1) |
Alan Viverette | d4527e6 | 2017-05-11 15:03:25 -0400 | [diff] [blame^] | 86 | maven_lib_vers = LooseVersion(matcher.group(2)) |
Alan Viverette | 95f6d36 | 2017-04-06 09:40:50 -0400 | [diff] [blame] | 87 | |
| 88 | if maven_lib_name in maven_to_make: |
Alan Viverette | d4527e6 | 2017-05-11 15:03:25 -0400 | [diff] [blame^] | 89 | if maven_lib_name not in maven_lib_info \ |
| 90 | or maven_lib_vers > maven_lib_info[maven_lib_name][0]: |
| 91 | maven_lib_info[maven_lib_name] = [maven_lib_vers, root, file] |
Alan Viverette | 95f6d36 | 2017-04-06 09:40:50 -0400 | [diff] [blame] | 92 | |
Alan Viverette | d4527e6 | 2017-05-11 15:03:25 -0400 | [diff] [blame^] | 93 | for info in maven_lib_info.values(): |
| 94 | transform_maven_lib(working_dir, info[1], info[2]) |
Alan Viverette | 95f6d36 | 2017-04-06 09:40:50 -0400 | [diff] [blame] | 95 | |
| 96 | # Replace the old directory. |
| 97 | output_dir = os.path.join(cwd, output_path) |
| 98 | if os.path.exists(output_dir): |
| 99 | rmtree(output_dir) |
| 100 | os.rename(working_dir, output_dir) |
| 101 | |
Alan Viverette | d4527e6 | 2017-05-11 15:03:25 -0400 | [diff] [blame^] | 102 | |
| 103 | def transform_maven_lib(working_dir, root, file): |
| 104 | matcher = artifact_pattern.match(file) |
| 105 | maven_lib_name = matcher.group(1) |
| 106 | maven_lib_vers = matcher.group(2) |
| 107 | maven_lib_type = matcher.group(3) |
| 108 | |
| 109 | make_lib_name = maven_to_make[maven_lib_name][0] |
| 110 | make_dir_name = maven_to_make[maven_lib_name][1] |
| 111 | artifact_file = os.path.join(root, file) |
| 112 | target_dir = os.path.join(working_dir, make_dir_name) |
| 113 | if not os.path.exists(target_dir): |
| 114 | os.makedirs(target_dir) |
| 115 | |
| 116 | if maven_lib_type == "aar": |
| 117 | process_aar(artifact_file, target_dir, make_lib_name) |
| 118 | else: |
| 119 | target_file = os.path.join(target_dir, make_lib_name + ".jar") |
| 120 | os.rename(artifact_file, target_file) |
| 121 | |
| 122 | print maven_lib_vers, ":", maven_lib_name, "->", make_lib_name |
| 123 | |
Alan Viverette | 95f6d36 | 2017-04-06 09:40:50 -0400 | [diff] [blame] | 124 | |
| 125 | def process_aar(artifact_file, target_dir, make_lib_name): |
| 126 | # Extract AAR file to target_dir. |
| 127 | with zipfile.ZipFile(artifact_file) as zip: |
| 128 | zip.extractall(target_dir) |
| 129 | |
| 130 | # Rename classes.jar to match the make artifact |
| 131 | classes_jar = os.path.join(target_dir, "classes.jar") |
| 132 | if os.path.exists(classes_jar): |
| 133 | # If it has resources, it needs a libs dir. |
| 134 | res_dir = os.path.join(target_dir, "res") |
| 135 | if os.path.exists(res_dir) and os.listdir(res_dir): |
| 136 | libs_dir = os.path.join(target_dir, "libs") |
| 137 | if not os.path.exists(libs_dir): |
| 138 | os.mkdir(libs_dir) |
| 139 | else: |
| 140 | libs_dir = target_dir |
| 141 | target_jar = os.path.join(libs_dir, make_lib_name + ".jar") |
| 142 | os.rename(classes_jar, target_jar) |
| 143 | |
| 144 | # Remove or preserve empty dirs. |
| 145 | for root, dirs, files in os.walk(target_dir): |
| 146 | for dir in dirs: |
| 147 | dir_path = os.path.join(root, dir) |
| 148 | if not os.listdir(dir_path): |
| 149 | os.rmdir(dir_path) |
| 150 | |
| 151 | # Remove top-level cruft. |
| 152 | for file in blacklist_files: |
| 153 | file_path = os.path.join(target_dir, file) |
| 154 | if os.path.exists(file_path): |
| 155 | os.remove(file_path) |
| 156 | |
| 157 | |
Alan Viverette | d4527e6 | 2017-05-11 15:03:25 -0400 | [diff] [blame^] | 158 | parser = argparse.ArgumentParser( |
| 159 | description=('Update prebuilt android extras')) |
| 160 | parser.add_argument( |
| 161 | 'buildId', |
| 162 | type=int, |
| 163 | nargs='?', |
| 164 | help='Build server build ID') |
| 165 | parser.add_argument( |
| 166 | '--target', |
| 167 | default='support_library', |
| 168 | help='Download m2repository from the specified build server target') |
| 169 | args = parser.parse_args() |
| 170 | if not args.buildId: |
| 171 | parser.error("You must specify a build ID") |
| 172 | sys.exit(1) |
| 173 | |
| 174 | try: |
| 175 | # Make sure we don't overwrite any pending changes. |
| 176 | subprocess.check_call(['git', 'diff', '--quiet', '--', '**']) |
| 177 | subprocess.check_call(['git', 'diff', '--quiet', '--cached', '--', '**']) |
| 178 | except subprocess.CalledProcessError: |
| 179 | print >> sys.stderr, "FAIL: There are uncommitted changes here; please revert or stash" |
| 180 | sys.exit(1) |
| 181 | |
| 182 | try: |
| 183 | repoArtifact = 'sdk-repo-linux-m2repository-' + str(args.buildId) + '.zip' |
| 184 | |
| 185 | # Download the repo zip archive. |
| 186 | fetchCmd = [FETCH_ARTIFACT, '--bid', str(args.buildId), '--target', args.target, repoArtifact] |
| 187 | try: |
| 188 | subprocess.check_output(fetchCmd, stderr=subprocess.STDOUT) |
| 189 | except subprocess.CalledProcessError: |
| 190 | print >> sys.stderr, "FAIL: Unable to retrieve artifact for build ID %d" % args.buildId |
| 191 | sys.exit(1) |
| 192 | |
| 193 | # Unzip the repo archive into a separate directory. |
| 194 | repoDir = os.path.basename(repoArtifact)[:-4] |
| 195 | with zipfile.ZipFile(repoArtifact) as zipFile: |
| 196 | zipFile.extractall(repoDir) |
| 197 | |
| 198 | # Transform the repo archive into a Makefile-compatible format. |
| 199 | transform_support(repoDir) |
| 200 | |
| 201 | # Commit all changes. |
| 202 | subprocess.check_call(['git', 'add', output_path]) |
| 203 | msg = ("Import support libs from build %s\n\n%s\n" % (args.buildId, " ".join(fetchCmd))) |
| 204 | subprocess.check_call(['git', 'commit', '-m', msg]) |
| 205 | print 'Be sure to upload this manually to gerrit.' |
| 206 | |
| 207 | finally: |
| 208 | # Revert all stray files, including the downloaded zip. |
| 209 | try: |
| 210 | with open(os.devnull, 'w') as bitbucket: |
| 211 | subprocess.check_call(['git', 'add', '-Af', '.'], stdout=bitbucket) |
| 212 | subprocess.check_call( |
| 213 | ['git', 'commit', '-m', 'COMMIT TO REVERT - RESET ME!!!'], stdout=bitbucket) |
| 214 | subprocess.check_call(['git', 'reset', '--hard', 'HEAD~1'], stdout=bitbucket) |
| 215 | except subprocess.CalledProcessError: |
| 216 | print >> sys.stderr, "ERROR: Failed cleaning up, manual cleanup required!!!" |