Customize IDEA model exposed via tooling api at build.gradle

Hello,

AFAIU it should be possible to customize build.gradle content in a way to affect ‘structural’ result of ProjectConnection.getModel(IdeaProject.class). ‘Structural’ here means that I want not to modify existing domain objects properties (like module name) but add/remove domain entities (e.g. replace particular module by two new modules)

Unfortunately, haven’t succeeded yet. Tried to add new module as a proof of concept:

// Create new test module
apply plugin: 'idea';
...
def newModuleLocation = file("/Users/denis/Downloads/new-module")
def existingModule = idea.project.modules[0]
def newIml = new org.gradle.plugins.ide.idea.model.IdeaModuleIml(existingModule.iml.xmlTransformer, newModuleLocation)
def newModule = new IdeaModule(existingModule.project, newIml)
newModule.name = "my-programmatically-created-module"
newModule.contentRoot = newModuleLocation
newModule.sourceDirs = [ newModuleLocation ]

Tried to add it into project during approaches below:

idea.project.modules << newModule

and

idea.project.ipr {
    beforeMerged {
        idea.project.modules << newModule
    }
}

Unfortunately, it didn’t do the trick.

Please advice if it’s possible to achieve that at all?

Denis

It’s weird, but a module is added when ‘+=’ is used instead of ‘<<’ :

def newModuleLocation = file("/Users/denis/Downloads/new-module")
def existingModule = idea.project.modules[0]
def newIml = new org.gradle.plugins.ide.idea.model.IdeaModuleIml(existingModule.iml.xmlTransformer, newModuleLocation)
def newModule = new IdeaModule(existingModule.project, newIml)
newModule.name = "my-programmatically-created-module"
newModule.contentRoot = newModuleLocation
newModule.sourceDirs = [ newModuleLocation ]

idea.project.modules += newModule

However, it fails on attempt to obtain a model via tooling api:

> Exception in thread "main" org.gradle.tooling.GradleConnectionException: Could not fetch model of type 'IdeaProject' using Gradle distribution 'https://services.gradle.org/distributions/gradle-2.5-bin.zip'.
> 	at org.gradle.tooling.internal.consumer.ResultHandlerAdapter.onFailure(ResultHandlerAdapter.java:63)
> 	at org.gradle.tooling.internal.consumer.async.DefaultAsyncConsumerActionExecutor$1$1.run(DefaultAsyncConsumerActionExecutor.java:57)
> 	at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:54)
> 	at org.gradle.internal.concurrent.StoppableExecutorImpl$1.run(StoppableExecutorImpl.java:40)
> 	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
> 	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
> 	at java.lang.Thread.run(Thread.java:745)
> 	at org.gradle.tooling.internal.consumer.BlockingResultHandler.getResult(BlockingResultHandler.java:46)
> 	at org.gradle.tooling.internal.consumer.DefaultModelBuilder.get(DefaultModelBuilder.java:49)
> 	at org.denis.StartClass.main(StartClass.java:23)
> Caused by: java.lang.NullPointerException
> 	at org.gradle.plugins.ide.internal.tooling.IdeaModelBuilder.srcDirs(IdeaModelBuilder.java:132)
> 	at org.gradle.plugins.ide.internal.tooling.IdeaModelBuilder.appendModule(IdeaModelBuilder.java:110)
> 	at org.gradle.plugins.ide.internal.tooling.IdeaModelBuilder.build(IdeaModelBuilder.java:69)
> 	at org.gradle.plugins.ide.internal.tooling.IdeaModelBuilder.buildAll(IdeaModelBuilder.java:47)
> 	at org.gradle.plugins.ide.internal.tooling.IdeaModelBuilder.buildAll(IdeaModelBuilder.java:30)
> 	at org.gradle.tooling.internal.provider.runner.BuildModelActionRunner.run(BuildModelActionRunner.java:75)
> 	at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35)
> 	at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:43)
> 	at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:27)
> 	at org.gradle.launcher.exec.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:72)
> 	at org.gradle.launcher.exec.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:44)
> 	at org.gradle.launcher.daemon.server.exec.ExecuteBuild.doBuild(ExecuteBuild.java:49)
> 	at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
> 	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
> 	at org.gradle.launcher.daemon.server.exec.WatchForDisconnection.execute(WatchForDisconnection.java:37)
> 	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
> 	at org.gradle.launcher.daemon.server.exec.ResetDeprecationLogger.execute(ResetDeprecationLogger.java:26)
> 	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
> 	at org.gradle.launcher.daemon.server.exec.RequestStopIfSingleUsedDaemon.execute(RequestStopIfSingleUsedDaemon.java:34)
> 	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
> 	at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.call(ForwardClientInput.java:74)
> 	at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.call(ForwardClientInput.java:72)
> 	at org.gradle.util.Swapper.swap(Swapper.java:38)
> 	at org.gradle.launcher.daemon.server.exec.ForwardClientInput.execute(ForwardClientInput.java:72)
> 	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
> 	at org.gradle.launcher.daemon.server.health.DaemonHealthTracker.execute(DaemonHealthTracker.java:47)
> 	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
> 	at org.gradle.launcher.daemon.server.exec.LogToClient.doBuild(LogToClient.java:66)
> 	at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
> 	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
> 	at org.gradle.launcher.daemon.server.exec.EstablishBuildEnvironment.doBuild(EstablishBuildEnvironment.java:71)
> 	at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
> 	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
> 	at org.gradle.launcher.daemon.server.health.HintGCAfterBuild.execute(HintGCAfterBuild.java:41)
> 	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
> 	at org.gradle.launcher.daemon.server.exec.StartBuildOrRespondWithBusy$1.run(StartBuildOrRespondWithBusy.java:50)
> 	at org.gradle.launcher.daemon.server.DaemonStateCoordinator$1.run(DaemonStateCoordinator.java:246)
> 	at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:54)
> 	at org.gradle.internal.concurrent.StoppableExecutorImpl$1.run(StoppableExecutorImpl.java:40)
> 	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
> 	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
> 	at java.lang.Thread.run(Thread.java:745)

Eventually, found out that new module is added if I completely overwrite modules collection instead of modifying it (’+=’ instead of ‘<<’)

Not sure why, though