blob: 72098a3f3a3700de8e7a26aface0dbcd237baa3d [file] [log] [blame]
Mads Agera9745612017-11-02 12:42:15 +01001#!/usr/bin/env python
2# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
3# for details. All rights reserved. Use of this source code is governed by a
4# BSD-style license that can be found in the LICENSE file.
5
Mads Agera9745612017-11-02 12:42:15 +01006import argparse
Mads Agera4911eb2017-11-22 13:19:36 +01007import gradle
8import hashlib
Mads Agera9745612017-11-02 12:42:15 +01009from os import makedirs
10from os.path import join
11from shutil import copyfile, make_archive, rmtree
12import subprocess
13import sys
14from string import Template
15import tempfile
Mads Ager0fab4912017-11-20 13:52:48 +010016import utils
Mads Agera9745612017-11-02 12:42:15 +010017
Mads Agera4911eb2017-11-22 13:19:36 +010018DEPENDENCYTEMPLATE = Template(
Mads Agera9745612017-11-02 12:42:15 +010019"""
Mads Agera4911eb2017-11-22 13:19:36 +010020 <dependency>
21 <groupId>$group</groupId>
22 <artifactId>$artifact</artifactId>
23 <version>$version</version>
24 </dependency>""")
Mads Agera9745612017-11-02 12:42:15 +010025
26POMTEMPLATE = Template(
27"""<project
28 xmlns="http://maven.apache.org/POM/4.0.0"
29 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
30 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
31 <modelVersion>4.0.0</modelVersion>
32 <groupId>com.android.tools</groupId>
33 <artifactId>r8</artifactId>
34 <version>$version</version>
35 <name>D8 dexer and R8 shrinker</name>
36 <description>
37 D8 dexer and R8 shrinker.
38 </description>
39 <url>http://r8.googlesource.com/r8</url>
40 <inceptionYear>2016</inceptionYear>
41 <licenses>
42 <license>
43 <name>BSD-3-Clause</name>
44 <url>https://opensource.org/licenses/BSD-3-Clause</url>
45 <distribution>repo</distribution>
Mads Agera4911eb2017-11-22 13:19:36 +010046 </license>
Mads Ager73460192017-11-08 10:02:50 +010047 </licenses>
Mads Agera4911eb2017-11-22 13:19:36 +010048 <dependencies>$dependencies
49 </dependencies>
Mads Agera9745612017-11-02 12:42:15 +010050 <developers>
51 <developer>
52 <name>The Android Open Source Project</name>
53 </developer>
54 </developers>
55 <scm>
56 <connection>
57 https://r8.googlesource.com/r8.git
58 </connection>
59 <url>
60 https://r8.googlesource.com/r8
61 </url>
62 </scm>
63</project>
64""")
65
66def parse_options(argv):
67 result = argparse.ArgumentParser()
Rico Wind8fc8bfa2019-03-22 09:57:36 +010068 result.add_argument('--out', help='The zip file to output')
69 result.add_argument('--r8lib', action='store_true',
70 help='Build r8 with dependencies included shrunken')
Mads Agera9745612017-11-02 12:42:15 +010071 return result.parse_args(argv)
72
Mads Agera4911eb2017-11-22 13:19:36 +010073def determine_version():
74 version_file = join(
75 utils.SRC_ROOT, 'com', 'android', 'tools', 'r8', 'Version.java')
76 with open(version_file, 'r') as file:
77 for line in file:
78 if 'final String LABEL ' in line:
Rico Wind6feccf22018-05-08 11:18:22 +020079 result = line[line.find('"') + 1:]
Mads Agera4911eb2017-11-22 13:19:36 +010080 result = result[:result.find('"')]
81 return result
82 raise Exception('Unable to determine version.')
Mads Agera9745612017-11-02 12:42:15 +010083
84def generate_library_licenses():
85 license_prefix = 'license: '
86 licenses = []
87 license_url_prefix = 'licenseUrl: '
88 license_urls = []
89 with open('LIBRARY-LICENSE', 'r') as file:
90 for line in file:
91 trimmed = line.strip()
92 if trimmed.startswith(license_prefix):
Mads Ager73460192017-11-08 10:02:50 +010093 # Assert checking that licenses come in name/url pairs.
Mads Agera9745612017-11-02 12:42:15 +010094 assert len(licenses) == len(license_urls)
95 name = trimmed[len(license_prefix):]
96 if not name in licenses:
97 licenses.append(name)
98 if trimmed.startswith(license_url_prefix):
99 url = trimmed[len(license_url_prefix):]
100 if not url in license_urls:
101 license_urls.append(url)
Mads Ager73460192017-11-08 10:02:50 +0100102 # Assert checking that licenses come in name/url pairs.
Mads Agera9745612017-11-02 12:42:15 +0100103 assert len(licenses) == len(license_urls)
104 result = ''
105 for i in range(len(licenses)):
106 name = licenses[i]
107 url = license_urls[i]
108 result += LICENSETEMPLATE.substitute(name=name, url=url)
109 return result
110
Mads Agera4911eb2017-11-22 13:19:36 +0100111
112# Generate the dependencies block for the pom file.
113#
114# We ask gradle to list all dependencies. In that output
115# we locate the runtimeClasspath block for 'main' which
116# looks something like:
117#
118# runtimeClasspath - Runtime classpath of source set 'main'.
119# +--- net.sf.jopt-simple:jopt-simple:4.6
120# +--- com.googlecode.json-simple:json-simple:1.1
121# +--- com.google.guava:guava:23.0
122# +--- it.unimi.dsi:fastutil:7.2.0
123# +--- org.ow2.asm:asm:6.0
124# +--- org.ow2.asm:asm-commons:6.0
125# | \--- org.ow2.asm:asm-tree:6.0
126# | \--- org.ow2.asm:asm:6.0
127# +--- org.ow2.asm:asm-tree:6.0 (*)
128# +--- org.ow2.asm:asm-analysis:6.0
129# | \--- org.ow2.asm:asm-tree:6.0 (*)
130# \--- org.ow2.asm:asm-util:6.0
131# \--- org.ow2.asm:asm-tree:6.0 (*)
132#
133# We filter out the repeats that are marked by '(*)'.
134#
135# For each remaining line, we remove the junk at the start
136# in chunks. As an example:
137#
138# ' | \--- org.ow2.asm:asm-tree:6.0 ' --strip-->
139# '| \--- org.ow2.asm:asm-tree:6.0' -->
140# '\--- org.ow2.asm:asm-tree:6.0' -->
141# 'org.ow2.asm:asm-tree:6.0'
142#
143# The end result is the dependency we are looking for:
144#
145# groupId: org.ow2.asm
146# artifact: asm-tree
147# version: 6.0
148def generate_dependencies():
149 dependencies = gradle.RunGradleGetOutput(['dependencies'])
150 dependency_lines = []
151 collect = False
152 for line in dependencies.splitlines():
153 if 'runtimeClasspath' in line and "'main'" in line:
154 collect = True
155 continue
156 if collect:
157 if not len(line) == 0:
158 if not '(*)' in line:
159 trimmed = line.strip()
160 while trimmed.find(' ') != -1:
161 trimmed = trimmed[trimmed.find(' ') + 1:].strip()
162 if not trimmed in dependency_lines:
163 dependency_lines.append(trimmed)
164 else:
165 break
166 result = ''
167 for dep in dependency_lines:
168 components = dep.split(':')
169 assert len(components) == 3
170 group = components[0]
171 artifact = components[1]
172 version = components[2]
173 result += DEPENDENCYTEMPLATE.substitute(
174 group=group, artifact=artifact, version=version)
175 return result
176
Rico Wind8fc8bfa2019-03-22 09:57:36 +0100177def write_pom_file(version, pom_file, exclude_dependencies):
178 dependencies = "" if exclude_dependencies else generate_dependencies()
Mads Agera4911eb2017-11-22 13:19:36 +0100179 version_pom = POMTEMPLATE.substitute(
180 version=version, dependencies=dependencies)
Mads Agera9745612017-11-02 12:42:15 +0100181 with open(pom_file, 'w') as file:
182 file.write(version_pom)
183
184def hash_for(file, hash):
185 with open(file, 'rb') as f:
186 while True:
187 # Read chunks of 1MB
188 chunk = f.read(2 ** 20)
189 if not chunk:
190 break
191 hash.update(chunk)
192 return hash.hexdigest()
193
194def write_md5_for(file):
195 hexdigest = hash_for(file, hashlib.md5())
196 with (open(file + '.md5', 'w')) as file:
197 file.write(hexdigest)
198
199def write_sha1_for(file):
200 hexdigest = hash_for(file, hashlib.sha1())
201 with (open(file + '.sha1', 'w')) as file:
202 file.write(hexdigest)
203
Rico Wind8fc8bfa2019-03-22 09:57:36 +0100204def run(out, is_r8lib=False):
205 if out == None:
206 print 'Need to supply output zip with --out.'
Mads Agera9745612017-11-02 12:42:15 +0100207 exit(1)
Mads Agera4911eb2017-11-22 13:19:36 +0100208 # Build the R8 no deps artifact.
Rico Wind8fc8bfa2019-03-22 09:57:36 +0100209 if not is_r8lib:
210 gradle.RunGradleExcludeDeps([utils.R8])
211 else:
Rico Wind1410dbb2019-03-22 13:10:31 +0100212 gradle.RunGradle([utils.R8LIB, '-Pno_internal'])
Mads Agera9745612017-11-02 12:42:15 +0100213 # Create directory structure for this version.
Mads Agera4911eb2017-11-22 13:19:36 +0100214 version = determine_version()
Mads Ager14d9b072017-11-20 13:42:55 +0100215 with utils.TempDir() as tmp_dir:
Rico Windc0b16382018-05-17 13:23:43 +0200216 version_dir = join(tmp_dir, utils.get_maven_path(version))
Mads Ager14d9b072017-11-20 13:42:55 +0100217 makedirs(version_dir)
218 # Write the pom file.
219 pom_file = join(version_dir, 'r8-' + version + '.pom')
Rico Wind8fc8bfa2019-03-22 09:57:36 +0100220 write_pom_file(version, pom_file, is_r8lib)
Mads Ager14d9b072017-11-20 13:42:55 +0100221 # Copy the jar to the output.
222 target_jar = join(version_dir, 'r8-' + version + '.jar')
Rico Wind8fc8bfa2019-03-22 09:57:36 +0100223 copyfile(utils.R8LIB_JAR if is_r8lib else utils.R8_JAR, target_jar)
Mads Ager14d9b072017-11-20 13:42:55 +0100224 # Create check sums.
225 write_md5_for(target_jar)
226 write_md5_for(pom_file)
227 write_sha1_for(target_jar)
228 write_sha1_for(pom_file)
Rico Wind8fc8bfa2019-03-22 09:57:36 +0100229 # Zip it up - make_archive will append zip to the file, so remove.
230 assert out.endswith('.zip')
231 base_no_zip = out[0:len(out)-4]
232 make_archive(base_no_zip, 'zip', tmp_dir)
233
234def main(argv):
235 options = parse_options(argv)
236 out = options.out
237 run(out, options.r8lib)
Mads Agera9745612017-11-02 12:42:15 +0100238
239if __name__ == "__main__":
240 exit(main(sys.argv[1:]))