简体   繁体   English

当它具有也是 aar 库的依赖项时,如何为 android 库生成 javadoc?

[英]How to generate javadoc for android library when it has dependencies which are also aar libraries?

I have android library project which depends on other android library projects.我有依赖于其他 android 库项目的 android 库项目。 I need to generate javadoc for library but it fails because gradle puts to javadoc classpath path to .aar locations but javadoc expects .jar files.我需要为库生成 javadoc,但它失败了,因为 gradle 将 javadoc 类路径放入 .aar 位置,但 javadoc 需要 .jar 文件。

simplified gradle file:简化的gradle文件:

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.2"

    configurations {
        javadocDeps
    }

    defaultConfig {
        minSdkVersion 7
        targetSdkVersion 23
        versionCode 1
        versionName "0.1.0"
    }
}

dependencies {
    compile 'com.android.support:support-v4:23.2.0'
    compile 'com.android.support:appcompat-v7:23.2.0'
    compile 'com.nineoldandroids:library:2.4.0'
    compile 'com.annimon:stream:1.0.7'
    javadocDeps 'com.android.support:support-annotations:23.2.0'
    javadocDeps 'com.nineoldandroids:library:2.4.0'
    javadocDeps 'com.android.support:support-v4:23.2.0'
}

task sourcesJar(type: Jar) {
    from android.sourceSets.main.java.srcDirs
    classifier = 'sources'
}    

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

task javadocJar(type: Jar, dependsOn: javadoc) {
    classifier = 'javadoc'
    from javadoc.destinationDir
}

artifacts {
    archives javadocJar
    archives sourcesJar
}

3 solutions possible: 3种可能的解决方案:

1) somehow to add to the classpath path classes.jar from every aar library it depends build/intermidiates/exploded-aar/library/version/jars/classes.jar I don't know how to include these paths in javadoc task. 1)以某种方式从它依赖的每个aar库中添加到类路径路径classes.jar build/intermidiates/exploded-aar/library/version/jars/classes.jar 我不知道如何在javadoc任务中包含这些路径。

2) manually unpack classes.jar from aar file and add them to classpath of javadoc task 2)从aar文件中手动解压classes.jar并将它们添加到javadoc任务的类路径中

3) very dirty hack - hardcoded paths to library - but I think this is so WRONG. 3)非常肮脏的黑客 - 库的硬编码路径 - 但我认为这是非常错误的。

How to achieve 1 or 2 with gradle dsl?如何使用 gradle dsl 实现 1 或 2?

I managed to automate the solution of Guillaume Perrot by extracting the classes.jar contained in each AAR file, and adding it to the classpath of the javadoc task.我设法通过提取每个 AAR 文件中包含的classes.jar并将其添加到 javadoc 任务的类路径来自动化Guillaume Perrot的解决方案。

It seems to work for AAR dependencies and AAR modules on Android Studio 2.3 and Gradle 3.3它似乎适用于 Android Studio 2.3 和 Gradle 3.3 上的 AAR 依赖项和 AAR 模块

import java.nio.file.Files
import java.nio.file.Paths
import java.io.FileOutputStream
import java.util.zip.ZipFile

task javadoc(type: Javadoc) {
    source = android.sourceSets.main.java.srcDirs
    classpath += configurations.compile
    classpath += configurations.provided

    afterEvaluate {
        // 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
        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)
            }
        }
    }
}

// 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 ZipFile(archive)
    zip.entries().each {
        if (it.name == entryPath) {
            def path = Paths.get(outputPath)
            if (!Files.exists(path)) {
                Files.createDirectories(path.getParent())
                Files.copy(zip.getInputStream(it), path)
            }
        }
    }
    zip.close()
}

This only works for Android Studio older than 2.3 and/or Gradle older than 3.3这仅适用于早于 2.3 的 Android Studio 和/或早于 3.3 的 Gradle

To add the JARs from the AARs you can add the following doFirst to the javadoc task:要从 AAR 添加 JAR,您可以将以下doFirst添加到 javadoc 任务:

task javadoc(type: Javadoc) {
    source = android.sourceSets.main.java.srcDirs
}
.doFirst {
    classpath += fileTree(dir: "$buildDir/intermediates/exploded-aar/", include:"**/classes.jar")
}

It will add all .jar files from all the AARs to the javadoc classpath.它将所有 AAR 中的所有.jar文件添加到 javadoc 类路径。 (option 1 from your proposed solutions) (您提出的解决方案中的选项1)

The solution from @rve is now broken on Android Studio 2.3 / Gradle 3.3 as the exploded-aar no longer exists (with no alternative inside the build directory). @rve 的解决方案现在在 Android Studio 2.3 / Gradle 3.3 上被破坏,因为exploded-aar不再存在(在构建目录中没有替代方案)。

If the aar you depend on is not a module in your project, you will need first to extract the classes.jar before referencing it in the classpath (basically re-create intermediates/exploded-aar manually).如果您依赖的 aar 不是项目中的模块,则需要先提取 classes.jar,然后在类路径中引用它(基本上手动重新创建intermediates/exploded-aar )。

If the aar you depend on is just another module in your project you can also make your javadoc task depends on the compile task of that module and reference the intermediates/classes/release of that module (if you make javadoc depends on assembleRelease for example).如果您依赖的 aar 只是项目中的另一个模块,您还可以使您的 javadoc 任务依赖于该模块的编译任务并引用该模块的intermediates/classes/release (如果您使 javadoc 依赖于 assembleRelease 例如) . An example of that workaround: https://github.com/Microsoft/mobile-center-sdk-android/pull/345/files该解决方法的一个示例: https ://github.com/Microsoft/mobile-center-sdk-android/pull/345/files

I really wish someone comes up with a better solution though.我真的希望有人提出更好的解决方案。

I am running the new Android Studio 3.0-beta7, and tried to use @nicopico's answer, but it failed with a number of different errors, so here's an adaptation of it that doesn't rely on the non-existent java.nio utilities.我正在运行新的 Android Studio 3.0-beta7,并尝试使用@nicopico 的答案,但它因许多不同的错误而失败,所以这里有一个不依赖于不存在的java.nio实用程序的改编版。

task javadoc(type: Javadoc) {
    failOnError false
    source = android.sourceSets.main.java.srcDirs
    // Also add the generated R class to avoid errors...
    // TODO: debug is hard-coded
    source += "$buildDir/generated/source/r/debug/"
    // ... but exclude the R classes from the docs
    excludes += "**/R.java"

    // TODO: "compile" is deprecated in Gradle 4.1, 
    // but "implementation" and "api" are not resolvable :(
    classpath += configurations.compile

    afterEvaluate {
        // 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
        aarDependencies.each { aar ->
            System.out.println("Adding classpath for aar: " + aar.name)
            // Extract classes.jar from the AAR dependency, and add it to the javadoc classpath
            def outputPath = "$buildDir/tmp/exploded-aar/${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)
            }
        }
    }
}

// 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()
}

It bothers me that we need all this code to produce a freaking javadoc for a library, but at least I got this working.困扰我的是,我们需要所有这些代码来为库生成一个可怕的 javadoc,但至少我得到了这个工作。 However, I do need to find a workaround for configuration.api and configuration.implementation not being resolvable.但是,我确实需要为 configuration.api 和 configuration.implementation 无法解决找到解决方法。

This is how I solved this issue, using zipTree .这就是我使用zipTree解决这个问题的方法。 Configuration: Gradle 4.10, Gradle Plugin: 3.3.2, Android Studio: 3.4.配置:Gradle 4.10,Gradle 插件:3.3.2,Android Studio:3.4。

task javadoc(type: Javadoc) {

    doFirst {
        configurations.implementation
                .filter { it.name.endsWith('.aar') }
        .each { aar ->
            copy {
                from zipTree(aar)
                include "**/classes.jar"
                into "$buildDir/tmp/aarsToJars/${aar.name.replace('.aar', '')}/"
            }
        }
    }

    configurations.implementation.setCanBeResolved(true)
    source = android.sourceSets.main.java.srcDirs
    classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
    classpath += configurations.implementation
    classpath += fileTree(dir: "$buildDir/tmp/aarsToJars/")
    destinationDir = file("${project.buildDir}/outputs/javadoc/")
    failOnError false
    exclude '**/BuildConfig.java'
    exclude '**/R.java'
}

All of the solutions listed here are out of date if you are developing an Android app/library using Kotlin.如果您正在使用 Kotlin 开发 Android 应用程序/库,则此处列出的所有解决方案都已过时。 To generate javadocs as well as documentation in several other formats, use KDoc and Dokka:要生成 javadocs 以及其他几种格式的文档,请使用 KDoc 和 Dokka:

https://kotlinlang.org/docs/kotlin-doc.html https://kotlinlang.org/docs/kotlin-doc.html

https://kotlin.github.io/dokka/1.5.0/ https://kotlin.github.io/dokka/1.5.0/

https://github.com/Kotlin/dokka https://github.com/Kotlin/dokka

I posted a solution for this problem at Android AAR depending on AAR fails with javadoc generation .我在Android AAR 上发布了针对此问题的解决方案,具体取决于 AAR failed with javadoc generation I think Johann comment that the listed solutions are out of date is probably correct, but mike192 solution looks pretty good, although I think it might have a problem handling androidx dependencies.我认为 Johann 评论列出的解决方案已过时可能是正确的,但 mike192 解决方案看起来相当不错,尽管我认为它在处理 androidx 依赖项时可能存在问题。 I haven't tried KDoc and Dokka yet, but in looking at the documentation, that looks promising.我还没有尝试过 KDoc 和 Dokka,但是在查看文档时,这看起来很有希望。 Hopefully it works for android java libraries.希望它适用于android java库。 The android studio's built-in javadoc tool (2021.2.1) has issues handling that module type; android studio 的内置 javadoc 工具 (2021.2.1) 在处理该模块类型时存在问题; hence the need to build a custom javadoc task to work around those issues.因此需要构建一个自定义的 javadoc 任务来解决这些问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM