How to filter by property and add conditional dependency in a multi-project Gradle project?

This is a simplified version of my problem:

I have a multi-project Gradle 5.4.1 build with three sub projects. These sub projects have an extra project property named isLibrary which may be true or false:

$ROOT_PROJ_DIR/library/build.gradle:

ext {
    isLibrary = true
}

description = "Library"

// ...

$ROOT_PROJ_DIR/module1/build.gradle:

ext {
    isLibrary = false
}

description = "App Module 1"

// ...

$ROOT_PROJ_DIR/module2/build.gradle:

ext {
    isLibrary = false
}

description = "App Module 2"

// ...

In my root build.gradle file, amongst other things, I want to:

  • Add a dependency on org.postgresql:postgresql:42.2.5 to all sub-projects
  • For all !isLibrary projects:
    • Apply the foo plugin
    • Add a dependency on :library to all non-library sub-projects

Here’s how I tried to do this:

plugins {
    id "foo" version "1.0.0" apply false
}

subprojects {
    apply plugin: 'java'

    group = '...'
    version = '...'

    sourceCompatibility = '1.8'

    // ...

    dependencies {
        implementation 'org.postgresql:postgresql:42.2.5'
        // ...
    }

    afterEvaluate { Project project ->
        if (!project.isLibrary) {
            apply plugin: 'foo'

            dependencies {
                compile project(':library')
            }
        }
    }
}

but this fails with an error:

* What went wrong:
A problem occurred configuring project ':module1'.
> Could not find method call() for arguments [:library] on project ':module1' of type org.gradle.api.Project.

To workaround this, for the time being I am adding the conditional dependency by filtering based on the project name:

subprojects {
    apply plugin: 'java'

    group = '...'
    version = '...'

    sourceCompatibility = '1.8'

    // ...

    dependencies {
        implementation 'org.postgresql:postgresql:42.2.5'
        // ...
    }

    afterEvaluate { Project project ->
        if (!project.isLibrary) {
            apply plugin: 'foo'
        }
    }
}

configure(subprojects.findAll { it.name != 'library' }) {
   dependencies {
       compile project(':library')
   }
}

But I prefer to:

  • Use conditional logic based on extra properties instead of project names
  • Keep the conditional dependencies logic together with the conditional apply plugin: 'foo' logic (e.g. in the afterEvaluate block)

Any ideas what am I doing wrong and how can this error be fixed?

Thanks in advance.

Damn! :man_facepalming:t2:

The closure variable “project”:

afterEvaluate { Project project ->

shadows the Project.project method.

This fixed the problem:

afterEvaluate { Project p ->
    if (!p.isLibrary) {
        apply plugin: 'foo'

        dependencies {
            compile project(':library')
        }
    }
}

It’s best to avoid afterEvaluate { ...} blocks where possible. Have you tried adding evaluationDependsOnChildren() in the root build.gradle?

Eg:

evaluationDependsOnChildren() 
subprojects {
    apply plugin: 'java'
    ... 
    if (!isLibrary) {...} 
} 

Will try that now – would be nice if it was added to the documentation of multi project builds too. :slight_smile:

Another option is to use an Extension object.

Eg: root/build.gradle

subprojects {
   apply plugin: 'java'
   ... 
   extensions.add('myplugin', new MyExtension(project)) 
} 
class MyExtension {
   final Project project
   MyExtension(Project project) {
      this.project = project 
   } 
   void setLibrary(boolean library) {
      if (library) {
         project.with {
            apply plugin: 'foo'
            dependencies {
               compile project(':library')
            }
         } 
      } 
   } 
} 

subproject/build.gradle

myplugin {
   library = true 
} 

See docs at custom plugins - making the plugin configurable