# Cross-subproject dependency on JavaCompile task outputs

**URL:** <https://discuss.gradle.org/t/cross-subproject-dependency-on-javacompile-task-outputs/44404>\
**Category:** Help/Discuss\
**Created:** [December 5, 2022, 3:09am UTC](https://discuss.gradle.org/t/cross-subproject-dependency-on-javacompile-task-outputs/44404 "2022-12-05T03:09:03Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![liblit](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/liblit/32/6697_2.png) [@liblit](https://discuss.gradle.org/u/liblit)\
**Post date:** [December 5, 2022, 3:09am UTC](https://discuss.gradle.org/t/cross-subproject-dependency-on-javacompile-task-outputs/44404/1 "2022-12-05T03:09:03Z")

</div>

My project has an `:individuals` subproject with a custom `SourceSet` named `testData`, compiled via the corresponding `JavaCompile` task named `compileTestDataJava`. A second subproject, `:aggregate`, has a `Jar` task named `testDataJar` that should include all bytecode files produced by the `:individuals:compileTestDataJava` task.

Gradle’s documentation [sternly discourages referencing tasks from other subprojects](https://docs.gradle.org/current/userguide/cross_project_publications.html#cross_project_publications#preamble), so I am trying to use configurations instead:

- **`individuals/build.gradle.kts`**

- **`aggregate/build.gradle.kts`**

Unfortunately, this approach fails when trying to create the `:individuals:assemble` task:

```nohighlight
FAILURE: Build failed with an exception.

* What went wrong:
Could not determine the dependencies of task ':individuals:build'.
> Could not create task ':individuals:assemble'.
   > Expected task 'compileTestDataJava' output files to contain exactly one file, however, it contains more than one file.

```

* * *

I changed the `artifacts` block to use just the single `destinationDirectory` of the `JavaCompile` task:

```nohighlight
artifacts {
  add(provide.name, tasks.named<JavaCompile>("compileTestDataJava").map { it.destinationDirectory })
}

```

This approach runs without failure, but the generated `aggregate.jar` archive contains no bytecode files, just a bare-bones `META-INF/MANIFEST.MF`.

* * *

What is the right way for a subproject to create a consumable configuration containing the multiple bytecode files created by a `JavaCompile` task, and then to consume that configuration in some other subproject?

---

<div class="post-metadata">

**Author:** ![Vampire](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/vampire/32/9082_2.png) [@Vampire](https://discuss.gradle.org/u/Vampire)\
**Post date:** [December 5, 2022, 2:07pm UTC](https://discuss.gradle.org/t/cross-subproject-dependency-on-javacompile-task-outputs/44404/2 "2022-12-05T14:07:42Z")

</div>

`testData` sounds like you might simply want to use the `java-test-fixtures` plugin: [Testing in Java & JVM projects](https://docs.gradle.org/current/userguide/java_testing.html#producing_and_using_test_fixtures_within_a_single_project)

---

<div class="post-metadata">

**Author:** ![liblit](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/liblit/32/6697_2.png) [@liblit](https://discuss.gradle.org/u/liblit)\
**Post date:** [December 5, 2022, 7:40pm UTC](https://discuss.gradle.org/t/cross-subproject-dependency-on-javacompile-task-outputs/44404/3 "2022-12-05T19:40:05Z")

</div>

The complete project is a code analysis tool. The `testData` classes truly are test _data_, not test-supporting _fixtures_. The byte code files produced by `compileTestDataJava` should not even be added to the class path when running tests. Thus, the `java-test-fixtures` plugin is not a suitable solution.

---

<div class="post-metadata">

**Author:** ![Vampire](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/vampire/32/9082_2.png) [@Vampire](https://discuss.gradle.org/u/Vampire)\
**Post date:** [December 6, 2022, 7:50am UTC](https://discuss.gradle.org/t/cross-subproject-dependency-on-javacompile-task-outputs/44404/4 "2022-12-06T07:50:25Z")

</div>

If you really want to add the single files, iirc you have to iterate through the files and add each as single artifact or something like that. You could probably look at the `java-library` plugin that adds the `classes` secondary variant.

Other than that, you could add the jar as artifact to your configuration and then unpack them on the consumer side if you need them unpacked.

You can also have a look at the `java-test-fixtures` plugin and add a feature variant like it does it for your use-case.

---

<div class="post-metadata">

**Author:** ![liblit](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/liblit/32/6697_2.png) [@liblit](https://discuss.gradle.org/u/liblit)\
**Post date:** [December 8, 2022, 4:38am UTC](https://discuss.gradle.org/t/cross-subproject-dependency-on-javacompile-task-outputs/44404/5 "2022-12-08T04:38:18Z")

</div>

@Vampire suggested:

> Other than that, you could add the jar as artifact to your configuration and then unpack them on the consumer side if you need them unpacked.

I like the simplicity of this idea, and I don’t mind putting things into a jar just to copy them out again later. Nice. _Thank you for this suggestion!_

On the consuming side, I tried using the configuration to create a `zipTree` in the obvious way:

```kotlin
val consume by
    configurations.creating {
      isCanBeConsumed = false
      isCanBeResolved = true
    }

...

tasks.register<Jar>("testDataJar") {
  group = "build"
  archiveBaseName.set("testData")
  from(zipTree(consume)) { include("**/*.class") }
}

```

Sadly, this approach fails:

```plaintext
Execution failed for task ':aggregate:testDataJar'.
> Cannot fingerprint input file property 'rootSpec$1$1': Cannot convert the provided notation to a File or URI: configuration ':aggregate:consume'.
  The following types/formats are supported:
    - A String or CharSequence path, for example 'src/main/java' or '/usr/include'.
    - A String or CharSequence URI, for example 'file:/usr/include'.
    - A File instance.
    - A Path instance.
    - A Directory instance.
    - A RegularFile instance.
    - A URI or URL instance.
    - A TextResource instance.

```

Everything works if I explicitly extract the configuration’s single file, and also add the `inputs.files` dependency explicitly:

```kotlin
tasks.register<Jar>("testDataJar") {
  group = "build"
  archiveBaseName.set("testData")
  from(zipTree(consume.files.single())) { include("**/*.class") }
  inputs.files(consume)
}

```

However, this feels inelegant. **Is there a more concise approach that gets all of its dependencies from `from` without needing help from an explicit `inputs.files`?**

---

<div class="post-metadata">

**Author:** ![Vampire](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/vampire/32/9082_2.png) [@Vampire](https://discuss.gradle.org/u/Vampire)\
**Post date:** [December 8, 2022, 5:09pm UTC](https://discuss.gradle.org/t/cross-subproject-dependency-on-javacompile-task-outputs/44404/6 "2022-12-08T17:09:32Z")

</div>

The elegant way would be an atrifact transform that unpacks that zips, that way you then again get it unpacked from the configuration already and it would also work if you declare multiple such dependencies on one configuration: [Transforming dependency artifacts on resolution](https://docs.gradle.org/current/userguide/artifact_transforms.html)

---

<div class="post-metadata">

**Author:** ![liblit](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/liblit/32/6697_2.png) [@liblit](https://discuss.gradle.org/u/liblit)\
**Post date:** [January 3, 2023, 1:09am UTC](https://discuss.gradle.org/t/cross-subproject-dependency-on-javacompile-task-outputs/44404/7 "2023-01-03T01:09:22Z")

</div>

[@Vampire’s suggested “elegant way”](https://discuss.gradle.org/t/cross-subproject-dependency-on-javacompile-task-outputs/44404/6) is a bit more involved than I want to take on right now, but I may revisit it in the future. Until then, [my version](https://discuss.gradle.org/t/cross-subproject-dependency-on-javacompile-task-outputs/44404/5) of [his earlier suggestion](https://discuss.gradle.org/t/cross-subproject-dependency-on-javacompile-task-outputs/44404/4) is working quite nicely.

Big thanks, @Vampire. This was the last puzzle I needed to solve in order to complete [a big revamp, with 2700 lines of Kotlin build logic replacing 2400 lines of Groovy build logic](https://github.com/wala/WALA/pull/1174). Your suggestions also forced me to finally learn more about how Gradle `Configuration`s work: knowledge I’m already putting to good use elsewhere. Gold star to @Vampire! 🌟
