@@ -25,8 +25,16 @@ allprojects {
2525 targetCompatibility = 1.7
2626}
2727
28+ ext {
29+ sdkBasePackage = " com.launchdarkly.client"
30+ sdkBaseName = " launchdarkly-client"
31+ }
32+
2833ext. libraries = [:]
2934
35+ // Add dependencies to "libraries.internal" that are not exposed in our public API. These
36+ // will be completely omitted from the "thin" jar, and will be embedded with shaded names
37+ // in the other two SDK jars.
3038libraries. internal = [
3139 " commons-codec:commons-codec:1.10" ,
3240 " com.google.guava:guava:19.0" ,
@@ -36,11 +44,14 @@ libraries.internal = [
3644 " redis.clients:jedis:2.9.0"
3745]
3846
47+ // Add dependencies to "libraries.external" that are exposed in our public API, or that have
48+ // global state that must be shared between the SDK and the caller.
3949libraries. external = [
4050 " com.google.code.gson:gson:2.7" ,
4151 " org.slf4j:slf4j-api:1.7.21"
4252]
4353
54+ // Add dependencies to "libraries.test" that are used only in unit tests.
4455libraries. test = [
4556 " com.squareup.okhttp3:mockwebserver:3.10.0" ,
4657 " org.hamcrest:hamcrest-all:1.3" ,
@@ -54,11 +65,14 @@ dependencies {
5465 compileClasspath libraries. external
5566 runtime libraries. internal, libraries. external
5667 testImplementation libraries. test, libraries. internal, libraries. external
68+
69+ // Unlike what the name might suggest, the "shadow" configuration specifies dependencies that
70+ // should *not* be shaded by the Shadow plugin when we build our shaded jars.
5771 shadow libraries. external
5872}
5973
6074jar {
61- baseName = ' launchdarkly-client '
75+ baseName = sdkBaseName
6276 // thin classifier means that the non-shaded non-fat jar is still available
6377 // but is opt-in since users will have to specify it.
6478 classifier = ' thin'
@@ -106,38 +120,77 @@ githubPages {
106120 }
107121}
108122
123+ // Returns the names of all Java packages defined in this library - not including
124+ // enclosing packages like "com" that don't have any classes in them.
125+ def getAllSdkPackages () {
126+ def names = []
127+ project. convention. getPlugin(JavaPluginConvention ). sourceSets. main. output. each { baseDir ->
128+ if (baseDir. getPath(). contains(" classes" + File . separator + " java" + File . separator + " main" )) {
129+ baseDir. eachFileRecurse { f ->
130+ if (f. name. endsWith(" .class" )) {
131+ def subPath = f. getPath(). substring(baseDir. getPath(). length() + File . separator. length())
132+ def pkgName = subPath. substring(0 , subPath. lastIndexOf(File . separator)). replace(File . separator, " ." )
133+ names + = pkgName
134+ }
135+ }
136+ }
137+ }
138+ names. unique()
139+ }
140+
141+ // Returns the names of all Java packages contained in the specified jar - not including
142+ // enclosing packages like "com" that don't have any classes in them.
143+ def getPackagesInDependencyJar (jarFile ) {
144+ new java.util.zip.ZipFile (jarFile). withCloseable { zf ->
145+ zf. entries(). findAll { ! it. directory && it. name. endsWith(" .class" ) }. collect {
146+ it. name. substring(0 , it. name. lastIndexOf(" /" )). replace(" /" , " ." )
147+ }. unique()
148+ }
149+ }
150+
151+ // Used by shadowJar and shadowJarAll to specify which packages should be shaded. We should
152+ // *not* shade any of the dependencies that are specified in the "shadow" configuration,
153+ // nor any of the classes from the SDK itself.
154+ //
155+ // This depends on our build products, so it can't be executed during Gradle's configuration
156+ // phase; instead we have to run it after configuration, with the "afterEvaluate" block below.
157+ def shadeDependencies (jarTask ) {
158+ def excludePackages = getAllSdkPackages() +
159+ configurations. shadow. collectMany { getPackagesInDependencyJar(it)}
160+ def topLevelPackages =
161+ configurations. runtime. collectMany {
162+ getPackagesInDependencyJar(it). collect { it. contains(" ." ) ? it. substring(0 , it. indexOf(" ." )) : it }
163+ }.
164+ unique(). findAll { it != " javax" } // also, don't shade javax
165+ topLevelPackages. forEach { top ->
166+ jarTask. relocate(top, " com.launchdarkly.shaded." + top) {
167+ excludePackages. forEach { exclude(it + " .*" ) }
168+ }
169+ }
170+ }
171+
172+ // We can't actually call shadeDependencies from within the configuration section of shadowJar
173+ // or shadowJarAll, because Groovy executes all the configuration sections before executing
174+ // any tasks, meaning we wouldn't have any build products yet to inspect. So we'll do that
175+ // configuration step at the last minute after the compile task has executed.
176+ compileJava. doLast {
177+ shadeDependencies(project. tasks. shadowJar)
178+ shadeDependencies(project. tasks. shadowJarAll)
179+ }
180+
109181shadowJar {
110- baseName = ' launchdarkly-client'
111- // no classifier means that the shaded jar becomes the default artifact
182+ baseName = sdkBaseName
183+
184+ // No classifier means that the shaded jar becomes the default artifact
112185 classifier = ' '
113186
114- // Don't shade or include slf4j
187+ // Don't include slf4j or gson. This is the only difference between this artifact
188+ // and shadowJarAll, which does include (but doesn't shade) slf4j and gson.
115189 dependencies{
116190 exclude(dependency(' org.slf4j:.*:.*' ))
117191 exclude(dependency(' com.google.code.gson:.*:.*' ))
118192 }
119193
120- // Shade all jars except for launchdarkly
121- relocate(' com' , ' com.launchdarkly.shaded.com' ) {
122- exclude(" com.launchdarkly.client.*" )
123- exclude(" com.google.gson.*" )
124- exclude(" com.google.gson.annotations.*" )
125- exclude(" com.google.gson.internal.*" )
126- exclude(" com.google.gson.internal.bind.*" )
127- exclude(" com.google.gson.internal.bind.util.*" )
128- exclude(" com.google.gson.reflect.*" )
129- exclude(" com.google.gson.stream.*" )
130- }
131- relocate(' okhttp3' , ' com.launchdarkly.shaded.okhttp3' )
132- relocate(' okio' , ' com.launchdarkly.shaded.okio' )
133- relocate(' org' , ' com.launchdarkly.shaded.org' ) {
134- exclude(" org.slf4j.*" )
135- exclude(" org.slf4j.event.*" )
136- exclude(" org.slf4j.helpers.*" )
137- exclude(" org.slf4j.spi.*" )
138- }
139- relocate(' redis' , ' com.launchdarkly.shaded.redis' )
140-
141194 manifest {
142195 attributes(" Implementation-Version" : version)
143196 }
@@ -146,35 +199,14 @@ shadowJar {
146199// This builds the "-all"/"fat" jar which also includes the external dependencies - SLF4J,
147200// Gson, and javax.annotations - in unshaded form, as well as all the internal shaded classes.
148201task shadowJarAll (type : com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar ) {
149- baseName = ' launchdarkly-client '
202+ baseName = sdkBaseName
150203 classifier = ' all'
151204 group = " shadow"
152205 description = " Builds a Shaded fat jar including SLF4J"
153206 from(project. convention. getPlugin(JavaPluginConvention ). sourceSets. main. output)
154207 configurations = [project. configurations. runtime]
155208 exclude(' META-INF/INDEX.LIST' , ' META-INF/*.SF' , ' META-INF/*.DSA' , ' META-INF/*.RSA' )
156209
157- // Shade all jars except for launchdarkly
158- relocate(' com' , ' com.launchdarkly.shaded.com' ) {
159- exclude(" com.launchdarkly.client.*" )
160- exclude(" com.google.gson.*" )
161- exclude(" com.google.gson.annotations.*" )
162- exclude(" com.google.gson.internal.*" )
163- exclude(" com.google.gson.internal.bind.*" )
164- exclude(" com.google.gson.internal.bind.util.*" )
165- exclude(" com.google.gson.reflect.*" )
166- exclude(" com.google.gson.stream.*" )
167- }
168- relocate(' okhttp3' , ' com.launchdarkly.shaded.okhttp3' )
169- relocate(' okio' , ' com.launchdarkly.shaded.okio' )
170- relocate(' org' , ' com.launchdarkly.shaded.org' ) {
171- exclude(" org.slf4j.*" )
172- exclude(" org.slf4j.event.*" )
173- exclude(" org.slf4j.helpers.*" )
174- exclude(" org.slf4j.spi.*" )
175- }
176- relocate(' redis' , ' com.launchdarkly.shaded.redis' )
177-
178210 manifest {
179211 attributes(" Implementation-Version" : version)
180212 }
@@ -236,7 +268,7 @@ publishing {
236268 shadow(MavenPublication ) { publication ->
237269 project. shadow. component(publication)
238270
239- artifactId = ' launchdarkly-client '
271+ artifactId = sdkBaseName
240272 artifact jar
241273 artifact sourcesJar
242274 artifact javadocJar
0 commit comments