10

i have a problem adding dependencies automatically to eclipse android project via gradle. I have only a little bit experience with gradle. Till now I have build two java projects with gradle. One jar and an executable-jar. This works without problems. I have used the eclipse plugin to generate the eclipse project and add the dependenies to the build path. I added new dependencies to the gradle script, started gradle with gradle eclipse ,update my project and the dependencies exist in the build path and I can used them. Here is the important part of that script.

apply plugin: 'java'
apply plugin: 'eclipse'
repositories {
   mavenCentral()
}

dependencies {
   compile 'commons-io:commons-io:2.4'
}

So, now I tried it in combination with the android plugin. Here is my hole gradle script.

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

apply plugin: 'android'
apply plugin: 'eclipse'

repositories {
mavenCentral()
}

dependencies {
compile 'org.apache.commons:commons-lang3:3.1'
}

android {
    compileSdkVersion 17
    buildToolsVersion "17"

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

        instrumentTest.setRoot('tests')
    }
}

If I use gradle eclipse nothing happens. Then I found out that the java plugin adds the dependencies to the build path. So I added

apply plugin: 'java'

to it and got the error that the java plugin is not compatible with the android plugin. Then I found a solution to copy the jars automatically to the lib folder of the project.

def libDir = file('libs')

task copyLibs(type: Copy) {

    doFirst {
        libDir.mkdirs()
    }
    from configurations.runtime
    into libDir
}

But this task needs the java plugin too for the configurations.runtime. I need the android plugin to create the apk file, so it is not a solution to remove the android plugin. Has somebody an idea if it is possible to add the dependencies to the build path or lib folder in ecipse project that is compatible with the android plugin?

EDIT: One of my ideas was to put the java-plugin to the eclipse-plugin, so that it will be only applied when the eclipse plugin will be applied. Something like this:

apply plugin: 'eclipse'

eclipse{
    apply plugin: 'java'
}

But I still get the error that the java and android plugins are not compatible. Maybe I understand gradle wrong, but normally the java plugin should be applied only when I start the eclipse plugin and not the android plugin. I´m afraid that my understanding and experience of gradle is not good enough to solve this this way or understand why it is not possible.

Rafael
  • 555
  • 1
  • 7
  • 15
  • Hi, I can't get your solution to work. Could you please check:http://stackoverflow.com/questions/28780426/consuming-aars-from-eclipse-does-not-work? – Jim Feb 28 '15 at 13:37

5 Answers5

12

My solution is based off Rafael's in that it copies dependencies to the libs directory which is only used by Android. However I go further to completely explode the referenced AAR's for use in Eclipse.

Gradle Build File

Add the following to the end of your Android projects build.gradle :

task copyJarDependencies(type: Copy) {
    description = 'Used for Eclipse. Copies all dependencies to the libs directory. If there are any AAR files it will extract the classes.jar and rename it the same as the AAR file but with a .jar on the end.'
    libDir = new File(project.projectDir, '/libs')
    println libDir
    println 'Adding dependencies from compile configuration'
    configurations.compile.filter {it.name.endsWith 'jar'}.each { File file -> moveJarIntoLibs(file)}
    println 'Adding dependencies from releaseCompile configuration'
    configurations.releaseCompile.filter {it.name.endsWith 'jar'}.each { File file -> moveJarIntoLibs(file)}
    println 'Adding dependencies from debugCompile configuration'
    configurations.debugCompile.filter {it.name.endsWith 'jar'}.each { File file -> moveJarIntoLibs(file)}
    println 'Adding dependencies from instrumentTestCompile configuration'
    configurations.instrumentTestCompile.filter {it.name.endsWith 'jar'}.each { File file -> moveJarIntoLibs(file)}
    println 'Extracting dependencies from compile configuration'
    configurations.compile.filter {it.name.endsWith 'aar'}.each { File file -> moveAndRenameAar(file) }
    println 'Extracting dependencies from releaseCompile configuration'
    configurations.releaseCompile.filter {it.name.endsWith 'aar'}.each { File file -> moveAndRenameAar(file) }
    println 'Extracting dependencies from debugCompile configuration'
    configurations.debugCompile.filter {it.name.endsWith 'aar'}.each { File file -> moveAndRenameAar(file) }
    println 'Extracting AAR dependencies from instrumentTestCompile configuration'
    configurations.instrumentTestCompile.filter {it.name.endsWith 'aar'}.each { File file -> moveAndRenameAar(file) }
}

void moveJarIntoLibs(File file){
    println 'Added jar ' + file
        copy{
            from file
            into 'libs'
        }
}

void moveAndRenameAar(File file){
    println 'Added aar ' + file
    def baseFilename = file.name.lastIndexOf('.').with {it != -1 ? file.name[0..<it] : file.name}

    // directory excluding the classes.jar
    copy{
        from zipTree(file)
        exclude 'classes.jar'
        into 'libs/'+baseFilename
    }

    // Copies the classes.jar into the libs directory of the expoded AAR.
    // In Eclipse you can then import this exploded ar as an Android project
    // and then reference not only the classes but also the android resources :D 
    copy{
        from zipTree(file)
        include 'classes.jar'
        into 'libs/' + baseFilename +'/libs'
        rename { String fileName ->
            fileName.replace('classes.jar', baseFilename + '.jar')
        }
    }
}

Building with Gradle

Run :

"gradle clean build"

You should find all dependencies and exploded AARs in your libs directory. This is all Eclipse should need.

Importing in Eclipse

Now this is where the real benefit begins. After you've generated the libs directory from the gradle step above you'll notice there are folders in there too. Those new folders are the exploded AAR dependencies from your build.gradle file.

Now the cool part is that when you import your existing Android project into Eclipse it will also detect the exploded AAR folders as projects it can import too!

1. Import those folders under your project's libs directory, don't import any 'build' folders, they're generated by Gradle

2. Ensure you perform a Project -> Clean on all AAR projects you've added. In your workspace check that each AAR exploded project has the following in the project.properties :

target=android-<YOUR INSTALLED SKD VERSION GOES HERE>
android.library=true

3. Now in your main Android project you can just add the library references with either ADT or you can just edit the project.properties file and add

android.libraries.reference.1=libs/someExplodedAAR/

4. Now you can right-click on your main Android project and Run as -> Android Application.

But what does this even mean?

  1. Well it means you don't need the source code for any of your Android AAR Gradle dependencies in order to reference both it's classes and resources in Eclipse.

  2. The gradle build script above takes the AAR file and prepares it for use in Eclipse. Once you add it to your workspace you're ready to just focus on your actual main Android project.

  3. You can now debug and develop using Eclipse and deploy using ADT with AAR dependencies being properly bundled in the APK. When you need to make some specific builds then you can use gradle.

Community
  • 1
  • 1
JavaJedi
  • 399
  • 3
  • 7
  • JavaJedi, thanks for your post. Did – CannyDuck May 09 '14 at 20:57
  • @JavaJedi Could you please copy markdown into README at https://github.com/Nodeclipse/nodeclipse-1/blob/master/org.nodeclipse.enide.editors.gradle/README.md – Paul Verest May 30 '14 at 08:57
  • It makes sense to add this solution into build-in Help https://github.com/Nodeclipse/nodeclipse-1/blob/master/org.nodeclipse.enide.gradle/HelpToc.xml – Paul Verest May 30 '14 at 09:04
  • This technique is great, and I'm really pleased to be able to streamline my repository a bit with it. I ran into an issue with having jar files already existing in the `/libs` folder of my project. Copying a file onto itself was resulting in a 0 byte file. Moving these jars into a `/libraries` directory so they could get moved into `libs` by the task solved the issue – MeanderingCode Sep 16 '14 at 21:36
  • @JavaJedi:Hi, I can't get your solution to work. Could you please check:http://stackoverflow.com/questions/28780426/consuming-aars-from-eclipse-does-not-work? – Jim Feb 28 '15 at 13:38
  • Hi, I'm running to some problem with this code. I'm trying to use it in my library project but I'm getting Cannot change configuration ':libprojectname:compile' after it has been resolved. – Ika May 17 '15 at 11:04
  • It seems that the problem I ran in to was the result of not placing the code last. I'm now facing another issue where ` configurations.compile.filter {it.name.endsWith 'jar'}.each { File file -> moveJarIntoLibs(file)}` fails since it can't find the support library. Error:Could not find com.android.support:support-v13:22.1.1. Searched in the following locations: file:/C:/Users/barmei/.m2/repository/com/android/support/support-v13/22.1.1/support-v13-22.1.1.pom file:/C:/Users/barmei/.m2/repository/com/android/support/support-v13/22.1.1/support-v13-22.1.1.jar .. – Ika May 17 '15 at 12:26
  • I managed to resolve the support library issue [see]: (http://stackoverflow.com/questions/30287022/cant-iterate-over-compile-dependecies-due-to-support-library/30296467#30296467) – Ika May 18 '15 at 13:59
  • Great Response, as an update, instrumentTestCompile has changed to androidTestCompile : http://stackoverflow.com/questions/25067325/android-error-on-imported-module-build-gradle-could-not-find-method-instrument – TheFuquan Jun 04 '15 at 13:37
4

Finally I have found a solution that works for me. This task copies the dependencies to the projects libs folder.

task copyDependencies(type: Copy) 
{
    description = 'Copy depencies to libs. Useful for Eclipse'
    libDir = new File(project.projectDir, '/libs')
    println libDir
    println 'Adding dependencies from compile configuration'
    for(file in configurations.compile) 
    {
        println 'Added ' + file
        copy 
        {
            from file
            into libDir
        }
    }
    println 'Adding dependencies from releaseCompile configuration'
    for(file in configurations.releaseCompile)
    {
        println 'Added ' + file
        copy
        {
            from file
            into libDir
        }
    }
    println 'Adding dependencies from debugCompile configuration'
    for(file in configurations.debugCompile)
    {
        println 'Added ' + file
        copy
        {
            from file
            into libDir
        }
    }

    println 'Adding dependencies from instrumentTestCompile configuration'
    for(file in configurations.instrumentTestCompile)
    {
        println 'Added ' + file
        copy
        {
            from file
            into libDir
        }
    }
}
Rafael
  • 555
  • 1
  • 7
  • 15
  • Is this the most elegant way to get a project to recognize new dependencies? Or has this been addressed ?? – Erik Nov 14 '13 at 02:13
2

The following gradle task just merges gradle dependencies into the eclipse classpath. Add it to build.gradle and invoke it with gradle gradleDep2EclipseClasspath.

  import groovy.xml.StreamingMarkupBuilder
  task gradleDep2EclipseClasspath() {
     description "Merge gradle dependencies into the eclipse class path"

     def files = ["compile", "releaseCompile", "debugCompile", "instrumentTestCompile"]
       .collect {project.configurations[it].files}
       .inject([] as Set) {result, files -> result + files}
       .asList()
       .unique()
       .grep {! it.@path.toString().contains("android-support")}

      def classpath = new XmlParser().parse(file(".classpath"))
      def libs = classpath.grep {it.'@gradle-dependency' == "true"}
      libs.each {classpath.remove(it)}

      files.collect {file ->
         new Node(null, "classpathentry", [
           "path": file.toString(),
           "kind": "lib",
           "exported": "true",
           "gradle-dependency": "true"
         ])
      }.each {classpath.append(it)}

      file(".classpath").withWriter {writer ->
         writer << new StreamingMarkupBuilder().bind { mkp.xmlDeclaration() }
         def printer = new XmlNodePrinter(new PrintWriter(writer))
         printer.print(classpath)
      }
   }
gubaer
  • 236
  • 2
  • 11
  • This is very useful and natural, many thanks! As a side note, these dependencies do not have sources attached. I used http://marketplace.eclipse.org/content/java-source-attacher to add missing sources. Also would it be difficult to exclude AARs from this list? – afrish Feb 07 '15 at 17:36
0

Thanks to JavaJedi's solution I've created another one, using Gradle's input/output annotations. It is based on the same principles, but is more general in use (e.g. you can easily define new tasks of type CopyJars or ExtractAars) and allows Gradle to decide whether file copying is needed or not.

Community
  • 1
  • 1
0

Here is my solution based on all previous solutions from @JavaJedi and @gubaer.

Step 1

Add copyAars task to build.gradle. It unpacks and copies AAR dependencies to a special folder "deps", rather than to "libs" to avoid any conflicts with Android Studio and/or other Gradle plugins and tasks. After that all exploded AARs should be manually imported as Android Library projects and added as dependencies to the main project in Eclipse. This is a modified solution from @JavaJedi.

task copyAars << {
    configurations.compile.filter { it.name.endsWith 'aar' }.each { File file -> copyAndRenameAar(file) }
}

void copyAndRenameAar(File file) {
    println 'Added aar ' + file
    def baseFilename = file.name.lastIndexOf('.').with { it != -1 ? file.name[0..<it] : file.name }

    // directory excluding the classes.jar
    copy {
        from zipTree(file)
        exclude 'classes.jar'
        into "deps/aars/exploded/" + baseFilename
    }

    // Copies the classes.jar into the libs directory of the expoded AAR.
    // In Eclipse you can then import this exploded aar as an Android project
    // and then reference not only the classes but also the android resources
    copy {
        from zipTree(file)
        include 'classes.jar'
        into "deps/aars/exploded/" + baseFilename + "/libs"
        rename { String fileName -> fileName.replace('classes.jar', baseFilename + '.jar') }
    }
}

Step 2

Add eclipseClasspath task to build.gradle. JAR dependencies are added in a different way. They are filtered and added directly to .classpath file, so they are automatically available in Eclipse main project. This is a modified solution from @gubaer. It includes only JARs, excluding AARs and only from "compile" configuration, but you can easily add more configs.

task eclipseClasspath << {
    def classpath = new XmlParser().parse(file(".classpath"))
    def libs = classpath.grep { it.'@gradle-dependency' == "true" }
    libs.each { classpath.remove(it) }

    configurations.compile.filter { it.name.endsWith 'jar' }
    .collect { file ->
        new Node(null, "classpathentry", [
            "path": file.toString(),
            "kind": "lib",
            "exported": "true",
            "gradle-dependency": "true"
        ])
    }
    .each { classpath.append(it) }

    file(".classpath").withWriter { writer ->
        writer << new groovy.xml.StreamingMarkupBuilder().bind { mkp.xmlDeclaration() }
        def printer = new XmlNodePrinter(new PrintWriter(writer))
        printer.print(classpath)
    }
}

Step 3

Install http://marketplace.eclipse.org/content/java-source-attacher plugin in Eclipse. After that you will be able to right click on each JAR and choose "Attach Java Source". For Gradle dependencies this works 99% of the time in my experience.

afrish
  • 3,167
  • 4
  • 30
  • 38