siouan / frontend-gradle-plugin

All-in-one Gradle Node/NPM/PNPM/Yarn plugin to build Javascript applications: package manager activation with Corepack, built-in tasks, additional task types.
https://siouan.github.io/frontend-gradle-plugin/
Apache License 2.0
153 stars 22 forks source link

Conflict Between Android Gradle Plugin and Frontend Gradle Plugin in Multi-Module Project #240

Open JBou opened 1 week ago

JBou commented 1 week ago

Issue Summary: In a multi-module Gradle project, adding an Android module causes the Frontend Gradle Plugin to stop working. The project originally functioned correctly, but once the Android Gradle Plugin is introduced, the build fails, and the frontend plugin is no longer operational.

Error Details:

Failed to query the value of task ':frontend:installPackageManager' property 'packageManagerExecutableFile'.
> Querying the mapped value of map(flatmap(provider(task 'resolvePackageManager', class org.siouan.frontendgradleplugin.infrastructure.gradle.ResolvePackageManagerTask))) before task ':frontend:resolvePackageManager' has completed is not supported

Project Setup: The project consists of:

Reproduction Steps: I created a simplified version of the project to reproduce the issue. The project can be found here. Initially, the project worked as expected with the frontend being successfully bundled into the backend. However, after adding the Android module, the frontend plugin encounters errors during the build process.

Commit Demonstrating the Problem: The issue starts after this commit, where the Android module was introduced. Before this change, the frontend plugin integrated seamlessly with the backend.

Steps to Reproduce:

  1. Clone the repository and switch to the commit linked above.
  2. Run ./gradlew -p backend build run.
  3. Observe the build failure related to the frontend plugin.

Expected Behavior: The frontend Gradle plugin should continue to function correctly even when an Android module is added to the project.

Actual Behavior: The build fails, and the frontend plugin stops working as soon as the Android module is added to the project. Specifically, the following error is encountered:

Failed to query the value of task ':frontend:installPackageManager' property 'packageManagerExecutableFile'.
> Querying the mapped value of map(flatmap(provider(task 'resolvePackageManager', class org.siouan.frontendgradleplugin.infrastructure.gradle.ResolvePackageManagerTask))) before task ':frontend:resolvePackageManager' has completed is not supported

Request: I would appreciate it if you could investigate this issue, as I would like to keep the Android module within the same project while still bundling the frontend with the backend. Currently, the frontend plugin becomes unusable in the presence of the Android module, which is blocking me from progressing.

Thank you for your attention to this matter!

Environment

Settings in frontend/build.gradle.kts file:

frontend {
    nodeVersion.set("20.15.0")
    assembleScript.set("run build")
    cleanScript.set("run clean")
    checkScript.set("run check")
}

Attachments Additionally, you may attach Gradle log file, screenshots, sample archive, JUnit test case to help reproducing the issue.

v1nc3n4 commented 1 week ago

Hi @JBou,

Thank you for using this plugin. I cannot reproduce the error itself, because I can't reach this step for other reasons. As far as I can tell you, the error you see is not the result of a plugin operation. It seems the Android plugins/libraries/environment you are loading with the new subproject are doing something that forces Gradle to query the exact value of task outputs before tasks are really executed. Since I am not a developer of these plugins, I cannot tell what's going on.

That being said, I faced another error when simply executing a gradle clean command: a conflict between dependencies used by this plugin and Kotlin plugin in the backend subproject. When I look at the build scripts located in the project, here are some ideas:

I hope this helps.

BR

v1nc3n4 commented 6 days ago

Hello @JBou,

I am sorry, I will not merge the PR for the moment.

As I explained previously, maybe not clearly, the issue is not reproducible with a minimal and clean test case. The repository/commit you kindly provided contains inconsistencies/questionable practices in build scripts about how plugins are applied. There are also unnecessary dependencies and code slowing the build task and making noise during reproduction. I need you ensure the problem is still reproducible after ensuring your Gradle multi-project configuration is as light and clean as possible. For this, restart from this example which is a clean and operational configuration (leave the backend subproject as it is for the moment). I suggest the step-by-step process hereafter:

As soon as you face the issue during this process, provide the project and the console output here. If you do not face the issue, then I maintain the problem probably comes from the way plugins are applied in your own project.

Proposed changes

The new code ensures that the property is queried only after the resolvePackageManager task has completed

Gradle already knows task installPackageManager depends on task resolvePackageManager because the plugin chains an output of the latter with an input of the former. Apply this plugin in the root build.gradle.kts file to print task dependencies in your project. Run gradlew build taskTree and provide the output here. You may execute this command line at any step in the process I introduced above to verify both tasks are chained as expected.

Thank you very much for your understanding! Let me know what the results are. BR

JBou commented 5 days ago

``Hello @v1nc3n4, thanks for all your help and information! I really appreciate your suggestions. I submitted the PR yesterday when it was late, and I didn't have any time left to respond to your questions. I will create a cleaner test case, as you are right, the project was not perfectly clean. I just wanted to provide a sample.

In the meantime, just 2 information about the Android Gradle Plugin (AGP):


While I was writing the above answer, I was running the sample above using IntelliJ, and IntelliJ was not throwing the same error as Android Studio. It seems that indeed the problem above is related to Android Studio. I have a Gradle scan here using Android Studio (modified IntelliJ), in the Stack trace there is a line with com.android.build.gradle.internal.attribution.BuildAnalyzerConfiguratorService, maybe the Build analyzer analyzes the plugin and throws this error. I will report the problem to the Android Gradle team to see their response. Maybe you can leave this issue open until we find a solution.

In addition, when I run the project without Android Studio (e.g. IntelliJ, or plain Gradle), I have another error:

'org.apache.commons.compress.archivers.zip.ZipFile$Builder org.apache.commons.compress.archivers.zip.ZipFile.builder()'

The problem here is, that frontend-gradle-plugin uses version 1.27.1 of the commons-compress dependency, and AGP uses version 1.21. Now, installNode task of the frontend-gradle-plugin uses new APIs from 1.27.1 (like ZipFile.builder()') that are not there in 1.21. As the AGP is added to the plugins block (and therefore all the dependencies are added to the build classpath) in the root build.gradle.kts file, the submodules all inherit that root classpath and the frontend module and the frontend-gradle-plugin uses the old dependency from the AGP plugin. You can see this clearly in the Gradle scan (using IntelliJ). I will report this problem too, as this can also happen with other plugins that use the same dependencies.

Thanks again for your response and help, I will try to find a way to use the frontend plugin without AGP affecting the frontend module. Best regards, Gabriel

v1nc3n4 commented 5 days ago

Thank you for your great answer.

In addition, when I run the project without Android Studio (e.g. IntelliJ, or plain Gradle), I have another error:

'org.apache.commons.compress.archivers.zip.ZipFile$Builder org.apache.commons.compress.archivers.zip.ZipFile.builder()'

The problem here is, that frontend-gradle-plugin uses version 1.27.1 of the commons-compress dependency, and AGP uses version 1.21. Now, installNode task of the frontend-gradle-plugin uses new APIs from 1.27.1 (like ZipFile.builder()') that are not there in 1.21. As the AGP is added to the plugins block (and therefore all the dependencies are added to the build classpath) in the root build.gradle.kts file, the submodules all inherit that root classpath and the frontend module and the frontend-gradle-plugin uses the old dependency from the AGP plugin. You can see this clearly in the Gradle scan (using IntelliJ). I will report this problem too, as this can also happen with other plugins that use the same dependencies.

This is exactly the dependency conflict I was talking about in my first answer. I agree with your analysis about the way these plugins are declared. If you follow the AGP documentation, resolving the AGP using the plugins block in the root build script and not applying it should be safe. But AGP introduce a typical configuration for a single-module project, not a multi-module project. Resolving and applying the AGP directly in your Android subproject should be perfectly fine because it's exactly the same. As a guideline, I highly suggest you resolve all plugins using the plugins block: no buildscript block anymore. Examples in this repository also show a different way to resolve all plugins in the settings.gradle.kts script, and apply them when needed in (sub)projects. Resolution should not have any side-effect by itself as long as the plugin is not applied.

While I was writing the above answer, I was running the sample above using IntelliJ, and IntelliJ was not throwing the same error as Android Studio. It seems that indeed the problem above is related to Android Studio. I have a Gradle scan here using Android Studio (modified IntelliJ), in the Stack trace there is a line with com.android.build.gradle.internal.attribution.BuildAnalyzerConfiguratorService, maybe the Build analyzer analyzes the plugin and throws this error. I will report the problem to the Android Gradle team to see their response. Maybe you can leave this issue open until we find a solution.

It confirms my initial feeling about the side-effect of the Android environment. But again, I am sure of anything. I can only tell the frontend gradle plugin never queries a task output without executing the task before. Maybe the AGP does the opposite.

The policy with issues in this repository is to close them when there is no sign of a bug after a relevant discussion. Discussion is still alive, and I will be glad to read how you dealt with the issue after. Please, let me know the results of your build scripts refactoring. If there's a confirmation about the plugin malfunction, I'll be happy to reopen the issue.

BR