Reputation: 3258
I have an android gradle project structure that looks like this
Key facts
Without javadocs, gpg, signing, or publishing, everything builds just fine. App runs, everything is great.
When i started adding in tasks to generate javadocs, that's when everything went haywire. module1-aar will build and generate javadocs with no problem. module2-aar however always fails during the javadoc task.
Task is below. Most of it was borrowed from here How to generate javadoc for android library when it has dependencies which are also aar libraries?
project.task("javadoc", type: Javadoc) {
afterEvaluate {
configurations.all
.each {item ->
item.setCanBeResolved(true)
}
classpath += configurations.api
classpath += configurations.implementation
// Wait after evaluation to add the android classpath
// to avoid "buildToolsVersion is not specified" error
classpath += files(android.getBootClasspath())
// Process AAR dependencies
def aarDependencies = classpath.filter { it.name.endsWith('.aar') }
classpath -= aarDependencies
//fails here when an AAR depends on an AAR
aarDependencies.each { aar ->
// Extract classes.jar from the AAR dependency, and add it to the javadoc classpath
def outputPath = "$buildDir/tmp/aarJar/${aar.name.replace('.aar', '.jar')}"
classpath += files(outputPath)
// Use a task so the actual extraction only happens before the javadoc task is run
dependsOn task(name: "extract ${aar.name}").doLast {
extractEntry(aar, 'classes.jar', outputPath)
}
}
}
source = android.sourceSets.main.java.srcDirs
classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
classpath += project.files(android.getBootClasspath())
classpath += configurations.implementation
classpath += fileTree(dir: project.buildDir.absolutePath + "/tmp/aarsToJars/")
classpath += files(project.buildDir.absolutePath + "/intermediates/compile_r_class_jar/release/R.jar")
classpath += files(project.buildDir.absolutePath + "/generated/source/buildConfig/release/release")
classpath += files(project.buildDir.absolutePath + "/generated/source/r/buildConfig/release/release")
destinationDir = file( project.buildDir.absolutePath + "/outputs/javadoc/")
failOnError true
options.charSet 'UTF-8'
options.docEncoding 'UTF-8'
options.encoding 'UTF-8'
options.addBooleanOption 'Xdoclint:none', true
exclude '**/BuildConfig.java'
exclude '**/R.java'
exclude '**/doc-files/*'
}
// Utility method to extract only one entry in a zip file
private def extractEntry(archive, entryPath, outputPath) {
if (!archive.exists()) {
throw new GradleException("archive $archive not found")
}
def zip = new java.util.zip.ZipFile(archive)
zip.entries().each {
if (it.name == entryPath) {
def path = new File(outputPath)
if (!path.exists()) {
path.getParentFile().mkdirs()
// Surely there's a simpler is->os utility except
// the one in java.nio.Files? Ah well...
def buf = new byte[1024]
def is = zip.getInputStream(it)
def os = new FileOutputStream(path)
def len
while ((len = is.read(buf)) != -1) {
os.write(buf, 0, len)
}
os.close()
}
}
}
zip.close()
}
//wires in the javadoc task to the normal build
tasks.named("build") { finalizedBy("generateJavadocJar") }
The error message i'm getting is the following
* What went wrong:
A problem occurred configuring project ':module2-aar'.
> Could not resolve all files for configuration ':module2-aar:implementation'.
> Could not resolve project :module1-aar.
Required by:
project :module2-aar
> Cannot choose between the following variants of project :module1-aar:
- debugRuntimeElements
- releaseRuntimeElements
All of them match the consumer attributes:
- Variant 'debugRuntimeElements' capability com.github.test:module1-aar:6.1.11-SNAPSHOT:
- Unmatched attributes:
- Provides com.android.build.api.attributes.AgpVersionAttr '7.1.3' but the consumer didn't ask for it
- Provides com.android.build.api.attributes.BuildTypeAttr 'debug' but the consumer didn't ask for it
- Provides com.android.build.gradle.internal.attributes.VariantAttr 'debug' but the consumer didn't ask for it
- Provides org.gradle.usage 'java-runtime' but the consumer didn't ask for it
- Variant 'releaseRuntimeElements' capability com.github.test:module1-aar:6.1.11-SNAPSHOT:
- Unmatched attributes:
- Provides com.android.build.api.attributes.AgpVersionAttr '7.1.3' but the consumer didn't ask for it
- Provides com.android.build.api.attributes.BuildTypeAttr 'release' but the consumer didn't ask for it
- Provides com.android.build.gradle.internal.attributes.VariantAttr 'release' but the consumer didn't ask for it
- Provides org.gradle.usage 'java-runtime' but the consumer didn't ask for it
I've been playing around with the gradle task a bit and it seems that the error message is generated anytime i attempt to iterate over the classpath of the module2-aar.
I have tried a number of other suggestions, like changing module2-aar's dependency declaration from
api project(':module2-aar')
to
api project(path:':module2-aar')
However that doesn't do anything
I also tried this:
api project(path: ':module1-aar', configuration: 'default')
While the above resolves the reported issue, it causes a compile issue whereby module2-aar doesn't appear to have module1-aar in the classpath during compile...and it seems to compile before module1-aar.
Unfortunately, the documentation for what configuration
means when referencing an android project is a bit thin, or perhaps I'm looking in the wrong place. I'm not sure what other valid values are available.
Anyhow, I'm not sure what's wrong here other than I've spent way too much time on this.
Upvotes: 0
Views: 878
Reputation: 59
I understand that you have a project structure like this: app ->
module2 ->
module1.
Where ->
means the dependency stream. So I deployed a project with that same dependency flow, but using gradle 7.0.2 (because that's what I'm currently using), and had no problem generating javadoc for module2 and module1
Basically it boils down to implementing this in every gradle of every module: https://stackoverflow.com/a/73096187/9902249
I hope it works for you.
Upvotes: 0
Reputation: 1692
I going to publish my solution to the problem of using "aar" files in javadoc. In the course of trying to solve the problem, I too, had been getting the same error that spy was referring to. That actually error means it can differentiate whether it should be using release or debug libraries. It seemed to me to be too futile to try and correct that issue, so instead, I took a different approach to solving what I think is essentially the same problem.
In my case, I have a project that contains multiple subprojects, and when I produce my javadoc documentation I wanted to produce a merged javadoc document, that consisted of just some of the subprojects (not all of the subprojects). As far as I know, this is not a capability, built into Android Studio. The current version of Android Studio(2021.2.1) seems to have problems producing javadoc documentation for android library modules. There are two issues:
1.) the javadoc classpath doesn't have the android bootclasses added. You get errors for referencing any android SDK method such as "Context", "Typeface", etc.
2.) the javadoc classpath doesn't get any AndroidX libraries added to it. Many of the AndroidX libraries are "aar" files. Android Studio(2021.2.1) does not handle aar files correctly when using javadoc.
My environment is similar to spy, except that I'm using android gradle plugin 7.2.0. I've created a custom javadoc task in the "app" module's build.gradle.kts script. My "app" module is an android application module. The code needs to be place in any module that contains either the plugin "com.android.application" or "com.android.library". Some of the modules that I produce the merged javadoc for are java libraries, and that's okay.
// create a custom configuration
val javadocDeps = configurations.create("javadocDeps")
// add javadoc dependencies that you need.
dependencies {
javadocDeps(project(":OsgiFramework"))
// note: I'm using a libs version catalog for the dependencies
// you can add hardwired dependencies if you prefer
javadocDeps (libs.androidx.appcompat)
javadocDeps (libs.androidx.fragment)
javadocDeps (libs.androidx.navigation.fragment)
javadocDeps (libs.androidx.navigation.ui)
javadocDeps (libs.androidx.constraint.layout)
}
// register the createCoreJavadoc task
// in my case, "gradlew app:createCoreJavadoc" creates the merged javadoc
tasks {
register<Javadoc>("createCoreJavadoc") {
setFailOnError(true)
val docDir: File = File(project.projectDir.parentFile.parentFile, "Doc/Core")
println("javadoc destination dir: " + docDir.absolutePath)
// set the location where the documentation is produced in
setDestinationDir(docDir)
// select the projects to produce merged javadoc for
var sourcepaths: FileCollection = project(":CoreInterfaces").files("src/main/java")
sourcepaths =
sourcepaths.plus(project(":CoreInternalInterfaces").files("src/main/java"))
sourcepaths = sourcepaths.plus(project(":CoreAndroidInterfaces").files("src/main/java"))
sourcepaths =
sourcepaths.plus(project(":CoreAndroidInternalInterfaces").files("src/main/java"))
sourcepaths = sourcepaths.plus(project(":OsgiInterface").files("src/main/java"))
sourcepaths =
sourcepaths.plus(project(":InstallPlanInterfaces_1_0_0").files("src/main/java"))
setSource(sourcepaths.asFileTree)
// fix the problem with the missing android bootclasses
android.bootClasspath.forEach{
classpath += fileTree(it)
}
// create a temporary directory for storing the "classes.jar" file contained in the *.aar files
val tmpDir:File = File(project.buildDir, "\\tmpAar\\")
if (tmpDir.exists()) tmpDir.delete()
tmpDir.mkdirs()
// add the javadoc dependencies
javadocDeps.forEach {
// I've got a custom class that allows me to treat jar or zip files and a file system
// you could replace this using spy's zip file extraction method
val zipFileSystem: com.phinneyridge.ZipFileSystem = ZipFileSystem(it.absolutePath,null)
if (it.name.endsWith(".aar")) {
// extract the classes.jar file from the aar file to the tmpDir
// renaming it to name of the aar file, but change the extension to jar
val tmpFile:File = File(tmpDir, it.name.replace(".aar", ".jar"))
zipFileSystem.extractEntry("classes.jar", tmpFile)
} else {
// for jar files, we just add it to the path
classpath += fileTree(it)
}
}
// now add the tmpDir files to the javadoc classpath
classpath += fileTree(tmpDir)
// for diagnosis purposes, we'll print the classpath.
// Notice that you have a lot more path entries then you have javadocDeps
println("classpath: " + classpath.asPath)
}
}
Upvotes: 0