Open realdadfish opened 2 years ago
On a related note - generated assets should be separated by build variant under Android, so that
build/reports/licenses/firstVariant
build/reports/licenses/secondVariant
...
are all separated from each other and each target file can keep its original file name.
That way it is easier in multi-variant environments, because consumers don't have to rename files by hand or via a separate Gradle Copy
task and also don't have to distinguish different filenames on runtime.
All you need to do is to add the appropriate variant-named build dir to the assets source set of the same variant and you're done.
The common output directory is even more problematic in Gradle 8, which introduced more strict task validation rules. Since all of the LicenseReport
tasks output to the same configured @OutputDirectory
, Gradle 8 cannot properly wire tasks that depend on the output of one specific task to another, further processing task. Consider the following example:
androidComponents.onVariants { variant ->
val variantName = variant.name.capitalize()
val licenseReportTask = tasks.matching { it.name == "license${variantName}Report" }
val copyTask = tasks.register("copy${variantName}LicenseReport", Copy::class.java) {
from(layout.buildDirectory.dir("reports/licenses/")) { // <<-- this is the problematic line
include("license${variantName}Report.*")
rename("license${variantName}Report.(.*)", "open_source_licenses.$1")
}
into(layout.projectDirectory.dir("src/${variant.name}/assets/"))
}
licenseReportTask.configureEach {
finalizedBy(copyTask)
}
tasks.matching { it.name == "merge${variantName}Assets" }.all {
dependsOn(licenseReportTask)
dependsOn(copyTask)
}
}
errors out with
Reason: Task ':app:copyDevelopReleaseLicenseReport' uses this output of task ':app:licenseDevelopDebugReport' without declaring an explicit or implicit dependency. This can lead to incorrect results being produced, depending on what order the tasks are executed.
when the tasks for more than one build variant are executed at the same time.
Are there any news on this? Because I am struggling with it in our gradle upgrade from 7 to 8, in which the plugin produces a lot of error output and does not work properly.
@realdadfish Are you still having this issue?
I am trying to repro with 0.9.4
:
git clone https://github.com/jaredsburrows/android-gif-search
cd android-gif-search
gradlew licenseDebugReport
gradlew licenseReleaseReport
gradlew assembleDebug
I switched projects in the meantime, in the current one I'm not using the plugin anymore, so I can't say. Maybe @AndreyBulgakow can say?
At least #339 seems to be fixed in 0.9.4
Actually the build failed to me on CI:
FAILURE: Build failed with an exception.
* What went wrong:
A problem was found with the configuration of task ':ui:licenseReleaseReport' (type 'LicenseReportTask').
- Gradle detected a problem with the following location: '/builds/app/ui/build/reports/licenses'.
Reason: Task ':ui:generateDebugLicenseReport' uses this output of task ':ui:licenseReleaseReport' without declaring an explicit or implicit dependency. This can lead to incorrect results being produced, depending on what order the tasks are executed.
Possible solutions:
1. Declare task ':ui:licenseReleaseReport' as an input of ':ui:generateDebugLicenseReport'.
2. Declare an explicit dependency on ':ui:licenseReleaseReport' from ':ui:generateDebugLicenseReport' using Task#dependsOn.
3. Declare an explicit dependency on ':ui:licenseReleaseReport' from ':ui:generateDebugLicenseReport' using Task#mustRunAfter.
For more information, please refer to https://docs.gradle.org/8.5/userguide/validation_problems.html#implicit_dependency in the Gradle documentation.
* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.
> Get more help at https://help.gradle.org/.
Deprecated Gradle features were used in this build, making it incompatible with Gradle 9.0.
BUILD FAILED
Where is :ui:generateDebugLicenseReport
coming from? There is only one task in this project: licenseReport
.
It's a custom task that triggers a licenseReport
task and transforms its output to a different format:
private fun Project.createLicenseTask(
variantName: String,
assetDirs: Set<File>,
ignoredPatterns: Set<String>
) = task("generate${variantName}LicenseReport") {
// Trigger the license plugin task first to generate the JSON report.
dependsOn("license${variantName}Report")
// Prepare output directions and mark them as outputs.
val outputDirName = "licenses"
val outputDirs = assetDirs.map {
it.toPath().resolve(outputDirName).toFile()
}
outputDirs.forEach {
outputs.dir(it)
}
// Prepare inputs.
val jsonReport = licenseReportFile(variantName)
inputs.file(jsonReport)
val knownLicensesDir = knownLicencesDir()
inputs.dir(knownLicensesDir)
val extraDeps = extraDepsFile()
inputs.file(extraDeps)
// Mapping between a license URL and its text.
val knownLicensesByUrl = knownLicencesByUrl()
doLast {
val deps = loadDeps(
from = jsonReport,
ignoredPatterns = ignoredPatterns
).toMutableMap()
// Load extra dependencies. These are dependencies coming from other
// sources then the license report plugin. E.g. if we use a native
// library or a jar-file requiring attribution.
deps.putAll(loadDeps(from = extraDeps))
// Make sure each dependency has a license defined.
checkLicenseExistence(deps)
// Add the license file name to the report or fail if missing.
deps.values.forEach { dep ->
dep.licenses?.forEach {
val licenseFile = knownLicensesByUrl[it.licenseUrl]
if (licenseFile != null) {
it.licenseFile = "$outputDirName/$licenseFile"
} else {
throw GradleException("Missing license file for license url: ${it.licenseUrl} coming from ${dep.dependency}")
}
}
}
// Copy result json report and licenses to assets
outputDirs.forEach { outputDir ->
writeOutput(outputDir, deps, knownLicensesDir)
}
}
}
While generate${variantName}LicenseReport
depends on license${variantName}Report
Gradle doesn't like that license${variantName}Report
writes reports into the same output folder.
In https://github.com/jaredsburrows/gradle-license-plugin/blob/a441c46b1b1f90792772d76638ddc48af53a2261/gradle-license-plugin/src/main/kotlin/com/jaredsburrows/license/projectAndroid.kt#L97 the plugin determines all configured assets directories of a specific build variant to be able to copy the generated resources to it.
IMHO this is the wrong approach, because this leads to a "duplicate assets" issue as shown below:
It doesn't make much sense to just take "the first" or only the first not in
/build
either, because you cannot foresee what the module owner had in mind when (s)he configured his module with multiple asset directories. A better approach would beObviously, these two options should be mutual exclusive, otherwise people will stumble upon the same error above.