aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2024-05-10 15:44:32 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2024-05-10 15:44:32 +0000
commit2d582ce551faa3f5e8761386f1865fcf8a346f60 (patch)
treef8a6754ce67abf8cc982f64cabf4ef77e303bb05
parent4337fb3a8f0519afe5bdccac6767d77fc9f074c8 (diff)
parent56e5191c9c63245fa33ad53a062a8911b1682502 (diff)
downloadvogar-busytown-mac-infra-release.tar.gz
Snap for 11819167 from 56e5191c9c63245fa33ad53a062a8911b1682502 to busytown-mac-infra-releasebusytown-mac-infra-release
Change-Id: I0dfef364512bb3aa65ca6bdbc179cda0459a23f7
-rw-r--r--Android.bp72
-rw-r--r--Android.mk86
-rw-r--r--src/vogar/Run.java4
-rw-r--r--src/vogar/Vogar.java5
-rw-r--r--src/vogar/android/AndroidSdk.java2
-rw-r--r--src/vogar/android/DeviceRuntime.java3
-rw-r--r--src/vogar/monitor/InterleavedReader.java10
-rw-r--r--src/vogar/target/CaliperRunnerFactory.java3
-rw-r--r--src/vogar/target/MainRunnerFactory.java3
-rw-r--r--src/vogar/target/RunnerFactory.java2
-rw-r--r--src/vogar/target/TestRunner.java16
-rw-r--r--src/vogar/target/junit/ExcludeFilter.java49
-rw-r--r--src/vogar/target/junit/JUnitRunnerFactory.java5
-rw-r--r--src/vogar/target/junit/JUnitTargetRunner.java12
-rw-r--r--src/vogar/target/testng/TestNgRunnerFactory.java2
-rw-r--r--src/vogar/target/testng/TestNgTargetRunner.java1
-rw-r--r--src/vogar/tasks/BuildActionTask.java10
17 files changed, 188 insertions, 97 deletions
diff --git a/Android.bp b/Android.bp
index 4d94783..c7c981f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -54,3 +54,75 @@ java_import_host {
name: "vogar-kxml-libcore-20110123",
jars: ["lib/kxml-libcore-20110123.jar"],
}
+
+// build vogar jar
+java_library_host {
+ name: "vogar-host-jar",
+ srcs: ["src/**/*.java"],
+ // Make the host jar file name to be vogar.jar due to in vogar-script it
+ // expect the jar to be vogar.jar.
+ stem: "vogar",
+ java_resource_dirs: ["resources"],
+ static_libs: [
+ "caliper",
+ "gson",
+ "guava",
+ "junit",
+ "testng",
+ "vogar-jsr305",
+ "vogar-kxml-libcore-20110123",
+ ],
+ // Vogar uses android.jar.
+ libs: [
+ "android_current",
+ ],
+}
+
+// copy vogar script
+sh_binary_host {
+ // Set filename to the same name as the stem of vogar-host-jar to align with
+ // original make behavior.
+ name: "vogar-script",
+ src: "bin/vogar-android",
+ filename: "vogar",
+}
+
+// User can build both vogar-host-jar and vogar-script just use vogar as target
+// name. This could be replace after soong has implemented the wrapper function
+// for java_library_host.
+phony_rule {
+ name: "vogar",
+ phony_deps: [
+ "vogar-host-jar",
+ "vogar-script",
+ ],
+}
+
+// build vogar tests jar
+java_library_host {
+ name: "vogar-tests",
+ srcs: ["test/**/*.java"],
+ static_libs: [
+ "junit",
+ "mockito",
+ "objenesis",
+ "vogar-host-jar",
+ ],
+}
+
+// Run the tests using using run-vogar-tests.
+java_genrule_host {
+ name: "vogar-tests-result",
+ srcs: [
+ ":vogar-tests",
+ ],
+ cmd: "ANDROID_BUILD_TOP=$$(pwd) java -cp $(in) org.junit.runner.JUnitCore vogar.AllTests 2>&1 | tee $(out)",
+ out: ["vogar-tests-result.txt"],
+}
+
+phony_rule {
+ name: "run-vogar-tests",
+ phony_deps: [
+ "vogar-tests-result",
+ ],
+}
diff --git a/Android.mk b/Android.mk
deleted file mode 100644
index 6bf77fe..0000000
--- a/Android.mk
+++ /dev/null
@@ -1,86 +0,0 @@
-# Copyright (C) 2014 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH := $(call my-dir)
-
-# build vogar jar
-# ============================================================
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := vogar
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 SPDX-license-identifier-BSD
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/LICENSE
-LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE_CLASS := JAVA_LIBRARIES
-LOCAL_SRC_FILES := $(call all-java-files-under, src/)
-LOCAL_JAVA_RESOURCE_DIRS := resources
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- caliper \
- caliper-gson \
- guava \
- junit \
- testng \
- vogar-jsr305 \
- vogar-kxml-libcore-20110123
-
-LOCAL_ADDITIONAL_DEPENDENCIES := \
- $(HOST_OUT_EXECUTABLES)/d8 \
- $(HOST_OUT_JAVA_LIBRARIES)/desugar.jar
-
-# Vogar uses android.jar.
-LOCAL_CLASSPATH := $(call resolve-prebuilt-sdk-jar-path,current)
-
-include $(BUILD_HOST_JAVA_LIBRARY)
-
-# build vogar tests jar
-# ============================================================
-# Run the tests using using the following target.
-.PHONY: run-vogar-tests
-run-vogar-tests: vogar-tests
- ANDROID_BUILD_TOP=$$(pwd) \
- java -cp ./out/host/linux-x86/framework/vogar-tests.jar \
- org.junit.runner.JUnitCore vogar.AllTests
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := vogar-tests
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 SPDX-license-identifier-BSD
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/LICENSE
-LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE_CLASS := JAVA_LIBRARIES
-LOCAL_SRC_FILES := $(call all-java-files-under, test/)
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- junit \
- mockito \
- objenesis \
- vogar
-
-include $(BUILD_HOST_JAVA_LIBRARY)
-
-# copy vogar script
-# ============================================================
-include $(CLEAR_VARS)
-LOCAL_IS_HOST_MODULE := true
-LOCAL_MODULE_CLASS := EXECUTABLES
-LOCAL_MODULE := vogar
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 SPDX-license-identifier-BSD
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/LICENSE
-LOCAL_SRC_FILES := bin/vogar-android
-include $(BUILD_PREBUILT)
diff --git a/src/vogar/Run.java b/src/vogar/Run.java
index 402b1c4..2725935 100644
--- a/src/vogar/Run.java
+++ b/src/vogar/Run.java
@@ -22,6 +22,7 @@ import com.google.common.base.Supplier;
import java.io.File;
import java.io.IOException;
import java.net.URL;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
@@ -72,6 +73,7 @@ public final class Run {
public final int firstMonitorPort;
public final int timeoutSeconds;
public final File javaHome;
+ public final List<String> excludeFilters;
public final Integer debugPort;
public final Language language;
public final List<String> javacArgs;
@@ -138,6 +140,8 @@ public final class Run {
this.javacArgs = vogar.javacArgs;
this.multidex = vogar.multidex;
this.javaHome = vogar.javaHome;
+ this.excludeFilters = new ArrayList<>();
+ this.excludeFilters.addAll(vogar.excludeFilters);
this.largeTimeoutSeconds = vogar.timeoutSeconds * Vogar.LARGE_TIMEOUT_MULTIPLIER;
this.maxConcurrentActions = (vogar.stream || vogar.modeId == ModeId.ACTIVITY)
? 1
diff --git a/src/vogar/Vogar.java b/src/vogar/Vogar.java
index 7fba4aa..c2136e2 100644
--- a/src/vogar/Vogar.java
+++ b/src/vogar/Vogar.java
@@ -155,6 +155,9 @@ public final class Vogar {
@Option(names = { "--java-home" })
File javaHome;
+ @Option(names = { "--exclude-filter" })
+ List<String> excludeFilters = new ArrayList<>();
+
@Option(names = { "--javac-arg" })
List<String> javacArgs = new ArrayList<String>();
@@ -371,6 +374,8 @@ public final class Vogar {
System.out.println(" using the specified java home directory. This does not impact");
System.out.println(" which javac gets used. When unset, java is used from the PATH.");
System.out.println();
+ System.out.println(" --exclude-filter <java_annotation>: exclude the annotated tests");
+ System.out.println();
System.out.println("EXOTIC OPTIONS");
System.out.println();
System.out.println(" --suggest-classpaths: build an index of jar files under the");
diff --git a/src/vogar/android/AndroidSdk.java b/src/vogar/android/AndroidSdk.java
index 8d18264..220e6a6 100644
--- a/src/vogar/android/AndroidSdk.java
+++ b/src/vogar/android/AndroidSdk.java
@@ -397,6 +397,8 @@ public class AndroidSdk {
builder.args(D8_COMMAND_NAME);
builder.args("-JXms16M").args("-JXmx1536M");
builder.args("-JXX:+TieredCompilation").args("-JXX:TieredStopAtLevel=1");
+ builder.args("-JDcom.android.tools.r8.emitRecordAnnotationsInDex");
+ builder.args("-JDcom.android.tools.r8.emitPermittedSubclassesAnnotationsInDex");
builder.args("--thread-count").args("1");
// d8 will not allow compiling with a single dex file as the target, but if given
diff --git a/src/vogar/android/DeviceRuntime.java b/src/vogar/android/DeviceRuntime.java
index 2b5f01f..e10bd36 100644
--- a/src/vogar/android/DeviceRuntime.java
+++ b/src/vogar/android/DeviceRuntime.java
@@ -89,6 +89,9 @@ public final class DeviceRuntime implements Mode {
// If you edit this, see also HostRuntime...
VmCommandBuilder vmCommandBuilder = new VmCommandBuilder(run.log)
.env("ANDROID_DATA", run.getAndroidDataPath())
+ // b/150126287: avoid using libnetd_client within chroot,
+ // as it might not match the netd running outside chroot.
+ .env("ANDROID_NO_USE_FWMARK_CLIENT", "1")
.workingDirectory(workingDirectory)
.vmCommand(vmCommand)
.vmArgs("-Duser.home=" + run.deviceUserHome)
diff --git a/src/vogar/monitor/InterleavedReader.java b/src/vogar/monitor/InterleavedReader.java
index 2f9f15a..a97ba56 100644
--- a/src/vogar/monitor/InterleavedReader.java
+++ b/src/vogar/monitor/InterleavedReader.java
@@ -78,7 +78,15 @@ public final class InterleavedReader implements Closeable {
int textEnd;
while (true) {
- int r = reader.read(buffer, count, buffer.length - count);
+ int r = -1;
+
+ try {
+ r = reader.read(buffer, count, buffer.length - count);
+ } catch (IOException e) {
+ // When running under gcstress, the output stream may be closed outside our control when
+ // the target process exits. In order to allow this, catch the stream closed exception and
+ // return whatever has been buffered already. (b/308917607)
+ }
if (r == -1) {
// the input is exhausted; return the remaining characters
diff --git a/src/vogar/target/CaliperRunnerFactory.java b/src/vogar/target/CaliperRunnerFactory.java
index f1dab97..2955c74 100644
--- a/src/vogar/target/CaliperRunnerFactory.java
+++ b/src/vogar/target/CaliperRunnerFactory.java
@@ -51,7 +51,8 @@ public class CaliperRunnerFactory implements RunnerFactory {
@Override @Nullable
public TargetRunner newRunner(TargetMonitor monitor, String qualification,
Class<?> klass, AtomicReference<String> skipPastReference,
- TestEnvironment testEnvironment, int timeoutSeconds, String[] args) {
+ TestEnvironment testEnvironment, int timeoutSeconds, String[] excludeFilters,
+ String[] args) {
if (benchmarkClassChecker != null && benchmarkClassChecker.isBenchmark(klass)) {
return new CaliperTargetRunner(monitor, klass, args);
} else {
diff --git a/src/vogar/target/MainRunnerFactory.java b/src/vogar/target/MainRunnerFactory.java
index cca463f..41280c3 100644
--- a/src/vogar/target/MainRunnerFactory.java
+++ b/src/vogar/target/MainRunnerFactory.java
@@ -28,7 +28,8 @@ public class MainRunnerFactory implements RunnerFactory {
@Override @Nullable
public TargetRunner newRunner(TargetMonitor monitor, String qualification,
Class<?> klass, AtomicReference<String> skipPastReference,
- TestEnvironment testEnvironment, int timeoutSeconds, String[] args) {
+ TestEnvironment testEnvironment, int timeoutSeconds, String[] excludeFilters,
+ String[] args) {
if (new ClassAnalyzer(klass).hasMethod(true, void.class, "main", String[].class)) {
return new MainTargetRunner(monitor, klass, args);
} else {
diff --git a/src/vogar/target/RunnerFactory.java b/src/vogar/target/RunnerFactory.java
index 993acd4..e90c070 100644
--- a/src/vogar/target/RunnerFactory.java
+++ b/src/vogar/target/RunnerFactory.java
@@ -32,5 +32,5 @@ public interface RunnerFactory {
@Nullable
TargetRunner newRunner(TargetMonitor monitor, String qualification, Class<?> klass,
AtomicReference<String> skipPastReference, TestEnvironment testEnvironment,
- int timeoutSeconds, String[] args);
+ int timeoutSeconds, String[] excludeFilters, String[] args);
}
diff --git a/src/vogar/target/TestRunner.java b/src/vogar/target/TestRunner.java
index 7edc5b5..5b6f539 100644
--- a/src/vogar/target/TestRunner.java
+++ b/src/vogar/target/TestRunner.java
@@ -52,6 +52,7 @@ public final class TestRunner {
private final RunnerFactory runnerFactory;
private final String[] args;
+ private final String[] excludeFilters;
private boolean useSocketMonitor;
public TestRunner(Properties properties, List<String> argsList) {
@@ -60,6 +61,8 @@ public final class TestRunner {
int monitorPort = Integer.parseInt(properties.getProperty(TestProperties.MONITOR_PORT));
String skipPast = null;
+ List<String> excludeFilters = new ArrayList<>();
+
for (Iterator<String> i = argsList.iterator(); i.hasNext(); ) {
String arg = i.next();
@@ -73,6 +76,11 @@ public final class TestRunner {
skipPast = i.next();
i.remove();
}
+ if (arg.equals("--exclude-filter")) {
+ i.remove();
+ excludeFilters.add(i.next());
+ i.remove();
+ }
}
// Select the RunnerFactory instances to use based on the selected runner type.
@@ -95,6 +103,7 @@ public final class TestRunner {
this.monitorPort = monitorPort;
this.skipPastReference = new AtomicReference<>(skipPast);
+ this.excludeFilters = excludeFilters.toArray(String[]::new);
this.args = argsList.toArray(new String[argsList.size()]);
}
@@ -201,7 +210,7 @@ public final class TestRunner {
TargetRunner targetRunner;
try {
targetRunner = runnerFactory.newRunner(monitor, qualification, klass,
- skipPastReference, testEnvironment, timeoutSeconds, args);
+ skipPastReference, testEnvironment, timeoutSeconds, excludeFilters, args);
} catch (RuntimeException e) {
monitor.outcomeStarted(klass.getName());
e.printStackTrace();
@@ -246,10 +255,11 @@ public final class TestRunner {
@Override @Nullable
public TargetRunner newRunner(TargetMonitor monitor, String qualification,
Class<?> klass, AtomicReference<String> skipPastReference,
- TestEnvironment testEnvironment, int timeoutSeconds, String[] args) {
+ TestEnvironment testEnvironment, int timeoutSeconds, String[] excludeFilters,
+ String[] args) {
for (RunnerFactory runnerFactory : runnerFactories) {
TargetRunner targetRunner = runnerFactory.newRunner(monitor, qualification, klass,
- skipPastReference, testEnvironment, timeoutSeconds, args);
+ skipPastReference, testEnvironment, timeoutSeconds, excludeFilters, args);
if (targetRunner != null) {
return targetRunner;
}
diff --git a/src/vogar/target/junit/ExcludeFilter.java b/src/vogar/target/junit/ExcludeFilter.java
new file mode 100644
index 0000000..1e98f3d
--- /dev/null
+++ b/src/vogar/target/junit/ExcludeFilter.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package vogar.target.junit;
+
+import org.junit.runner.Description;
+import org.junit.runner.manipulation.Filter;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class ExcludeFilter extends Filter {
+
+ private final Set<String> excludeFilters;
+
+ public ExcludeFilter(String[] excludeFilters) {
+ Set<String> set = new HashSet<>();
+ if (excludeFilters != null) {
+ set.addAll(List.of(excludeFilters));
+ }
+ this.excludeFilters = set;
+ }
+
+ @Override
+ public boolean shouldRun(Description description) {
+ return !description.getAnnotations().stream()
+ .anyMatch(annotation ->
+ excludeFilters.contains(annotation.annotationType().getName()));
+ }
+
+ @Override
+ public String describe() {
+ return "ExcludeFilter[" + String.join(",", excludeFilters) + "]";
+ }
+}
diff --git a/src/vogar/target/junit/JUnitRunnerFactory.java b/src/vogar/target/junit/JUnitRunnerFactory.java
index 2719356..3a83576 100644
--- a/src/vogar/target/junit/JUnitRunnerFactory.java
+++ b/src/vogar/target/junit/JUnitRunnerFactory.java
@@ -30,10 +30,11 @@ public class JUnitRunnerFactory implements RunnerFactory {
@Override @Nullable
public TargetRunner newRunner(TargetMonitor monitor, String qualification,
Class<?> klass, AtomicReference<String> skipPastReference,
- TestEnvironment testEnvironment, int timeoutSeconds, String[] args) {
+ TestEnvironment testEnvironment, int timeoutSeconds, String[] excludeFilters,
+ String[] args) {
if (supports(klass)) {
return new JUnitTargetRunner(monitor, skipPastReference, testEnvironment,
- timeoutSeconds, klass, qualification, args);
+ timeoutSeconds, klass, qualification, excludeFilters, args);
} else {
return null;
}
diff --git a/src/vogar/target/junit/JUnitTargetRunner.java b/src/vogar/target/junit/JUnitTargetRunner.java
index 3fd2232..906115a 100644
--- a/src/vogar/target/junit/JUnitTargetRunner.java
+++ b/src/vogar/target/junit/JUnitTargetRunner.java
@@ -44,19 +44,23 @@ public final class JUnitTargetRunner implements TargetRunner {
private final Class<?> testClass;
private final RunnerParams runnerParams;
+ private final String[] excludeFilters;
+
public JUnitTargetRunner(TargetMonitor monitor, AtomicReference<String> skipPastReference,
TestEnvironment testEnvironment,
int timeoutSeconds, Class<?> testClass,
- String qualification, String[] args) {
+ String qualification, String[] excludeFilters, String[] args) {
this.monitor = monitor;
this.skipPastReference = skipPastReference;
this.testEnvironment = testEnvironment;
this.testClass = testClass;
+ this.excludeFilters = excludeFilters;
TimeoutAndAbortRunRule timeoutRule = new TimeoutAndAbortRunRule(timeoutSeconds);
runnerParams = new RunnerParams(qualification, args, timeoutRule);
}
+ @Override
public boolean run() {
// Use JUnit infrastructure to run the tests.
RunnerBuilder builder = new VogarRunnerBuilder(runnerParams);
@@ -79,6 +83,12 @@ public final class JUnitTargetRunner implements TargetRunner {
}
try {
+ new ExcludeFilter(excludeFilters).apply(runner);
+ } catch (NoTestsRemainException e) {
+ return true;
+ }
+
+ try {
JUnitCore core = new JUnitCore();
// The TestEnvironmentRunListener resets static state between tests.
core.addListener(new TestEnvironmentRunListener(testEnvironment));
diff --git a/src/vogar/target/testng/TestNgRunnerFactory.java b/src/vogar/target/testng/TestNgRunnerFactory.java
index 2e214fd..f6cd279 100644
--- a/src/vogar/target/testng/TestNgRunnerFactory.java
+++ b/src/vogar/target/testng/TestNgRunnerFactory.java
@@ -35,6 +35,7 @@ public class TestNgRunnerFactory implements RunnerFactory {
AtomicReference<String> skipPastReference,
TestEnvironment testEnvironment,
int timeoutSeconds,
+ String[] excludeFilters,
String[] args) {
if (supports(klass)) {
return new TestNgTargetRunner(
@@ -44,6 +45,7 @@ public class TestNgRunnerFactory implements RunnerFactory {
timeoutSeconds,
klass,
qualification,
+ excludeFilters,
args);
} else {
return null;
diff --git a/src/vogar/target/testng/TestNgTargetRunner.java b/src/vogar/target/testng/TestNgTargetRunner.java
index 6b10509..40824c7 100644
--- a/src/vogar/target/testng/TestNgTargetRunner.java
+++ b/src/vogar/target/testng/TestNgTargetRunner.java
@@ -39,6 +39,7 @@ public class TestNgTargetRunner implements TargetRunner {
int timeoutSeconds,
Class<?> testClass,
String qualification,
+ String[] excludeFilters,
String[] args) {
this.monitor = monitor;
this.testClass = testClass;
diff --git a/src/vogar/tasks/BuildActionTask.java b/src/vogar/tasks/BuildActionTask.java
index fd4c43a..04605fd 100644
--- a/src/vogar/tasks/BuildActionTask.java
+++ b/src/vogar/tasks/BuildActionTask.java
@@ -17,6 +17,7 @@
package vogar.tasks;
import java.io.File;
+import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
@@ -90,7 +91,14 @@ public final class BuildActionTask extends Task {
if (run.debugging) {
javac.debug();
}
- if (javaFile != null) {
+ if (javaFile == null) {
+ if (JAVA_SOURCE_PATTERN.matcher(action.getTargetClass()).find()) {
+ // This is likely a missing source file rather than a class
+ // named something.java....
+ throw new FileNotFoundException("Unlikely classname, assuming missing source file: "
+ + action.getTargetClass());
+ }
+ } else {
if (!JAVA_SOURCE_PATTERN.matcher(javaFile.toString()).find()) {
throw new CommandFailedException(Collections.<String>emptyList(),
Collections.singletonList("Cannot compile: " + javaFile));