Build jacoco from sources

Excludes source files that depend on non-Android classes and modifies
the source code when necessary.

Adds a README.android file to describe those changes and the process
to build jacoco.

Bug: 27719795
Change-Id: I2cdb91030b15e24e6d90f343bc71deec3f26343d
diff --git a/Android.mk b/Android.mk
index 0fc0f54..b55fb27 100644
--- a/Android.mk
+++ b/Android.mk
@@ -15,8 +15,50 @@
 #
 LOCAL_PATH := $(call my-dir)
 
+# Build jacoco from sources for the platform
+#
+# Note: this is only intended to be used for the platform development. This is *not* intended
+# to be used in the SDK where apps can use the official jacoco release.
 include $(CLEAR_VARS)
-LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES := \
-    jacocoagent:org.jacoco.agent.rt-0.7.5.201505241946-all$(COMMON_JAVA_PACKAGE_SUFFIX)
+
+jacoco_src_files := $(call all-java-files-under,org.jacoco.core/src)
+jacoco_src_files += $(call all-java-files-under,org.jacoco.agent/src)
+jacoco_src_files += $(call all-java-files-under,org.jacoco.agent.rt/src)
+
+# Some Jacoco source files depend on classes that do not exist in Android. While these classes are
+# not executed at runtime (because we use offline instrumentation), they will cause issues when
+# compiling them with ART during dex pre-opting. Therefore, it would prevent from applying code
+# coverage on classes in the bootclasspath (frameworks, services, ...) or system apps.
+# Note: we still may need to update the source code to cut dependencies in mandatory jacoco classes.
+jacoco_android_exclude_list := \
+  %org.jacoco.core/src/org/jacoco/core/runtime/ModifiedSystemClassRuntime.java \
+  %org.jacoco.agent.rt/src/org/jacoco/agent/rt/internal/PreMain.java \
+  %org.jacoco.agent.rt/src/org/jacoco/agent/rt/internal/CoverageTransformer.java \
+  %org.jacoco.agent.rt/src/org/jacoco/agent/rt/internal/JmxRegistration.java
+
+LOCAL_SRC_FILES := $(filter-out $(jacoco_android_exclude_list),$(jacoco_src_files))
+
+# In order to include Jacoco in core libraries, we cannot depend on anything in the
+# bootclasspath (or we would create dependency cycle). Therefore we compile against
+# the SDK android.jar which gives the same APIs Jacoco depends on.
+LOCAL_SDK_VERSION := 9
+
+LOCAL_MODULE := jacocoagent
 LOCAL_MODULE_TAGS := optional
-include $(BUILD_MULTI_PREBUILT)
+LOCAL_STATIC_JAVA_LIBRARIES := jacoco-asm
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+#
+# Build asm-5.0.1 as a static library.
+#
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := jacoco-asm
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := JAVA_LIBRARIES
+LOCAL_SRC_FILES := asm-debug-all-5.0.1$(COMMON_JAVA_PACKAGE_SUFFIX)
+# Workaround for b/27319022
+LOCAL_JACK_FLAGS := -D jack.import.jar.debug-info=false
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_PREBUILT)
diff --git a/PREBUILT b/PREBUILT
index 9f9684f..c8d9fd8 100644
--- a/PREBUILT
+++ b/PREBUILT
@@ -1,13 +1,7 @@
-The jacocoagent.jar is updated in the following way:
+The ASM jar required by jacoco is updated in the following way:
 
-mvn install
-rm -f jacocoagent.jar
-cp org.jacoco.agent.rt/target/org.jacoco.agent.rt-0.7.5.201505241946-all.jar jacocoagent.jar
+mvn -f org.jacoco.agent.rt/pom.xml dependency:copy-dependencies
+cp org.jacoco.agent.rt/target/dependency/asm-debug-all-5.0.1.jar ./
 mvn clean
 
 The Android.mk must be updated to reference the right prebuilt.
-
-Also, the config.mk file must be updated to indicate what is the internal package name
-containing the core classes of Jacoco (org.jacoco.agent.rt.internal_*)
-Note: this information is available in the static field RUNTIMEPACKAGE
-of the org.jacoco.core.JaCoCo class (http://eclemma.org/jacoco/trunk/doc/api/org/jacoco/core/JaCoCo.html#RUNTIMEPACKAGE)
diff --git a/README.android b/README.android
new file mode 100644
index 0000000..6df57d1
--- /dev/null
+++ b/README.android
@@ -0,0 +1,15 @@
+We build an equivalent of the jacoco-agent.jar which contains classes from org.jacoco.core,
+org.jacoco.agent and org.jacoco.agent.rt packages but also classes from asm 5.0.1.
+
+However, Jacoco depends on classes that do not exist in Android (java.lang.instrument.* or
+javax.management.*) for runtime instrumentation only. The ART compiler would reject those classes
+when they are either in the bootclasspath (core, frameworks, ...) or system apps.
+
+Since we only use offline instrumentation for code coverage (using Jack) and do not execute these
+classes at runtime, we simply not compile them here.
+
+We also need to modify the source code to cut dependencies to the classes that we exclude from the
+compilation. The changes are surrounded by "BEGIN android-change" and "END android-change". Here
+is the list of the changes:
+
+1) Remove the creation of JmxRegistration in org.jacoco.agent.rt.internal.Agent.
diff --git a/asm-debug-all-5.0.1.jar b/asm-debug-all-5.0.1.jar
new file mode 100644
index 0000000..76d4b6a
--- /dev/null
+++ b/asm-debug-all-5.0.1.jar
Binary files differ
diff --git a/config.mk b/config.mk
index 506caad..d645621 100644
--- a/config.mk
+++ b/config.mk
@@ -12,5 +12,5 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-JACOCO_PACKAGE_NAME := org.jacoco.agent.rt.internal_d695e14
+JACOCO_PACKAGE_NAME := org.jacoco.agent.rt.internal
 
diff --git a/org.jacoco.agent.rt-0.7.5.201505241946-all.jar b/org.jacoco.agent.rt-0.7.5.201505241946-all.jar
deleted file mode 100644
index a916255..0000000
--- a/org.jacoco.agent.rt-0.7.5.201505241946-all.jar
+++ /dev/null
Binary files differ
diff --git a/org.jacoco.agent.rt/src/org/jacoco/agent/rt/internal/Agent.java b/org.jacoco.agent.rt/src/org/jacoco/agent/rt/internal/Agent.java
index 434fecd..69ffc01 100644
--- a/org.jacoco.agent.rt/src/org/jacoco/agent/rt/internal/Agent.java
+++ b/org.jacoco.agent.rt/src/org/jacoco/agent/rt/internal/Agent.java
@@ -121,7 +121,9 @@
 			output = createAgentOutput();
 			output.startup(options, data);
 			if (options.getJmx()) {
-				jmxRegistration = new JmxRegistration(this);
+// BEGIN android-change
+//				jmxRegistration = new JmxRegistration(this);
+// END android-change
 			}
 		} catch (final Exception e) {
 			logger.logExeption(e);