Define nested extension containers in Gradle

I am in the process of implementing my first Gradle plugin where I need to define nested extension objects. The user manual does not cover this yet. Last year there were a few questions about this, but I canโ€™t put together a solution from what I read.

Here is what I believe that I want the user to be able to:

yang { yangFilesRootDir "src/main/resources/yang" inspectDependencies true generators { generator { // line 25 generatorClassName "org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl" outputDir "build/gen1" } generator { generatorClassName "org.opendaylight.yangtools.yang.unified.doc.generator.maven.DocumentationGeneratorImpl" outputDir "build/gen2" } } } 

My first random hit in this looks like this, and I'm sure it is not closed yet:

 public void apply(Project project) { project.plugins.apply(JavaPlugin) YangExtension yang = project.extensions.create(YANG_EXT, YangExtension) project.yang.extensions.generators = project.container(CodeGeneratorsContainer) { println it } 

Here are my extension classes:

 class YangExtension { CodeGeneratorsContainer generators String yangFilesRootDir String[] excludeFiles boolean inspectDependencies String yangFilesConfiguration String generatorsConfiguration } public class CodeGeneratorsContainer { Collection<CodeGenerator> generators } class CodeGenerator { String generatorClassName File outputBaseDir Map additionalConfiguration } 

When I started it right now, it fails with the following:

 Caused by: java.lang.NullPointerException: Cannot get property 'name' on null object at org.gradle.api.internal.DynamicPropertyNamer.determineName(DynamicPropertyNamer.groovy:36) at org.gradle.api.internal.DefaultNamedDomainObjectCollection.add(DefaultNamedDomainObjectCollection.java:70) at org.gradle.api.internal.AbstractNamedDomainObjectContainer.create(AbstractNamedDomainObjectContainer.java:58) at org.gradle.api.internal.AbstractNamedDomainObjectContainer.create(AbstractNamedDomainObjectContainer.java:52) at org.gradle.api.internal.NamedDomainObjectContainerConfigureDelegate._configure(NamedDomainObjectContainerConfigureDelegate.java:39) at org.gradle.api.internal.ConfigureDelegate.invokeMethod(ConfigureDelegate.java:73) at build_2a353qtggi869feteaqu2qgc3$_run_closure1_closure3.doCall(....\workspace2\YangUsingProject\build.gradle:25) 

And I highlighted line 25 in the build script, if that matters.

Update

After integrating the solution, this is a summary of the required configuration.

The structure of the sample in the file "build.gradle":

 yang { yangFilesRootDir "src/main/resources/yang" //excludeFiles "target.yang" inspectDependencies true generator { generatorClassName = "org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl" outputDir = "build/gen1" } generator { generatorClassName = "org.opendaylight.yangtools.yang.unified.doc.generator.maven.DocumentationGeneratorImpl" outputDir = "build/gen2" } } 

Method of using plugins:

 public void apply(Project project) { project.plugins.apply(JavaPlugin) YangExtension yang = project.extensions.create(YANG_EXT, YangExtension, project) YangGenerateTask task = project.task(YANG_GENERATE_TASK, type: YangGenerateTask) project.afterEvaluate { task.init(project, yang) } } 

Extension Class:

 class YangExtension { private Project project Collection<CodeGenerator> generators String yangFilesRootDir String[] excludeFiles boolean inspectDependencies String yangFilesConfiguration String generatorsConfiguration YangExtension(Project project) { this.project = project } CodeGenerator generator(Closure closure) { def generator = project.configure(new CodeGenerator(), closure) generators.add(generator) return generator } } 

And finally, CodeGenerator POGO:

 class CodeGenerator { String generatorClassName String outputDir Map additionalConfiguration } 
+6
source share
1 answer

The error is caused by Project.container() trying to create a NamedDomainObjectContainer . This special collection type requires that the collection type specify the name property. A common example is sourceSets , where each element has a unique name (main, test, etc.). Then the DSL will look like this:

 generators { codeGenerator { generatorClassName "org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl" outputDir "build/gen1" } docGenerator { generatorClassName "org.opendaylight.yangtools.yang.unified.doc.generator.maven.DocumentationGeneratorImpl" outputDir "build/gen2" } } 

If it makes no sense to name each generator, it is best to define a method on its extension that sets up a new instance of your Generator object. The trap is here, since you are creating an instance of the generator if you do not have all the fancy decorations that Gradle will be (for example, setter methods). If you want features, you will have to manually define these methods in the Generator class.

Update:

To use Project.container() and create a NamedDomainObjectContainer , the container type must have a name property and a public constructor that takes a String argument for the name. The value of this property is what I use when setting up the object for the first time. For instance:

 sourceSets { integTest { srcDir 'src/integTest' } } 

The code above creates and sets up a new SourceSet named "integTest" and adds it to the container. You can use this with your "generators" by simply adding this property. You would also like to modify your extension class to create a generators property of type NamedDomainObjectContainer<CodeGenerator> , and there is no need for an additional CodeGeneratorsContainer class.

 class YangExtension { NamedDomainObjectContainer<CodeGenerator> generators String yangFilesRootDir String[] excludeFiles boolean inspectDependencies String yangFilesConfiguration String generatorsConfiguration YangExtension(Project project) { this.generators = project.container(CodeGenerator) } } class CodeGenerator { String generatorClassName File outputBaseDir Map additionalConfiguration private String name CodeGenerator(String name) { this.name = name } String getName() { return this.name } } 

Update 2:

If it makes no sense to have a named object in DSL, you can simply provide a method on your extension that sets up a new CodeGenerator and adds it to the collection.

 class YangExtension { Collection<CodeGenerator> generators = new ArrayList<>() private Project project YangExtension(Project project) { this.project = project } CodeGenerator generator(Closure closure) { def generator = project.configure(new CodeGenerator(), closure) generators.add(generator) return generator } } 

There will be no generators block in your new DSL. It will look like this:

 yang { generator { generatorClassName = "org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl" outputDir = "build/gen1" } generator { generatorClassName = "org.opendaylight.yangtools.yang.unified.doc.generator.maven.DocumentationGeneratorImpl" outputDir = "build/gen2" } } 
+15
source

Source: https://habr.com/ru/post/983680/


All Articles