Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

With two res directories per build variant, Gradle stopped tracking changes in resource files

I, quite innocently, switched to using different app icons for each product flavour along these lines:

sourceSets.production {
    res.srcDirs = ['res', 'res-production']
}

sourceSets.beta {
    res.srcDirs = ['res', 'res-beta']
}

sourceSets.internal {
    res.srcDirs = ['res', 'res-internal']
}

Thing is, after that, Gradle stopped noticing changes in any layout files, such as res/layout/activity_faq.xml, and requires a clean build each time, if I want my XML changes to be included in the APK.

I first thought this was an Android Studio problem, but in fact I can reproduce it with pure Gradle, on the command line, simply looking at the files that appear under build/res/all/internal/debug/layout.

When I run ./gradlew assembleInternalDebug, it outputs:

:compileInternalDebugNdk UP-TO-DATE
:preBuild UP-TO-DATE
:preInternalDebugBuild UP-TO-DATE
:prepareInternalDebugDependencies
:compileInternalDebugAidl UP-TO-DATE
:compileInternalDebugRenderscript UP-TO-DATE
:generateInternalDebugBuildConfig UP-TO-DATE
:mergeInternalDebugAssets UP-TO-DATE
:mergeInternalDebugResources
:processInternalDebugManifest UP-TO-DATE
:processInternalDebugResources UP-TO-DATE
:generateInternalDebugSources UP-TO-DATE
:compileInternalDebugJava UP-TO-DATE
:preDexInternalDebug UP-TO-DATE
:dexInternalDebug UP-TO-DATE
:processInternalDebugJavaRes UP-TO-DATE
:validateDebugSigning
:packageInternalDebug UP-TO-DATE
:assembleInternalDebug UP-TO-DATE

BUILD SUCCESSFUL

Here, processInternalDebugResources is shown to be UP-TO-DATE, while I think it should not be, when there are changed resource files. Before the icon-per-flavour change, in similar Gradle output, processInternalDebugResources was not shown up-to-date.

Question is, any way to fix this, or did I stumble upon a bug in Android Gradle plugin?

My build.gradle:

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:0.7.3'
    }
}
apply plugin: 'android'

repositories {
    mavenCentral()
}
dependencies {
    compile 'com.google.code.gson:gson:2.2.4'
    compile 'com.google.guava:guava:15.0'
    compile 'com.netflix.rxjava:rxjava-core:0.14.2'
    compile 'com.netflix.rxjava:rxjava-android:0.14.2'
    compile 'com.squareup.okhttp:okhttp:1.2.1'

    compile 'com.android.support:support-v4:13.0.0'
    compile 'com.android.support:support-v13:13.0.0'
}

android {
    compileSdkVersion 19
    buildToolsVersion "19"

    defaultConfig {
        minSdkVersion 14
        targetSdkVersion 19
    }

    productFlavors {
        production {
            packageName "fi.company.app"
        }
        beta {
            packageName "fi.company.app.beta"
        }
        internal {
            packageName "fi.company.app.internal"
        }
    }

    sourceSets {
        main {
            manifest.srcFile 'AndroidManifest.xml'
            java.srcDirs = ['src']
            resources.srcDirs = ['src']
            aidl.srcDirs = ['src']
            renderscript.srcDirs = ['src']
            res.srcDirs = ['res']
            assets.srcDirs = ['assets']
        }

        // Move the tests to tests/java, tests/res, etc...
        instrumentTest.setRoot('tests')

        debug.setRoot('build-types/debug')
        release.setRoot('build-types/release')
    }

    // Custom res directories for different flavors; used for 
    // setting different app icon.

    sourceSets.production {
        res.srcDirs = ['res', 'res-production']
    }

    sourceSets.beta {
        res.srcDirs = ['res', 'res-beta']
    }

    sourceSets.internal {
        res.srcDirs = ['res', 'res-internal']
    }

    signingConfigs {
        release {
            // ...
        }
    }
    buildTypes {
        release {
            signingConfig signingConfigs.release
            // ...
        }
    }
}

Gradle 1.9; Android Gradle plugin 0.7.3.

Edit: For other reasons, I switched from product flavours to custom build types for my app icon customisation needs. This problem still remains, so at least this isn't product flavour specific.

like image 442
Jonik Avatar asked Jan 07 '14 14:01

Jonik


People also ask

Does Gradle run tasks in parallel?

Parallel executionBy using the --parallel switch, you can force Gradle to execute tasks in parallel as long as those tasks are in different projects. You could see big improvements in build times as soon as you enable parallel builds.

Is Gradle multithreaded?

Compile projects in parallelIn parallel mode, Gradle will run multiple executor threads, which can execute tasks from different projects in parallel. By default Gradle will create one executor thread for each CPU core on your machine, but this is configurable.

Which of the following are methods for using Gradle to profile a build in android?

In addition, there are two options for profiling your build outside of Android Studio: The standalone gradle-profiler tool, a robust tool for deep analysis of your build. The Gradle --profile option, a convenient tool available from the Gradle command line.

What is Buildtype in Gradle android?

Build types define certain properties that Gradle uses when building and packaging your app, and are typically configured for different stages of your development lifecycle.


1 Answers

Ok, got this fixed. It turned out to be a small problem in my build.gradle.

What helped me was trying to upgrade to Gradle plugin 0.9.0, after which I started getting errors like these:

* What went wrong:
A problem occurred configuring root project 'MyProject'.
> SourceSets 'debug' and 'main' use the same file/folder for 'res': /path/to/MyProject/res

Well, I simply tried removing res.srcDirs = ['res'] from main sourceSets, and that was it.

sourceSets {
    main {
        manifest.srcFile 'AndroidManifest.xml'
        java.srcDirs = ['src']
        resources.srcDirs = ['src']
        aidl.srcDirs = ['src']
        renderscript.srcDirs = ['src']
        // res.srcDirs = ['res']
        assets.srcDirs = ['assets']
    }
    // ...
}

I left the rest of the sourceSets definitions untouched (res.srcDirs = ['res', 'res-beta'] for sourceSets.beta and so on).

Everything works again: the build type (or product flavour) specific custom icons are in use, and Gradle once again correctly notices changes to resource files (e.g. layouts) in incremental builds!

(I verified that the problem indeed was in my config by testing with Gradle plugin versions 0.8.1, 0.9.0 and 0.9.3 with my changed build.gradle. Indeed, changes in the plugin were not what fixed this, even if 0.9.0 was the version that started giving me the useful error message.)

Possibly the root cause here was using "old project structure" and build.gradle that was originally generated by Eclipse. In any case, studying source sets and project structure more carefully in the User Guide might have helped too.

like image 91
Jonik Avatar answered Nov 09 '22 11:11

Jonik