Android studio / Gradle javadoc task - android

I've been struggling to setup a gradle task to generate Javadocs for my Android library, but when there are external dependencies to other libraries, doc generation fails. This seems to be a common task, but somehow there doesn't seem to be an easy solution, as for example this answer will reveal (re-generating exploded-aar manually is a bit absurd, and also, on Android Studio 3.0 even that doesn't work anymore due to the new dependency directives).
However, I have noticed that generating Javadoc through the Android Studio GUI (Tools menu) works just fine - dependencies to other libraries are resolved etc. So how does this work - does this menu not utilize a gradle task for generating Javadoc?
Since I need to generate Javadoc using gradle as part of CI I find it very frustrating that there is no documented way of getting it to work, while there is a way that works through the menues. Doesn't the Android Studio Tools -> Generate Javadoc menu in turn use a gradle task? Since dependencies are listed with gradle files, and the Javadoc tools menu apparently is able to resolve those dependencies - how is it implemented? How does it source the jars embedded in the dependant aar libraries, etc? How can it be used stand-alone and not though the Android Studio GUI?

Maybe you have got the solution to this. Just in case not, below is how I generate API doc for my Jenkins CI.
task generateApiDoc() {
group "reporting"
description "Generates Javadoc."
}
android.libraryVariants.all { variant ->
// Only consider release
if (variant.buildType.name == "release") {
def task = project.tasks.create("generate${variant.name.capitalize()}Javadoc", Javadoc) {
group "ApiDoc"
description "Generates Javadoc for $variant.name."
// Source files from the variant
source = variant.javaCompiler.source
// Classpath from the variant + android.jar
classpath = variant.javaCompiler.classpath + files(prj.android.getBootClasspath()) + files("$buildDir/intermediates/classes/release")
/* add the excluded packages */
exclude "**/R**"
exclude "**/BuildConfig*"
options.windowTitle = "My Library"
options.memberLevel = JavadocMemberLevel.PROTECTED
options.linkSource false
options.author = true
//options.links("http://docs.oracle.com/javase/7/docs/api/", "http://d.android.com/reference");
failOnError false
}
task.dependsOn assemble
generateApiDoc.dependsOn task
}
}
Then run below gradle commands to get your api doc in place of "$buildDir/docs".
./gradlew assembleRelease
./gradlew generateApiDoc
Edit for Gradle Plugin 3.4.1
android.libraryVariants.all { variant ->
def task = project.tasks.create("generate${variant.name.capitalize()}Javadoc", Javadoc) {
title "API Documentation (${project.android.defaultConfig.versionName})"
group "ApiDoc"
description "Generates Javadoc for $variant.name."
// Source files from the variant
source = variant.sourceSets.collect { it.java.sourceFiles }.inject { m, i -> m + i }
// To fix issue: Error: Can not create variant 'android-lint' after configuration ': library: debugRuntimeElements' has been resolved
doFirst {
classpath = project.files(variant.javaCompileProvider.get().classpath.files,
project.android.getBootClasspath())
}
if (JavaVersion.current().isJava8Compatible()) {
options.addStringOption('Xdoclint:none', '-quiet')
}
exclude "**/R"
exclude "**/R.**"
exclude "**/R\$**"
exclude "**/BuildConfig*"
if (JavaVersion.current().isJava8Compatible()) {
options.addStringOption('Xdoclint:none', '-quiet')
}
options.windowTitle = "API Documentation (${project.android.defaultConfig.versionName})"
options.memberLevel = JavadocMemberLevel.PROTECTED
options.linkSource false
options.author = false
failOnError true
}
task.dependsOn "assemble${variant.name.capitalize()}"
generateApiDoc.dependsOn task
}

I use a gradle task that just executes a bash script file, with a single (pretty long) javadoc command.
What you can do is run the Javadoc generation from Android Studio once, then copy the executed javadoc command from the Studio log, with all the right parameters, and automate the execution of the same command in your gradle.

The tool to generate java style documentation is called javadoc and it comes installed in every JDK. You can configure which classes or packages you want to be included, which ones should be excluded and many other options. Type javadoc in a terminal where a JDK is available and you'll get an idea. See also https://docs.oracle.com/javase/9/javadoc/javadoc.htm#JSJAV-GUID-7A344353-3BBF-45C4-8B28-15025DDCC643
After you get to your optimal configuration, you can include a javadoc step in your CI.

Related

How can a debug flavor variant depend on a release code

I'm publishing a multi-module Android library. The use case is that I want to run a sample app (in debug) against the release AARs (that are proguarded) of my library. The ultimate goal is to be able to test the exact artifacts what would be published.
My modules and their desired build types are:
:my-library (release)
:mock-server (debug)
:sample (debug)
:sample depends on :my-library and :mock-server. :mock-server also depends on :my-library.
To achieve this, I set up a flavor dimension:
android {
flavorDimensions "SOURCE_OR_BINARY"
productFlavors {
source {
// indicates that project dependencies are used directly
}
binary {
// dependencies added via here will be AARs
}
}
dependencies{
binaryImplementation(name: ':my-library', ext: 'aar')
sourceImplementation project(':my-library')
implementation project(':mock-server')
}
task prepareBinaryDependencies(dependsOn: [
":my-library:assembleRelease" // to generate AARs
]) {}
//
tasks.whenTaskAdded { task ->
def taskName = task.name.toLowerCase()
if (taskName.toLowerCase().contains("binary")) {
// Prepare libs as binaries
task.dependsOn prepareBinaryDependencies
task.doFirst {
prepareBinaryDependencies
}
}
}
This works fine with ./gradlew on the command line, but Android Studio often reports a Failed to resolve: :my-library-release: during gradle sync. If I do a ./gradlew assemble on the command line, then sync Android Studio, the the AS Gradle sync succeeds.
It seems that I am doing something wrong here. Is there a way to do this better that will avoid the Failed to resolve Android Studio Gradle sync error?

Could not get unknown property 'processReleaseGoogleServices'

I updated to use Android Studio 2.2 and Gradle 2.2.0. And now I have a problem building.
I followed this post https://medium.com/google-cloud/automatic-per-variant-google-services-json-configurations-with-gradle-d3d3e40abc0e#.g1p7c1tx2 to configure two "google-services.json" files to be used for dev vs prod builds and use the following method in my app/build.gradle file to toggle between the copying the two "google-services.json" files.
afterEvaluate {
processDebugGoogleServices.dependsOn switchToDebug
processReleaseGoogleServices.dependsOn switchToRelease
}
task switchToDebug(type: Copy) {
description = 'Switches to DEBUG google-services.json'
from "src/gcm-dev"
include "google-services.json"
into "."
}
task switchToRelease(type: Copy) {
description = 'Switches to RELEASE google-services.json'
from "src/gcm-prod"
include "google-services.json"
into "."
}
Gradle complies fine but when I click on the "Run app" (triangle "play" icon) or "Debug app" (triangle "play" icon with a bug behind) buttons in Android Studio, I get the following:
* What went wrong:
A problem occurred configuring project ':app'.
> Could not get unknown property 'processReleaseGoogleServices' for object of type com.android.build.gradle.AppExtension.
Please help, much appreciated.
I had the same issue and problem was in enabled instant run.Try to disable it and run again.
You should update Google Play Services gradle plugin as well, follow the documentation to set it up: https://developers.google.com/android/guides/google-services-plugin
The great thing is that you no longer need to write gradle tasks which create appropriate google-services.json files in your root directory. Build type specific google-services.json are now supported by the plugin:
"As of version 2.2.0 the plugin supports build type and product flavor
specific JSON files. All of the following directory structures are
valid"
An alternative way to this is to refer to the task in the following way:
tasks.whenTaskAdded { task ->
if (task.name == 'assembleDebug') {
task.dependsOn 'switchToDebug'
} else if (task.name == 'assembleRelease') {
task.dependsOn 'switchToRelease'
}
}
UPDATE
The problem you mentioned in comment is related to your google-services.json file. You need to place google-services.json into app/ dir. And for each build type there should be accordant director in app/src folder.
If file already exists check if correct package name inside it
"client_info": {
"mobilesdk_app_id": "1:6596814400689:android:65d6f25f5006145",
"android_client_info": {
"package_name": "com.my.app.package.name"
}
As described by #Singed, add a directory pr build type/flavor under src-directory and the corresponding google-services.jsonand Google Play gradle plugin will take care of the rest, e.g.:
src/
debug/google-services.json
release/google-services.json
During build the correct file will be processed, ending up in build/generated/res/google-services/debug|release/values/values.xml

Gradle multi-project dependency doesn't include

I'm trying to set up a Gradle build to build and include an NDK static library into an Android project, but can't get the inclusion to work. I'm running Gradle 2.8 on a Linux system directly from the command line (AndroidStudio, Android SDK, and Android NDK are installed, but I'm not using them directly in the sample below). I've boiled down my issue to the following purely Gradle-based sample, and was hoping someone more versed in Gradle could show me the way.
Let's say I have the following directory structure:
gradle_test
mySubLibs
common.gradle
mySubA
build.gradle
settings.gradle
myProjA
common.gradle
Projects
Android
build.gradle
settings.gradle
My library sub projects are under the mySubLibs directory. I'll have gradle_test be the root project directory to get around the fact that Gradle sucks with dealing with arbitrary paths (another gripe for another time).
First, I set up my library to build with "assembleRelease" and "clean" tasks. In reality it will call to command-line compilers to work, but for testing Gradle this simplification will do.
gradle_test/mySubLibs/common.gradle
buildscript {
}
task NDKBuildReleaseLib(type: Exec) {
println "Running NDKBuildReleaseLib for " + rootProject.name
commandLine 'touch', 'obj/local/myOutput.a'
}
task NDKBuildCleanLib(type: Exec) {
println "Running NDKBuildCleanLib for " + rootProject.name
commandLine 'rm', '../libs/myOutput.a', 'obj/local/myOutput.a'
}
task copyLibs(type:Copy) {
from 'obj/local'
into '../libs'
include '*.a'
outputs.upToDateWhen {false}
}
NDKBuildReleaseLib.finalizedBy copyLib
gradle_test/mySubLibs/mySubA/build.gradle
apply from: '../common.gradle'
configurations.create('default')
allprojects {
task assembleRelease(dependsOn: NDKBuildReleaseLib) << {}
task clean(dependsOn: NDKBuildCleanLib) << {}
}
gradle_test/mySubLibs/mySubA/settings.gradle
rootProject.name = "mySubA"
include ':'
This works fine. I can call "gradle assembleRelease" from the mySubA directory, and it builds my library file (the build being simulated by running "touch" to create a file). Now let's create a similar setup for the main project.
gradle_test/myProjA/common.gradle
buildscript {
}
task NDKBuildRelease(type: Exec) {
println "Running NDKBuildRelease for " + rootProject.name
commandLine 'touch', 'obj/local/myProjOutput.a'
}
task NDKBuildClean(type: Exec) {
println "Running NDKBuildClean for " + rootProject.name
commandLine 'rm', 'obj/local/myProjOutput.a'
}
gradle_test/myProjA/Projects/Android/build.gradle
apply from: '../../common.gradle'
configurations.create('default')
dependencies {
// default project(':mySubLibs:mySubA')
}
allprojects {
task assembleRelease(dependsOn: NDKBuildRelease) << {
println "Assemble Release for " + project.name
}
task clean(dependsOn: NDKBuildClean) << {
println "Clean for " + project.name
}
}
gradle_test/myProjA/Projects/Android/settings.gradle
rootProject.projectDir = new File(settingsDir, '../../..')
rootProject.name = "myTestBase"
include ':'
include 'mySubLibs:mySubA'
include 'myProjA:Projects:Android'
This mostly works, except that it is not causing the sub project to build. So my questions are:
How do I get the sub-project to build? If I uncomment the line above in the "dependencies" section, I get an undefined token error. Almost all samples dealing with dependencies use "compile" instead of "default" there, but since this example doesn't have a compile configuration created by the java plugin inclusion, I can't do that (nor did it work when I tried that). I know the sub project is being evaluated from "gradle --info", but it doesn't run.
How would I specify an arbitrary task to build the sub-project? In the above examples, I've set both up to build off a task named "assembleRelease". How could I call "assembleRelease" to build the project, but have a task called "buildMySubLib" called to build the library project?
The end goal is to have multiple library projects called to each build their own static library and copy it to a known common location, then for the main project to include all the static lib files from that common location in its own compile and link stage. It seems silly to have to set up a task for each library in the main project that uses an Exec task to recursively call the command line version of "gradle" for each library project.
Thanks for any insight. So far Gradle is proving to be more hindrance than helpful in trying to set up what should actually be a pretty common build scenario.

How to release one single jar for android?

I want to use the gradle to release Jar for android project. The jar may contain other 3rd jars. I can use the following script to release jar by my source code without 3rd jars:
android.libraryVariants.all { variant ->
def name = variant.buildType.name
if (name.equals(com.android.builder.core.BuilderConstants.DEBUG)) {
return; // Skip debug builds.
}
def task = project.tasks.create "jar${name.capitalize()}", Jar
task.dependsOn variant.javaCompile
task.from variant.javaCompile.destinationDir
artifacts.add('archives', task);
}
Then I found a open source code and it seems can do the release task. Unfortunately, when I add it into my project, the following compile error occurred.
The 'java' plugin has been applied, but it is not compatible with the Android plugins.
PS: I also need merge shared library into the jar if possible.

Android library - how to assemble jar with dependencies using gradle?

I am trying to migrate my android library project from ant to gradle and I am totally stuck with including project dependencies in final jar + excluding some other resources (mostly android auto generated classes like BuildConfig, etc) from it.
My ant jar task looks like this:
<jar destfile="${dist}/lib/LIB_NAME.jar"
basedir="${build.dir}/classes"
includes="com/**"
excludes="**/R.class, **/R$*.class, **/Manifest*.class">
<manifest>
<attribute name="Main-Class" value="PACKAGE"/>
</manifest>
</jar>
I would like to reach similar effect using gradle + include subset of dependencies in final jar. My current approach is based on Jake Wharton solution, which does not bundle dependencies:
android.libraryVariants.all { variant ->
def name = variant.buildType.name
if (name.equals(com.android.builder.BuilderConstants.DEBUG)) {
return; // Skip debug builds.
}
def task = project.tasks.create "jar${name.capitalize()}", Jar
task.dependsOn variant.javaCompile
task.from variant.javaCompile.destinationDir
artifacts.add('archives', task);
}
Any easy solutions how to modify it to include dependencies + exclude other content? Or maybe there is some more standard way of achieving my goals - I expected it more easy in gradle.

Categories

Resources