Merge "Don't delay emitting debug positions, local starts and local ends."
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 6598ae4..0f0b38f 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -55,7 +55,6 @@
*/
public final class D8 {
- private static final String VERSION = "v0.2.0";
private static final int STATUS_ERROR = 1;
private D8() {}
@@ -111,7 +110,7 @@
return;
}
if (command.isPrintVersion()) {
- System.out.println("D8 " + VERSION);
+ System.out.println("D8 " + Version.LABEL);
return;
}
run(command);
@@ -161,7 +160,7 @@
return options.getMarker();
}
return new Marker(Tool.D8)
- .put("version", VERSION)
+ .put("version", Version.LABEL)
.put("min-api", options.minApiLevel);
}
diff --git a/src/main/java/com/android/tools/r8/Disassemble.java b/src/main/java/com/android/tools/r8/Disassemble.java
index bb72d5d..a0770bc 100644
--- a/src/main/java/com/android/tools/r8/Disassemble.java
+++ b/src/main/java/com/android/tools/r8/Disassemble.java
@@ -144,7 +144,7 @@
return;
}
if (command.isPrintVersion()) {
- System.out.println("R8 v0.0.1");
+ System.out.println("R8 " + Version.LABEL);
return;
}
R8.disassemble(command);
diff --git a/src/main/java/com/android/tools/r8/GenerateMainDexList.java b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
index 44d523e..5a18b41 100644
--- a/src/main/java/com/android/tools/r8/GenerateMainDexList.java
+++ b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
@@ -28,7 +28,6 @@
import java.util.stream.Collectors;
public class GenerateMainDexList {
- private static final String VERSION = "v0.2.0";
private final Timing timing = new Timing("maindex");
private final InternalOptions options;
@@ -113,7 +112,7 @@
return;
}
if (command.isPrintVersion()) {
- System.out.println("MainDexListGenerator " + VERSION);
+ System.out.println("MainDexListGenerator " + Version.LABEL);
return;
}
List<String> result = run(command);
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index b4c19ea..e8990f3 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -71,7 +71,6 @@
public class R8 {
- private static final String VERSION = "v0.2.0";
private final Timing timing = new Timing("R8");
private final InternalOptions options;
@@ -86,7 +85,7 @@
return options.getMarker();
}
return new Marker(Tool.R8)
- .put("version", VERSION)
+ .put("version", Version.LABEL)
.put("min-api", options.minApiLevel);
}
@@ -535,7 +534,7 @@
return;
}
if (command.isPrintVersion()) {
- System.out.println("R8 " + VERSION);
+ System.out.println("R8 " + Version.LABEL);
return;
}
run(command);
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
new file mode 100644
index 0000000..ed19d71
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -0,0 +1,13 @@
+// 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;
+
+public final class Version {
+
+ public static final String LABEL = "v0.2.0-dev";
+
+ private Version() {
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java b/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java
index 9a8f8a5..d6063f3 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java
@@ -50,6 +50,15 @@
}
@Override
+ public boolean equals(Object other) {
+ if (other instanceof DebugPosition) {
+ DebugPosition o = (DebugPosition) other;
+ return line == o.line && file == o.file;
+ }
+ return false;
+ }
+
+ @Override
public int maxInValueRegister() {
throw new Unreachable();
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionsIterator.java b/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionsIterator.java
index 760a7ed..1390620 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionsIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionsIterator.java
@@ -51,4 +51,9 @@
public void replaceCurrentInstruction(Instruction newInstruction) {
instructionIterator.replaceCurrentInstruction(newInstruction);
}
+
+ @Override
+ public void removeOrReplaceByNop() {
+ instructionIterator.removeOrReplaceByNop();
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index 66957fc..c110f2f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -86,6 +86,7 @@
instruction.inValues.forEach(Value::clearUsersInfo);
if (instruction.debugValues != null) {
instruction.debugValues.forEach(Value::clearUsersInfo);
+ instruction.debugValues = null;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstructionIterator.java b/src/main/java/com/android/tools/r8/ir/code/InstructionIterator.java
index 2cb8185..56d81a2 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstructionIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstructionIterator.java
@@ -31,4 +31,10 @@
* @param instruction The instruction to add.
*/
void add(Instruction instruction);
+
+ /**
+ * Safe removal function that will insert a Nop to take over the debug values if any are
+ * associated with the current instruction.
+ */
+ void removeOrReplaceByNop();
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
index 18bbe0e..cbd8941 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
@@ -150,6 +150,11 @@
// Populate the builder info objects.
numberOfInstructions = 0;
+
+ // Remove redundant debug position instructions. They would otherwise materialize as
+ // unnecessary nops.
+ removeRedundantDebugPositions();
+
ListIterator<BasicBlock> iterator = ir.listIterator();
assert iterator.hasNext();
BasicBlock block = iterator.next();
@@ -237,6 +242,26 @@
return code;
}
+ private void removeRedundantDebugPositions() {
+ ListIterator<BasicBlock> iterator = ir.listIterator();
+ DebugPosition lastMoveExceptionPosition = null;
+ while (iterator.hasNext()) {
+ BasicBlock next = iterator.next();
+ InstructionListIterator it = next.listIterator();
+ while (it.hasNext()) {
+ com.android.tools.r8.ir.code.Instruction instruction = it.next();
+ if (instruction.isDebugPosition()) {
+ if (instruction.asDebugPosition().equals(lastMoveExceptionPosition)) {
+ it.remove();
+ }
+ lastMoveExceptionPosition = null;
+ } else if (instruction.isMoveException()) {
+ lastMoveExceptionPosition = instruction.asMoveException().getPosition();
+ }
+ }
+ }
+ }
+
// Rewrite ifs with offsets that are too large for the if encoding. The rewriting transforms:
//
//
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index f6a1c38..fbde02b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -1087,7 +1087,7 @@
user -> user.isCheckCast()
&& user.asCheckCast().getType().isSubtypeOf(checkCast.getType(), appInfo))) {
checkCast.outValue().replaceUsers(checkCast.inValues().get(0));
- it.remove();
+ it.removeOrReplaceByNop();
}
}
}
diff --git a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
index c89e792..9a28fac 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
@@ -12,8 +12,10 @@
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.IteratorUtils;
import com.android.tools.r8.utils.OffOrAuto;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import it.unimi.dsi.fastutil.longs.LongList;
import java.io.File;
@@ -269,6 +271,12 @@
return step(StepKind.INTO, stepFilter);
}
+ protected static List<Variable> getVisibleKotlinInlineVariables(
+ JUnit3Wrapper.DebuggeeState debuggeeState) {
+ return debuggeeState.getVisibleVariables().stream()
+ .filter(v -> v.getName().matches("^\\$i\\$f\\$.*$")).collect(Collectors.toList());
+ }
+
public enum StepKind {
INTO(StepDepth.INTO),
OVER(StepDepth.OVER),
@@ -317,10 +325,7 @@
}
protected final JUnit3Wrapper.Command checkNoLocal(String localName) {
- return inspect(t -> {
- List<String> localNames = t.getLocalNames();
- Assert.assertFalse("Unexpected local: " + localName, localNames.contains(localName));
- });
+ return inspect(t -> t.checkNoLocal(localName));
}
protected final JUnit3Wrapper.Command checkNoLocal() {
@@ -508,8 +513,9 @@
.getMethodSignature(debuggeeState.getLocation().classID,
debuggeeState.getLocation().methodID);
System.out.println(String
- .format("Suspended in %s#%s%s@0x%x", classSig, methodName, methodSig,
- Long.valueOf(debuggeeState.getLocation().index)));
+ .format("Suspended in %s#%s%s@0x%x (line=%d)", classSig, methodName, methodSig,
+ Long.valueOf(debuggeeState.getLocation().index),
+ Integer.valueOf(debuggeeState.getLineNumber())));
}
// Handle event.
@@ -555,6 +561,16 @@
return new ArtTestOptions(debuggeePath);
}
+ public void enqueueCommandFirst(Command command) {
+ commandsQueue.addFirst(command);
+ }
+
+ public void enqueueCommandsFirst(List<Command> commands) {
+ for (int i = commands.size() - 1; i >= 0; --i) {
+ enqueueCommandFirst(commands.get(i));
+ }
+ }
+
//
// Inspection
//
@@ -572,6 +588,8 @@
// Locals
+ List<Variable> getVisibleVariables();
+
/**
* Returns the names of all local variables visible at the current location
*/
@@ -581,6 +599,7 @@
* Returns the values of all locals visible at the current location.
*/
Map<String, Value> getLocalValues();
+ void checkNoLocal(String localName);
void checkLocal(String localName);
void checkLocal(String localName, Value expectedValue);
}
@@ -624,7 +643,7 @@
// Code indices are in ascending order.
assert currentLineCodeIndex >= startCodeIndex;
assert currentLineCodeIndex <= endCodeIndex;
- assert currentLineCodeIndex > previousLineCodeIndex;
+ assert currentLineCodeIndex >= previousLineCodeIndex;
previousLineCodeIndex = currentLineCodeIndex;
if (location.index >= currentLineCodeIndex) {
@@ -652,13 +671,19 @@
}
}
- public List<String> getLocalNames() {
- Location location = getLocation();
- return JUnit3Wrapper.getVariablesAt(mirror, location).stream()
- .map(v -> v.getName())
+ @Override
+ public List<Variable> getVisibleVariables() {
+ // Get variable table and keep only variables visible at this location.
+ Location frameLocation = getLocation();
+ return getVariables(getMirror(), frameLocation.classID, frameLocation.methodID).stream()
+ .filter(v -> inScope(frameLocation.index, v))
.collect(Collectors.toList());
}
+ public List<String> getLocalNames() {
+ return getVisibleVariables().stream().map(v -> v.getName()).collect(Collectors.toList());
+ }
+
@Override
public Map<String, Value> getLocalValues() {
return JUnit3Wrapper.getVariablesAt(mirror, location).stream()
@@ -687,6 +712,13 @@
"line " + getLineNumber() + ": Expected local '" + localName + "' not present");
}
+ @Override
+ public void checkNoLocal(String localName) {
+ Optional<Variable> localVar = JUnit3Wrapper
+ .getVariableAt(mirror, getLocation(), localName);
+ Assert.assertFalse("Unexpected local: " + localName, localVar.isPresent());
+ }
+
public void checkLocal(String localName) {
Optional<Variable> localVar = JUnit3Wrapper
.getVariableAt(mirror, getLocation(), localName);
@@ -780,6 +812,10 @@
return threadId;
}
+ public int getFrameDepth() {
+ return frames.size();
+ }
+
public FrameInspector getFrame(int index) {
return frames.get(index);
}
@@ -799,6 +835,11 @@
}
@Override
+ public void checkNoLocal(String localName) {
+ getTopFrame().checkNoLocal(localName);
+ }
+
+ @Override
public void checkLocal(String localName) {
getTopFrame().checkLocal(localName);
}
@@ -848,6 +889,11 @@
return getTopFrame().getMethodSignature();
}
+ @Override
+ public List<Variable> getVisibleVariables() {
+ return getTopFrame().getVisibleVariables();
+ }
+
public Value getStaticField(String className, String fieldName, String fieldSignature) {
String classSignature = DescriptorUtils.javaTypeToDescriptor(className);
byte typeTag = TypeTag.CLASS;
@@ -920,19 +966,19 @@
}
}
- private static boolean inScope(long index, Variable var) {
- long varStart = var.getCodeIndex();
- long varEnd = varStart + var.getLength();
- return index >= varStart && index < varEnd;
- }
-
- private static Optional<Variable> getVariableAt(VmMirror mirror, Location location,
+ public static Optional<Variable> getVariableAt(VmMirror mirror, Location location,
String localName) {
return getVariablesAt(mirror, location).stream()
.filter(v -> localName.equals(v.getName()))
.findFirst();
}
+ protected static boolean inScope(long index, Variable var) {
+ long varStart = var.getCodeIndex();
+ long varEnd = varStart + var.getLength();
+ return index >= varStart && index < varEnd;
+ }
+
private static List<Variable> getVariablesAt(VmMirror mirror, Location location) {
// Get variable table and keep only variables visible at this location.
return getVariables(mirror, location.classID, location.methodID).stream()
@@ -1059,10 +1105,9 @@
wrapper.getMirror().clearEvent(JDWPConstants.EventKind.CLASS_PREPARE,
classPrepareRequestId);
- // Breakpoint then resume. Note: we add them at the beginning of the queue (to be the
- // next commands to be processed), thus they need to be pushed in reverse order.
- wrapper.commandsQueue.addFirst(new JUnit3Wrapper.Command.RunCommand());
- wrapper.commandsQueue.addFirst(BreakpointCommand.this);
+ // Breakpoint then resume.
+ wrapper.enqueueCommandsFirst(
+ Arrays.asList(BreakpointCommand.this, new JUnit3Wrapper.Command.RunCommand()));
// Set wrapper ready to process next command.
wrapper.setState(State.ProcessCommand);
@@ -1262,7 +1307,7 @@
}
if (repeatStep) {
// In order to repeat the step now, we need to add it at the beginning of the queue.
- testBase.commandsQueue.addFirst(stepCommand);
+ testBase.enqueueCommandFirst(stepCommand);
}
super.handle(testBase);
}
diff --git a/src/test/java/com/android/tools/r8/debug/KotlinDebugTestBase.java b/src/test/java/com/android/tools/r8/debug/KotlinDebugTestBase.java
new file mode 100644
index 0000000..a0a9c3f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/KotlinDebugTestBase.java
@@ -0,0 +1,83 @@
+// 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.debug;
+
+import java.util.Arrays;
+import java.util.List;
+import org.apache.harmony.jpda.tests.framework.jdwp.Frame.Variable;
+import org.apache.harmony.jpda.tests.framework.jdwp.Location;
+
+/**
+ * A specialization for Kotlin-based tests which provides extra commands.
+ */
+public abstract class KotlinDebugTestBase extends DebugTestBase {
+
+ protected final JUnit3Wrapper.Command kotlinStepOver() {
+ return testBaseBeforeStep -> {
+ final JUnit3Wrapper.DebuggeeState debuggeeStateBeforeStep = testBaseBeforeStep
+ .getDebuggeeState();
+ final int frameDepthBeforeStep = debuggeeStateBeforeStep.getFrameDepth();
+ final Location locationBeforeStep = debuggeeStateBeforeStep.getLocation();
+ final List<Variable> kotlinLvsBeforeStep = getVisibleKotlinInlineVariables(
+ debuggeeStateBeforeStep);
+
+ // This is the command that will be executed after the initial (normal) step over. If we
+ // reach an inlined location, this command will step until reaching a non-inlined location.
+ JUnit3Wrapper.Command commandAfterStep = testBaseAfterStep -> {
+ // Get the new debuggee state (previous one is stale).
+ JUnit3Wrapper.DebuggeeState debuggeeStateAfterStep = testBaseBeforeStep.getDebuggeeState();
+
+ // Are we in the same frame ?
+ final int frameDepthAfterStep = debuggeeStateAfterStep.getFrameDepth();
+ final Location locationAfterStep = debuggeeStateAfterStep.getLocation();
+ if (frameDepthBeforeStep == frameDepthAfterStep
+ && locationBeforeStep.classID == locationAfterStep.classID
+ && locationBeforeStep.methodID == locationAfterStep.methodID) {
+ // We remain in the same method. Do we step into an inlined section ?
+ List<Variable> kotlinLvsAfterStep = getVisibleKotlinInlineVariables(
+ debuggeeStateAfterStep);
+ if (kotlinLvsBeforeStep.isEmpty() && !kotlinLvsAfterStep.isEmpty()) {
+ assert kotlinLvsAfterStep.size() == 1;
+
+ // We're located in an inlined section. Instead of doing a classic step out, we must
+ // jump out of the inlined section.
+ Variable inlinedSectionLv = kotlinLvsAfterStep.get(0);
+ testBaseAfterStep.enqueueCommandFirst(stepUntilOutOfInlineScope(inlinedSectionLv));
+ }
+ }
+ };
+
+ // Step over then check whether we need to continue stepping.
+ testBaseBeforeStep.enqueueCommandsFirst(Arrays.asList(stepOver(), commandAfterStep));
+ };
+ }
+
+ protected final JUnit3Wrapper.Command kotlinStepOut() {
+ return wrapper -> {
+ final List<Variable> kotlinLvsBeforeStep = getVisibleKotlinInlineVariables(
+ wrapper.getDebuggeeState());
+
+ JUnit3Wrapper.Command nextCommand;
+ if (!kotlinLvsBeforeStep.isEmpty()) {
+ // We are in an inline section. We need to step until being out of inline scope.
+ assert kotlinLvsBeforeStep.size() == 1;
+ final Variable inlinedSectionLv = kotlinLvsBeforeStep.get(0);
+ nextCommand = stepUntilOutOfInlineScope(inlinedSectionLv);
+ } else {
+ nextCommand = stepOut();
+ }
+ wrapper.enqueueCommandFirst(nextCommand);
+ };
+ }
+
+ private JUnit3Wrapper.Command stepUntilOutOfInlineScope(Variable inlineScopeLv) {
+ return stepUntil(StepKind.OVER, StepLevel.LINE, debuggeeState -> {
+ boolean inInlineScope = JUnit3Wrapper
+ .inScope(debuggeeState.getLocation().index, inlineScopeLv);
+ return !inInlineScope;
+ });
+ }
+
+}
diff --git a/src/test/java/com/android/tools/r8/debug/KotlinInlineTest.java b/src/test/java/com/android/tools/r8/debug/KotlinInlineTest.java
index e62a17b..8e9d145 100644
--- a/src/test/java/com/android/tools/r8/debug/KotlinInlineTest.java
+++ b/src/test/java/com/android/tools/r8/debug/KotlinInlineTest.java
@@ -10,9 +10,9 @@
import org.junit.Ignore;
import org.junit.Test;
-public class KotlinInlineTest extends DebugTestBase {
+// TODO check double-depth inline (an inline in another inline)
+public class KotlinInlineTest extends KotlinDebugTestBase {
- @Ignore("Requires kotlin-specific stepping behavior")
@Test
public void testStepOverInline() throws Throwable {
String methodName = "singleInline";
@@ -26,7 +26,6 @@
assertEquals(41, s.getLineNumber());
s.checkLocal("this");
}),
- // TODO(shertz) stepping over must take kotlin inline range into account.
stepOver(),
inspect(s -> {
assertEquals("KotlinInline", s.getClassName());
@@ -35,7 +34,7 @@
assertEquals(42, s.getLineNumber());
s.checkLocal("this");
}),
- stepOver(),
+ kotlinStepOver(),
inspect(s -> {
assertEquals("KotlinInline", s.getClassName());
assertEquals(methodName, s.getMethodName());
@@ -46,7 +45,6 @@
run());
}
- @Ignore("Requires kotlin-specific stepping behavior")
@Test
public void testStepIntoInline() throws Throwable {
String methodName = "singleInline";
@@ -60,7 +58,14 @@
assertEquals(41, s.getLineNumber());
s.checkLocal("this");
}),
- // TODO(shertz) stepping over must take kotlin inline range into account.
+ stepOver(),
+ inspect(s -> {
+ assertEquals("KotlinInline", s.getClassName());
+ assertEquals(methodName, s.getMethodName());
+ assertEquals("KotlinInline.kt", s.getSourceFile());
+ assertEquals(42, s.getLineNumber());
+ s.checkLocal("this");
+ }),
stepInto(),
inspect(s -> {
assertEquals("KotlinInline", s.getClassName());
@@ -76,7 +81,6 @@
run());
}
- @Ignore("Requires kotlin-specific stepping behavior")
@Test
public void testStepOutInline() throws Throwable {
String methodName = "singleInline";
@@ -90,13 +94,20 @@
assertEquals(41, s.getLineNumber());
s.checkLocal("this");
}),
- // TODO(shertz) stepping out must take kotlin inline range into account.
+ stepOver(),
+ inspect(s -> {
+ assertEquals("KotlinInline", s.getClassName());
+ assertEquals(methodName, s.getMethodName());
+ assertEquals("KotlinInline.kt", s.getSourceFile());
+ assertEquals(42, s.getLineNumber());
+ s.checkLocal("this");
+ }),
stepInto(),
inspect(s -> {
assertEquals("KotlinInline", s.getClassName());
assertEquals(methodName, s.getMethodName());
}),
- stepOut(),
+ kotlinStepOut(),
inspect(s -> {
assertEquals("KotlinInline", s.getClassName());
assertEquals(methodName, s.getMethodName());
diff --git a/src/test/java/com/android/tools/r8/debug/KotlinTest.java b/src/test/java/com/android/tools/r8/debug/KotlinTest.java
index a6b57b8..2baa426 100644
--- a/src/test/java/com/android/tools/r8/debug/KotlinTest.java
+++ b/src/test/java/com/android/tools/r8/debug/KotlinTest.java
@@ -8,7 +8,7 @@
import org.apache.harmony.jpda.tests.framework.jdwp.Value;
import org.junit.Test;
-public class KotlinTest extends DebugTestBase {
+public class KotlinTest extends KotlinDebugTestBase {
// TODO(shertz) simplify test
// TODO(shertz) add more variables ?
diff --git a/tools/checkout_aosp.py b/tools/checkout_aosp.py
new file mode 100755
index 0000000..d7208ab
--- /dev/null
+++ b/tools/checkout_aosp.py
@@ -0,0 +1,55 @@
+#!/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.
+
+from os.path import basename, join
+from shutil import copy2
+from subprocess import check_call
+import argparse
+import multiprocessing
+import os
+import sys
+
+import utils
+
+AOSP_MANIFEST_XML = join(utils.REPO_ROOT, 'third_party',
+ 'aosp_manifest.xml')
+AOSP_MANIFEST_URL = 'https://android.googlesource.com/platform/manifest'
+
+J_DEFAULT = multiprocessing.cpu_count() - 2
+
+# Checkout AOSP source to the specified direcotry using the speficied manifest.
+def checkout_aosp(aosp_root, manifest_xml, concurrency):
+ manifests_dir = join(aosp_root, '.repo', 'manifests')
+ utils.makedirs_if_needed(manifests_dir)
+
+ copy2(manifest_xml, manifests_dir)
+ check_call(['repo', 'init', '-u', AOSP_MANIFEST_URL, '-m',
+ basename(manifest_xml), '--depth=1'], cwd = aosp_root)
+
+ check_call(['repo', 'sync', '-dq', '-j' + concurrency], cwd = aosp_root)
+
+def parse_arguments():
+ parser = argparse.ArgumentParser(
+ description = 'Checkout the AOSP source tree.')
+ parser.add_argument('--aosp-root',
+ help='Root of the AOSP checkout. ' +
+ 'Defaults to current working directory.',
+ default=os.getcwd())
+ parser.add_argument('--manifest',
+ help='Manifest to use for the checkout. ' +
+ 'Defaults to ' + AOSP_MANIFEST_XML + '.',
+ default=AOSP_MANIFEST_XML)
+ parser.add_argument('-j',
+ help='Projects to fetch simultaneously. ' +
+ 'Defaults to ' + str(J_DEFAULT) + '.',
+ default=str(J_DEFAULT))
+ return parser.parse_args()
+
+def Main():
+ args = parse_arguments()
+ checkout_aosp(args.aosp_root, args.manifest, args.j)
+
+if __name__ == '__main__':
+ sys.exit(Main())
diff --git a/tools/test_android_cts.py b/tools/test_android_cts.py
index 887e3ae..ccb762b 100755
--- a/tools/test_android_cts.py
+++ b/tools/test_android_cts.py
@@ -30,6 +30,7 @@
import sys
import time
+import checkout_aosp
import gradle
import utils
@@ -53,7 +54,8 @@
CTS_TRADEFED = join(OUT_CTS,
'host/linux-x86/cts/android-cts/tools/cts-tradefed')
-J_OPTION = '-j8'
+J_DEFAULT = '8'
+J_OPTION = '-j' + J_DEFAULT
EXIT_FAILURE = 1
@@ -142,17 +144,6 @@
if counter > 0:
print('Removed {} dex files.'.format(counter))
-def checkout_aosp():
- # checkout AOSP source
- manifests_dir = join(AOSP_ROOT, '.repo', 'manifests')
- utils.makedirs_if_needed(manifests_dir)
-
- copy2(AOSP_MANIFEST_XML, manifests_dir)
- check_call(['repo', 'init', '-u', AOSP_MANIFEST_URL, '-m',
- 'aosp_manifest.xml', '--depth=1'], cwd = AOSP_ROOT)
-
- check_call(['repo', 'sync', '-dq', J_OPTION], cwd = AOSP_ROOT)
-
def Main():
args = parse_arguments()
@@ -179,7 +170,7 @@
setup_and_clean(args.tool == 'd8', args.clean_dex)
- checkout_aosp()
+ checkout_aosp.checkout_aosp(AOSP_ROOT, AOSP_MANIFEST_XML, J_DEFAULT)
# activate OUT_CTS and build Android CTS
# AOSP has no clean way to set the output directory.