From b6741110b239ad4dfcd0209dd35e09ecbafb0a92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=BE=D0=B1=D0=BE=D0=BB=D0=B5=D0=B2=20=D0=92=D0=B0?= =?UTF-8?q?=D1=81=D0=B8=D0=BB=D0=B8=D0=B9?= Date: Sun, 26 Jul 2026 21:17:01 +0300 Subject: [PATCH] Add Kotlin support with automatic kapt configuration - Detect Kotlin plugin (org.jetbrains.kotlin.jvm) in project - Automatically apply kapt plugin when Kotlin is detected - Add mapstruct-processor to kapt configuration instead of annotationProcessor - Update optional dependencies (Lombok, Spring) to use kapt when Kotlin is present - Add comprehensive tests for Kotlin support - Update README with Kotlin examples and detection mechanism - Add Kotlin Gradle Plugin 2.0.21 to test dependencies --- README.md | 15 ++--- build.gradle | 4 +- .../plugins/mapstruct/MapstructPlugin.java | 3 +- .../mapstruct/manager/DependencyManager.java | 38 ++++++++---- .../plugins/mapstruct/DependencyTest.java | 58 +++++++++++++++++++ 5 files changed, 97 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index dee42c9..e6ba230 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@

Actions Status - Plugin Version - Quality Gate Status - Coverage + Plugin Version + Quality Gate Status + Coverage
- Security Rating - Reliability Rating - Maintainability Rating - Vulnerabilities + Security Rating + Reliability Rating + Maintainability Rating + Vulnerabilities
Java Gradle @@ -39,6 +39,7 @@ The plugin automatically: - Adds MapStruct dependencies (`mapstruct` and `mapstruct-processor`) - Detects optional dependencies (Lombok, Spring, Camel, Quarkus, Protobuf) in your project - Adds required binding libraries when needed (e.g., `lombok-mapstruct-binding` for Lombok) +- Detects Kotlin and automatically applies `kapt` plugin with proper configuration - Configures compiler arguments based on your `mapstruct {}` block settings - Runs after project evaluation to ensure all dependencies are resolved diff --git a/build.gradle b/build.gradle index f372f29..08d5fc5 100644 --- a/build.gradle +++ b/build.gradle @@ -1,8 +1,8 @@ plugins { + id 'com.vanniktech.maven.publish' version '0.37.0' id 'com.gradle.plugin-publish' version '2.1.1' id 'io.freefair.lombok' version '9.5.0' id 'org.sonarqube' version '7.3.1.8318' - id 'com.vanniktech.maven.publish' version '0.37.0' id 'java-gradle-plugin' id 'jacoco' } @@ -40,6 +40,8 @@ dependencies { testImplementation 'org.springframework.boot:org.springframework.boot.gradle.plugin:4.1.0' // id 'com.google.protobuf' version '0.10.0' testImplementation 'com.google.protobuf:com.google.protobuf.gradle.plugin:0.10.0' + // id 'org.jetbrains.kotlin.jvm' version '2.0.21' + testImplementation 'org.jetbrains.kotlin:kotlin-gradle-plugin:2.0.21' testImplementation 'org.assertj:assertj-core:3.27.7' testImplementation 'org.junit.jupiter:junit-jupiter:6.1.2' diff --git a/src/main/java/io/github/akazver/gradle/plugins/mapstruct/MapstructPlugin.java b/src/main/java/io/github/akazver/gradle/plugins/mapstruct/MapstructPlugin.java index ba13c77..83a0f2a 100644 --- a/src/main/java/io/github/akazver/gradle/plugins/mapstruct/MapstructPlugin.java +++ b/src/main/java/io/github/akazver/gradle/plugins/mapstruct/MapstructPlugin.java @@ -21,8 +21,7 @@ public void apply(@NotNull Project project) { project.getExtensions().create("mapstruct", MapstructExtension.class); project.afterEvaluate(it -> { - dependencyManager.addRequiredDependencies(); - dependencyManager.addOptionalDependencies(); + dependencyManager.addDependencies(); compilerArgsManager.addCompilerArgs(); }); } diff --git a/src/main/java/io/github/akazver/gradle/plugins/mapstruct/manager/DependencyManager.java b/src/main/java/io/github/akazver/gradle/plugins/mapstruct/manager/DependencyManager.java index d3439a1..f6d77b3 100644 --- a/src/main/java/io/github/akazver/gradle/plugins/mapstruct/manager/DependencyManager.java +++ b/src/main/java/io/github/akazver/gradle/plugins/mapstruct/manager/DependencyManager.java @@ -8,6 +8,7 @@ import org.gradle.api.logging.Logger; import org.gradle.api.logging.Logging; import org.gradle.api.plugins.ExtensionContainer; +import org.gradle.api.plugins.PluginManager; import static io.github.akazver.gradle.plugins.mapstruct.dependency.AdditionalDependency.*; import static io.github.akazver.gradle.plugins.mapstruct.dependency.MarkerDependency.*; @@ -27,19 +28,34 @@ public class DependencyManager { private final ConfigurationContainer configurations; private final ExtensionContainer extensions; private final DependencyHandler dependencies; + private final PluginManager pluginManager; public DependencyManager(Project project) { this.configurations = project.getConfigurations(); this.extensions = project.getExtensions(); this.dependencies = project.getDependencies(); + this.pluginManager = project.getPluginManager(); } - public void addRequiredDependencies() { + public void addDependencies() { + boolean hasKotlin = hasExtension("kotlin"); + String processorConfig = hasKotlin ? "kapt" : "annotationProcessor"; + + if (hasKotlin) { + pluginManager.apply("org.jetbrains.kotlin.kapt"); + } + + addRequiredDependencies(processorConfig); + addOptionalDependencies(processorConfig); + } + + private void addRequiredDependencies(String processorConfig) { LOGGER.lifecycle(ADDING_MESSAGE, "MapStruct"); - addDependencies(MAPSTRUCT, MAPSTRUCT_PROCESSOR); + addDependency(MAPSTRUCT); + addDependency(MAPSTRUCT_PROCESSOR, processorConfig); } - public void addOptionalDependencies() { + private void addOptionalDependencies(String processorConfig) { boolean hasLombok = hasExtension("lombok") || hasDependency(LOMBOK); boolean hasBinding = hasDependency(LOMBOK_MAPSTRUCT_BINDING); boolean hasSpringBoot = hasExtension("springBoot") || hasDependency(SPRING_BOOT); @@ -50,12 +66,14 @@ public void addOptionalDependencies() { if (hasLombok && !hasBinding) { LOGGER.lifecycle(ADDING_MESSAGE, "Lombok"); - addDependency(LOMBOK_MAPSTRUCT_BINDING); + addDependency(LOMBOK_MAPSTRUCT_BINDING, processorConfig); } if (hasSpringBoot || hasSpring) { LOGGER.lifecycle(ADDING_MESSAGE, "Spring"); - addDependencies(MAPSTRUCT_SPRING_EXTENSIONS, MAPSTRUCT_SPRING_ANNOTATIONS, MAPSTRUCT_SPRING_TEST_EXTENSIONS); + addDependency(MAPSTRUCT_SPRING_EXTENSIONS, processorConfig); + addDependency(MAPSTRUCT_SPRING_ANNOTATIONS); + addDependency(MAPSTRUCT_SPRING_TEST_EXTENSIONS); } if (hasCamel) { @@ -97,15 +115,13 @@ private boolean hasExtension(String extensionName) { return extensions.findByName(extensionName) != null; } - private void addDependency(PluginDependency pluginDependency) { + private void addDependency(PluginDependency pluginDependency, String configuration) { LOGGER.lifecycle(DEPENDENCY_PREFIX, pluginDependency.getId()); - dependencies.add(pluginDependency.getConfiguration(), pluginDependency.getId()); + dependencies.add(configuration, pluginDependency.getId()); } - private void addDependencies(PluginDependency... pluginDependencies) { - for (PluginDependency pluginDependency : pluginDependencies) { - addDependency(pluginDependency); - } + private void addDependency(PluginDependency pluginDependency) { + addDependency(pluginDependency, pluginDependency.getConfiguration()); } } diff --git a/src/test/java/io/github/akazver/gradle/plugins/mapstruct/DependencyTest.java b/src/test/java/io/github/akazver/gradle/plugins/mapstruct/DependencyTest.java index 9250742..bdac36d 100644 --- a/src/test/java/io/github/akazver/gradle/plugins/mapstruct/DependencyTest.java +++ b/src/test/java/io/github/akazver/gradle/plugins/mapstruct/DependencyTest.java @@ -7,6 +7,7 @@ import org.gradle.api.Project; import org.gradle.api.artifacts.Dependency; import org.gradle.api.artifacts.DependencySet; +import org.jetbrains.kotlin.gradle.plugin.KotlinPluginWrapper; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.boot.gradle.plugin.SpringBootPlugin; @@ -303,6 +304,63 @@ void addOptionalDependenciesWithProtobufPlugin() { optionalDependenciesWithoutSpringTest(project, expectedAnnotationProcessor, expectedImplementation); } + @Test + @DisplayName("Add dependencies with Kotlin plugin") + void addDependenciesWithKotlinPlugin() { + Project project = fetchBaseProject(); + project.getPluginManager().apply(KotlinPluginWrapper.class); + evaluate(project); + + assertThat(project.getPlugins().findPlugin("org.jetbrains.kotlin.jvm")).isNotNull(); + assertThat(project.getPlugins().findPlugin("org.jetbrains.kotlin.kapt")).isNotNull(); + + assertThat(fetchDependencyIds(project, "kapt")) + .hasSize(1) + .first() + .isEqualTo(MAPSTRUCT_PROCESSOR.getId()); + + assertThat(fetchDependencyIds(project, "implementation")) + .hasSize(1) + .first() + .isEqualTo(MAPSTRUCT.getId()); + + assertThat(fetchDependencyIds(project, "annotationProcessor")) + .isEmpty(); + } + + @Test + @DisplayName("Add dependencies with Kotlin plugin and Spring") + void addDependenciesWithKotlinPluginAndSpring() { + Project project = fetchBaseProject(); + project.getPluginManager().apply(KotlinPluginWrapper.class); + project.getDependencies().add(SPRING_CORE.getConfiguration(), SPRING_CORE.getId()); + evaluate(project); + + assertThat(project.getPlugins().findPlugin("org.jetbrains.kotlin.jvm")).isNotNull(); + assertThat(project.getPlugins().findPlugin("org.jetbrains.kotlin.kapt")).isNotNull(); + + String[] expectedKapt = { + MAPSTRUCT_PROCESSOR.getId(), + MAPSTRUCT_SPRING_EXTENSIONS.getId() + }; + + String[] expectedImplementation = { + SPRING_CORE.getId(), MAPSTRUCT.getId(), + MAPSTRUCT_SPRING_ANNOTATIONS.getId() + }; + + assertThat(fetchDependencyIds(project, "kapt")) + .hasSize(expectedKapt.length) + .containsExactlyInAnyOrder(expectedKapt); + + assertThat(fetchDependencyIds(project, "implementation")) + .hasSize(expectedImplementation.length) + .containsExactlyInAnyOrder(expectedImplementation); + + assertThat(fetchDependencyIds(project, "annotationProcessor")) + .isEmpty(); + } + private void optionalDependenciesTest(Project project, String[] expectedAnnotationProcessor, String[] expectedImplementation) { String[] expectedTestImplementation = Stream.concat(Arrays.stream(expectedImplementation), Stream.of(MAPSTRUCT_SPRING_TEST_EXTENSIONS.getId()))