modules-graph-assert icon indicating copy to clipboard operation
modules-graph-assert copied to clipboard

Not all modules are detected at once

Open haluzpav opened this issue 2 years ago • 7 comments

I have a project with about 100 modules. When running your plugin on the root app module it doesn't see all modules.

My setup:

app/build.gradle.kts:

plugins {
    // ...
    id("com.jraska.module.graph.assertion") version "2.4.1"
}
// no custom configuration in moduleGraphAssert
dependencies {
    implementation(project(":amount"))
    // ...
}

core/build.gradle.kts:

plugins {
    // ...
    id("com.jraska.module.graph.assertion") version "2.4.1"
}
// no custom configuration in moduleGraphAssert
dependencies {
    api(project(":tools"))
    // ...
}

Gradle version: 8.1.1

Actual

Output of ./gradlew generateModulesGraphStatistics --rerun-tasks:

...
> Task :app:generateModulesGraphStatistics
GraphStatistics(modulesCount=79, edgesCount=82, height=2, longestPath=':app -> :amount -> :core')

> Task :core:generateModulesGraphStatistics
GraphStatistics(modulesCount=7, edgesCount=6, height=1, longestPath=':core -> :tools')

Expected

From the longestPath, you can notice that app depends on core, and core depends on tools. But then the longest path of app should actually be longestPath=':app -> :amount -> :core -> :tools', no? (In my case, there should be also much longer chains, I just use output of the plugin here as an example.)

Notes

The other tasks seem to have the same problem. For example when I set moduleGraphAssert { maxHeight = 2 } and run assertModuleGraph, it doesn't fail.

It also doesn't change the output of app tasks when I remove the plugin from core module - I've added it there for debugging.

Switching api and implementation in various ways doesn't help either.

I have tested this also with org.gradle.configuration-cache=false in gradle.properties.

Let me know if you have any hint or you want more info. I can't share my project.

haluzpav avatar Jun 20 '23 11:06 haluzpav

Hi! I had a similar problem. The solution for me was to put all the relevant configuration to the root build.gradle file, not into the app module's build.gradle file.

In my root build gradle file I applied the plugin (right after the buildscript closure):

plugins {
    id "com.jraska.module.graph.assertion" version "$modules_graph_assert_version"
}

apply from: "${project.rootDir}/buildsystem/moduleConfig.gradle"

allprojects {
    moduleGraphAssert {
        maxHeight = 10
        allowed = [
                'app-module -> feature-module',
                'app-module -> feature-module-top',
                'app-module -> common-module',
                'app-module -> legacy-module',
                'feature-module -> common-module',
                'feature-module -> legacy-module',
                'feature-module-top -> common-module',
                'feature-module-top -> legacy-module',
                'feature-module-middle -> common-module',
                'feature-module-middle -> legacy-module',
                'feature-module-bottom -> common-module',
                'feature-module-bottom -> legacy-module',
                'feature-module-top -> feature-module-middle',
                'feature-module-middle -> feature-module-bottom',
                'common-module -> common-module',
                'common-module -> legacy-module',
                'legacy-module -> common-module',
                'legacy-module -> legacy-module'
        ]
        configurations = ['api', 'implementation']
        assertOnAnyBuild = true
    }
}

In the moduleConfig.gradle file I have the module aliases defined (and some other stuff that is not related to this plugin). This way for example (omitting the real module names here):

project(":my-awasome-module") {
    ext {
        moduleNameAssertAlias = 'feature-module'
    }
}

It seems for me that the 2 main things that helped are to apply the plugin to the root project (and not to the project that is the root of the dependency graph) and to apply the config to all the modules. Maybe the documentation could be a little bit more obvious on this.

nyekilajos avatar Jun 26 '23 11:06 nyekilajos

Hey, thanks for reporting those - there might be 2 actions now which would help to understand this:

  1. Try using --no-configure-on-demand - this might be one hypothesis that Gradle simply doesn't configure these projects for some cases

  2. Debug the Gradle modules extraction and see if the modules are in the subprojects collection - see if the modules are actually loaded into Graph - if 1. ☝️ is true - they won't be there. If false then there is another bug.

Thanks for checking and sorry for the late response - summer season :)

jraska avatar Jun 29 '23 21:06 jraska

--no-configure-on-demand did not fix the issue on our monorepo, and we can see all modules configuring properly with debug logs and gradle scans. I'll try debugging it, but our project already relies on included-build that need this dependency. Not sure how it will behave.

SimonMarquis avatar Jun 30 '23 07:06 SimonMarquis

Adding breakpoints inside the IDE was enough, and here is the result for subprojects

image

SimonMarquis avatar Jun 30 '23 08:06 SimonMarquis

Applying the plugin on the root module as well (without the configuration block) and executing this did the trick for us.

gradlew :generateModulesGraphStatistics -Pmodules.graph.of.module=:MyAppModule --no-configure-on-demand --no-configuration-cache

SimonMarquis avatar Jun 30 '23 11:06 SimonMarquis

Applying the plugin on the root module as well (without the configuration block) and executing this did the trick for us.

@SimonMarquis thanks for sharing!

Thanks all for sharing insights! + sorry all for later reply - holidays season 🌴

What we know: Workaround: Apply the plugin to the root project solves the issue

I would still like to fully understand why. The hypothesis is that the plugin tries to iterate over the modules and dependencies in memory too soon, whilst Gradle loads some of these lazily or perhaps not at all. 🤔 Without reproducible sample it is really hard to understand, therefore I have 4 options for proposal on how to get to the bottom of it.

  1. Sharing your project Gradle configuration - likely not possible or requiring a lot of effort to not expose intellectual property - though if anyone is willing to do that - that would help the most to resolve the issue fast. :)
  2. Sharing a sample reproducing the issue.
  3. Connecting with me on Twitter or LinkedIn and arranging a call debugging the issue.
  4. I can provide a branch with some debugging info and anonymizing the names of the modules - might not be enough, but easiest for anyone to try 🤷

If anyone in this issue is willing to spend some time on this, let me know ;)

jraska avatar Jul 16 '23 19:07 jraska

Hey, anyone willing to follow up with example or help debugging please? Options here.

I think the problem still exists but without reproducing it is hard to move forward. I intend to close the issue as stale otherwise. 🤷

jraska avatar Mar 04 '24 15:03 jraska

hey @jraska

I was able to reproduce a similar issue in the github-client repo. Here is the patch to the current master, or fork.

I'm struggling with running commands per individual module, eg:

./gradlew :feature:about:dependencies --configuration debugCompileClasspath

Adding --no-configure-on-demand helps execute the command above, but I wonder if there’s a way to fix this on the plugin side so I don’t have to explicitly tell gradle that.

Hope this helps EDIT: created #260

max1lyu avatar May 30 '24 18:05 max1lyu

@haluzpav , @nyekilajos , @SimonMarquis , @max1lyu

Please if you can, give version 2.7.0 a try. There is a possibility it will solve your issue.

In case it does, please let me know. Thanks!

jraska avatar Aug 27 '24 20:08 jraska

Hi @jraska. I just tried version 2.7.0, it doesn't seem to fix the issue that I had

max1lyu avatar Aug 29 '24 21:08 max1lyu

Thanks a lot @max1lyu. I assume also not even with the --no-configure-on-demand flag. 🤔

jraska avatar Aug 30 '24 07:08 jraska

no no, it does work when --no-configure-on-demand flag is used, so it's a saver yes

max1lyu avatar Sep 08 '24 20:09 max1lyu