56

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

Stoyan
  • 627
  • 1
  • 6
  • 8

5 Answers5

96

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') 
Xavier Ducrohet
  • 28,383
  • 5
  • 88
  • 64
  • I have tired to include 'facebook-sdk' but gradle complains with this error: `What went wrong:` `A problem occurred evaluating settings 'ProjectA'.` `> Project with path 'Facebook-sdk' could not be found.` I have also tried ':facebook-sdk' (with colon) but gives exactly the same error. I have tried `project(':facebook-sdk').projectDir = new File('...')` but I am not sure what I should specify in the parentheses. I would really appreciate a detailed example. – Adam Swinden Jun 07 '13 at 10:29
  • Some tweaks on this, to make sure the support library works in a similar fashion and things seem to be working as far as running assemble from the command line, will keep going and see if anything else comes up – Stoyan Jun 10 '13 at 23:12
  • Thank you for answering this (and many others I see online). But does this still hold, isn’t there a plan to change such behavior, or to add at least some sort of support to have subrojects fully encapsulate its own dependencies? This seems like a very serious limitation for complex projects. I’m puzzled as to even why/how it is possible to choose this route (to require top projects reference sub-sub projects). – minsk Jan 15 '14 at 05:08
  • 1
    @minsk You could actually put some code in settings.gradle to go read and parse the other settings.gradle (I think). It's more a general Gradle question. I would ask on their user forums (http://http://forums.gradle.org/) – Xavier Ducrohet Jan 15 '14 at 05:31
  • ah thanks for pointing it out, I loose truck of what is android vs gradle in general. FYI, this link somehow gets broken for me on click (colon disappears in chrome) – minsk Jan 15 '14 at 08:42
  • Doesn't work for me. Error:Project :app declares a dependency from configuration 'compile' to configuration 'default' which is not declared in the descriptor for project :commons. – StarWind0 Sep 25 '17 at 17:13
  • @XavierDucrochet Is this still the recommended way today? There's includeBuild(), but I can't find a proper example – Maragues Jun 22 '18 at 11:29
6

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 ;)

stuckless
  • 6,515
  • 2
  • 19
  • 27
  • It's "convention over customization" assuming you didn't start with a different convention. In that case it's pretty much just good luck... – Dave Jan 13 '14 at 21:57
4

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.

GrkEngineer
  • 2,122
  • 19
  • 20
3

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

Weslor
  • 22,180
  • 2
  • 20
  • 31
1

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
Mike Kuasinski
  • 139
  • 1
  • 1
  • 8
  • 3
    First android-support-v13.jar includes the classes in android-support-v4.jar (the old build system would detect both and remove -v4.jar) so you should only use -v13.jar. Second, don't use those jars directly. Use the new artifact that we published in the "Repository" SDK component. Just do compile 'com.android.support:support-v13:13.0.0' – Xavier Ducrohet Jun 08 '13 at 18:46
  • Thanks, replacing the the support v4 jar with the v13 artifact fixed the above issue for me. – speedynomads Aug 26 '13 at 20:00
  • 2
    Why do you have more than 1 settings.gradle? – IgorGanapolsky Jan 15 '14 at 21:02