Android gradle runtimeClasspath equivalent - android

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

Related

Use Android module runtime classpath in JavaExec gradle task

I have and Android module which contains a Gradle JavaExec task. When running the JavaExec task, I would like it to use the classpath of the module.
The JavaExec task executes a Kotlin main function which uses some 3rd party library (kotlinpoet). But when running the Gradle task, I'm getting a java.lang.ClassNotFoundException due to kotlinpoet library not being included in the classpath.
I've found similar issues in StackOverflow, and tried many variants for the classpath parameter in the myTask, but nothing that worked.
Here's is the build.gradle file:
plugins {
id 'com.android.library'
id 'kotlin-android'
}
apply plugin: 'kotlinx-serialization'
android {
compileSdkVersion 30
defaultConfig {
...
}
buildTypes {
...
}
compileOptions {
...
}
kotlinOptions {
jvmTarget = '1.8'
}
}
task myTask(type: JavaExec) {
classpath += (files('build/tmp/kotlin-classes/debug', "${android.sdkDirectory}/tools/lib/kotlin-stdlib-1.1.3-2.jar", getBuildDir().toString() + "/intermediates/classes/debug"))
main = 'com.foo.app.home.parser.MainKt'
}
tasks.named('build') { dependsOn('configGeneratorTask') }
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "androidx.core:core-ktx:$androidx_core_ktx"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-core:$kotlinx_serialization_version"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlinx_serialization_version"
implementation 'com.squareup:kotlinpoet:1.10.2'
}
You can get the classpath from each variant. This should do the trick:
afterEvaluate { // needed to make sure all android setup is available
task myTask(type: JavaExec) {
// you probably don't even need to set all these paths manually anymore
classpath += files(
'build/tmp/kotlin-classes/debug',
"${android.sdkDirectory}/tools/lib/kotlin-stdlib-1.1.3-2.jar",
getBuildDir().toString() + "/intermediates/classes/debug"
)
android.applicationVariants.each { variant -> // or libraryVariants
// add each variant's compiler classpath onto this classpath
classpath += variant.javaCompileProvider.get().classpath
}
// uncomment the line below if you need the android.jar etc
// classpath += files(android.bootClasspath)
// setting main directly is now deprecated. Set it like this:
mainClass.set('com.foo.app.home.parser.MainKt')
}
}
Also see this answer here: https://stackoverflow.com/a/37268008/3968618

Compile project dependency with build tool version - react native

In react-native project app/build.gradle is defined as follows. The root project uses a build tool version of 26.0.2 while the dependencies has various versions.
dependencies {
...
compile project(':react-native-config')
...
}
Is there a way that I could define the buildToolVersion while defining the dependencies where all gradle files of the project would normalized with a same buildToolVersion?
Somehow I figured out a way. By adding following snippet to the root build.gradle it can be easily done.
subprojects {
...
afterEvaluate { subproject ->
if ((subproject.plugins.hasPlugin('android') ||
subproject.plugins.hasPlugin('android-library'))) {
android {
buildToolsVersion "26.0.2"
}
}
}
}
During build, it change each of android dependency version into the required.

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.

Android Data Binding build.gradle syncing prob?

For databinding
1) I have added
android {
dataBinding {
enabled = true
}
to my project build.gradle, but this error occurs:
Error:(5, 0) Gradle DSL method not found: 'dataBinding()'
Possible causes:
.The project 'exampleDatabinding' may be using a version of Gradle that does
not contain the method.
Gradle settings
.The build file may be missing a Gradle plugin.
Apply Gradle plugin
2) Then I added:
apply plugin: "com.android.databinding" (to project build.gradle)
and classpath "com.android.databinding:dataBinder:1.0-rc1" (to project build.gradle)
But the same error occured.
In the file build.gradle of the project add the dependency
dependencies {
classpath 'com.android.tools.build:gradle:1.5.0-beta2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
And in the file build.gradle of the module include the dataBinding section:
android{
...
dataBinding {
enabled = true
}
...
}
The versions of build.gradle can be found here: Versions
The project 'exampleDatabinding' may be using a version of Gradle that does
not contain the method.
You need to update your gradle to latest version 2.10
To update gradle do as follows
YourProject->gradle->wrapper->gradle-wrapper.properties
update the distributionUrl=https://services.gradle.org/distributions/gradle-2.10-all.
Also add dataBinding:
android{
...
dataBinding {
enabled = true
}
...
}
Also update your classpath:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.1.0'
}
}

Get classpath for gradle project using Android plugin

I've been building some tasks for a gradle multi-project build and have a need to get the class path for a project. The build script has projects that use the Java plugin and projects that use the Android plugin.
For the Java projects I was able to use the top voted answer in this question to get the class path using configurations.runtime.asPath; however, this is not working for the Android projects because there is no configurations.runtime property.
How can generate a classpath for a gradle project using the Android plugin?
Android projects may build multiple versions of the app. These are called variants. The most basic variants are "debug" and "release" The following code should create the classpath assignment for all the variants in a project. Place this code in the "build.gradle" file for the module.
android.applicationVariants.each { variant ->
variant.javaCompile.classpath += configurations.provided
}
You should be able to refer to a specific variant using the variant name:
debug.javaCompile.classpath
Here is a gradle task that generates the module jar and includes also the test classpath for all variants.
It is including libraries and the android.jar from selected runtime.
I've added two commandline executions for updating some env var inside emacs and for killing any running beanshell (with previous classpath).
task classpath(type: Jar) {
from android.sourceSets.main.java.srcDirs,
android.sourceSets.test.java.srcDirs
outputs.upToDateWhen { false }
doLast {
println "Building classpath..."
def cp2 = [android.getBootClasspath()[0], it.archivePath]
android.applicationVariants.all { v ->
cp2 += v.getApkLibraries()
}
def classpath = cp2.unique().join(":")
println "Updating emacs..."
exec {
executable "sh"
args "-c", "emacsclient --eval '(setenv \"CLASSPATH\" \""+classpath+"\")'"
}
exec {
executable "sh"
args "-c", "emacsclient --eval '(jdee-bsh-exit)'"
}
}
}
Be aware that I'm using ":" for joining the classpath
project.android.applicationVariants.all { v ->
v.getCompileClasspath(null).getFiles().each{
File f->
f.getAbsolutePath()//this is the one of classpath
}
}
Here is another example a gradle task that generates javadocs with umlgraph + graphiz in an android project and includes classpath for all variants using the coding example given in the user1737310's previous answer. It is manually including android.jar from the selected runtime, I am still looking for a way to retrieve it dynamically.
task javadoc(dependsOn: build) {
setDescription('Generates Javadoc API documentation with UMLGraph diagrams')
setGroup(JavaBasePlugin.DOCUMENTATION_GROUP)
doLast {
def javaFilePath = file('src/main/java')
def cp = [System.getenv('ANDROID_HOME')+'/platforms/android-26/android.jar'];
project.android.applicationVariants.all { v ->
v.getCompileClasspath(null).getFiles().each{
File f->
cp.add(f.getAbsolutePath())//this is the one of classpath
}
}
def classpath = ":"+cp.join(':')
if (javaFilePath.exists()) {
ant.javadoc(classpath: (configurations.umljavadoc).asPath + classpath,
sourcepath: file('src/main/java'),
packagenames: '*',
destdir: "${docsDir}/javadoc",
private: 'false',
docletpath: configurations.umljavadoc.asPath) {
doclet(name: 'org.umlgraph.doclet.UmlGraphDoc') {
param(name: '-inferrel')
param(name: '-inferdep')
param(name: '-qualify')
param(name: '-postfixpackage')
param(name: '-hide', value: 'java.*')
param(name: '-collpackages', value: 'java.util.*')
param(name: '-nodefontsize', value: '9')
param(name: '-nodefontpackagesize', value: '7')
param(name: '-link', value: 'http://java.sun.com/j2se/1.5.0/docs/guide/javadoc/doclet/spec')
param(name: '-link', value: 'http://java.sun.com/j2se/1.5/docs/api')
}
}
}
}
}
`

Categories

Resources