78

I used to copy 'compile' dependencies to a specific folder using this simple gradle task :

task copyLibs(type: Copy) {
    from configurations.compile
    into "$project.rootDir/reports/libs/"
}

But it stopped working just after upgrading my Android project using gradle plugin 3.0.1 for Android Studio and Gradle tool to 4.1. As the dependency configuration 'compile' is now deprecated by https://developer.android.com/studio/build/gradle-plugin-3-0-0-migration.html#new_configurations I changed it to 'implementation'. However, I am not able to use my copyLibs task as resolving configuration 'implementation' directly is not allowed as per Gradle build error output :

$ ./gradlew.bat clean build

FAILURE: Build failed with an exception.

* What went wrong:
Could not determine the dependencies of task ':app:copyLibs'.
> Resolving configuration 'implementation' directly is not allowed

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

* Get more help at https://help.gradle.org

BUILD FAILED in 1s

See following my current build.gradle file for app module : apply plugin: 'com.android.application'

android {
    compileSdkVersion 26
    defaultConfig {
        applicationId "newgradle.com.testingnewgradle"
        minSdkVersion 21
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:26.1.0'
    implementation 'com.android.support:support-v4:26.1.0'
    implementation 'com.android.support:design:26.1.0'
    implementation 'com.android.support.constraint:constraint-layout:1.0.2'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.1'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
}

task copyLibs(type: Copy) {
    from configurations.implementation
    into "$project.rootDir/reports/libs/"
}
build.dependsOn copyLibs

If I use 'compile' it works but I would like to be compliant with the latest recommendation on this plugin the usage.

I need help to upgrade my copyLibs task in order to work as before upgrading my enviromment. I was using gradle plugin 2.2.3 for Android Studio and Gradle tool 2.14.1.

Rafael
  • 1,534
  • 1
  • 12
  • 19
  • See aslo a ticket raised from a collegue at https://discuss.gradle.org/t/not-able-to-copy-implementation-dependencies/25344 – Rafael Jan 05 '18 at 16:22

7 Answers7

88

instead of using configurations.implementation, the best option is to use configurations.runtimeClasspath.

You can also think about: compileClasspath default

de-jcup
  • 1,402
  • 12
  • 27
  • 18
    Actually, this is a more elegant solution. Simply replace `configurations.compile` by `configurations.compileClasspath` and it works just fine (for me at least). – racs Oct 22 '18 at 05:18
  • 17
    It says: `Could not get unknown property 'runtimeClasspath' for configuration container of type org.gradle.api.internal.artifacts.configurations.DefaultConfigurationContainer.`. Did I miss something? The same for `compileClasspath`. My code looks the same except it is a library not an app. – kristyna Feb 07 '19 at 17:23
  • @Bot it has been a few months so I don't really remember how I solved it. I needed to include all dependencies in maven/artifactory and that is working for me now. What exactly are you trying to achieve? – kristyna Aug 30 '19 at 16:49
  • @kristyna, I upgraded the gradle plugin to 3.1.2 and it worked for me. Thanks for your reply. – Bot Sep 03 '19 at 06:35
  • 1
    `configurations.compileClasspath.collect { it.isDirectory() ? it : zipTree(it) }` worked well for me (for android `java-library`) – Ivan Oct 20 '19 at 16:39
  • I also suffer for Could not get unknown property 'runtimeClasspath' for configuration container of type org.gradle.api.internal.artifacts.configurations.DefaultConfigurationContainer. Answer with setting configurations.implementation.setCanBeResolved(true) worked for me instead. – Michał Powłoka Nov 19 '19 at 11:15
31

I make the configuration resolvable, so there is no exception when getting the dependencies

configurations.implementation.setCanBeResolved(true)
configurations.api.setCanBeResolved(true)

println configurations.implementation.resolve()
println configurations.api.resolve()
Michael Mior
  • 28,107
  • 9
  • 89
  • 113
act262
  • 463
  • 4
  • 7
  • This is the cleanest solution in my opinion! +1 – curd0 Aug 22 '19 at 20:37
  • 8
    Where would you put this code in your `build.gradle`? I keep getting the error `> Cannot change strategy of configuration ':app:api' after it has been resolved.` when trying to add it. – Destiny Faith Apr 14 '20 at 17:29
  • 1
    Hi, in any task, like this, in front of another configurations or just in front of :): task copyAndroidNatives() { ... configurations.implementation.setCanBeResolved(true) configurations.api.setCanBeResolved(true) configurations.... – Iefimenko Ievgen Feb 21 '22 at 03:55
16

Another suggestion.

I created my custom config and then used it as configurations.customConfig:

configurations {
  customConfig.extendsFrom implementation
}

The copy task must be correspondingly edited:

task copyLibs(type: Copy) {
    from configurations.customConfig
    into "$project.rootDir/reports/libs/"
}
mortalis
  • 2,060
  • 24
  • 34
  • I was super skeptical about this solution but it actually works. – dora2010 Nov 29 '19 at 14:57
  • 1
    I understand that this is in fact the correct solution. Gradle 6.4 already suggests `[...] Resolving dependency configuration 'implementation' is not allowed as it is defined as 'canBeResolved=false'. Instead, a resolvable ('canBeResolved=true') dependency configuration that extends 'implementation' should be resolved.` The corresponding task `copyLibs` will copy `from configurations.customConfig`. – Alberto May 18 '20 at 08:11
  • Shouldn't it be `from configuration.customConfig`? – Chris Watts Dec 01 '20 at 16:20
  • It should. Thanks. – mortalis Dec 03 '20 at 14:30
9

It doesn't look as if there's a way to acquire the list of implementation dependencies and the compileClasspath mentioned at the Gradle ticket Rafael posted won't work if you're working with Android directly, like my case where I need the dependencies to be exported so that Unity3D can package them up for release.

So.. it looks like the only solution in this case is to use the deprecated compile type.

Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
Cornelia Xaos
  • 159
  • 2
  • 8
7

This probably won't help or have a better way to solve but...

You can put your dependencies in a way that is possible to copy doing the following:

android { ... }

// Add a new configuration to hold your dependencies
configurations {
    myConfig
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:26.1.0'
    implementation 'com.android.support:support-v4:26.1.0'
    implementation 'com.android.support:design:26.1.0'
    implementation 'com.android.support.constraint:constraint-layout:1.0.2'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.1'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'

    // Now you have to repeat adding the dependencies you want to copy in the 'myConfig'
    myConfig fileTree(dir: 'libs', include: ['*.jar'])
    myConfig 'com.android.support:appcompat-v7:26.1.0'
    myConfig 'com.android.support:support-v4:26.1.0'
    ...
}

task copyLibs(type: Copy) {
    // Now you can use 'myConfig' instead of 'implementation' or 'compile' 
    from configurations.myConfig 
    into "$project.rootDir/reports/libs/"
}

This also helps if you have a Jar task that stops placing the dependencies in to the jar file because you changed from compile to implementation.

You can use:

from {configurations.myConfig.collect { it.isDirectory() ? it : zipTree(it) }}

Instead of:

from {configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }}
ViníciusPJ
  • 524
  • 5
  • 13
  • 1
    With `classpath 'com.android.tools.build:gradle:3.2.1'` and `distributionUrl=https\://services.gradle.org/distributions/gradle-4.10-all.zip`. I'm not sure if it stills work... – Damiii Feb 12 '19 at 09:49
  • If you do what you are suggesting you should receive some sort of duplication error. Like, "Duplicate class android.arch.core.internal.SafeIterableMap". – portfoliobuilder Jul 09 '19 at 22:16
0

I started getting this error after upgrading from gradle 5.5 to 5.6, and it happens when I try to sync the project in intelliJ.

Thanks to this answer on another question, I solved it by applying the idea plugin to all projects and then running gradle cleanIdea and after that everything started working again.

Another day, another #inexplicable solution to a problem.

smac89
  • 39,374
  • 15
  • 132
  • 179
0

there is some documentation about this, at least at this moment. You can not directly use 'implementation' configuration, but only use one that extends this. Now you can create a custom configuration, but there are preconfigured extensions already, like 'compileClasspath'.

compileClasspath extends compileOnly, implementation Compile classpath, used when compiling source. Used by task compileJava.

Documentation is here: https://docs.gradle.org/current/userguide/java_plugin.html#sec:java_plugin_and_dependency_management