How can I extend the DSL dynamically?

Hi all,

I’m quite new to Gradle, Groovy, the forum … and I have a problem where I can not find some helpful info for:

I would like to extend my buildscript DSL with something like this:

buildtools {
  tool1 {
    groupid = "group"
    version = "1.0"
  }
  tool2 { ... }
}

I saw some explanations to do this statically in Multi-level DSL for plugin extension?

First, this does not work for me. I have:
project.extensions.create("buildtools", GradleToolInstallConvention);
and

class GradleToolInstallConvention {
  GradleToolInstallHandler tool1 = new GradleToolInstallHandler();
     
  GradleToolInstallHandler getTool1()
  {
    return tool1;
  }
  void tool1(Closure cfg) {
    // ConfigureUtil.configure() is not available anymore so I added my own.
    tool1.configure(cfg)
  }
}

class GradleToolInstallHandler {
  String groupId;
  String version;
}

I can access tool1 afterwards and the functions are invoked. But as long as I do not have “groupId” as property in the GradleToolInstallConvention class I get an error. Did I miss something?

What I finally really would like to have is something dynamic like in the model {} configuration block:

  • Tools shall be added dynamically. The name of the tool should be any string.
  • The perfect thing would be to have an additional type, e.g. MyTool(CompileTool) { groupId : " … " }

I made some experiments with the ExpandoMetaClass and was able to dynamically create properties and methods. In the above example, “MyTool” is a missing method while “CompileTool” is a missing property. Is this basically the way to go for this?

Since the name MyTool is just a name for me I can ignore it after adding. But the property is still not accessible. Everything after the inner braces {} is still expected to be found as property of the outer class which might have the same reason as the static example?

Any hints would be great! And thanks for reading this far ,-)

BR,
Heiko

You are looking for Project.container().

The problem is most probably the homebaked configure method not passing the correct delegate. Either use ConfigureUtil.configure() or just use a method that takes an Action and Gradle will automatically add the Closure variant at runtime. But if you use Project.container() as suggested above, all this will just work out of the box anyway.

1 Like

Thank you, exactly was I was looking for.

If you know for what you have to look you even find it in the documentation: 40.6 Maintaining multiple domain objects :wink:

def buildTools = project.container(BuildToolInfo);
project.extensions.buildTools = buildTools;

with

class BuildToolInfo {
  final String name;
  
  String groupId;
  String artifactId;
  String version;
  
  BuildToolInfo(String name) {
    this.name = name;
  }
}

does it.

Thank you,
Heiko