Merge "[CF] Even align index of locals to avoid overlap between single and wide."
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfArrayLoad.java b/src/main/java/com/android/tools/r8/cf/code/CfArrayLoad.java
new file mode 100644
index 0000000..9043ab6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/cf/code/CfArrayLoad.java
@@ -0,0 +1,47 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.cf.code;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.ir.code.MemberType;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+public class CfArrayLoad extends CfInstruction {
+
+ private final MemberType type;
+
+ public CfArrayLoad(MemberType type) {
+ this.type = type;
+ }
+
+ private int getLoadType() {
+ switch (type) {
+ case OBJECT:
+ return Opcodes.AALOAD;
+ case BYTE:
+ case BOOLEAN:
+ return Opcodes.BALOAD;
+ case CHAR:
+ return Opcodes.CALOAD;
+ case SHORT:
+ return Opcodes.SALOAD;
+ case INT:
+ return Opcodes.IALOAD;
+ case FLOAT:
+ return Opcodes.FALOAD;
+ case LONG:
+ return Opcodes.LALOAD;
+ case DOUBLE:
+ return Opcodes.DALOAD;
+ default:
+ throw new Unreachable("Unexpected type " + type);
+ }
+ }
+
+ @Override
+ public void write(MethodVisitor visitor) {
+ visitor.visitInsn(getLoadType());
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstNull.java b/src/main/java/com/android/tools/r8/cf/code/CfConstNull.java
new file mode 100644
index 0000000..f022f78
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstNull.java
@@ -0,0 +1,15 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.cf.code;
+
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+public class CfConstNull extends CfInstruction {
+
+ @Override
+ public void write(MethodVisitor visitor) {
+ visitor.visitInsn(Opcodes.ACONST_NULL);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNew.java b/src/main/java/com/android/tools/r8/cf/code/CfNew.java
new file mode 100644
index 0000000..b528ea5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNew.java
@@ -0,0 +1,23 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.cf.code;
+
+import com.android.tools.r8.graph.DexType;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+public class CfNew extends CfInstruction {
+
+ private final DexType type;
+
+ public CfNew(DexType type) {
+ this.type = type;
+ }
+
+ @Override
+ public void write(MethodVisitor visitor) {
+ visitor.visitTypeInsn(Opcodes.NEW, Type.getType(type.toDescriptorString()).getInternalName());
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfPop.java b/src/main/java/com/android/tools/r8/cf/code/CfPop.java
new file mode 100644
index 0000000..e058df6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/cf/code/CfPop.java
@@ -0,0 +1,22 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.cf.code;
+
+import com.android.tools.r8.ir.code.ValueType;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+public class CfPop extends CfInstruction {
+
+ private final ValueType type;
+
+ public CfPop(ValueType type) {
+ this.type = type;
+ }
+
+ @Override
+ public void write(MethodVisitor visitor) {
+ visitor.visitInsn(type.isWide() ? Opcodes.POP2 : Opcodes.POP);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java b/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
index e0aa965..be0e6db 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
+import com.android.tools.r8.cf.code.CfArrayLoad;
import com.android.tools.r8.code.Aget;
import com.android.tools.r8.code.AgetBoolean;
import com.android.tools.r8.code.AgetByte;
@@ -14,6 +15,8 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.conversion.CfBuilder;
+import com.android.tools.r8.ir.conversion.CfBuilder.StackHelper;
import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.ir.optimize.Inliner.Constraint;
import com.android.tools.r8.ir.regalloc.RegisterAllocator;
@@ -126,4 +129,15 @@
public Constraint inliningConstraint(AppInfoWithSubtyping info, DexType holder) {
return Constraint.ALWAYS;
}
+
+ @Override
+ public void insertLoadAndStores(InstructionListIterator it, StackHelper stack) {
+ stack.loadInValues(this, it);
+ stack.storeOutValue(this, it);
+ }
+
+ @Override
+ public void buildCf(CfBuilder builder) {
+ builder.add(new CfArrayLoad(type));
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java b/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
index b887e97..c78f9ad 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
+import com.android.tools.r8.cf.code.CfConstNull;
import com.android.tools.r8.cf.code.CfConstNumber;
import com.android.tools.r8.code.Const;
import com.android.tools.r8.code.Const16;
@@ -139,7 +140,11 @@
@Override
public void buildCf(CfBuilder builder) {
- builder.add(new CfConstNumber(value, outType()));
+ if (outType().isObject()) {
+ builder.add(new CfConstNull());
+ } else {
+ builder.add(new CfConstNumber(value, outType()));
+ }
}
// Estimated size of the resulting dex instruction in code units.
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
index 0fd4804..fef03c3 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
@@ -90,7 +90,7 @@
return;
}
if (outValue == null) {
- stack.popOutValue(this, it);
+ stack.popOutValue(ValueType.fromDexType(method.proto.returnType), this, it);
} else {
stack.storeOutValue(this, it);
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
index 434b415..979b491 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
@@ -3,9 +3,12 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
+import com.android.tools.r8.cf.code.CfNew;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.conversion.CfBuilder;
+import com.android.tools.r8.ir.conversion.CfBuilder.StackHelper;
import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.ir.optimize.Inliner.Constraint;
@@ -76,4 +79,14 @@
public Constraint inliningConstraint(AppInfoWithSubtyping info, DexType holder) {
return Constraint.classIsVisible(holder, clazz, info);
}
+
+ @Override
+ public void insertLoadAndStores(InstructionListIterator it, StackHelper stack) {
+ stack.storeOutValue(this, it);
+ }
+
+ @Override
+ public void buildCf(CfBuilder builder) {
+ builder.add(new CfNew(clazz));
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Pop.java b/src/main/java/com/android/tools/r8/ir/code/Pop.java
index 61ab130..33d5557 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Pop.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Pop.java
@@ -3,9 +3,11 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
+import com.android.tools.r8.cf.code.CfPop;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.optimize.Inliner.Constraint;
public class Pop extends Instruction {
@@ -38,4 +40,9 @@
public Constraint inliningConstraint(AppInfoWithSubtyping info, DexType holder) {
throw new Unreachable();
}
+
+ @Override
+ public void buildCf(CfBuilder builder) {
+ builder.add(new CfPop(inValues.get(0).type));
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
index d29fe79..8c00fcc 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
@@ -20,6 +20,7 @@
import com.android.tools.r8.ir.code.StackValue;
import com.android.tools.r8.ir.code.Store;
import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.code.ValueType;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@@ -55,8 +56,8 @@
add(new Store(oldOutValue, newOutValue), instruction, it);
}
- public void popOutValue(Instruction instruction, InstructionListIterator it) {
- StackValue newOutValue = new StackValue(instruction.outType());
+ public void popOutValue(ValueType type, Instruction instruction, InstructionListIterator it) {
+ StackValue newOutValue = new StackValue(type);
instruction.swapOutValue(newOutValue);
add(new Pop(newOutValue), instruction, it);
}
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 67add9c..4c30d36 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -160,6 +160,12 @@
*/
private final Map<DexType, Set<DexAnnotation>> deferredAnnotations = new IdentityHashMap<>();
+ /**
+ * Methods that have been seen as not reachable due to related classes not being instantiated.
+ * As more instantiated types are collected this set needs to be re-visited.
+ */
+ private List<DexEncodedMethod> pendingAdditionalInstantiatedTypes = new ArrayList<>();
+
public Enqueuer(AppInfoWithSubtyping appInfo, InternalOptions options) {
this.appInfo = appInfo;
this.options = options;
@@ -679,33 +685,39 @@
SetWithReason<DexEncodedMethod> reachable = reachableVirtualMethods
.computeIfAbsent(encodedMethod.method.holder, (ignore) -> new SetWithReason<>());
if (reachable.add(encodedMethod, reason)) {
- // If the holder type is instantiated, the method is live. Otherwise check whether we find
- // a subtype that does not shadow this methods but is instantiated.
- // Note that library classes are always considered instantiated, as we do not know where
- // they are instantiated.
- if (isInstantiatedOrHasInstantiatedSubtype(encodedMethod.method.holder)) {
- if (instantiatedTypes.contains(encodedMethod.method.holder)) {
- markVirtualMethodAsLive(encodedMethod,
- KeepReason.reachableFromLiveType(encodedMethod.method.holder));
- } else {
- Deque<DexType> worklist = new ArrayDeque<>();
- fillWorkList(worklist, encodedMethod.method.holder);
- while (!worklist.isEmpty()) {
- DexType current = worklist.pollFirst();
- DexClass currentHolder = appInfo.definitionFor(current);
- if (currentHolder == null
- || currentHolder.findVirtualTarget(encodedMethod.method) != null) {
- continue;
- }
- if (instantiatedTypes.contains(current)) {
- markVirtualMethodAsLive(encodedMethod, KeepReason.reachableFromLiveType(current));
- break;
- }
- fillWorkList(worklist, current);
- }
+ handleIsInstantiatedOrHasInstantiatedSubtype(encodedMethod);
+ }
+ }
+ }
+
+ private void handleIsInstantiatedOrHasInstantiatedSubtype(DexEncodedMethod encodedMethod) {
+ // If the holder type is instantiated, the method is live. Otherwise check whether we find
+ // a subtype that does not shadow this methods but is instantiated.
+ // Note that library classes are always considered instantiated, as we do not know where
+ // they are instantiated.
+ if (isInstantiatedOrHasInstantiatedSubtype(encodedMethod.method.holder)) {
+ if (instantiatedTypes.contains(encodedMethod.method.holder)) {
+ markVirtualMethodAsLive(encodedMethod,
+ KeepReason.reachableFromLiveType(encodedMethod.method.holder));
+ } else {
+ Deque<DexType> worklist = new ArrayDeque<>();
+ fillWorkList(worklist, encodedMethod.method.holder);
+ while (!worklist.isEmpty()) {
+ DexType current = worklist.pollFirst();
+ DexClass currentHolder = appInfo.definitionFor(current);
+ if (currentHolder == null
+ || currentHolder.findVirtualTarget(encodedMethod.method) != null) {
+ continue;
}
+ if (instantiatedTypes.contains(current)) {
+ markVirtualMethodAsLive(encodedMethod, KeepReason.reachableFromLiveType(current));
+ break;
+ }
+ fillWorkList(worklist, current);
}
}
+ } else {
+ pendingAdditionalInstantiatedTypes.add(encodedMethod);
}
}
@@ -778,38 +790,22 @@
private AppInfoWithLiveness trace(Timing timing) {
timing.begin("Grow the tree.");
try {
- while (!workList.isEmpty()) {
- Action action = workList.poll();
- switch (action.kind) {
- case MARK_INSTANTIATED:
- processNewlyInstantiatedClass((DexClass) action.target, action.reason);
- break;
- case MARK_REACHABLE_FIELD:
- markFieldAsReachable((DexField) action.target, action.reason);
- break;
- case MARK_REACHABLE_VIRTUAL:
- markVirtualMethodAsReachable((DexMethod) action.target, false, action.reason);
- break;
- case MARK_REACHABLE_INTERFACE:
- markVirtualMethodAsReachable((DexMethod) action.target, true, action.reason);
- break;
- case MARK_REACHABLE_SUPER:
- markSuperMethodAsReachable((DexMethod) action.target,
- (DexEncodedMethod) action.context);
- break;
- case MARK_METHOD_KEPT:
- markMethodAsKept((DexEncodedMethod) action.target, action.reason);
- break;
- case MARK_FIELD_KEPT:
- markFieldAsKept((DexEncodedField) action.target, action.reason);
- break;
- case MARK_METHOD_LIVE:
- processNewlyLiveMethod(((DexEncodedMethod) action.target), action.reason);
- break;
- default:
- throw new IllegalArgumentException(action.kind.toString());
+ int instantiatedTypesCount = 0;
+ while (true) {
+ doTrace();
+ // If methods where not considered due to relevant types not being instantiated reconsider
+ // if more instantiated types where collected.
+ if (pendingAdditionalInstantiatedTypes.size() > 0
+ && instantiatedTypes.items.size() > instantiatedTypesCount) {
+ instantiatedTypesCount = instantiatedTypes.items.size();
+ List<DexEncodedMethod> reconsider = pendingAdditionalInstantiatedTypes;
+ pendingAdditionalInstantiatedTypes = new ArrayList<>();
+ reconsider.forEach(this::handleIsInstantiatedOrHasInstantiatedSubtype);
+ } else {
+ break;
}
}
+
if (Log.ENABLED) {
Set<DexEncodedMethod> allLive = Sets.newIdentityHashSet();
for (Entry<DexType, SetWithReason<DexEncodedMethod>> entry : reachableVirtualMethods
@@ -837,6 +833,41 @@
return new AppInfoWithLiveness(appInfo, this);
}
+ private void doTrace() {
+ while (!workList.isEmpty()) {
+ Action action = workList.poll();
+ switch (action.kind) {
+ case MARK_INSTANTIATED:
+ processNewlyInstantiatedClass((DexClass) action.target, action.reason);
+ break;
+ case MARK_REACHABLE_FIELD:
+ markFieldAsReachable((DexField) action.target, action.reason);
+ break;
+ case MARK_REACHABLE_VIRTUAL:
+ markVirtualMethodAsReachable((DexMethod) action.target, false, action.reason);
+ break;
+ case MARK_REACHABLE_INTERFACE:
+ markVirtualMethodAsReachable((DexMethod) action.target, true, action.reason);
+ break;
+ case MARK_REACHABLE_SUPER:
+ markSuperMethodAsReachable((DexMethod) action.target,
+ (DexEncodedMethod) action.context);
+ break;
+ case MARK_METHOD_KEPT:
+ markMethodAsKept((DexEncodedMethod) action.target, action.reason);
+ break;
+ case MARK_FIELD_KEPT:
+ markFieldAsKept((DexEncodedField) action.target, action.reason);
+ break;
+ case MARK_METHOD_LIVE:
+ processNewlyLiveMethod(((DexEncodedMethod) action.target), action.reason);
+ break;
+ default:
+ throw new IllegalArgumentException(action.kind.toString());
+ }
+ }
+ }
+
private void markMethodAsKept(DexEncodedMethod target, KeepReason reason) {
DexClass holder = appInfo.definitionFor(target.method.holder);
// If this method no longer has a corresponding class then we have shaken it away before.
diff --git a/tools/create_maven_release.py b/tools/create_maven_release.py
new file mode 100755
index 0000000..9eb4174
--- /dev/null
+++ b/tools/create_maven_release.py
@@ -0,0 +1,160 @@
+#!/usr/bin/env python
+# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+import hashlib
+import argparse
+from os import makedirs
+from os.path import join
+from shutil import copyfile, make_archive, rmtree
+import subprocess
+import sys
+from string import Template
+import tempfile
+
+LICENSETEMPLATE = Template(
+"""
+ <license>
+ <name>$name</name>
+ <url>$url</url>
+ <distribution>repo</distribution>
+ </license>""")
+
+POMTEMPLATE = Template(
+"""<project
+ xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>com.android.tools</groupId>
+ <artifactId>r8</artifactId>
+ <version>$version</version>
+ <name>D8 dexer and R8 shrinker</name>
+ <description>
+ D8 dexer and R8 shrinker.
+ </description>
+ <url>http://r8.googlesource.com/r8</url>
+ <inceptionYear>2016</inceptionYear>
+ <licenses>
+ <license>
+ <name>BSD-3-Clause</name>
+ <url>https://opensource.org/licenses/BSD-3-Clause</url>
+ <distribution>repo</distribution>
+ </license>$library_licenses
+ <developers>
+ <developer>
+ <name>The Android Open Source Project</name>
+ </developer>
+ </developers>
+ <scm>
+ <connection>
+ https://r8.googlesource.com/r8.git
+ </connection>
+ <url>
+ https://r8.googlesource.com/r8
+ </url>
+ </scm>
+</project>
+""")
+
+def parse_options(argv):
+ result = argparse.ArgumentParser()
+ result.add_argument('--jar', help='jar file to package')
+ result.add_argument('--out', help='directory in which to put the output zip file')
+ return result.parse_args(argv)
+
+def determine_version(jar):
+ cmd = []
+ cmd.append('java')
+ cmd.extend(['-jar', jar]);
+ cmd.append('--version')
+ output = subprocess.check_output(cmd)
+ version_string = output.split()[1]
+ assert version_string.startswith("v")
+ return version_string[1:]
+
+def generate_library_licenses():
+ license_prefix = 'license: '
+ licenses = []
+ license_url_prefix = 'licenseUrl: '
+ license_urls = []
+ with open('LIBRARY-LICENSE', 'r') as file:
+ for line in file:
+ trimmed = line.strip()
+ if trimmed.startswith(license_prefix):
+ // Assert checking that licenses come in name/url pairs.
+ assert len(licenses) == len(license_urls)
+ name = trimmed[len(license_prefix):]
+ if not name in licenses:
+ licenses.append(name)
+ if trimmed.startswith(license_url_prefix):
+ url = trimmed[len(license_url_prefix):]
+ if not url in license_urls:
+ license_urls.append(url)
+ // Assert checking that licenses come in name/url pairs.
+ assert len(licenses) == len(license_urls)
+ result = ''
+ for i in range(len(licenses)):
+ name = licenses[i]
+ url = license_urls[i]
+ result += LICENSETEMPLATE.substitute(name=name, url=url)
+ return result
+
+def write_pom_file(version, pom_file):
+ library_licenses = generate_library_licenses()
+ version_pom = POMTEMPLATE.substitute(version=version, library_licenses=library_licenses)
+ with open(pom_file, 'w') as file:
+ file.write(version_pom)
+
+def hash_for(file, hash):
+ with open(file, 'rb') as f:
+ while True:
+ # Read chunks of 1MB
+ chunk = f.read(2 ** 20)
+ if not chunk:
+ break
+ hash.update(chunk)
+ return hash.hexdigest()
+
+def write_md5_for(file):
+ hexdigest = hash_for(file, hashlib.md5())
+ with (open(file + '.md5', 'w')) as file:
+ file.write(hexdigest)
+
+def write_sha1_for(file):
+ hexdigest = hash_for(file, hashlib.sha1())
+ with (open(file + '.sha1', 'w')) as file:
+ file.write(hexdigest)
+
+def main(argv):
+ options = parse_options(argv)
+ jar = options.jar
+ outdir = options.out
+ if jar == None or outdir == None:
+ print 'Need to supply --jar and --out.'
+ exit(1)
+ # Create directory structure for this version.
+ version = determine_version(jar)
+ tmp_dir = tempfile.mkdtemp()
+ version_dir = join(
+ tmp_dir, 'com', 'google', 'android', 'tools', 'r8', version, 'r8')
+ makedirs(version_dir)
+ # Write the pom file.
+ pom_file = join(version_dir, 'r8-' + version + '.pom')
+ write_pom_file(version, pom_file)
+ # Copy the jar to the output.
+ target_jar = join(version_dir, 'r8-' + version + '.jar')
+ copyfile(jar, target_jar)
+ # Create check sums.
+ write_md5_for(target_jar)
+ write_md5_for(pom_file)
+ write_sha1_for(target_jar)
+ write_sha1_for(pom_file)
+ # Zip it up.
+ make_archive(join(outdir, 'r8'), 'zip', tmp_dir)
+ # Cleanup.
+ rmtree(tmp_dir)
+
+if __name__ == "__main__":
+ exit(main(sys.argv[1:]))