@@ -188,6 +191,19 @@ public class ConvertMojo extends AbstractMojo { @Parameter(property = "failOnNoContracts", defaultValue = "true") private boolean failOnNoContracts; + /** + * If set to true then stubs are created only when contracts have changed since last + * build. + */ + @Parameter(property = "incrementalContractStubs", defaultValue = "true") + private boolean incrementalContractStubs = true; + + @Parameter(defaultValue = "${mojoExecution}", readonly = true, required = true) + private MojoExecution mojoExecution; + + @Parameter(defaultValue = "${session}", readonly = true, required = true) + private MavenSession session; + @Override public void execute() throws MojoExecutionException { if (this.skip) { @@ -205,7 +221,17 @@ public class ConvertMojo extends AbstractMojo { config.setExcludeBuildFolders(this.excludeBuildFolders); File contractsDirectory = locationOfContracts(config); contractsDirectory = contractSubfolderIfPresent(contractsDirectory); + + if (this.incrementalContractStubs && !inputFilesChangeDetected(contractsDirectory, + mojoExecution, session)) { + getLog().info("Nothing to generate - all stubs are up to date"); + return; + } + File contractsDslDir = contractsDslDir(contractsDirectory); + LeftOverPrevention leftOverPrevention = new LeftOverPrevention( + this.stubsDirectory, mojoExecution, session); + File copiedContracts = copyContracts(rootPath, config, contractsDirectory); if (this.convertToYaml) { contractsDslDir = copiedContracts; @@ -220,6 +246,7 @@ public class ConvertMojo extends AbstractMojo { config.getExcludedFiles(), config.getIncludedContracts(), config.isExcludeBuildFolders()); converter.processFiles(); + leftOverPrevention.deleteLeftOvers(); } private void convertBackedUpDslsToYaml(String rootPath, diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/main/java/org/springframework/cloud/contract/maven/verifier/GenerateStubsMojo.java b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/main/java/org/springframework/cloud/contract/maven/verifier/GenerateStubsMojo.java index ffa2383c13..6eeba83ee7 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/main/java/org/springframework/cloud/contract/maven/verifier/GenerateStubsMojo.java +++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/main/java/org/springframework/cloud/contract/maven/verifier/GenerateStubsMojo.java @@ -21,7 +21,9 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import org.apache.maven.execution.MavenSession; import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecution; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.Component; @@ -33,6 +35,8 @@ import org.apache.maven.project.MavenProjectHelper; import org.codehaus.plexus.archiver.Archiver; import org.codehaus.plexus.archiver.jar.JarArchiver; +import static org.springframework.cloud.contract.maven.verifier.ChangeDetector.inputFilesChangeDetected; + /** * Picks the converted .json files and creates a jar. Requires convert to be executed * first. @@ -86,6 +90,19 @@ public class GenerateStubsMojo extends AbstractMojo { @Parameter(defaultValue = "stubs") private String classifier; + /** + * If set to true then stubs jar is created only when stubs have changed since last + * build. + */ + @Parameter(property = "incrementalContractStubsJar", defaultValue = "true") + private boolean incrementalContractStubsJar = true; + + @Parameter(defaultValue = "${mojoExecution}", readonly = true, required = true) + private MojoExecution mojoExecution; + + @Parameter(defaultValue = "${session}", readonly = true, required = true) + private MavenSession session; + /** * When enabled, this flag will tell stub runner to throw an exception when no stubs / * contracts were found. @@ -111,6 +128,11 @@ public class GenerateStubsMojo extends AbstractMojo { + this.outputDirectory.getAbsolutePath() + "] .\nPlease make sure that spring-cloud-contract:convert was invoked"); } + if (this.incrementalContractStubsJar + && !inputFilesChangeDetected(outputDirectory, mojoExecution, session)) { + getLog().info("Nothing to generate - stubs jar is up to date"); + return; + } File stubsJarFile = createStubJar(this.outputDirectory); this.projectHelper.attachArtifact(this.project, "jar", this.classifier, stubsJarFile); diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/main/java/org/springframework/cloud/contract/maven/verifier/GenerateTestsMojo.java b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/main/java/org/springframework/cloud/contract/maven/verifier/GenerateTestsMojo.java index 61d0b49692..a970108a99 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/main/java/org/springframework/cloud/contract/maven/verifier/GenerateTestsMojo.java +++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/main/java/org/springframework/cloud/contract/maven/verifier/GenerateTestsMojo.java @@ -21,9 +21,11 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.apache.maven.execution.MavenSession; import org.apache.maven.model.Dependency; import org.apache.maven.model.Resource; import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecution; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.LifecyclePhase; @@ -245,6 +247,19 @@ public class GenerateTestsMojo extends AbstractMojo { @Parameter(property = "failOnInProgress", defaultValue = "true") private boolean failOnInProgress = true; + /** + * If set to true then tests are created only when contracts have changed since last + * build. + */ + @Parameter(property = "incrementalContractTests", defaultValue = "true") + private boolean incrementalContractTests = true; + + @Parameter(defaultValue = "${mojoExecution}", readonly = true, required = true) + private MojoExecution mojoExecution; + + @Parameter(defaultValue = "${session}", readonly = true, required = true) + private MavenSession session; + @Override public void execute() throws MojoExecutionException, MojoFailureException { if (this.skip || this.mavenTestSkip || this.skipTests) { @@ -280,6 +295,13 @@ public class GenerateTestsMojo extends AbstractMojo { this.contractsDirectory); getLog().info( "Directory with contract is present at [" + contractsDirectory + "]"); + + if (this.incrementalContractTests && !ChangeDetector + .inputFilesChangeDetected(contractsDirectory, mojoExecution, session)) { + getLog().info("Nothing to generate - all classes are up to date"); + return; + } + setupConfig(config, contractsDirectory); this.project .addTestCompileSourceRoot(this.generatedTestSourcesDir.getAbsolutePath()); @@ -297,9 +319,12 @@ public class GenerateTestsMojo extends AbstractMojo { + this.baseClassMappings); } try { + LeftOverPrevention leftOverPrevention = new LeftOverPrevention( + this.generatedTestSourcesDir, mojoExecution, session); TestGenerator generator = new TestGenerator(config); int generatedClasses = generator.generate(); getLog().info("Generated " + generatedClasses + " test classes."); + leftOverPrevention.deleteLeftOvers(); } catch (ContractVerifierException e) { throw new MojoExecutionException( diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/main/java/org/springframework/cloud/contract/maven/verifier/LeftOverPrevention.java b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/main/java/org/springframework/cloud/contract/maven/verifier/LeftOverPrevention.java new file mode 100644 index 0000000000..051bf0d93a --- /dev/null +++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/main/java/org/springframework/cloud/contract/maven/verifier/LeftOverPrevention.java @@ -0,0 +1,60 @@ +/* + * Copyright 2013-2019 the original author or 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 org.springframework.cloud.contract.maven.verifier; + +import java.io.File; + +import org.apache.maven.execution.MavenSession; +import org.apache.maven.plugin.MojoExecution; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.shared.incremental.IncrementalBuildHelper; +import org.apache.maven.shared.incremental.IncrementalBuildHelperRequest; + +/** + * Prevents the following scenario: + * + * 1. Contract is added + * + * 2. Derived file is generated + * + * 3. Contract is deleted + * + * 4. Derived file still exists + */ +class LeftOverPrevention { + + private final IncrementalBuildHelper incrementalBuildHelper; + + private final File generatedDirectory; + + LeftOverPrevention(File generatedDirectory, MojoExecution mojoExecution, + MavenSession session) throws MojoExecutionException { + this.generatedDirectory = generatedDirectory; + this.incrementalBuildHelper = new IncrementalBuildHelper(mojoExecution, session); + this.incrementalBuildHelper.beforeRebuildExecution( + new IncrementalBuildHelperRequest().outputDirectory(generatedDirectory)); + } + + void deleteLeftOvers() throws MojoExecutionException { + if (generatedDirectory.exists()) { + incrementalBuildHelper + .afterRebuildExecution(new IncrementalBuildHelperRequest() + .outputDirectory(generatedDirectory)); + } + } + +}