My Android application has duplicated log4j2plugins.dat in dependencies, which caused an error when building:
2 files found with path 'META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat'.
Adding a packagingOptions block may help, please refer to https://developer.android.com/reference/tools/gradle-api/7.3/com/android/build/api/dsl/ResourcesPackagingOptions
for more information
The duplicated resources log4j2plugins.dat are important, so I can't just pick one and ignore the rest.
After google around, I found a plug-in shadowjar that maybe help. According to the official documentation, I added the following directives to build.gradle:
buildscript {
repositories {
mavenLocal()
maven { url 'https://maven.aliyun.com/nexus/content/groups/public/' }
google()
gradlePluginPortal()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.3.1'
classpath 'gradle.plugin.com.github.jengelman.gradle.plugins:shadow:7.0.0'
}
}
plugins {
id 'com.github.johnrengelman.shadow' version '7.1.2'
id 'java'
}
shadowJar {
transform(com.github.jengelman.gradle.plugins.shadow.transformers.Log4j2PluginsCacheFileTransformer)
}
But it doesn't work. the same error, nothing merged.
I don't know much about gradle. (I'm familiar with maven.)
Now the questions:
Some simple merge methods can be performed with packagingOptions, such as pickFirst and merge (just concatenize text lines). Can I customize a new merge method to use in packagingOptions? (for example, merging xml, merging plugins.dat, etc.)
The functions of plugin shadowjar seems to overlap with the functions packagingOptions provides, what is their relationship? What if both are specified? any conflict or sequenced processing by order?
In my android application, how to configure and use the shadowjar plugin to merge log4j2plugins.dat? Though there is official documentation, but It's not work in my case, and I'm not sure if the plugin is useful for general purpose or for android apps.
Thanks!
Related
Going through the documentation of Gradle's build configuration I can see several conflicts with my build on Android Studio 2020.3.1 using Gradle 7.0.3.
For example, in the documentation is says that allProjects block for repositories settings used by all modules should be added to the root build.gradle while in fact it only allows me to configure modules repositories in the settings.gradle under dependencyResolutionManagement block.
Another example is applying plugins to application modules, in documentations it says the first line should be:
apply plugin: 'com.android.application'
While the one used in my build is:
plugins {
id 'com.android.application'
}
What I also noticed is that also JitPack uses the same configurations suggested in that documentations page for using a JitPack published library in projects:
Add it in your root build.gradle at the end of repositories:
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
Step 2. Add the dependency
dependencies {
implementation 'com.github.arcm111.ScaleNumberPicker:final:1.0.1'
}
So my question is, are these documentation pages outdated, is Gradle 7 not fully supported yet, or am I looking at the wrong documentation?
in the documentation is says that allProjects block for repositories settings used by all modules should be added to the root build.gradle while in fact it only allows me to configure modules repositories in the settings.gradle under dependencyResolutionManagement block.
You may wish to ask a separate Stack Overflow question with a minimum verifiable example and details of your errors. This GitLab repo, in tag v2.2 contains a multi-module Android Studio project using Gradle 7.0.2 that uses allProjects for configuring repositories.
Another example is applying plugins to application modules, in documentations it says the first line should be... While the one used in my build is
Either syntax works. The plugins approach has been available for a year or so in Android Studio projects. The project that I linked to above uses the older apply plugin syntax. This sample project, by contrast, uses plugins.
are these documentation pages outdated, is Gradle 7 not fully supported yet, or am I looking at the wrong documentation?
None of the above, AFAICT. allProjects and apply plugin still work, at least with Gradle 7.0.2.
when I sync my Android project, I keep seeing the following messages:
Gradle: Download https://s3.amazonaws.com/moat-sdk-builds/com/google/android/gms/play-services-ads-base/maven-metadata.xml
Gradle: Download https://s3.amazonaws.com/moat-sdk-builds/com/google/android/gms/play-services-measurement-base/maven-metadata.xml
Gradle: Download https://s3.amazonaws.com/moat-sdk-builds/com/google/firebase/firebase-iid/maven-metadata.xml
These libraries should be found in google() repo, which is the first one in my settings:
allprojects {
repositories {
google()
jcenter()
// ...
maven { url "https://s3.amazonaws.com/moat-sdk-builds" }
}
}
However, it looks into maven { url "https://s3.amazonaws.com/moat-sdk-builds" } and wastes a lot of time. What's going on here? And is there any way to debug it? Thanks.
You can try to customize dependency resolution behaviour or declare repository filters.
Declaring a repository filter is as easy as this:
allprojects {
repositories {
google()
jcenter()
// ...
maven {
url "https://s3.amazonaws.com/moat-sdk-builds"
content {
// Does only include this group
includeGroup "moat.sdk"
}
}
}
}
There is also the option to exclude groups and enhance for example the build performance.
Take care that "Matching repositories to dependencies is an incubating feature." The API documentation provide more information about filter options.
You can find more information on the specific behaviour you experience below. When it comes to dependency resolution Gradle does inspect repositories in order.
How dependency resolution works
[...]
Given a required dependency, Gradle attempts to resolve the dependency by searching for the module the dependency points at. Each repository is inspected in order. Depending on the type of repository, Gradle looks for metadata files describing the module (.module, .pom or ivy.xml file) or directly for artifact files.
[...]
But as i understand it gradle 'visits' each repository irrespective of whether it has already found the 'correct' artifacts or not.
Once each repository has been inspected for the module, Gradle will choose the 'best' one to use. This is done using the following criteria:
For a dynamic version, a 'higher' concrete version is preferred over a 'lower' version.
Modules declared by a module metadata file (.module, .pom or ivy.xml file) are preferred over modules that have an artifact file only.
Modules from earlier repositories are preferred over modules in later repositories.
When the dependency is declared by a concrete version and a module metadata file is found in a repository, there is no need to continue searching later repositories and the remainder of the process is short-circuited.
[...]
Introduction to Dependency Management - How dependency resolution works
I want to build .AAR with dependencies inside. I was looking for a lot but nothing works. Topics are so old. I realized that resultant .AAR should have classes.jar inside and there are directories with .class files. But I don't know how to automatize this process in gradle.
Android tooling doesn't support it. There's an issue open requesting Google to implement it (feel free to star it to show your support and to help Google to prioritize it).
In the meantime, there are two plugins to try to fix or workaround this lack of support, with limited support of different functionalities:
https://github.com/adwiv/android-fat-aar Old, no longer supported.
https://github.com/Mobbeel/fataar-gradle-plugin Still active, but lagging behind AGP versions (currently working on supporting 3.2 and 3.3... while we're already at 3.4 stable)
So, as you can see the future doesn't look bright.
See also this article for more information.
We manage to do this using this Mobbeel fat AAR Gradle plugin:
https://github.com/Mobbeel/fataar-gradle-plugin
buildscript {
repositories {
//...
maven {
url 'https://plugins.gradle.org/m2/'
}
}
}
//...
dependencies {
classpath 'gradle.plugin.com.mobbeel.plugin:mobbeel-fataar:1.2.0'
}
Mark dependencies with api instead of implementation
apply plugin: 'com.mobbeel.plugin'
dependencies {
api 'org.greenrobot:eventbus:3.0.0'
//...
}
fatAARConfig {
includeAllInnerDependencies false
}
This article was helpful: http://wittchen.io/2018/10/02/creating-fat-aar/
I am building android library project, which has a dependency on another internal library project.
I am wondering if there is a way to package a single AAR library, which already contains internal library inside it. I would like to share only 1 AAR library package to my application developers.
This is how my build.gradle files look currently, but currently they produce separate AAR files and both needs to be included in Application's build.gradle. As application is being built by another company, we need to share the final AAR file with them and not the complete library projects.
----- internalLib -------->>>>>>>>>>
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:0.7.+'
}
}
apply plugin: 'android-library'
repositories {
mavenCentral()
}
android {
compileSdkVersion 18
buildToolsVersion '18.1.1'
}
dependencies {
compile 'com.android.support:support-v4:18.0.0'
}
----- externalLib --------
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:0.7.+'
}
}
apply plugin: 'android-library'
repositories {
mavenCentral()
}
android {
compileSdkVersion 18
buildToolsVersion '18.1.1'
}
dependencies {
compile 'com.android.support:support-v4:18.0.0'
compile project(':internalLib')
}
There is no mechanism to combine library. It's a bit complicated as you probably want to control which dependencies get merged (for instance you probably don't want to include support-v4 in there). Also you'd need to merge the resources and Android manifest.
At this time there's no way to easily hack something, unless you are sure the resources have no conflicts between the two res folders (for instance you could have strings_a.xml in one lib and strings_b.xml in the other lib). This way you can just "merge" the two res folders by copying them both into the same location (as opposed to do a merge at the android res level).
For the Manifest it'd be more complicated, but doable with some custom code.
Providing a built-in mechanism for this is very low on our priority so don't expect it anytime soon.
For the sake you have to upload each library as separately on maven and use its implementation in parent library modules till the main library module. Only then when you publish your main library on maven will include your all child dependencies.
As far as we have only one option add aar as api dependency inside the module.
For that we have to generate aar file and publish it to Maven and make it accessible by another module and consume it in app.
https://developer.android.com/studio/projects/android-library
As mentioned above android developer document.
The library module with source code is copied to your project, so you can actually edit the library code. If you want to maintain a single version of the library code, then this is probably not what you want and you should instead add the compiled AAR file as described above.
If there anything else we can do, please let us know by jot down in the command section.
It is not supported
It is not recommended to include one library into another because it leads to a serious issues with managing versions and complexity of creating and supporting such solution.
You should stick to native approaches like dependency manager or rearchitect your codebase
[iOS Umbrella framework]
I already saw this question, but it is not helping me. First of all, I tried to add google play services in my project using:
dependencies{
compile 'com.google.android.gms:play-services:6.5.87'
}
It was showing me error:
Then I updated my studio to 1.0.1 and gradle to 1.0.0. And then I again synced the project with gradle. And it worked! It showed me another option despite of two options shown in above screenshot. It was "Install the library"(something like that). I clicked it and it popped up a dialog, and I installed the library(it was like downloadind using SDK manager and not like gradle downloads).
Now, I tried to download this library using:
compile('com.fortysevendeg.swipelistview:swipelistview:1.0-SNAPSHOT#aar') {
transitive = true
}
And it gives me error:
My android repository is updated:
Also, my internet connection is working fine. I tried to sync project many times, but same error all the time. I am not running gradle in offline mode:
How to fix this? And what is the permanent solution? And why is all this happening?
I found this question: Studio failed to download library from gradle repository which describes the exact same error, and that question had this bit of build script that you need to add to the build file that has the dependency statement in question:
repositories {
maven { url 'http://clinker.47deg.com/nexus/content/groups/public' }
}
When I do this, it works for me.
As to the general question of why this happens (and the better question of why the solution is different for different libraries):
Gradle, the build system that Android Studio uses, has the ability to automatically download library dependencies from the Internet. By and large this is a big boon for developers, because instead of having to manually download archive files, put them in the right place in your project, check them into source control, and repeat the process for new versions, now you just have to add a line of build script and the build system takes care of the housekeeping for you. The major downsides are Internet connectivity woes, which affect different developers to different degrees, and some added confusion about what it means when you get an error.
How does Gradle know where to download dependencies? Most Gradle build scripts contain a block that looks like this:
repositories {
jcenter()
}
or it may be mavenCentral() instead of jcenter(). This tells the build system to look in the JCenter or Maven Central global repositories (and JCenter is in a simplistic way of thinking about it a value-added mirror of MavenCentral); these contain archives of many versions of many, many, many libraries and are very convenient to use.
You can specify other repositories as well. This swipelistview library hasn't been uploaded to Maven Central, so the developer has made a repository for it available via a URL: if you add that URL to your repositories block, it will look for it there.
I was worried about the fact that you're accessing a SNAPSHOT version of the library -- these are supposed to be unpublished by definition. But adding a dependency on the snapshot version of the library in my test project worked for me, and looking around that URL in a web browser reveals that there's only a "1.0-" (trailing dash included) version of the library, so there's some subtletly there I'm missing; if you know more, please edit my answer or comment.
In any event, there are a couple caveats to this explanation. Some libraries aren't on Maven Central or on any Internet-accessible archive (at least they're not officially published by Android), but are instead published as part of the Android SDK download and maintained via the SDK manager. The Android support libraries and Google libraries fall under this category. If you get errors about those not being found, you have to fix it via the SDK manager.
How does the build system know to look in the SDK for those, since you didn't tell it via the repositories block? This behavior is hardcoded into the Android Gradle plugin.
The other caveat is that there's a detail that trips up a lot of people, which is that you actually have two repositories blocks, though with the usual Android Studio setup they're often in different files. One is in a buildscript block, which usually lives in the top-level build.gradle file and looks like this:
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
}
}
The other often also lives in the top-level build.gradle, but you can augment it with another block in your module's build.gradle file. The top-level one looks like this:
allprojects {
repositories {
jcenter()
}
}
and a module-level one would look like one of the previous examples in this answer. What do all of these mean?
The buildscript block tells Gradle where to find build system plugins. These are plugins that enhance the functionality of the build system itself but don't say anything about your actual project. In Android projects, the Android Gradle plugin is in this category, and unlike the Android/Google libraries, this one does live on Maven Central. The repositories block (in coordination with the dependencies block, which is not the same as the dependencies block for your project, keep reading) in buildscript tells the build system where to go look for these plugins.
The allprojects block in the top-level build file tells the build system to apply the bit of contained script to all build files in the project. In this example, it's telling it to add a repositories block pointing to JCenter to all subprojects. This is a convenience so you don't have to copy/paste it into multiple build files in your modules.
In your modules, you also have a repositories block, which in conjunction with the allprojects thingy, tells the build system where to go to get library dependencies for your project, as was previously discussed.