Gradle 3.0 Upgrade Causes androidJavadocs Error - android

I updated the following:
//gradle
classpath 'com.android.tools.build:gradle:3.0.0-beta6'
// library dependencies
implementation "com.android.support:appcompat-v7:26.1.0"
implementation "com.google.code.gson:gson:2.7"
implementation "com.google.android.gms:play-services-location:11.2.2"
I am now getting the following exception kinds of exceptions for the gradle task androidJavadocs.
error: package com.google.android.gms.security does not exist
error: package com.google.gson does not exist
error: cannot find symbol class NonNull
Here is the gradle task that used to allow me to package up the javadocs but this no longer suffices:
libraryVariants.all { variant ->
if (variant.name == 'release') {
task docs(type: Javadoc) {
println 'docs task'
source = variant.javaCompiler.source
classpath += files(((Object) android.bootClasspath.join(File.pathSeparator)))
classpath += files(variant.javaCompiler.classpath.files)
}
}
}
I have tried lots of different combinations of gradle tasks and workarounds that I've found searching around but nothing works and I continue to get these errors. I have tried cleaning the project and invalidating the cache. Any ideas?

Adding the following to my upload-archives.gradle file fixed the problem:
task androidJavadocs(type: Javadoc) {
source = android.sourceSets.main.java.srcDirs
// this is new
android.libraryVariants.all { variant ->
if (variant.name == 'release') {
owner.classpath += variant.javaCompiler.classpath
}
}
// end of new
classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
}

Related

How to retrieve library version in gradle task

Suppose that the project dependencies are declared as follows:
dependencies {
implementation 'io.mylib:core:1.0.0-SNAPSHOT'
// ....
}
I would like to have a gradle task that will retrieve the mylib dependency and check the version. If the version of the lib is SNAPSHOT then fail the build.
Something like this:
task checkLibVersion(){
version = getDependencyVersion("io.mylib:core") // <-- how to retrieve version
if(version.endsWith("-SNAPSHOT"){
throw new GradleException("Mylib uses snapshot version")
}
}
This task would be part of the release pipeline and will fail the job if current version of mylib is snapshot.
Does anyone know how to retrieve the version of a particular dependency from a gradle task?
OK, it seems this can be done pretty easy if the version number is extracted into an extension property:
In the build.gradle of the root project:
buildscript {
ext.libVersion = '1.3.21-SNAPHOT'
// ....
}
task checkLibVersion(){
def version = "$libVersion"
if (version.endsWith("-SNAPSHOT")){
throw new GradleException("Mylib uses SNAPSHOT version!")
}
}
tasks.whenTaskAdded { task ->
if (task.name == 'assembleRelease') {
task.dependsOn checkLibVersion()
}
}

Android gradle runtimeClasspath equivalent

The gradle java plugin has a FileCollection property which contains the runtime classes - sourcesets.main.runtimeClasspath.
Is there an equivalent within the com.android.application plugin?
What I've found is that the destinationDir property of applicationVariants can be appended to the javaCompile.classpath property, which will result in a FileCollection which contains the dependency classpaths and the compiled classes.
My use case is trying to run a java executable post-compile:
afterEvaluate {
android.applicationVariants.each { variant ->
variant.javaCompile.doLast {
javaexec {
classpath += variant.javaCompile.classpath
classpath += files(variant.javaCompile.destinationDir)
main = 'com.mydomain.Main'
}
}
}
}
Tested on Android Studio 2.1.1 running 'com.android.tools.build:gradle:2.1.0' and gradle 2.10.
Reference: http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Shrinking-Resources

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. 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.
simplified gradle file:
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:
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.
2) manually unpack classes.jar from aar file and add them to classpath of javadoc task
3) very dirty hack - hardcoded paths to library - but I think this is so WRONG.
How to achieve 1 or 2 with gradle dsl?
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.
It seems to work for AAR dependencies and AAR modules on Android Studio 2.3 and Gradle 3.3
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()
}
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).
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).
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). An example of that workaround: https://github.com/Microsoft/mobile-center-sdk-android/pull/345/files
I really wish someone comes up with a better solution though.
This only works for Android Studio older than 2.3 and/or Gradle older than 3.3
To add the JARs from the AARs you can add the following doFirst to the javadoc task:
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. (option 1 from your proposed solutions)
This is how I solved this issue, using zipTree. Configuration: Gradle 4.10, Gradle Plugin: 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'
}
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.
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. However, I do need to find a workaround for configuration.api and configuration.implementation not being resolvable.
All of the solutions listed here are out of date if you are developing an Android app/library using Kotlin. To generate javadocs as well as documentation in several other formats, use KDoc and Dokka:
https://kotlinlang.org/docs/kotlin-doc.html
https://kotlin.github.io/dokka/1.5.0/
https://github.com/Kotlin/dokka
I posted a solution for this problem at Android AAR depending on AAR fails 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. I haven't tried KDoc and Dokka yet, but in looking at the documentation, that looks promising. Hopefully it works for android java libraries. The android studio's built-in javadoc tool (2021.2.1) has issues handling that module type; hence the need to build a custom javadoc task to work around those issues.

Aggregate Javadoc for Android project with multiple library modules

I've gone through almost entire Internet in search of a way how to aggregate Javadocs in the project consisting of separate library modules into single Javadoc.
There is a plugin that apparently allows to do that:
https://github.com/nebula-plugins/gradle-aggregate-javadocs-plugin
However, if I run the command specified by the plugin Gradle finds the task and executes it but no output directory is generated.
Any help how to build single Javadoc from multiple modules much appreciated.
I managed to get it working some time ago, apologies for a late response. The solution for aggregatable Javadoc creation is the following:
In each of the subprojects maintained within the project create a task generating the Javadoc:
android.libraryVariants.all { variant ->
task("generate${variant.name.capitalize()}Javadoc", type: Javadoc) {
destinationDir = project.file("$project.projectDir/javadoc/$project.PROJECT_NAME") //Project name in the Project's gradle.properties
title = "A title of my project - $project.PROJECT_VERSION_NAME" //Project version name in the Project's gradle.properties
description "Generates Javadoc for $variant.name."
source = variant.javaCompile.source
ext.androidJar ="${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar"
classpath = files(variant.javaCompile.classpath.files) + files(ext.androidJar) + project.files(android.getBootClasspath().join(File.pathSeparator))
options {
memberLevel = org.gradle.external.javadoc.JavadocMemberLevel.PUBLIC //change the modifier according to your needs
links "http://docs.oracle.com/javase/8/docs/api/"
linksOffline "http://d.android.com/reference", "${android.sdkDirectory}/docs/reference"
}
exclude '**/BuildConfig.java'
exclude '**/R.java'
}
task("bundle${variant.name.capitalize()}Javadoc", type: Jar) {
baseName = "Compass API - ($version)"
description "Bundles Javadoc into zip for $variant.name."
classifier = "javadoc"
from tasks["generate${variant.name.capitalize()}Javadoc"]
}
}
The configuration above adds a Javadoc generation task for each buildVariant of your subproject. At this point you can you can generate Javadoc for each module separately by typing
gradle :myRootProject:mySubproject:generateDebugJavadoc
gradle :myRootProject:mySubproject:generateReleaseJavadoc
gradle :myRootProject:mySubproject:generateMyFancyFlavourDebugJavadoc
gradle :myRootProject:mySubproject:generateMyFancyFlavourReleaseJavadoc
If you use JRE 8 the following configuration disables errors raised by doclint during the Javadoc build (explanation in greater detail here)
if (JavaVersion.current().isJava8Compatible()) {
tasks.withType(Javadoc) {
// disable the crazy super-strict doclint tool in Java 8
//noinspection SpellCheckingInspection
options.addStringOption('Xdoclint:none', '-quiet')
}
}
To aggregate Javadocs of each submodules into a single one create a Plugin in to build.gradle which will add a task to the submodule a partial Javadoc generation of which you are interested in:
class JavadocAggregationPlugin implements Plugin<Project> {
static final String AGGREGATE_JAVADOCS_TASK_NAME = 'aggregateJavadocs'
#Override
void apply(Project project) {
Project rootProject = project.rootProject
rootProject.gradle.projectsEvaluated {
Set<Project> librarySubprojects = getLibraryProjects(rootProject)
if (!librarySubprojects.isEmpty()) {
rootProject.task(AGGREGATE_JAVADOCS_TASK_NAME, type: Javadoc) {
description = 'Aggregates Javadoc API documentation of all subprojects.'
group = JavaBasePlugin.DOCUMENTATION_GROUP
dependsOn librarySubprojects.generateReleaseJavadoc //please note that generateReleaseJavadoc is the name of the separate Javadoc generation task in each library module
source librarySubprojects.generateReleaseJavadoc.source
destinationDir rootProject.file("$rootProject.buildDir/docs/javadoc") //Javadoc destination directory
classpath = rootProject.files(librarySubprojects.generateReleaseJavadoc.classpath)
}
}
}
}
private Set<Project> getLibraryProjects(Project rootProject) {
rootProject.subprojects.findAll { subproject -> subproject.plugins.findPlugin("com.android.library") } //In this case every library module is selected
}
}
Finally, include your plugin to the gradle configuration in the Project's build.gradle below your plugin definition.
apply plugin: JavadocAggregationPlugin
By doing this and rebuilding gradle's configuration you should be able to create aggregated Javadoc in specified directory by typing the following command via cli:
gradle aggregateJavadocs
Hope that helps somehow.
Helpful link: Android Gradle DSL

How to set up unit tests for Android / Gradle

After having read the android guide on testing with the android gradle plugin, I wanted to set up JUnit tests for my POJO's that don't run with the instrumented tests. The idea was that tests for code that doesn't depend on Android should be very fast (and facilitate TDD).
Is there a standard way to set up a source set and task in build.gradle to accomplish this? That is the main question, the secondary question is what's wrong with my attempt below...
I'm using Android Studio 0.4.2 and Gradle 1.9, experimenting with a simple JUnit test class in a new "test" folder. Here is what I have so far, but when I run "gradle testPojo" I get this result:
:android:assemble UP-TO-DATE
:android:compileUnitTestJava UP-TO-DATE
:android:processUnitTestResources UP-TO-DATE
:android:unitTestClasses UP-TO-DATE
:android:testPojo FAILED
* What went wrong:
Execution failed for task ':android:testPojo'.
> failed to read class file
/path/to/project/android-app/build/classes/unitTest/TestClass.class
I verified that the class is in fact there, so I'm confused as to why the task is not able to read the file.
Here is the build.gradle file:
...
sourceSets {
unitTest {
java.srcDir file('src/test/java')
resources.srcDir file('src/test/resources')
}
}
dependencies {
...
unitTestCompile 'junit:junit:4.11'
}
configurations {
unitTestCompile.extendsFrom instrumentTestCompile
unitTestRuntime.extendsFrom instrumentTestRuntime
}
task testPojo(type: Test, dependsOn: assemble){
description = "Run pojo unit tests (located in src/test/java...)."
testClassesDir = sourceSets.unitTest.output.classesDir
android.sourceSets.main.java.srcDirs.each { dir ->
def buildDir = dir.getAbsolutePath().split('/')
buildDir = (buildDir[0..(buildDir.length - 4)] + ['build', 'classes', 'debug']).join('/')
sourceSets.unitTest.compileClasspath += files(buildDir)
sourceSets.unitTest.runtimeClasspath += files(buildDir)
}
classpath = sourceSets.unitTest.runtimeClasspath
}
check.dependsOn testPojo
I'd suggest you to use robolectric, declare it like this:
classpath 'org.robolectric:robolectric-gradle-plugin:0.11.+'
And then apply the plugin:
apply plugin: 'robolectric'

Categories

Resources