Retrieve cached transformed AAR dependency classes.jar - android

Context
I'm trying to implement Dokka in a project using the it's fat-jar. To be able to generate documentation. I need the classpath of the dependencies that the project uses. Dokka accepts jars as classpath to be able to generate documentation. I using Groovy to generate a Gradle task to be able to extract the dependencies for each subproject
Problem
Since the project uses AAR dependencies, I need the classes.jar of those files.
What I came up so far
I'm able to retrieve the AAR directory in the .gradle cache directory, however that points to, as I said, the AAR. I don't want to unzip the file to be able to extract the classes.jar in it, since that will be very performance-heavy.
def classpaths = []
subprojects.each { project ->
classpaths.addAll(project.android.getBootClasspath().collect { it.path })
project.configurations.each { configuration ->
try {
classpaths.addAll(configuration.files.collect { it.path })
} catch (Exception ignored) {
}
}
}
This code will retrieve most of the AARs. But it doesn't seem to be printing the dependencies (e.g. Glide)
Question
How do I get the transformed directory of the dependencies or their classes.jar path for all the dependencies the project uses? I need to be able to get the path dynamically.
Example of the directory I need: /Users/user/.gradle/caches/transforms-1/files-1.1/glide-4.8.0.aar/efaa2fb5a51ab0ecf0ac00dd6a8d019b/jars/classes.jar

Related

Find cached files of unpacked aar files in gradle script

I have an android project where a library is included (aar). Gradle will unpack and store the files in the gradle cache (.gradle\caches\transforms-2). I need a gradle script where I can retrieve a file from this cache and move it into my project. Does anybody know how this can be done?
The moving part is working. I just need to get the correct path
tasks.register('moveFile', Copy) {
from "${path_to_build_cache}/path/to/folder/file.conf"
into "${project.rootDir.path}/path/to/folder/"
}
EDIT
I have now tried the solution posted by #tim_yates. The problem is that I am now receiving the error CustomMessageMissingMethodException: Could not find method from() for arguments [ZIP 'C:\Users\nthemmer\.gradle\caches\modules-2\files-2.1\path\to\aar on task ':my-project:movefiles' of type org.gradle.api.DefaultTask It seems that the aar file is read correctly but it has not only one file.
Instead of searching the cache, you should be able to use Gradle to find it for you...
Here's an example:
configurations {
aar
}
dependencies {
aar('com.mikhaellopez:circularimageview:4.3.0#aar')
}
tasks.register('moveFile', Copy) {
from(zipTree(configurations.aar.singleFile)) {
include "res/values/values.xml"
}
into project.layout.buildDirectory.dir('here')
}
That copies the file into ./build/here/res/values/values.xml
Edit
So there's probably multiple ways of doing this, but here's one.
Define a configuration that we will use for the single dependency you want a file from, and make compileClasspath extend from it (so the dependency ends up back in the compile classpath where it was previously)
configurations {
aar
compileClasspath.extendsFrom aar
}
Then in the dependencies where you reference the aar, you should be able to use aar instead of compileClasspath
dependencies {
aar('com.mikhaellopez:circularimageview:4.3.0#aar')
}
Then you can use the moveFile task from above, and there will just be a single file
Not 100% sure what you have currently, so not sure how this fits in, but it should give you a good direction.
Here's the full build file which runs on Gradle 7.2 and uses the circularimageview arr off maven central as the test subject
plugins {
id('java')
}
repositories {
mavenCentral()
}
configurations {
aar
compileClasspath.extendsFrom aar
}
dependencies {
aar('com.mikhaellopez:circularimageview:4.3.0#aar')
}
tasks.register('moveFile', Copy) {
from(zipTree(configurations.aar.singleFile)) {
include "res/values/values.xml"
}
into project.layout.buildDirectory.dir('here')
}

Module library Jar not shown in External Libraries in Android Studio

I am having trouble getting a jar library to show up under External Libraries in Android Studio. I am trying to add javadoc for this library, and the only method I've found online is to right click on the library in External Libraries and select Library Properties....
The project structure is a tree of many modules:
rootsdk /
main.jar
main-javadoc.jar
plugins /
plugin1 /
build.gradle
...
plugin2 /
build.gradle
...
...
The dependency is declared in the build.gradle files like:
compileOnly files('../../main.jar')
If I open up the individual directories plugin1, then the dependency shows up in External Libraries correctly. But if I open up the rootsdk project, it does not appear. All of the modules are listed and compilable from the root project, and I can use classes defined in the library just fine, but it does not appear under External Libraries, so I cannot add the javadoc for it.
The strange thing is some of the plugins use other libraries, but defined differently:
repositories {
flatDir {
dirs 'libs'
}
}
...
implementation(name: 'core-debug', ext: 'aar')
And these libraries show up under External Libraries as expected.
Is there something missing to force main.jar to show up under External Libraries, or is this a bug in AS?
It's a silly thing, but if you make it an Ivy or Maven repository, it should work. files don't. Both of the below solutions should support -sources and -javadoc suffixes. I think IDEA only implemented artifact resolution from repositories, and didn't think about direct file references.
Ivy
repositories {
def repoRoot = file(rootProject.projectDir)
ivy {
name = "local libs"
url = repoRoot.toURI()
patternLayout {
artifact("[module](-[classifier]).[ext]")
}
metadataSources {
artifact()
}
}
}
dependencies {
// `local` group and version `0` are just a hack so Gradle dependency notation can be used.
implementation("local:main:0")
implementation("local:core-debug:0#aar")
}
Maven
You can do something similar to above, but the repo structure is less flexible. You need to move the .jar/.aar files around. I recommend creating a folder for them (even if there's one right now). In the example I called it libs.
repositories {
def repoRoot = file(rootProject.projectDir.resolve("libs"))
exclusiveContent {
// Work around
// > Could not GET 'https://repo.gradle.org/gradle/libs-releases-local/local/main/0/main-0.pom'. Received status code 409 from server: Conflict
// by not allowing Gradle to contact other repositories for "local" files.
filter {
includeGroup("local")
}
forRepository {
maven {
name = "local libs"
url = repoRoot.toURI()
metadataSources {
artifact()
}
}
}
}
}
dependencies {
// `local` group and version `0` are just a hack so Gradle dependency notation can be used.
implementation("local:main:0")
implementation("local:core-debug:0#aar")
}
The error message will tell you where to put it, for example:
* What went wrong:
Execution failed for task ':...'.
> Could not resolve all files for configuration ':...'.
> Could not find local:main:0.
Searched in the following locations:
- file:/.../libs/local/main/0/main-0.jar
Required by:
project :...
> Could not find local:core-debug:0.
Searched in the following locations:
- file:/.../libs/local/core-debug/0/core-debug-0.aar
Required by:
project :...
Note that while this is more complex, it's also more flexible, because you can hand-write or download POM files and therefor include transitive dependencies for the local JAR files, just remove the metadataSources block if you have POM XML files for your artifacts.
metadataSources
This magic is worth a mention (docs):
metadataSources {
artifact()
}
it tells Gradle, that there's no metadata (POM or Ivy XML file) associated with the artifacts in the repository, that there's only artifacts exists. Without this, it would fail by looking for metadata.

How to develop a library and an application side-by-side in Android Studio?

I'm currently developing both a library (with no activities) and an application that depends on the library. Currently, I have these as separate projects, and I can copy the generated .aar file from the library project into the application project's libs folder, and re-sync gradle. However, this is an inefficient process because I have to rebuild and manually re-copy the .aar file every time I make a change to the library project. My question is, how can I streamline this process so that my application automatically uses the library's most recently generated .aar file?
1) In your app's settings.gradle include your lib as a project:
include ':lib-project'
project(':lib-project').projectDir = new File('../path/to/lib/project/lib-project')
The path to your lib project is relative to the settings.gradle location on your filesystem
2) in your app's build.gradle add lib project as a dependency:
dependencies {
compile project(':lib-project')
...
}
how can I streamline this process so that my application automatically uses the library's most recently generated .aar file?
Option #1: Dedicated Library
Step #1: Put your app project and the library project as children of a common root directory for the overall project. For the purposes of this answer, I'll call these app/ and library/, respectively.
Step #2: In the top level (i.e., the common root directory), have a settings.gradle file that lists these modules:
include ':app', ':library'
Step #3: In the top level, have a build.gradle file that sets up the Gradle for Android plugin and any other common stuff of interest, such as:
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.0.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
jcenter()
}
}
(note that the above file is what you get from a native Android Studio project, created by the IDE)
Step #3: In the library/ directory, have a build.gradle file that uses the com.android.library plugin
Step #4: In the app/ directory, have a build.gradle file that has compile project(':library') in its dependencies to pull in the library
It may be that your AAR is the deliverable, not the app (e.g., the library is an open source one for community use, and the app is a demo app). In that case, you might use debugCompile in app/ to pull in the local library project for debug builds, but have releaseCompile to pull in the AAR from a published source, to confirm that you can build from the same thing that users of the AAR use.
Most of my CWAC libraries are set up this way (e.g., cwac-richedit).
Option #2: Publish the AAR Locally
You can use the maven plugin and the uploadArchives task to upload to a local Maven-style repo:
apply plugin: 'maven'
uploadArchives {
repositories.mavenDeployer {
pom.groupId = PUBLISH_GROUP_ID
pom.artifactId = PUBLISH_ARTIFACT_ID
pom.version = PUBLISH_VERSION
repository(url: LOCAL_REPO)
}
}
Here, my constants are pulled in from a gradle.properties file, and LOCAL_REPO is a file:/// URL pointing to a local repo. You can then run gradle uploadArchives to generate the AAR and push it to the local repo.
Then, your app can have a maven { url LOCAL_REPO } closure in the repositories closure, and can pull in the AAR artifact from there as if it was coming from a public repo (e.g., Maven Central).
My CWAC libraries use the uploadArchives task, but only for publishing to my local mirror of my Amazon S3-hosted Maven repo.
This approach would be if you really wanted to work off of the AAR, but wanted to do so from multiple projects. Note that you can certainly publish this to some other sort of Maven repo (e.g., a Sonatype server) for enterprise use.
Option #3: Mod a Module to Point to the Library Elsewhere
This is Pavel Dudka's approach in his answer. I haven't tried this. Off the cuff, this would be a good approach if you want to depend upon the library from multiple apps, but you're not really concerned about having an actual AAR as a thing to distribute around.
And I'm sure there are other options than these three.

How to Exclude Duplicate C Shared Libraries (.so) in a Multi-Project Android Build?

I get a "duplicate files" conflict when building a parent project with two library modules, which make use of the same libc++_shared.so shared library.
(NOTE: Please do not consider this a "duplicate question". I have read several related posts, which have helped me get this far. However, no posts have provided an answer that works in my case involving NDK artifacts.)
The build was working correctly when I only had 1 such library module. The addition of the second library module is now creating the conflict.
Consider the following project structure: 1 parent project, 2 "child" projects - but each project is located at the same directory level (i.e. Not nested hierarchically)
ProjectA/ (Parent)
LibraryModuleA1/
build/exploded-aar/com.package.name/
LibraryModuleB1/<version>/jni/armeabi-v7a/libc++_shared.so
LibraryModuleC1/<version>/jni/armeabi-v7a/libc++_shared.so
build.gradle (bgA1)
Test_APK_Module A1T/
build.gradle (bgA1T)
build.gradle (bgPA)
ProjectB/
LibraryModuleB1/ (Uses NDK)
build/lib/armeabi-v7a/libc++_shared.so
build.gradle (bgB1)
build.gradle (bgPB)
ProjectC/
LibraryModuleC1/ (Uses NDK)
build/lib/armeabi-v7a/libc++_shared.so
build.gradle (bgC1)
build.gradle (bgPC)
Library Module A1 depends on both Library Modules B1 & C1.
A1 -> B1
A1 -> C1
Projects B and C both have NDK-based code and build/test correctly. Both depend on the libc++_shared.so shared library.
However, when building Project A, I get the following error during the :LibraryModuleA1:packageDebugTest task:
Error: duplicate files during packaging of APK /ProjectA/LibraryModuleA1/build/apk/LibraryModuleA1-debug-test-unaligned.apk
Path in archive: lib/armeabi-v7a/libc++_shared.so
Origin 1: /ProjectA/LibraryModuleA1/build/exploded-aar/com.package.name/LibraryModuleB1/<version>/jni/armeabi-v7a/libc++_shared.so
Origin 2: /ProjectA/LibraryModuleA1/build/exploded-aar/com.package.name/LibraryModuleC1/<version>/jni/armeabi-v7a/libc++_shared.so
You can ignore those files in your build.gradle:
android {
packagingOptions {
exclude 'lib/armeabi-v7a/libc++_shared.so'
}
}
* What went wrong:
Execution failed for task ':LibraryModuleA1:packageDebugTest'.
> Duplicate files copied in APK lib/armeabi-v7a/libc++_shared.so
File 1: /ProjectA/LibraryModuleA1/build/exploded-aar/com.package.name/LibraryModuleC1/<version>/jni/armeabi-v7a/libc++_shared.so
File 2: /ProjectA/LibraryModuleA1/build/exploded-aar/com.package.name/LibraryModuleC1/<version>/jni/armeabi-v7a/libc++_shared.so
:LibraryModuleA1:packageDebugTest FAILED
What I've Tried So Far
I attempted to add the suggested closure to my build.gradle file, but which build.gradle file do I add it to? I have added the closure to bgA1, bgB1, and bgC1 (one at a time), with no success.
The suggested closure says to use exclude 'lib/armeabi-v7a/libc++_shared.so'. Each "child" library module builds the libc++_shared.so file under the build/lib path. However, I noticed that the parent library module copies the libc++_shared.so file under jni/armeabi-v7a/libc++_shared.so inside the build/exploded-aar directory structure. (See above) Should the closure instead read exclude 'jni/armeabi-v7a/libc++_shared.so (i.e. jni vs. lib)?
Since I am using Gradle plugin 0.9.1, I tried using pickFirst in place of exclude, but that wasn't successful either.
Can someone help determine how I should configure the `packagingOptions' closure for my given case?
Thank you for your help!
I ran into the same problem and had no luck with exclude or pickFirst. So I used a somewhat ugly workaround. The idea is to create a 'native-libs' folder in the build directory of the main project, copy all required *.so files from ndk library projects there and then tell the build system to package those libs in the apk.
In my main project (the app project), I explicitely define the list of modules that contain ndk codes on which I depend
// Ndk stuff. We have to explicitely manage our NDK dependencies
ext.jniProjects = [project(':ndklib1'), project(':ndklib2'), project(':ndklib3')]
apply from: '../depend_ndk.gradle'
And then, 'depend_ndk.gradle' is a gradle external script that contains
// Build helper for projects that depends on a native library with a NDK part
// Define the list of ndk library you depend on in project main file :
// ext.jniProjects = [project(':ndklib1')]
// apply from : 'depend_ndk.gradle'
buildscript {
repositories {
jcenter()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:0.12.+'
}
}
import com.android.build.gradle.tasks.PackageApplication
// As a workaround, we create a new 'native-libs' folder in the current project and
// copy all the .so we depend on into it
def ndkLibsDir = new File(buildDir, 'native-libs')
ndkLibsDir.mkdir()
task copyDependingNativeLibs(type: Copy) {
// Doc for copy http://www.gradle.org/docs/current/dsl/org.gradle.api.tasks.Copy.html
println 'jniProjects ' + jniProjects
jniProjects.each {
from(new File(it.buildDir, 'native-libs')) {
include '**/*.so'
}
}
into ndkLibsDir
}
tasks.withType(PackageApplication) { pkgTask ->
pkgTask.jniFolders = new HashSet<File>()
pkgTask.jniFolders.add(ndkLibsDir)
pkgTask.dependsOn copyDependingNativeLibs
}

How to export library to Jar in Android Studio?

I have downloaded some library sources and would like to export it as a Jar file using
Android Studio. Is there a way to export to jar file using Android studio ?
edit:
The library I want to export as jar is an Android library.
It's called "StandOut" and can be downloaded from GitHub.
https://github.com/pingpongboss/StandOut
It is not possible to export an Android library as a jar file. It is possible, however, to export it as aar file. Aar files being the new binary format for Android libraries. There's info about them in Google I/O, the New Build System video.
First, build the library in Android Studio or from command line issuing gradle build from your library's root directory.
This will result in <yourlibroot>/libs/build/yourlib.aar file.
This aar file is a binary representation of your library and can be added to your project instead of the library as a dependency project.
To add aar file as a dependency you have to publish it to the maven central or to your local maven repository, and then refer the aar file in your project's gradle.build file.
However, this step is a bit convoluted. I've found a good explanation how to do so here:
http://www.flexlabs.org/2013/06/using-local-aar-android-library-packages-in-gradle-builds
I was able to build a library source code to compiled .jar file, using approach from this solution:
https://stackoverflow.com/a/19037807/1002054
Here is the breakdown of what I did:
1. Checkout library repository
In may case it was a Volley library
2. Import library in Android Studio.
I used Android Studio 0.3.7. I've encountered some issues during that step, namely I had to copy gradle folder from new android project before I was able to import Volley library source code, this may vary depending on source code you use.
3. Modify your build.gradle file
// If your module is a library project, this is needed
//to properly recognize 'android-library' plugin
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:0.6.3'
}
}
apply plugin: 'android-library'
android {
compileSdkVersion 17
buildToolsVersion = 17
sourceSets {
main {
// Here is the path to your source code
java {
srcDir 'src'
}
}
}
}
// This is the actual solution, as in https://stackoverflow.com/a/19037807/1002054
task clearJar(type: Delete) {
delete 'build/libs/myCompiledLibrary.jar'
}
task makeJar(type: Copy) {
from('build/bundles/release/')
into('build/libs/')
include('classes.jar')
rename ('classes.jar', 'myCompiledLibrary.jar')
}
makeJar.dependsOn(clearJar, build)
4. Run gradlew makeJar command from your project root.
I my case I had to copy gradlew.bat and gradle files from new android project into my library project root.
You should find your compiled library file myCompiledLibrary.jar in build\libs directory.
I hope someone finds this useful.
Edit:
Caveat
Althought this works, you will encounter duplicate library exception while compiling a project with multiple modules, where more than one module (including application module) depends on the same jar file (eg. modules have own library directory, that is referenced in build.gradle of given module).
In case where you need to use single library in more then one module, I would recommend using this approach:
Android gradle build and the support library
Since Android Studio V1.0 the jar file is available inside the following project link:
debug ver: "your_app"\build\intermediates\bundles\debug\classes.jar
release ver: "your_app"\build\intermediates\bundles\release\classes.jar
The JAR file is created on the build procedure,
In Android Studio GUI it's from Build->Make Project and from CMD line it's "gradlew build".
Include the following into build.gradle:
android.libraryVariants.all { variant ->
task("generate${variant.name}Javadoc", type: Javadoc) {
description "Generates Javadoc for $variant.name."
source = variant.javaCompile.source
ext.androidJar = "${android.plugin.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar"
classpath = files(variant.javaCompile.classpath.files) + files(ext.androidJar)
}
task("javadoc${variant.name}", type: Jar) {
classifier = "javadoc"
description "Bundles Javadoc into a JAR file for $variant.name."
from tasks["generate${variant.name}Javadoc"]
}
task("jar${variant.name}", type: Jar) {
description "Bundles compiled .class files into a JAR file for $variant.name."
dependsOn variant.javaCompile
from variant.javaCompile.destinationDir
exclude '**/R.class', '**/R$*.class', '**/R.html', '**/R.*.html'
}
}
You can then execute gradle with: ./gradlew clean javadocRelease jarRelease which will build you your Jar and also a javadoc jar into the build/libs/ folder.
EDIT: With android gradle tools 1.10.+ getting the android SDK dir is different than before. You have to change the following (thanks Vishal!):
android.sdkDirectory
instead of
android.plugin.sdkDirectory
I was able to export a jar file in Android Studio using this tutorial:
https://www.youtube.com/watch?v=1i4I-Nph-Cw
"How To Export Jar From Android Studio "
I updated my answer to include all the steps for exporting a JAR in Android Studio:
1) Create Android application project, go to app->build.gradle
2) Change the following in this file:
modify apply plugin: 'com.android.application' to apply plugin: 'com.android.library'
remove the following: applicationId, versionCode and versionName
Add the following code:
// Task to delete old jar
task deleteOldJar(type: Delete){
delete 'release/AndroidPlugin2.jar'
}
// task to export contents as jar
task exportJar(type: Copy) {
from ('build/intermediates/bundles/release/')
into ('release/')
include ('classes.jar')
rename('classes.jar', 'AndroidPlugin2.jar')
}
exportJar.dependsOn(deleteOldJar, build)
3) Don't forget to click sync now in this file (top right or use sync button).
4) Click on Gradle tab (usually middle right) and scroll down to exportjar
5) Once you see the build successful message in the run window, using normal file explorer go to exported jar using the path: C:\Users\name\AndroidStudioProjects\ProjectName\app\release
you should see in this directory your jar file.
Good Luck :)
Here's yet another, slightly different answer with a few enhancements.
This code takes the .jar right out of the .aar. Personally, that gives me a bit more confidence that the bits being shipped via .jar are the same as the ones shipped via .aar. This also means that if you're using ProGuard, the output jar will be obfuscated as desired.
I also added a super "makeJar" task, that makes jars for all build variants.
task(makeJar) << {
// Empty. We'll add dependencies for this task below
}
// Generate jar creation tasks for all build variants
android.libraryVariants.all { variant ->
String taskName = "makeJar${variant.name.capitalize()}"
// Create a jar by extracting it from the assembled .aar
// This ensures that products distributed via .aar and .jar exactly the same bits
task (taskName, type: Copy) {
String archiveName = "${project.name}-${variant.name}"
String outputDir = "${buildDir.getPath()}/outputs"
dependsOn "assemble${variant.name.capitalize()}"
from(zipTree("${outputDir}/aar/${archiveName}.aar"))
into("${outputDir}/jar/")
include('classes.jar')
rename ('classes.jar', "${archiveName}-${variant.mergedFlavor.versionName}.jar")
}
makeJar.dependsOn tasks[taskName]
}
For the curious reader, I struggled to determine the correct variables and parameters that the com.android.library plugin uses to name .aar files. I finally found them in the Android Open Source Project here.
We can export a jar file for Android library project without resource files by Android studio. It is also requirement what I met recently.
1. Config your build.gradle file
// Task to delete old jar
task clearJar(type: Delete){
delete 'release/lunademo.jar'
}
// task to export contents as jar
task makeJar(type: Copy) {
from ('build/intermediates/bundles/release/')
into ('build/libs/')
include ('classes.jar')
rename('classes.jar', 'lunademo.jar')
}
makeJar.dependsOn(clearJar, build)
2. Run gradlew makeJar under your project root
You will see your libs under dir as build/libs/ if you are luckily.
============================================================
If you met issue as "Socket timeout exception" on command line as below,
You can follow this steps to open Gradle window in the right part and click "makeJar" on Android studio like this,
Then go to build/libs dir, you will see your jar file.
Hope that it is helpful for u.
Good Luck #.#
Luna

Categories

Resources