blob: 42dcc63a8a437bde57542e4c4740192adf4f4fef [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
Søren Gjessea70d3bd2019-09-24 15:07:00 +02009import jdk
Søren Gjesse6e5e5842019-09-03 08:48:30 +020010import json
Mads Agera9745612017-11-02 12:42:15 +010011from os import makedirs
12from os.path import join
Søren Gjesse6e5e5842019-09-03 08:48:30 +020013from shutil import copyfile, make_archive, move, rmtree
Mads Agera9745612017-11-02 12:42:15 +010014import subprocess
15import sys
16from string import Template
17import tempfile
Mads Ager0fab4912017-11-20 13:52:48 +010018import utils
Søren Gjesse17fc67d2019-12-04 14:50:17 +010019import zipfile
Mads Agera9745612017-11-02 12:42:15 +010020
Mads Agera4911eb2017-11-22 13:19:36 +010021DEPENDENCYTEMPLATE = Template(
Mads Agera9745612017-11-02 12:42:15 +010022"""
Mads Agera4911eb2017-11-22 13:19:36 +010023 <dependency>
24 <groupId>$group</groupId>
25 <artifactId>$artifact</artifactId>
26 <version>$version</version>
27 </dependency>""")
Mads Agera9745612017-11-02 12:42:15 +010028
Søren Gjessec425e6a2019-06-28 11:41:14 +020029LICENSETEMPLATE = Template(
30"""
31 <license>
32 <name>$name</name>
33 <url>$url</url>
34 <distribution>repo</distribution>
35 </license>""")
36
Søren Gjesse6e5e5842019-09-03 08:48:30 +020037R8_POMTEMPLATE = Template(
Mads Agera9745612017-11-02 12:42:15 +010038"""<project
39 xmlns="http://maven.apache.org/POM/4.0.0"
40 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
41 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
42 <modelVersion>4.0.0</modelVersion>
43 <groupId>com.android.tools</groupId>
44 <artifactId>r8</artifactId>
45 <version>$version</version>
46 <name>D8 dexer and R8 shrinker</name>
47 <description>
48 D8 dexer and R8 shrinker.
49 </description>
50 <url>http://r8.googlesource.com/r8</url>
51 <inceptionYear>2016</inceptionYear>
52 <licenses>
53 <license>
54 <name>BSD-3-Clause</name>
55 <url>https://opensource.org/licenses/BSD-3-Clause</url>
56 <distribution>repo</distribution>
Søren Gjessec425e6a2019-06-28 11:41:14 +020057 </license>$library_licenses
Mads Ager73460192017-11-08 10:02:50 +010058 </licenses>
Mads Agera4911eb2017-11-22 13:19:36 +010059 <dependencies>$dependencies
60 </dependencies>
Mads Agera9745612017-11-02 12:42:15 +010061 <developers>
62 <developer>
63 <name>The Android Open Source Project</name>
64 </developer>
65 </developers>
66 <scm>
67 <connection>
68 https://r8.googlesource.com/r8.git
69 </connection>
70 <url>
71 https://r8.googlesource.com/r8
72 </url>
73 </scm>
74</project>
75""")
76
Søren Gjesse6e5e5842019-09-03 08:48:30 +020077DESUGAR_CONFIGUATION_POMTEMPLATE = Template(
78"""<project
79 xmlns="http://maven.apache.org/POM/4.0.0"
80 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
81 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
82 <modelVersion>4.0.0</modelVersion>
83 <groupId>com.android.tools</groupId>
84 <artifactId>desugar_jdk_libs_configuration</artifactId>
85 <version>$version</version>
86 <name>D8 configuration to desugar desugar_jdk_libs</name>
87 <description>
88 D8 configuration to desugar desugar_jdk_libs.
89 </description>
90 <url>http://r8.googlesource.com/r8</url>
91 <inceptionYear>2019</inceptionYear>
92 <licenses>
93 <license>
94 <name>BSD-3-Clause</name>
95 <url>https://opensource.org/licenses/BSD-3-Clause</url>
96 <distribution>repo</distribution>
97 </license>
98 </licenses>
Søren Gjesse6e5e5842019-09-03 08:48:30 +020099 <developers>
100 <developer>
101 <name>The Android Open Source Project</name>
102 </developer>
103 </developers>
104 <scm>
105 <connection>
106 https://r8.googlesource.com/r8.git
107 </connection>
108 <url>
109 https://r8.googlesource.com/r8
110 </url>
111 </scm>
112</project>
113""")
114
Mads Agera9745612017-11-02 12:42:15 +0100115def parse_options(argv):
116 result = argparse.ArgumentParser()
Rico Wind8fc8bfa2019-03-22 09:57:36 +0100117 result.add_argument('--out', help='The zip file to output')
118 result.add_argument('--r8lib', action='store_true',
119 help='Build r8 with dependencies included shrunken')
Søren Gjesse6e5e5842019-09-03 08:48:30 +0200120 result.add_argument('--desugar-configuration', action='store_true',
121 help='Build r8 with dependencies included shrunken')
Mads Agera9745612017-11-02 12:42:15 +0100122 return result.parse_args(argv)
123
Mads Agera4911eb2017-11-22 13:19:36 +0100124def determine_version():
125 version_file = join(
126 utils.SRC_ROOT, 'com', 'android', 'tools', 'r8', 'Version.java')
127 with open(version_file, 'r') as file:
128 for line in file:
129 if 'final String LABEL ' in line:
Rico Wind6feccf22018-05-08 11:18:22 +0200130 result = line[line.find('"') + 1:]
Mads Agera4911eb2017-11-22 13:19:36 +0100131 result = result[:result.find('"')]
132 return result
133 raise Exception('Unable to determine version.')
Mads Agera9745612017-11-02 12:42:15 +0100134
135def generate_library_licenses():
Søren Gjessec425e6a2019-06-28 11:41:14 +0200136 artifact_prefix = '- artifact: '
Mads Agera9745612017-11-02 12:42:15 +0100137 license_prefix = 'license: '
138 licenses = []
139 license_url_prefix = 'licenseUrl: '
140 license_urls = []
Søren Gjesseea678302019-08-27 14:41:03 +0200141 # The ./LIBRARY-LICENSE file is a simple yaml file, which for each dependency
142 # has the following information:
143 #
144 # - artifact: <maven artifact> // in the form <group-id>:<artifact-id>:+
145 # name: <name of dependency>
146 # copyrightHolder: <name of copyright holder>
147 # license: <license name>
148 # licenseUrl: <url to license test>
149 #
150 # E.g. for Guava:
151 #
152 # - artifact: com.google.guava:guava:+
153 # name: Guava Google Core Libraries for Java
154 # copyrightHolder: The Guava Authors
155 # license: The Apache Software License, Version 2.0
156 # licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
157 #
158 # This file should always be up to date as the build will fail if it
159 # is does not have information for all dependencies.
Mads Agera9745612017-11-02 12:42:15 +0100160 with open('LIBRARY-LICENSE', 'r') as file:
Søren Gjessec425e6a2019-06-28 11:41:14 +0200161 name = None
162 url = None
Mads Agera9745612017-11-02 12:42:15 +0100163 for line in file:
164 trimmed = line.strip()
Søren Gjessec425e6a2019-06-28 11:41:14 +0200165 # Collect license name and url for each artifact. They must come in
166 # pairs for each artifact.
167 if trimmed.startswith(artifact_prefix):
168 assert not name
169 assert not url
Mads Agera9745612017-11-02 12:42:15 +0100170 if trimmed.startswith(license_prefix):
Mads Agera9745612017-11-02 12:42:15 +0100171 name = trimmed[len(license_prefix):]
Mads Agera9745612017-11-02 12:42:15 +0100172 if trimmed.startswith(license_url_prefix):
173 url = trimmed[len(license_url_prefix):]
Søren Gjessec425e6a2019-06-28 11:41:14 +0200174 # Licenses come in name/url pairs. When both are present add pair
175 # to collected licenses if either name or url has not been recorded yet,
176 # as some licenses with slightly different names point to the same url.
177 if name and url:
178 if (not name in licenses) or (not url in license_urls):
179 licenses.append(name)
Mads Agera9745612017-11-02 12:42:15 +0100180 license_urls.append(url)
Søren Gjessec425e6a2019-06-28 11:41:14 +0200181 name = None
182 url = None
183 assert len(licenses) == len(license_urls)
Mads Agera9745612017-11-02 12:42:15 +0100184 result = ''
185 for i in range(len(licenses)):
186 name = licenses[i]
187 url = license_urls[i]
188 result += LICENSETEMPLATE.substitute(name=name, url=url)
189 return result
190
Mads Agera4911eb2017-11-22 13:19:36 +0100191
192# Generate the dependencies block for the pom file.
193#
194# We ask gradle to list all dependencies. In that output
195# we locate the runtimeClasspath block for 'main' which
196# looks something like:
197#
198# runtimeClasspath - Runtime classpath of source set 'main'.
199# +--- net.sf.jopt-simple:jopt-simple:4.6
200# +--- com.googlecode.json-simple:json-simple:1.1
201# +--- com.google.guava:guava:23.0
202# +--- it.unimi.dsi:fastutil:7.2.0
203# +--- org.ow2.asm:asm:6.0
204# +--- org.ow2.asm:asm-commons:6.0
205# | \--- org.ow2.asm:asm-tree:6.0
206# | \--- org.ow2.asm:asm:6.0
207# +--- org.ow2.asm:asm-tree:6.0 (*)
208# +--- org.ow2.asm:asm-analysis:6.0
209# | \--- org.ow2.asm:asm-tree:6.0 (*)
210# \--- org.ow2.asm:asm-util:6.0
211# \--- org.ow2.asm:asm-tree:6.0 (*)
212#
213# We filter out the repeats that are marked by '(*)'.
214#
215# For each remaining line, we remove the junk at the start
216# in chunks. As an example:
217#
218# ' | \--- org.ow2.asm:asm-tree:6.0 ' --strip-->
219# '| \--- org.ow2.asm:asm-tree:6.0' -->
220# '\--- org.ow2.asm:asm-tree:6.0' -->
221# 'org.ow2.asm:asm-tree:6.0'
222#
223# The end result is the dependency we are looking for:
224#
225# groupId: org.ow2.asm
226# artifact: asm-tree
227# version: 6.0
228def generate_dependencies():
229 dependencies = gradle.RunGradleGetOutput(['dependencies'])
230 dependency_lines = []
231 collect = False
232 for line in dependencies.splitlines():
233 if 'runtimeClasspath' in line and "'main'" in line:
234 collect = True
235 continue
236 if collect:
237 if not len(line) == 0:
238 if not '(*)' in line:
239 trimmed = line.strip()
240 while trimmed.find(' ') != -1:
241 trimmed = trimmed[trimmed.find(' ') + 1:].strip()
242 if not trimmed in dependency_lines:
243 dependency_lines.append(trimmed)
244 else:
245 break
246 result = ''
247 for dep in dependency_lines:
248 components = dep.split(':')
249 assert len(components) == 3
250 group = components[0]
251 artifact = components[1]
252 version = components[2]
253 result += DEPENDENCYTEMPLATE.substitute(
254 group=group, artifact=artifact, version=version)
255 return result
256
Rico Wind257044c2019-11-22 08:21:21 +0100257def write_default_r8_pom_file(pom_file, version):
258 write_pom_file(R8_POMTEMPLATE, pom_file, version, generate_dependencies(), '')
259
Søren Gjesse6e5e5842019-09-03 08:48:30 +0200260def write_pom_file(template, pom_file, version, dependencies='', library_licenses=''):
261 version_pom = template.substitute(
Søren Gjessec425e6a2019-06-28 11:41:14 +0200262 version=version, dependencies=dependencies, library_licenses=library_licenses)
Mads Agera9745612017-11-02 12:42:15 +0100263 with open(pom_file, 'w') as file:
264 file.write(version_pom)
265
266def hash_for(file, hash):
267 with open(file, 'rb') as f:
268 while True:
269 # Read chunks of 1MB
270 chunk = f.read(2 ** 20)
271 if not chunk:
272 break
273 hash.update(chunk)
274 return hash.hexdigest()
275
276def write_md5_for(file):
277 hexdigest = hash_for(file, hashlib.md5())
278 with (open(file + '.md5', 'w')) as file:
279 file.write(hexdigest)
280
281def write_sha1_for(file):
282 hexdigest = hash_for(file, hashlib.sha1())
283 with (open(file + '.sha1', 'w')) as file:
284 file.write(hexdigest)
285
Søren Gjesse6e5e5842019-09-03 08:48:30 +0200286def generate_maven_zip(name, version, pom_file, jar_file, out):
Mads Ager14d9b072017-11-20 13:42:55 +0100287 with utils.TempDir() as tmp_dir:
Søren Gjesse6e5e5842019-09-03 08:48:30 +0200288 # Create the base maven version directory
289 version_dir = join(tmp_dir, utils.get_maven_path(name, version))
Mads Ager14d9b072017-11-20 13:42:55 +0100290 makedirs(version_dir)
291 # Write the pom file.
Søren Gjesse6e5e5842019-09-03 08:48:30 +0200292 pom_file_location = join(version_dir, name + '-' + version + '.pom')
293 copyfile(pom_file, pom_file_location)
294 # Write the jar file.
295 jar_file_location = join(version_dir, name + '-' + version + '.jar')
296 copyfile(jar_file, jar_file_location)
Mads Ager14d9b072017-11-20 13:42:55 +0100297 # Create check sums.
Søren Gjesse6e5e5842019-09-03 08:48:30 +0200298 write_md5_for(jar_file_location)
299 write_md5_for(pom_file_location)
300 write_sha1_for(jar_file_location)
301 write_sha1_for(pom_file_location)
Rico Wind8fc8bfa2019-03-22 09:57:36 +0100302 # Zip it up - make_archive will append zip to the file, so remove.
303 assert out.endswith('.zip')
304 base_no_zip = out[0:len(out)-4]
305 make_archive(base_no_zip, 'zip', tmp_dir)
306
Søren Gjesse6e5e5842019-09-03 08:48:30 +0200307def generate_r8_maven_zip(out, is_r8lib=False):
308 # Build the R8 no deps artifact.
309 if not is_r8lib:
310 gradle.RunGradleExcludeDeps([utils.R8])
311 else:
312 gradle.RunGradle([utils.R8LIB, '-Pno_internal'])
313
314 version = determine_version()
315 # Generate the pom file.
316 pom_file = 'r8.pom'
317 write_pom_file(
318 R8_POMTEMPLATE,
319 pom_file,
320 version,
321 "" if is_r8lib else generate_dependencies(),
322 generate_library_licenses() if is_r8lib else "")
323 # Write the maven zip file.
324 generate_maven_zip(
325 'r8',
326 version,
327 pom_file,
328 utils.R8LIB_JAR if is_r8lib else utils.R8_JAR,
329 out)
330
331# Write the desugaring configuration of a jar file with the following content:
Søren Gjesse17fc67d2019-12-04 14:50:17 +0100332# java/
333# util/
334# <java.util conversions classes>
335# time/
336# <java.time conversions classes>
337# META-INF/
338# desugar/
339# d8/
Søren Gjesse6e5e5842019-09-03 08:48:30 +0200340# desugar.json
Søren Gjesse17fc67d2019-12-04 14:50:17 +0100341# lint/
342# <lint files>
343def generate_jar_with_desugar_configuration(configuration, conversions, destination):
Søren Gjesse6e5e5842019-09-03 08:48:30 +0200344 with utils.TempDir() as tmp_dir:
Søren Gjesse17fc67d2019-12-04 14:50:17 +0100345 # Add conversion classes.
346 with zipfile.ZipFile(conversions, 'r') as conversions_zip:
347 conversions_zip.extractall(tmp_dir)
348
349 # Add configuration
Søren Gjesse6e5e5842019-09-03 08:48:30 +0200350 configuration_dir = join(tmp_dir, 'META-INF', 'desugar', 'd8')
351 makedirs(configuration_dir)
352 copyfile(configuration, join(configuration_dir, 'desugar.json'))
Søren Gjessea70d3bd2019-09-24 15:07:00 +0200353
Søren Gjesse17fc67d2019-12-04 14:50:17 +0100354 # Add lint configuartion.
Søren Gjessea70d3bd2019-09-24 15:07:00 +0200355 lint_dir = join(configuration_dir, 'lint')
356 makedirs(lint_dir)
357 cmd = [
358 jdk.GetJavaExecutable(),
359 '-cp',
360 utils.R8_JAR,
361 'com.android.tools.r8.GenerateLintFiles',
362 configuration,
363 lint_dir]
364 utils.PrintCmd(cmd)
365 subprocess.check_call(cmd)
366
Søren Gjesse6e5e5842019-09-03 08:48:30 +0200367 make_archive(destination, 'zip', tmp_dir)
368 move(destination + '.zip', destination)
369
370# Generate the maven zip for the configuration to desugar desugar_jdk_libs.
371def generate_desugar_configuration_maven_zip(out):
372 version = utils.desugar_configuration_version()
373 # Generate the pom file.
374 pom_file = 'desugar_configuration.pom'
375 write_pom_file(DESUGAR_CONFIGUATION_POMTEMPLATE, pom_file, version)
376 # Generate the jar with the configuration file.
377 jar_file = 'desugar_configuration.jar'
378 generate_jar_with_desugar_configuration(
Søren Gjesse17fc67d2019-12-04 14:50:17 +0100379 utils.DESUGAR_CONFIGURATION,
380 utils.LIBRARY_DESUGAR_CONVERSIONS_ZIP,
381 jar_file)
Søren Gjesse6e5e5842019-09-03 08:48:30 +0200382 # Write the maven zip file.
383 generate_maven_zip(
384 'desugar_jdk_libs_configuration', version, pom_file, jar_file, out)
385
386
Rico Wind8fc8bfa2019-03-22 09:57:36 +0100387def main(argv):
388 options = parse_options(argv)
Søren Gjesse6e5e5842019-09-03 08:48:30 +0200389 if options.r8lib and options.desugar_configuration:
390 raise Exception(
391 'Cannot combine options --r8lib and --desugar-configuration')
392 if options.out == None:
393 raise Exception(
394 'Need to supply output zip with --out.')
395 if options.desugar_configuration:
396 generate_desugar_configuration_maven_zip(options.out)
397 else:
398 generate_r8_maven_zip(options.out, options.r8lib)
Mads Agera9745612017-11-02 12:42:15 +0100399
400if __name__ == "__main__":
401 exit(main(sys.argv[1:]))