Skip to content

Commit a56941b

Browse files
committed
Document problems with building coroutines-using Android projects
Added instructions to work around the long-standing problem #2023. Also, the answer to #2274 is now documented.
1 parent d2ed1d8 commit a56941b

File tree

2 files changed

+102
-0
lines changed

2 files changed

+102
-0
lines changed

README.md

+10
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,16 @@ threads are handled by Android runtime.
176176
R8 and ProGuard rules are bundled into the [`kotlinx-coroutines-android`](ui/kotlinx-coroutines-android) module.
177177
For more details see ["Optimization" section for Android](ui/kotlinx-coroutines-android/README.md#optimization).
178178

179+
#### Avoiding including the debug agent infrastructure in the resulting APK
180+
181+
`kotlinx-coroutines-core` provides resources that are not needed for the applications to operate. To exclude them at no
182+
loss of functionality, add the following to the `android` block in your gradle file for the application subproject:
183+
```groovy
184+
packagingOptions {
185+
exclude "DebugProbesKt.bin"
186+
}
187+
```
188+
179189
### JS
180190

181191
[Kotlin/JS](https://kotlinlang.org/docs/reference/js-overview.html) version of `kotlinx.coroutines` is published as

kotlinx-coroutines-debug/README.md

+92
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,98 @@ java.lang.NoClassDefFoundError: Failed resolution of: Ljava/lang/management/Mana
170170
at kotlinx.coroutines.debug.DebugProbes.install(DebugProbes.kt:49)
171171
-->
172172

173+
#### Build failures due to duplicate resource files
174+
175+
Building a project for Android that depends on `kotlinx-coroutines-debug` (usually introduced by being a transitive
176+
dependency of `kotlinx-coroutines-test`) may fail with `DuplicateRelativeFileException` for `META-INF/AL2.0`,
177+
`META-INF/LGPL2.1`, or `win32-x86/attach_hotspot_windows.dll` when trying to merge the Android resource.
178+
179+
The problem is with the fact that Android merges the resources of all its dependencies into a single directory and
180+
complains about conflicts. However:
181+
* `kotlinx-coroutines-debug` transitively depends on JNA and JNA-platform, both of which include license files in their
182+
META-INF directories. Trying to merge their resources leads to problems, which means that any Android project that
183+
depends on JNA and JNA-platform will experience build failures.
184+
* Additionally, `kotlinx-coroutines-debug` embeds `byte-buddy-agent` and `byte-buddy`, along with their resource files.
185+
Then, if the project separately depends on `byte-buddy`, merging the resources of `kotlinx-coroutines-debug` with ones
186+
from `byte-buddy` and `byte-buddy-agent` will lead to problems as the resource files are duplicated.
187+
188+
One possible workaround for these issues is to add the following to the `android` block in your gradle file for the
189+
application subproject:
190+
```groovy
191+
packagingOptions {
192+
// for JNA and JNA-platform
193+
exclude "META-INF/AL2.0"
194+
exclude "META-INF/LGPL2.1"
195+
// for byte-buddy
196+
exclude "META-INF/licenses/ASM"
197+
pickFirst "win32-x86-64/attach_hotspot_windows.dll"
198+
pickFirst "win32-x86/attach_hotspot_windows.dll"
199+
}
200+
```
201+
This will cause the resource merge algorithm to exclude the problematic license files altogether and only leave a single
202+
copy of the files needed for `byte-buddy-agent` to work.
203+
204+
Alternatively, avoid depending on `kotlinx-coroutines-debug`. In particular, if the only reason why this library a
205+
dependency of your project is that `kotlinx-coroutines-test` in turn depends on it, you may replace your dependency on
206+
`kotlinx.coroutines.test` with one that excludes `kotlinx-coroutines-debug`. For example, you could replace
207+
```kotlin
208+
androidTestImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version")
209+
```
210+
with
211+
```groovy
212+
androidTestImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version") {
213+
exclude group: "org.jetbrains.kotlinx", module: "kotlinx-coroutines-debug"
214+
}
215+
```
216+
<!---
217+
Snippets of stacktraces for googling:
218+
219+
org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':app:mergeDebugAndroidTestJavaResource'.
220+
...
221+
Caused by: org.gradle.workers.intelrnal.DefaultWorkerExecutor$WorkExecutionException: A failure occurred while executing com.android.build.gradle.internal.tasks.Workers$ActionFacade
222+
...
223+
Caused by: com.android.builder.merge.DuplicateRelativeFileException: More than one file was found with OS independent path 'META-INF/AL2.0'.
224+
at com.android.builder.merge.StreamMergeAlgorithms.lambda$acceptOnlyOne$2(StreamMergeAlgorithms.java:85)
225+
at com.android.builder.merge.StreamMergeAlgorithms.lambda$select$3(StreamMergeAlgorithms.java:106)
226+
at com.android.builder.merge.IncrementalFileMergerOutputs$1.create(IncrementalFileMergerOutputs.java:88)
227+
at com.android.builder.merge.DelegateIncrementalFileMergerOutput.create(DelegateIncrementalFileMergerOutput.java:64)
228+
at com.android.build.gradle.internal.tasks.MergeJavaResourcesDelegate$run$output$1.create(MergeJavaResourcesDelegate.kt:230)
229+
at com.android.builder.merge.IncrementalFileMerger.updateChangedFile(IncrementalFileMerger.java:242)
230+
at com.android.builder.merge.IncrementalFileMerger.mergeChangedInputs(IncrementalFileMerger.java:203)
231+
at com.android.builder.merge.IncrementalFileMerger.merge(IncrementalFileMerger.java:80)
232+
at com.android.build.gradle.internal.tasks.MergeJavaResourcesDelegate.run(MergeJavaResourcesDelegate.kt:276)
233+
at com.android.build.gradle.internal.tasks.MergeJavaResRunnable.run(MergeJavaResRunnable.kt:81)
234+
at com.android.build.gradle.internal.tasks.Workers$ActionFacade.run(Workers.kt:242)
235+
at org.gradle.workers.internal.AdapterWorkAction.execute(AdapterWorkAction.java:50)
236+
at org.gradle.workers.internal.DefaultWorkerServer.execute(DefaultWorkerServer.java:50)
237+
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:63)
238+
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:59)
239+
at org.gradle.internal.classloader.ClassLoaderUtils.executeInClassloader(ClassLoaderUtils.java:98)
240+
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.lambda$execute$0(NoIsolationWorkerFactory.java:59)
241+
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:44)
242+
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:41)
243+
at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:416)
244+
at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:406)
245+
at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165)
246+
at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250)
247+
at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158)
248+
at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:102)
249+
at org.gradle.internal.operations.DelegatingBuildOperationExecutor.call(DelegatingBuildOperationExecutor.java:36)
250+
at org.gradle.workers.internal.AbstractWorker.executeWrappedInBuildOperation(AbstractWorker.java:41)
251+
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:53)
252+
at org.gradle.workers.internal.DefaultWorkerExecutor.lambda$submitWork$2(DefaultWorkerExecutor.java:200)
253+
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runExecution(DefaultConditionalExecutionQueue.java:215)
254+
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:164)
255+
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.run(DefaultConditionalExecutionQueue.java:131)
256+
257+
Execution failed for task ':app:mergeStagingDebugAndroidTestJavaResource'.
258+
Execution failed for task ':app:mergeDebugAndroidTestJavaResource'.
259+
Execution failed for task ':app:mergeDebugTestJavaResource'
260+
261+
More than one file was found with OS independent path 'META-INF/LGPL2.1'
262+
More than one file was found with OS independent path 'win32-x86/attach_hotspot_windows.dll'
263+
More than one file was found with OS independent path 'win32-x86-64/attach_hotspot_windows.dll'
264+
-->
173265
<!--- MODULE kotlinx-coroutines-core -->
174266
<!--- INDEX kotlinx.coroutines -->
175267
[Job]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/index.html

0 commit comments

Comments
 (0)