Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate Maven and Gradle plugin code stubs #209

Open
andriy-dmytruk opened this issue Dec 12, 2024 · 2 comments
Open

Generate Maven and Gradle plugin code stubs #209

andriy-dmytruk opened this issue Dec 12, 2024 · 2 comments
Assignees
Labels
enhancement New feature or request

Comments

@andriy-dmytruk
Copy link
Contributor

andriy-dmytruk commented Dec 12, 2024

Description

There is a lot of maven and gradle-specific code for micronaut plugins that seems to be generateable. An example is micronaut openapi generators. We will also need plugins for JSON schema generations and possibly others in the future.

For openapi all the parameters are repeated 3 times currently:

Generating would have the advanatage that both versions would have consistent parameters and possibly similar API.

Generation description

Add a new module to generate plugins.

Given a configuration for an plugin executable, like

/**
 * Test configuration.
 *
 * @param packageName The package name to generate to
 */
@PluginConfig(mavenPropertyPrefix = "test")
public record TestConfig(
   @PluginParameter(mavenProperty = "packageName", required = true)
   String packageName,
   @PluginParameter(defaultValue = false)
   boolean serverGeneration,
   Map<String, String> additionalProperties,
   @PluginParameter(internal = true)
   String outputDirectory
) implements PluginExecutable {

   public void execute() {
       doWork(packageName, serverGeneration, additionalProperties, outputDirectory);
   }

}

we would want to generate plugin code stubs for reading these properties and calling the execute method.

Maven

we could generate maven Mojo:

public abstract class AbstractTestMojo extends AbstractMicronautMojo {

    /**
     * The package name to generate to.
     */
    @Parameter(property = "test.packageName", required = true)
    protected String packageName;

    @Parameter(defaultValue = "false")
    protected boolean serverGeneration;
    
    @Parameter
    protected Map<String, Object> additionalProperties;
    
    @Parameter(property = "test.enabled")
    protected boolean enabled;
    
    protected abstract String getOutputDirectory();
    
    @Override
    public void execute() throws MojoExecutionException, MojoFailureException {
        if (!isEnabled()) {
            getLog().debug(this.getClass().getSimpleName() + " is disabled");
            return;
        }
        
        new TestConfig(packageName, serverGeneration, additionalProperties, getOutputDirectory()).execute();
   }
}

The mojo is abstract, so that it can be extended with custom code like (the project logic is different for gradle and maven, so we have to keep it write it separate):

public class TestMojo extends AbstractTestMojo {
    @Parameter(defaultValue = "${project.build.directory}/generated-sources/openapi", required = true)
    protected File outputDirectory;
    
    @Parameter(defaultValue = "${project}", readonly = true)
    protected MavenProject project;
    
    protected String getOutputDirectory() {
        return outputDirectory;
    }
    
    public void execute() throws MojoExecutionException, MojoFailureException {
       super.execute();
       project.addCompileSourceRoot(outputDirectory.getAbsolutePath());
    }
}

Gradle

Generate a Gradle Task:

public abstract class  AbstractTestTask extends DefaultTask {
   
   /**
     * The package name to generate to.
     */
   @Input
   public abstract Property<String> getPackageName();
   
   @Input
   @Optional
   public abstract Property<Boolean> getServerGeneration();
   
   @Input
   @Optional
   public abstract MapProperty<String, Object> getAdditionalProperties();
   
   protected abstract Provider<String> getOutputDirectory();
  
   @Classpath
   public abstract ConfigurableFileCollection getClasspath();
  
   @Inject
   protected abstract WorkerExecutor getWorkerExecutor();
    
   @TaskAction
   public final void execute() {
        getWorkerExecutor().classLoaderIsolation(spec -> spec.getClasspath().from(getClasspath()))
            .submit(getWorkerAction(), params -> {
                params.getPackageName().set(getPackageName());
                params.getServerGeneration().set(getServerGeneration.orElse(false)); // default value here
                params.getAdditionalProperties().set(getAdditionalProperties.orElse(null)); // optional, so set null
                params.getOutputDirectory().set(getOutputDirectory().orElse(null)); // optional internal
            });
            
    }
    
    protected interface TestWorkActionParameters extends AbstractOpenApiWorkAction.OpenApiParameters {
        Property<String> getPackageName();
        Property<Boolean> getServerGeneration();
        MapProperty<String, Object> getAdditionalProperties();
        Property<String> getOutputDirectory();
    }
    
    public abstact TestWorkAction implements WorkAction<TestWorkActionParameters> {
    
        public void execute() {
            var parameters = getParameters();
            new TestConfig(getPackageName().get(), getServerGeneration.get(), getAdditionalProperties.get(), getoutputDirectory().get()).execute();
        }
    
    }

}

The task is abstract, so that it can be extended with custom code like (the project logic is different for gradle and maven, so we have to keep it write it separate):

@CacheableTask
public abstract class TestTask extends AbstractTestTask {
   
   @OutputDirectory
    public abstract DirectoryProperty getOutputDirectoryProp();
    
    public abstract Property<String> getOutputDirectory() {
        return getOutputDirectoryProp().map(dir -> dir.toAbsolutePath().toString());
    }

}

Additionally, gradle plugin will be written by user to extend the logic. For it an extension can be generated, as well:

public interface TestExtension {

   public abstract Property<String> getPackageName();
   
   @Optional
   public abstract Property<Boolean> getServerGeneration();
   
   @Optional
   public abstract MapProperty<String, Object> getAdditionalProperties();

}

@melix Could you take a look and give feedback if you think this could be possible? I see for OpenAPI there is more logic and it might be impossible to change now. But perhaps we can add it for new plugins.

cc @elifKurtay @graemerocher

@andriy-dmytruk andriy-dmytruk added the enhancement New feature or request label Dec 12, 2024
@graemerocher
Copy link
Contributor

looks like a good idea

@melix
Copy link
Contributor

melix commented Dec 12, 2024

Sounds like a good idea. As always the devil is in the details, but we can probably try something with the new json schema plugins. We should make sure not to end in a chicken and egg situation though, that is the Maven/Gradle plugin depends on Micronaut Sourcegen to generate sources of its plugins, but also at runtime. I think whatever we generate shouldn't depend on Micronaut runtime at all (basically we should use the generator infrastructure, but not the runtime).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants