36

Is there a reasonably simple way for a module's build.gradle file to indicate that certain files from a dependency should be excluded? I am specifically interested in excluding certain resources from an AAR.


LeakCanary is an interesting library for helping to track down memory leaks. However, it has an undocumented requirement of compileSdkVersion of 21 or higher. While most projects should not have a problem with this, it's unseemly for a library to require a certain compileSdkVersion without a good reason. A development team may have frozen their compileSdkVersion as part of a general policy to only change those sorts of settings as part of major version updates of their app or something.

In this case, for v1.3.1 of LeakCanary at least, the only reason compileSdkVersion is required, AFAICT, is because the AAR has a res/values-v21/ directory, containing a theme definition that inherits from Theme.Material. This theme is used by a diagnostic activity. That activity is never seen by end users, only by developers in debug builds. Frankly, what that activity looks like, theme-wise, does not really matter. Forcing a compileSdkVersion of 21 just to have that diagnostic activity have a certain theme is, IMHO, stupid.

It'd be nice if as part of a compile directive we could say "hey, please skip res/values-v21/ from this AAR, m'kay?". Since the -v21 theme is simply providing an alternative definition of a theme defined elsewhere, dropping the -v21 theme will not break the build or break things at runtime, but merely will give us a Holo-themed diagnostic activity.

I fail to see how this answer works with dependencies. I am also uncertain if it is complete, and it certainly does not appear to be supported. It also doesn't really qualify as "simple" — I would not expect somebody to try dropping this in a build.gradle file just to block a single file from a diagnostic library like LeakCanary.

So, is there something simpler than this that works with now-current editions of the Android Plugin for Gradle?

3
  • I think, the workaround for LeakCanary (github.com/square/leakcanary) would be to fork it and compile your own version with proper compileSdkVersion. I'm not sure if it can be counted as an answer to the question though. Commented Dec 26, 2015 at 9:19
  • @KonstantinLoginov: LeakCanary appears to have a fair number of interconnected moving parts, which is why I am skeptical that a fork will be all that easy. I did enough poking around to determine that the only Android 5.0+ feature was Theme.Material, which is what led me to ask this question. While I am framing the question in the context of LeakCanary, the issue transcends that one library. Commented Dec 26, 2015 at 12:15
  • It would be nice if google's current gradle plugin source was public. Tags for the DSL's source [1] are way behind the release tags [2]. [1]android.googlesource.com/platform/tools/gradle/+refs [2]jcenter.bintray.com/com/android/tools/build/gradle Commented Dec 30, 2015 at 15:32

3 Answers 3

21
+75

EDIT:

Wrote advanced gradle task for you:

final List<String> exclusions = []; Dependency.metaClass.exclude = { String[] currentExclusions -> currentExclusions.each { exclusions.add("${getGroup()}/${getName()}/${getVersion()}/${it}") } return thisObject } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' compile ('com.android.support:appcompat-v7:20.+') debugCompile ('com.squareup.leakcanary:leakcanary-android:1.3.1') .exclude("res/values-v21/values-v21.xml") releaseCompile ('com.squareup.leakcanary:leakcanary-android-no-op:1.3.1') } tasks.create("excludeTask") << { exclusions.each { File file = file("${buildDir}/intermediates/exploded-aar/${it}") println("Excluding file " + file) if (file.exists()) { file.delete(); } } } tasks.whenTaskAdded({ if (it.name.matches(/^process.*Resources$/)) { it.dependsOn excludeTask } }) 

Now you can use method .exclude() on each dependency, providing into list of paths, you want to exclude from specified dependency. Also, you can stack the .exclude() method calls.

Sign up to request clarification or add additional context in comments.

7 Comments

I think your LEAKCANARY_ARTIFACT_NOOP definition has a bug. And while this is interesting, it does not answer the actual question: "Is there a reasonably simple way for a module's build.gradle file to indicate that certain files from a dependency should be excluded?" It addresses my example scenario in an alternative fashion (modifying resources). Thanks, though!
I don't think that there's any way to do what you want out of the box. But you can write your own task, like this, that will be executed before resource merging and will delete resources that you don't need. All resources of each aar module are stored in build/exploded-aar directory, and you can safely remove them from there and they would not be processed at all. However, about simplicity: you can write your own wrapper for "compile" closure which can take array of files/dirs that will be excluded in task. I will try to make this tomorrow.
Edited the answer. Now excluding is much simplier, i think.
This isn't a complete solution, from the perspective that if you put this in a library module it doesn't prevent the entire AAR file from being included in modules that depend on it. I'm not sure what the solution to that would be.
Too bad it no longer works in 2020 with gradle 4.0.1
|
5

I believe you can solve this problem more elegantly using the PackagingOptions facility of the Android Gradle Plugin DSL.

I was able to use this myself to exclude some native libraries I didn't need introduced by an AAR in my project.

android { ... packagingOptions { exclude '/lib/armeabi-v7a/<file_to_exclude>' } } 

For the case outlined in the question, I believe this would work:

android { ... packagingOptions { exclude '/res/values-v21/<file_to_exclude>' } } 

3 Comments

I'll give it a try sometime, but I am skeptical that this will work. First, pacakgingOptions is much too late in the build process -- the build should fail due to the compileSdkVersion before it gets there. Second, AFAIK, packagingOptions is referring to excluding things based on where they are in the APK, and XML resources do not go in the APK as ordinary files. Thanks, though!
any chance to do it dynamically? stackoverflow.com/questions/46543271/…
packagingOptions was what I needed to exclude specific files from the classes.jar of an aar package
4

Try compileOnly keyword to mark the resource is used for compile only.

dependencies { compileOnly fileTree(include: ['*.jar'], dir: 'libs') } 

1 Comment

This tip is dangerous, this also excludes the class files. That will require that those classes are already on the device. So if you get some random, ClassNotFound exceptions this is the reason

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.