diff --git a/starter-core/src/main/java/io/micronaut/starter/build/dependencies/MicronautDependencyUtils.java b/starter-core/src/main/java/io/micronaut/starter/build/dependencies/MicronautDependencyUtils.java index 798869862a3..ac60433bb93 100644 --- a/starter-core/src/main/java/io/micronaut/starter/build/dependencies/MicronautDependencyUtils.java +++ b/starter-core/src/main/java/io/micronaut/starter/build/dependencies/MicronautDependencyUtils.java @@ -48,6 +48,7 @@ public final class MicronautDependencyUtils { public static final String GROUP_ID_MICRONAUT_SECURITY = "io.micronaut.security"; public static final String GROUP_ID_MICRONAUT_SESSION = "io.micronaut.session"; public static final String GROUP_ID_MICRONAUT_SERVLET = "io.micronaut.servlet"; + public static final String GROUP_ID_MICRONAUT_SOURCEGEN = "io.micronaut.sourcegen"; public static final String GROUP_ID_MICRONAUT_TRACING = "io.micronaut.tracing"; public static final String GROUP_ID_MICRONAUT_TEST = "io.micronaut.test"; public static final String GROUP_ID_MICRONAUT_R2DBC = "io.micronaut.r2dbc"; @@ -125,10 +126,16 @@ public static Dependency.Builder sessionDependency() { return micronautDependency(GROUP_ID_MICRONAUT_SESSION); } + @NonNull public static Dependency.Builder servletDependency() { return micronautDependency(GROUP_ID_MICRONAUT_SERVLET); } + @NonNull + public static Dependency.Builder sourcegenDependency() { + return micronautDependency(GROUP_ID_MICRONAUT_SOURCEGEN); + } + @NonNull public static Dependency.Builder testDependency() { return micronautDependency(GROUP_ID_MICRONAUT_TEST); @@ -240,6 +247,7 @@ public static Dependency.Builder jmsDependency() { return micronautDependency(GROUP_ID_IO_MICRONAUT_JMS); } + @NonNull public static Dependency.Builder neo4j() { return micronautDependency(GROUP_ID_IO_MICRONAUT_NEO4J); } @@ -268,7 +276,8 @@ public static Dependency.Builder picocliDependency() { public static Dependency.Builder discovery() { return micronautDependency(GROUP_ID_IO_MICRONAUT_DISCOVERY); } - + + @NonNull public static Dependency.Builder flywayDependency() { return micronautDependency(GROUP_ID_MICRONAUT_FLYWAY); } @@ -344,14 +353,17 @@ public static Dependency.Builder oracleCloudDependency() { return micronautDependency(GROUP_ID_IO_MICRONAUT_ORACLE_CLOUD); } + @NonNull public static Dependency.Builder microstreamDependency() { return micronautDependency(GROUP_ID_MICRONAUT_MICROSTREAM); } + @NonNull public static Dependency.Builder springDependency() { return micronautDependency(GROUP_ID_MICRONAUT_SPRING); } + @NonNull public static Dependency.Builder viewsDependency() { return micronautDependency(GROUP_ID_MICRONAUT_VIEWS); } diff --git a/starter-core/src/main/java/io/micronaut/starter/feature/sourcegen/SourcegenFeatureValidator.java b/starter-core/src/main/java/io/micronaut/starter/feature/sourcegen/SourcegenFeatureValidator.java new file mode 100644 index 00000000000..5ae4c8e6383 --- /dev/null +++ b/starter-core/src/main/java/io/micronaut/starter/feature/sourcegen/SourcegenFeatureValidator.java @@ -0,0 +1,56 @@ +/* + * Copyright 2017-2024 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.starter.feature.sourcegen; + +import io.micronaut.core.util.StringUtils; +import io.micronaut.starter.application.ApplicationType; +import io.micronaut.starter.feature.Feature; +import io.micronaut.starter.feature.validation.FeatureValidator; +import io.micronaut.starter.options.Language; +import io.micronaut.starter.options.Options; +import jakarta.inject.Singleton; + +import java.util.List; +import java.util.Set; +import java.util.stream.Stream; + +@Singleton +public class SourcegenFeatureValidator implements FeatureValidator { + + @Override + public void validatePreProcessing(Options options, ApplicationType applicationType, Set features) { + + } + + @Override + public void validatePostProcessing(Options options, ApplicationType applicationType, Set features) { + if (features.stream().anyMatch(f -> f instanceof SourcegenJava)) { + if (!supports(options.getLanguage())) { + throw new IllegalArgumentException("sourcegen-generator is not supported in " + StringUtils.capitalize(options.getLanguage().getName()) + " applications"); + } + } + } + + public static List supportedLanguages() { + return Stream.of(Language.values()) + .filter(SourcegenFeatureValidator::supports) + .toList(); + } + + public static boolean supports(Language language) { + return language != Language.GROOVY; + } +} diff --git a/starter-core/src/main/java/io/micronaut/starter/feature/sourcegen/SourcegenJava.java b/starter-core/src/main/java/io/micronaut/starter/feature/sourcegen/SourcegenJava.java new file mode 100644 index 00000000000..f99f1d12351 --- /dev/null +++ b/starter-core/src/main/java/io/micronaut/starter/feature/sourcegen/SourcegenJava.java @@ -0,0 +1,93 @@ +/* + * Copyright 2017-2024 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.starter.feature.sourcegen; + +import io.micronaut.core.annotation.NonNull; +import io.micronaut.starter.application.ApplicationType; +import io.micronaut.starter.application.generator.GeneratorContext; +import io.micronaut.starter.build.dependencies.Dependency; +import io.micronaut.starter.build.dependencies.MicronautDependencyUtils; +import io.micronaut.starter.build.dependencies.Scope; +import io.micronaut.starter.feature.Category; +import io.micronaut.starter.feature.Feature; +import io.micronaut.starter.options.Language; +import jakarta.inject.Singleton; + +@Singleton +public class SourcegenJava implements Feature { + + public static final String NAME = "sourcegen-generator"; + + private static final Dependency DEPENDENCY_MICRONAUT_SOURCEGEN_ANNOTATIONS = MicronautDependencyUtils.sourcegenDependency() + .artifactId("micronaut-sourcegen-annotations") + .scope(Scope.COMPILE) + .build(); + + private static final Dependency DEPENDENCY_MICRONAUT_SOURCEGEN_GENERATOR_JAVA = MicronautDependencyUtils.sourcegenDependency() + .artifactId("micronaut-sourcegen-generator-java") + .scope(Scope.ANNOTATION_PROCESSOR) + .build(); + + private static final Dependency DEPENDENCY_MICRONAUT_SOURCEGEN_GENERATOR_KOTLIN = MicronautDependencyUtils.sourcegenDependency() + .artifactId("micronaut-sourcegen-generator-kotlin") + .scope(Scope.ANNOTATION_PROCESSOR) + .build(); + + @Override + @NonNull + public String getName() { + return NAME; + } + + @Override + public String getTitle() { + return "Micronaut source code generator for Java and Kotlin"; + } + + @Override + public String getDescription() { + return "Micronaut SourceGen exposes a language-neutral API for source code generation"; + } + + @Override + public String getCategory() { + return Category.API; + } + + @Override + public String getMicronautDocumentation() { + return "https://micronaut-projects.github.io/micronaut-sourcegen/latest/guide/"; + } + + @Override + public boolean supports(ApplicationType applicationType) { + return true; + } + + @Override + public void apply(GeneratorContext generatorContext) { + addDependencies(generatorContext); + } + + private void addDependencies(GeneratorContext generatorContext) { + generatorContext.addDependency(DEPENDENCY_MICRONAUT_SOURCEGEN_ANNOTATIONS); + if (generatorContext.getLanguage() == Language.JAVA) { + generatorContext.addDependency(DEPENDENCY_MICRONAUT_SOURCEGEN_GENERATOR_JAVA); + } else if (generatorContext.getLanguage() == Language.KOTLIN) { + generatorContext.addDependency(DEPENDENCY_MICRONAUT_SOURCEGEN_GENERATOR_KOTLIN); + } + } +} diff --git a/starter-core/src/test/groovy/io/micronaut/starter/feature/sourcegen/SourcegenJavaSpec.groovy b/starter-core/src/test/groovy/io/micronaut/starter/feature/sourcegen/SourcegenJavaSpec.groovy new file mode 100644 index 00000000000..8b32ab415c4 --- /dev/null +++ b/starter-core/src/test/groovy/io/micronaut/starter/feature/sourcegen/SourcegenJavaSpec.groovy @@ -0,0 +1,75 @@ +package io.micronaut.starter.feature.sourcegen + +import io.micronaut.starter.ApplicationContextSpec +import io.micronaut.starter.BuildBuilder +import io.micronaut.starter.application.ApplicationType +import io.micronaut.starter.build.BuildTestUtil +import io.micronaut.starter.build.BuildTestVerifier +import io.micronaut.starter.build.dependencies.Scope +import io.micronaut.starter.feature.Category +import io.micronaut.starter.fixture.CommandOutputFixture +import io.micronaut.starter.options.BuildTool +import io.micronaut.starter.options.Language +import spock.lang.Shared + +class SourcegenJavaSpec extends ApplicationContextSpec implements CommandOutputFixture { + + @Shared + SourcegenJava sourcegenJava = beanContext.getBean(SourcegenJava) + + void 'test readme.md with feature sourcegen-generator contains links to micronaut docs'() { + when: + Map output = generate([SourcegenJava.NAME]) + String readme = output["README.md"] + + then: + readme + readme.contains("https://micronaut-projects.github.io/micronaut-sourcegen/latest/guide/") + } + + void "feature sourcegen-generator supports applicationType=#applicationType"(ApplicationType applicationType) { + expect: + sourcegenJava.supports(applicationType) + + where: + applicationType << ApplicationType.values() + } + + void "feature sourcegen-generator feature is API category"() { + expect: + sourcegenJava.category == Category.API + } + + void 'sourcegen-generator feature not supported for groovy'() { + when: + new BuildBuilder(beanContext, BuildTool.GRADLE) + .features([SourcegenJava.NAME]) + .language(Language.GROOVY) + .render() + + then: + IllegalArgumentException e = thrown() + e.message == 'sourcegen-generator is not supported in Groovy applications' + } + + + void "test dependencies are present for #buildTool and #language"(BuildTool buildTool, Language language) { + when: + String template = new BuildBuilder(beanContext, buildTool) + .features([SourcegenJava.NAME]) + .language(language) + .render() + BuildTestVerifier verifier = BuildTestUtil.verifier(buildTool, language, template) + + then: + verifier.hasDependency("io.micronaut.sourcegen", "micronaut-sourcegen-annotations", Scope.COMPILE) + if (language == Language.JAVA) { + assert verifier.hasAnnotationProcessor("io.micronaut.sourcegen", "micronaut-sourcegen-generator-java") + } else if (language == Language.KOTLIN) { + assert verifier.hasAnnotationProcessor("io.micronaut.sourcegen", "micronaut-sourcegen-generator-kotlin") + } + + where: + [buildTool, language] << [BuildTool.values(), [Language.JAVA, Language.KOTLIN]].combinations() + } +}