1+ buildscript {
2+ repositories {
3+ mavenCentral()
4+ }
5+ dependencies {
6+ classpath ' net.sf.proguard:proguard-gradle:6.2.2'
7+ }
8+ }
9+
10+
111plugins {
212 id ' java'
313 id ' org.jetbrains.kotlin.jvm' version ' 1.4.10'
@@ -9,21 +19,13 @@ version '1.0-SNAPSHOT'
919
1020repositories {
1121 mavenCentral()
12- }
13-
14- jar {
15- manifest {
16- attributes ' Premain-Class' : ' tech.httptoolkit.javaagent.HttpProxyAgent'
17- attributes ' Agent-Class' : ' tech.httptoolkit.javaagent.HttpProxyAgent'
18- attributes ' Main-Class' : ' tech.httptoolkit.javaagent.AttachMain'
19-
20- attributes ' Can-Redefine-Classes' : ' true'
21- attributes ' Can-Retransform-Classes' : ' true'
22+ maven {
23+ url " https://maven.google.com/"
2224 }
25+ }
2326
24- // We include classes in our package. We *don't* include stub classes used to support multiple
25- // dependency versions, which are under their corresponding real package names.
26- include(' tech/httptoolkit/javaagent/**/*' )
27+ configurations {
28+ r8
2729}
2830
2931dependencies {
@@ -40,6 +42,9 @@ dependencies {
4042 testImplementation group : ' io.kotest' , name : ' kotest-runner-junit5-jvm' , version : ' 4.4.0'
4143 testImplementation group : ' io.kotest' , name : ' kotest-assertions-core-jvm' , version : ' 4.4.0'
4244 testImplementation " com.github.tomakehurst:wiremock-jre8:2.27.2"
45+
46+ // Only used during the R8 build task
47+ r8 group : ' com.android.tools' , name : ' r8' , version : ' 2.1.75'
4348}
4449
4550compileJava {
@@ -53,13 +58,19 @@ compileKotlin {
5358 }
5459}
5560
56- test {
57- // We need to build both JARs before the integration tests can run
58- dependsOn(' shadowJar' )
59- dependsOn(' :test-app:shadowJar' )
60- useJUnitPlatform()
61+ tasks. withType(Jar ) {
62+ manifest {
63+ attributes ' Premain-Class' : ' tech.httptoolkit.javaagent.HttpProxyAgent'
64+ attributes ' Agent-Class' : ' tech.httptoolkit.javaagent.HttpProxyAgent'
65+ attributes ' Main-Class' : ' tech.httptoolkit.javaagent.AttachMain'
66+
67+ attributes ' Can-Redefine-Classes' : ' true'
68+ attributes ' Can-Retransform-Classes' : ' true'
69+ }
6170}
6271
72+ // First, we bundle everything into a workable standalone JAR, with all runtime source included plus
73+ // dependencies plus agent metadata:
6374shadowJar {
6475 minimize()
6576 exclude ' **/*.kotlin_metadata'
@@ -68,16 +79,82 @@ shadowJar {
6879 exclude ' **/module_info.class'
6980 exclude ' META-INF/maven/**'
7081
71- // We have to specifically exclude packages here, because we *do* want to include lots of non-local code, just not
72- // these specific client stubs:
82+ // We have to specifically exclude our reactor stub code here, because we don't want to the type
83+ // stubs that we've manually defined in our own source included here.
7384 exclude ' reactor/'
7485}
7586
87+ // As part of bundling the JAR, we relocate all dependencies into our namespace:
7688import com.github.jengelman.gradle.plugins.shadow.tasks.ConfigureShadowRelocation
77-
7889task relocateShadowJar (type : ConfigureShadowRelocation ) {
7990 target = tasks. shadowJar
8091 prefix = " tech.httptoolkit.relocated"
8192}
93+ tasks. shadowJar. dependsOn tasks. relocateShadowJar
94+
95+ // Then we take this bundled JAR and optimize it. This shrinks it dramatically, but also breaks it, because
96+ // bytebuddy depends on some of our source being unmodified by R8 (frames in advice classes get messed with).
97+ def r8File = new File (" $buildDir /libs/$archivesBaseName -r8.jar" )
98+ tasks. register(' r8Jar' , JavaExec ) { task ->
99+ def rules = file(' r8-rules.txt' )
100+ task. dependsOn(tasks. shadowJar)
101+ task. outputs. file(r8File)
102+
103+ task. classpath(configurations. r8)
104+ task. main = ' com.android.tools.r8.R8'
105+ task. args = [
106+ ' --release' ,
107+ ' --classfile' ,
108+ ' --output' , r8File. toString(),
109+ ' --pg-conf' , rules. toString()
110+ ] + configurations. compileOnly. collect {path ->
111+ [' --lib' , path. toString()] // Include libs from every runtime-only dep, so R8 can resolve them
112+ }. flatten()
113+
114+ doFirst {
115+ def java8Home = System . getenv(" JAVA_HOME_8_X64" )
116+ if (java8Home == null || java8Home. empty) {
117+ throw new GradleException (" \$ JAVA_HOME_8_X64 must be set to build a minified distributable" )
118+ } else {
119+ // AFAICT R8 only supports the Java 8 lib files. We require that to be available, configured by env
120+ task. args + = " --lib"
121+ task. args + = java8Home
122+ }
123+
124+ task. args + = shadowJar. getArchiveFile(). get(). asFile. toString()
125+ }
126+ }
127+
128+ // Then we fix this, by taking the raw advice classes for our own source from the original bundled JAR (i.e. including
129+ // any relocated references) and combining that with the minified & optimized dependencies from R8, to get a single
130+ // bundled and 99% optimized JAR.
131+ task distJar (type : Jar ) {
132+ dependsOn(tasks. shadowJar, tasks. r8Jar)
133+ archiveClassifier = ' dist'
134+
135+ // Pull raw advice classes from the shadow JAR, unminified:
136+ from (zipTree(shadowJar. getArchiveFile())) {
137+ include " tech/httptoolkit/javaagent/advice/**/*"
138+ }
139+
140+ // Pull other source & bundled dependencies in their minified form, from R8:
141+ from (zipTree(r8Jar. outputs. files[0 ])) {
142+ exclude " tech/httptoolkit/javaagent/advice/**/*"
143+ }
144+ }
145+
146+ tasks. withType(Test ) {
147+ // We need to build both JARs before the integration tests can run
148+ dependsOn(' shadowJar' )
149+ dependsOn(' :test-app:shadowJar' )
150+ useJUnitPlatform()
151+ }
152+
153+ task quickTest (type : Test ) {
154+ environment ' TEST_JAR' , tasks. shadowJar. getArchiveFile(). get(). asFile. toString()
155+ }
82156
83- tasks. shadowJar. dependsOn tasks. relocateShadowJar
157+ task distTest (type : Test ) {
158+ environment ' TEST_JAR' , tasks. distJar. getArchiveFile(). get(). asFile. toString()
159+ dependsOn(' distJar' )
160+ }
0 commit comments