24

I'm importing an android library in an application built with gradle, like that:

dependencies {
    compile 'com.example:great-lib:0.1-SNAPSHOT'
}

This library contains only assets, js, css and images to be used in a webview, with a layout like that:

assets/
|-> great.css
|-> great.min.js
|-> great.min.js.map
|-> js/
|   |-> plop.js
|   |-> foo.js
|   ...
|-> img/
|   ...

The js folder contains source files (to be used with source maps). I would like to include it and the .map file for the debug builds, and have only the minified js in release builds, but I can't find a way to do that.

So far I've tried : 

android {
    // this doesn't exclude anything
    packageOptions {
        exclude 'assets/js'
    }
    buildTypes {
        release {
            // this does exclude the js folder, but in both release and debug
            aaptOptions {
                ignoreAssetsPattern "!js"
            }
        }
    }
}

Any idea if what I want is possible to achieve, and if so how?

(I've also thought of publishing two versions of the library (great-lib and great-lib-debug), and have the dependency in debugCompile and releaseCompile, but I'd prefer avoiding that and publishing a single version)

Xavier
  • 1,536
  • 2
  • 16
  • 29

5 Answers5

25

I had success with this approach (updated 2019-5-13 for TaskProvider support; see edit history for older versions):

android {
⋮
    applicationVariants.all { variant ->
        if (variant.buildType.name == 'release') {
            variant.mergeAssetsProvider.configure {
                doLast {
                    delete(fileTree(dir: outputDir, includes: ['**/js', '**/*.js.map']))
                }
            }
        }
    }
⋮
}

This should address the issues with @Xavier's answer:

  • The deletion is done as part of the variant's mergeAssets task so the deletion is reflected in the task's output and up-to-date checking should be unaffected.
  • The paths are calculated without magic strings. You may need to adjust the include patterns in case my example is too permissive.
  • The variant is being selected by the buildType name, which is less problematic than matching the entire variant name (though it is still stringly typed).

Note that this approach also works for res files rather than assets: just replace mergeAssets with mergeResources.

Other answers mentioning packagingOptions and aaptOptions are barking up the wrong tree, as these are scoped to all variants (they are defined in the android scope, not buildType or productFlavor).

Aaron
  • 915
  • 9
  • 21
  • Works like a charm! – Volo Oct 27 '16 at 13:47
  • Could this be done inside variants.outputs.each? And check the output architecture? – Ramon Canales Jan 09 '17 at 15:43
  • 3
    It's worth mentioning that this code goes _inside_ the `buildTypes` section which is inside the `android` section of your app's build.gradle. – Drarok Nov 15 '17 at 13:23
  • Thank you so much! You've saved me a lot of time. – Sergiy Sep 19 '18 at 16:28
  • Thanks @Drarok I wish everyone had as much concern and awareness for clarity as you! – TKoL May 10 '19 at 08:46
  • Trying this in 2019, getting `WARNING: API 'variant.getMergeAssets()' is obsolete and has been replaced with 'variant.getMergeAssetsProvider()'.`. would love an edit that shows the proper newer syntax – TKoL May 10 '19 at 09:06
  • Updated to address deprecation warning (`TaskProvider` support) and to note that the code goes in the `android {}` block (it does *not* need to be in the `buildTypes` block). – Aaron May 13 '19 at 02:04
13

I ended up doing the following:

android.applicationVariants.all { variant ->

  if (variant.name.contains('Release')) {
    // exclude source and sourcemap from release builds
    def noJsSourceTask = task("delete${variant.name}JsSource", type: Delete) {
      delete "${buildDir}/intermediates/assets/${variant.dirName}/js"
      delete "${buildDir}/intermediates/assets/${variant.dirName}/great.min.js.map"
    }
    variant.mergeAssets.finalizedBy noCeJsSourceTask
  }
}

It works ok, but there are a few things I don't really like:

  • I'm touching at the files produced by a task after it is done (the finalizedBy), so it doesn't work well with "up-to-date" checking. But it's only for release builds, I'm doing debug ones more often
  • the path of the files to delete is manually built. I'm not sure if it's generic enough to be reused in other projects as-is
  • I'm selecting the variants based on their name. I would have liked something more structured.
Xavier
  • 1,536
  • 2
  • 16
  • 29
  • 1
    Thanks for sharing your solution! I'm still hoping for a solution which is less cumbersome/fragile. – Divisible by Zero Mar 26 '15 at 11:00
  • 5
    Trying this in 2019, getting `WARNING: API 'variant.getMergeAssets()' is obsolete and has been replaced with 'variant.getMergeAssetsProvider()'.`. would love an edit that shows the proper newer syntax – TKoL May 10 '19 at 09:06
7

Gradle provides "aaptOptions, ignoreAssetsPattern" to filter/exclude assets folders and files from release or debug build.

Example for debug build (js folder and great.css files):

debug {            
        aaptOptions {
            ignoreAssetsPattern '!js:!great.css:'
        }
    }

Example for release build (js folder and great.css files):

release {
        aaptOptions {
            ignoreAssetsPattern '!js:!great.css:'
        }
    }
mnille
  • 1,328
  • 4
  • 16
  • 20
Pramoda S
  • 151
  • 2
  • 5
  • 1
    ignoreAssetsPattern works for me, but not working on a per-build-type basis - see question here: https://stackoverflow.com/questions/56075155/using-build-specific-aaptoptions-in-android-for-excluding-specific-assets-files – TKoL May 10 '19 at 10:19
0

I think you can use proguard. Proguard is include with android studio,obfuscate code, and remove not used classes, and if you want remove all resources that app not used. Only put in your build.gradle this:

 release {

            minifyEnabled true //remove classes, obfuscate code and zipalign
            shrinkResources true //remove resources
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'//autogenerated files
        }

This is link information about that:

You can personalize, exclude particular files or ignore particular files

encastellano
  • 435
  • 3
  • 5
  • Thanks for your suggestion to use proguard. I already use proguard, but I can't find any documentation about using it to deleting/not including specific files. Can you be more specific? – Divisible by Zero Mar 26 '15 at 11:04
  • Yep, you should activate shrinkResources in gradle.build, this should remove all resources not used in the project, this should remove all js files, for keep some resources you should create a xml file, inside this file define what files or resources keep from remove. in this [link](http://tools.android.com/tech-docs/new-build-system/resource-shrinking) in KeepResources section. After that you should testing well, proguard its complicated to configure and understand. I hope this help you – encastellano Mar 26 '15 at 11:14
  • I already have `shrinkResources` activated. It does not remove the assets I don't want, since the library that I use, uses those files. I however don't use that part of the library, so I can and want to do without those files. – Divisible by Zero Mar 26 '15 at 16:10
  • Not much information to do that with Proguard, in theory should detect that you do not use. Create a script in build.gradle as they say in the other answer, should be much better solution, sorry for the misunderstanding – encastellano Mar 26 '15 at 16:30
0

It's not possible through a filter.

You could have 2 assets folders though. a main one (src/main/assets) used for both debug and release and one (src/debug/assets) used only for the debug build.

source

Fahim
  • 12,198
  • 5
  • 39
  • 57