Android Gradle build with sub projects

AndroidGradleFacebook Android-Sdk

Android Problem Overview


I am currently in the process of converting one of our projects to Gradle from maven. The folder structure is as follows:

gitRoot
    settings.gradle
    build.gradle
    ProjectA
        build.gradle
        src/main/java
    Libraries
        SomeLib (git submodule)
        ProjectBRoot (git submodule)
            settings.gradle
            build.gradle
            ProjectB
                build.gradle
                src/main/java
            Libraries
                FacebookSDK/facebook
                    build.gradle
                    src

So already it looks complicated. But the idea is that ProjectB is a library project and it should be able to be built and packaged separately, which is why it has its own settings.gradle and as far as i can tell it seems to be working ok, i have it building and its finding facebook just fine.

The ProjectB/build.gradle contains this line

compile project(':libraries:facebook-android-sdk:facebook')

The ProjectBRoot/settings.gradle contains this line

include ':ProjectB', ':libraries:facebook-android-sdk:facebook'

The gitRoot/settings.gradle contains this line

include ':ProjectA', ':Libraries:ProjectBRoot:ProjectB'

The ProjectA/build.gradle contains this line

compile project(':Libraries:ProjectBRoot:ProjectB')

When I run the build i get this error

The TaskContainer.add() method has been deprecated and is scheduled to be removed in Gradle 2.0. Please use the create() method instead.

FAILURE: Build failed with an exception.

* Where:
Build file '/gitRoot/Libraries/ProjectBRoot/ProjectB/build.gradle' line: 17

* What went wrong:
A problem occurred evaluating project ':Libraries:ProjectBRoot:ProjectB'.
> Project with path ':libraries:facebook-android-sdk:facebook' could not be found in project ':Libraries:ProjectBRoot:ProjectB'.

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

BUILD FAILED

Total time: 4.652 secs

So my guess as to whats wrong is that facebook is not in a direct subfolder from ProjectB...but that doesn't matter when building within ProjectBRoot. This is probably due to the face that I am referencing ProjectB directly and not through the ProjectBRoot/gradle.build but I tried that and it also did not work. Can someone please help me out, I have looked through the documentation and it doesn't talk about multiple projects that have their own settings.gradle files and I think thats the thing that is messing me up.

Update:

So I followed Xav's answer and I am now able to build with the command line however i can't import/build with android studio. I know the problem is still with the facebook project. The error i get is that it could not configure ProjectB.

 Gradle: A problem occurred configuring project ':ProjectA'.
   > Failed to notify project evaluation listener.
     > A problem occurred configuring project ':Libraries:ProjectBRoot:ProjectB'.
       > Failed to notify project evaluation listener.
         > Configuration with name 'default' not found.

The error is caused by the line

 compile project(':facebook-sdk')

inside the ProjectB/build.gradle

Android Solutions


Solution 1 - Android

settings.gradle must define all the modules. It won't load other settings.gradle found in the tree to load more module.

You'll have to either

  1. define a module for the facebook SDK in the top level settings.gradle. Yes it's redundant with the other settings.gradle.

  2. publish Project B somehow (as well as its dependencies so in this case the facebook SDK library), somewhere (a corporate artifact repository for instance) and access it from Project A.

While #1 is better, it makes the ProjectB -> Facebook dependency tricky as the path will be different depending on the settings.gradle used. One way to fix this is to split the module name/path from its actual location on disk. this is done inside the settings.gradle file.

In your top level settings.gradle file, do

include 'facebook-sdk'
project(':facebook-sdk').projectDir = new File('Libraries/ProjectBRoot/Libraries/FacebookSDK/facebook')

In the setting.gradle file inside your Project B, do the same with the different relative path:

include 'facebook-sdk'
project(':facebook-sdk').projectDir = new File('Libraries/FacebookSDK/facebook')

This makes both project setup define the same 'facebook-sdk' module located at the same absolute location on disk.

All projects depending on this module should just declare the dependency as

compile project(':facebook-sdk') 

Solution 2 - Android

This isn't the answer that you are looking for, but...

I spent about 4 days trying to migrate my complex project structure (much like yours actually), into gradle. In the end, I ditched the entire sub-project concept (since in my case, it just didn't work), and I opted to use the local maven repository approach, as described in this article: http://www.flexlabs.org/2013/06/using-local-aar-android-library-packages-in-gradle-builds

So, in my Library projects that had sub-projects, I basically created complete library projects for each of those, and then I used the link above to publish those to the maven local repository. Then in the parent project, I removed any references to sub projects, and in the dependencies, I just referenced the published libraries. And then in my main project, I removed all references to sub-projects, and I referenced published maven local versions of my libraries.

In the end, it all works very well, but it did take some time to convert everything over. I had about 6 library projects with sub projects, that that sub projects, etc, and now everything works fine.

The thing that I dislike about systems like Gradle and Maven is that they really expect you to change "how" you structure your code/projects. So, if you are migrating to Gradle vs starting with Gradle, then the process can be quite frustrating. Once you figure it out though, the next time is much easier ;)

Solution 3 - Android

I would just have the app with the libraries all at the same level. You can still build and package each library based on the build.gradle file. I might be missing something, but the structure isn't as important as what's in the build.gradle files. You could still have projectB depend on Facebook, even if they are at the same folder level.

Solution 4 - Android

Had the same problem, in my case, I just forgot to add the project in settings.gradle. After that, it worked

Solution 5 - Android

I have finally managed to build the project (without gradle errors). Answer from Xavier was very helpful.

I spend almost 3 days trying to setup my project, I know I am very close to be finsihed but I have UNEXPECTED TOP-LEVEL EXCEPTION at very last step of gradle build process (dexDebug).

My project setup is very similar to Stoyan's however I have multiple android-library projects that are referencing android-support libraries. I suggest that if you have problems with compiling your top root project (error saying support android is already added) than you need to move the jar into separate android library project (decompose/separate into stand alone instance).

Example

--| ProjectARoot
--| ProjectA (where main/java etc are)
  --| build.gradle
--| settings.gradle
--| libraries
  --| ProjectBRoot
    --| settings.gradle
    --| ProjectB
    --| libraries
       --| android-supports (android lib project)
         --| libs
           --| android-support-v4.jar
           --| android-support-v13.jar
         --| build.gradle
       --| libA
         --| build.gradle (referencing android-supports)'

Example build scrip for libA referencing android supports projects

buildscript {
  repositories {
      maven { url 'http://repo1.maven.org/maven2' }
  }
  dependencies {
      classpath 'com.android.tools.build:gradle:0.4'
  }
}
apply plugin: 'android-library'

dependencies {
  compile project(':android-support')
}

android {
  compileSdkVersion 17
  buildToolsVersion "17.0.0"

  defaultConfig {
      minSdkVersion 7
      targetSdkVersion 17
  }
}

// top root settings.gradle

include 'ProjectB', 'android-support', ':ProjectA' (notice Project B is first than android-support and lastly Project A)

project(':android-support').projectDir = new File('libraries/ProjectBRoot/libraries/android-support')
project(':ProjectB').projectDir = new File('libraries/ProjectBRoot/ProjectB')

Currently when I run gradle build I get this error

:ProjectA:dexDebug

UNEXPECTED TOP-LEVEL EXCEPTION:
java.lang.IllegalArgumentException: already added: Landroid/support/v4/accessibilityservice/AccessibilityServiceInfoCompat;
at com.android.dx.dex.file.ClassDefsSection.add(ClassDefsSection.java:123)
at com.android.dx.dex.file.DexFile.add(DexFile.java:163)
at com.android.dx.command.dexer.Main.processClass(Main.java:490)
at com.android.dx.command.dexer.Main.processFileBytes(Main.java:459)
at com.android.dx.command.dexer.Main.access$400(Main.java:67)
at com.android.dx.command.dexer.Main$1.processFileBytes(Main.java:398)
at com.android.dx.cf.direct.ClassPathOpener.processArchive(ClassPathOpener.java:245)
at com.android.dx.cf.direct.ClassPathOpener.processOne(ClassPathOpener.java:131)
at com.android.dx.cf.direct.ClassPathOpener.process(ClassPathOpener.java:109)
at com.android.dx.command.dexer.Main.processOne(Main.java:422)
at com.android.dx.command.dexer.Main.processAllFiles(Main.java:333)
at com.android.dx.command.dexer.Main.run(Main.java:209)
at com.android.dx.command.dexer.Main.main(Main.java:174)
at com.android.dx.command.Main.main(Main.java:91)
1 error; aborting
:ProjectA:dexDebug FAILED

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionStoyanView Question on Stackoverflow
Solution 1 - AndroidXavier DucrohetView Answer on Stackoverflow
Solution 2 - AndroidstucklessView Answer on Stackoverflow
Solution 3 - AndroidGrkEngineerView Answer on Stackoverflow
Solution 4 - AndroidWeslorView Answer on Stackoverflow
Solution 5 - AndroidMike KuasinskiView Answer on Stackoverflow