14

I'm trying to prepare and upload my Android library to Bintray and part of that process runs the following javadoc task:

task javadoc(type: Javadoc) {
    source = android.sourceSets.main.java.srcDirs
    classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
}

This task is part of a larger gradle script here: https://raw.githubusercontent.com/attwellBrian/JCenter/master/bintrayv1.gradle

When the javadoc task runs, the following problems occur:

  1. Every @NonNull and @Nullable annotation in the project reports an error of "error: cannot find symbol"
  2. Every Javadoc reference I've written for an Android class, like {@link Toolbar}, reports an error of "error: reference not found"

How can I correct these reference issues when generating Javadocs?

EDIT It looks like its not all Android class links that are creating an issue, it may just be classes that come from the Android support library (which is also where the annotations come from). Does something special need to be done to link to source files in gradle dependencies?

SuperDeclarative
  • 1,609
  • 4
  • 18
  • 32

3 Answers3

32

You should also add all your dependency to the javadoc.classpath. Try this:

task javadoc(type: Javadoc) {
    source = android.sourceSets.main.java.srcDirs
    classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
}

afterEvaluate {
    javadoc.classpath += files(android.libraryVariants.collect { variant ->
        variant.javaCompileProvider.get().classpath.files
    })
}
swooby
  • 3,005
  • 2
  • 36
  • 43
xkor
  • 646
  • 5
  • 11
  • 2
    Welcome to stackoverflow, please dont just post code but also explain what your code is doing and why its working. – LJᛃ Jan 03 '16 at 01:55
  • Can you explain why we have to add those files in "afterEvaluate" instead of adding them directly in the "javadoc()" task? – SuperDeclarative Jan 03 '16 at 03:08
  • Unfortunaly the javadoc task's body is called before `android.libraryVariants` is filled. So we have to wait until `android.libraryVariants` is filled. – xkor Jan 03 '16 at 15:42
  • 2
    Another module in the same project cannot be added as a dependency using above solution. I am facing that problem in my case, Anyone has any idea? – Vignesh Sundaramoorthy Dec 10 '16 at 11:35
  • Necroing here, but this outputs `WARNING: API 'variant.getJavaCompile()' is obsolete and has been replaced with 'variant.getJavaCompileProvider()'. It will be removed at the end of 2019.` I tried replacing `javaCompile` with `javaCompileProvider`, but that fails with `Could not get unknown property 'classpath' for provider(task compileDebugJavaWithJavac, class com.android.build.gradle.tasks.AndroidJavaCompile) of type org.gradle.api.internal.tasks.DefaultTaskContainer$TaskCreatingProvider_Decorated.` Any other ideas, or just ignore it? – swooby Mar 19 '19 at 19:12
  • I know this is 3 years old, but why does including the build variants solve the annotation errors the original poster had? – Belphegor Jul 09 '19 at 23:26
  • 1
    @swooby you should replace `javaCompile` with `javaCompileProvider.get()`. So the full line should be like this: `variant.javaCompileProvider.get().classpath.files`. Then it works without errors. – mfb Jun 04 '20 at 09:38
0

So these error mean that JavaDoc can't link the classes which are not in the same module (without further config). If you don't care about this (ie. having hyperlinks to classes outside of the module) you could just ignore the errors with

task javadoc(type: Javadoc) {
    failOnError false
    source = android.sourceSets.main.java.srcDirs
    classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
}

See here for more info on the Javadoc Task

Patrick
  • 33,984
  • 10
  • 106
  • 126
0

I had the same problem (not only with android classes, but also with androidx classes), and I couldn't get "Patrick" suggestion to work. I'm using kts instead of Groovy for my build scripts and I was having a problem converting the Groovy to KTS build scripts. I never could get javadoc working using android studio "Tools/Generate Javadoc". What I did to work around the problem was to add my own Javadoc task to one of my build.gradle.kts script files. The problem of course is that javadoc is missing some classpath dependencies for android classes. That's off course what Patrik's solution is trying to address. In my case, my build scripts are a little unconventional. They usually don't include an android block in them. They get dynamically built by the inclusion of some custom plugins. That's probably the reason Patricks solution won't work for me; "android" is undefined. I also have some other requirements in that I only wanted to produce Javadoc for 4 of the 16 modules in my Projects (those 4 modules are public API that I expose). That's another reason why I add my own javadoc task. I also don't think Patrick's solution resolves the issue for androidx classes. I'll now go about explaining how my custom javadoc task work:

I added my custom javadoc task only to my root project module. Here's what the dependency block looked like:

val javadocDeps = configurations.create("javadocDeps")

dependencies {
implementation (libs.androidx.appcompat)
implementation (libs.androidx.fragment)
implementation (libs.androidx.navigation.fragment)
implementation (libs.androidx.navigation.ui)
implementation (libs.androidx.constraintlayout)
implementation(libs.androidx.drawerlayout)
implementation (project(":CoreInterfaces"))
implementation (project(":CoreInternalInterfaces"))
implementation (project(":CoreAndroidInterfaces"))
implementation (project(":CoreAndroidInternalInterfaces"))
implementation (project(":OsgiFrameworkConfiguration"))
implementation (project(":Log"))
implementation (project(":CryptoImpl"))
implementation (project(":SystemProperties"))
implementation (project(":CommunicationImpl"))
implementation (project(":CommunicationAndroidImpl"))
implementation (project(":Executor"))
implementation(project(":OsgiFramework"))
implementation(project(":OsgiInterface"))
implementation(project(":OsgiWrapper"))
implementation(project(":InstallPlanImpl_1_0_0"))
implementation (project(":InstallPlanInterfaces_1_0_0"))
javadocDeps(project(":OsgiFramework"))
javadocDeps (libs.androidx.appcompat)
javadocDeps (libs.androidx.fragment)
javadocDeps (libs.androidx.navigation.fragment)
javadocDeps (libs.androidx.navigation.ui)
javadocDeps (libs.androidx.constraintlayout)
javadocDeps (libs.androidx.drawerlayout)
}

Notice that I created a configuration called "javadocDeps".

Now here's how my custom javadoc task was defined:

    tasks {
    register<Javadoc>("createCoreJavadoc") {
        setFailOnError(true)
        val docDir: File = File(project.projectDir.parentFile.parentFile, "Doc/Core")
        println("javadoc destination dir: " + docDir.absolutePath)
        setDestinationDir(docDir)
        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)
        options.overview = "src/main/java/Overview.html"
        android.bootClasspath.forEach{
            classpath += fileTree(it)
        }
        val tmpDir:File = File(project.buildDir, "\\tmpAar\\")
        if (tmpDir.exists()) tmpDir.delete()
        tmpDir.mkdirs()
        javadocDeps.forEach {
            val zipFileSystem: com.phinneyridge.ZipFileSystem = ZipFileSystem(it.absolutePath,null)
            if (it.name.endsWith(".aar")) {
                val tmpFile:File = File(tmpDir, it.name.replace(".aar", ".jar"))
                zipFileSystem.extractEntry("classes.jar", tmpFile)
            } else {
                classpath += fileTree(it)
            }
        }
        classpath += fileTree(tmpDir)
        //classpath += depCollection
        //classpath += javadocDeps
        println("classpath: " + classpath.asPath)

    }
}

So you can see, 1.) I define the sourcepath of the modules that I want to produce javadoc for, and 2.) add the missing android boot classes. The next part might seem a little bit confusing. I create a temporary directory and iterate over the set of JavaDoc dependencies. If the dependency is a jar file, I just added it to the tasks classpath. If it's an aar file, I extract the classes.jar file from the aar file giving it a unique name and adding that file to my temporary directory. When that's all done, I add the temporary directory to the classpath.

This works for me and I no longer get undefined android classes and androidx classes. Notice that I had to specially treat the aar file. The javadoc task doesn't know how to handle aar files directly.

Tom Rutchik
  • 1,183
  • 1
  • 12
  • 15