From 9bf0d7bdaa56ed1dbeec408c3db01996f5a0983e Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Thu, 20 Jun 2024 08:53:58 +0200 Subject: [PATCH] [MCOMPILER-593] Switch to Maven 4 API --- .github/workflows/maven-verify.yml | 10 +- pom.xml | 175 +++- src/it/MCOMPILER-192/verify.groovy | 2 +- src/it/MCOMPILER-525/invoker.properties | 3 +- src/it/mcompiler-106/pom.xml | 8 +- src/it/settings.xml | 6 + src/it/setup_annotation-verify-plugin/pom.xml | 4 +- .../plugin/compiler/AbstractCompilerMojo.java | 909 ++++++++---------- .../compiler/CompilationFailureException.java | 4 +- .../maven/plugin/compiler/CompilerMojo.java | 191 ++-- .../maven/plugin/compiler/DeltaList.java | 54 -- .../compiler/IncrementalBuildHelper.java | 204 ++++ .../compiler/ModuleInfoTransformer.java | 2 +- .../maven/plugin/compiler/Providers.java | 78 ++ .../plugin/compiler/TestCompilerMojo.java | 145 ++- .../plugin/compiler/CompilerMojoTest.java | 301 ------ .../plugin/compiler/CompilerMojoTestCase.java | 671 +++++++++++++ .../maven/plugin/compiler/MojoTestUtils.java | 75 -- .../plugin/compiler/TestCompilerMojoTest.java | 209 ---- .../compiler/stubs/DebugEnabledLog.java | 34 - .../unit/compiler-args-test/plugin-config.xml | 7 +- .../plugin-config.xml | 8 +- .../compiler-basic-test/plugin-config.xml | 7 +- .../plugin-config.xml | 10 +- .../unit/compiler-fail-test/plugin-config.xml | 7 +- .../plugin-config.xml | 7 +- .../unit/compiler-fork-test/plugin-config.xml | 11 +- .../plugin-config-none.xml | 7 +- .../plugin-config-not-set.xml | 7 +- .../plugin-config.xml | 8 +- .../plugin-config.xml | 7 +- .../plugin-config.xml | 7 +- .../unit/compiler-skip-main/plugin-config.xml | 7 +- .../unit/compiler-skip-test/plugin-config.xml | 7 +- 34 files changed, 1727 insertions(+), 1465 deletions(-) delete mode 100644 src/main/java/org/apache/maven/plugin/compiler/DeltaList.java create mode 100644 src/main/java/org/apache/maven/plugin/compiler/IncrementalBuildHelper.java create mode 100644 src/main/java/org/apache/maven/plugin/compiler/Providers.java delete mode 100644 src/test/java/org/apache/maven/plugin/compiler/CompilerMojoTest.java create mode 100644 src/test/java/org/apache/maven/plugin/compiler/CompilerMojoTestCase.java delete mode 100644 src/test/java/org/apache/maven/plugin/compiler/MojoTestUtils.java delete mode 100644 src/test/java/org/apache/maven/plugin/compiler/TestCompilerMojoTest.java delete mode 100644 src/test/java/org/apache/maven/plugin/compiler/stubs/DebugEnabledLog.java diff --git a/.github/workflows/maven-verify.yml b/.github/workflows/maven-verify.yml index cce83592..60c3ce50 100644 --- a/.github/workflows/maven-verify.yml +++ b/.github/workflows/maven-verify.yml @@ -27,11 +27,7 @@ jobs: uses: apache/maven-gh-actions-shared/.github/workflows/maven-verify.yml@v4 with: # maven-args: "-Dinvoker.parallelThreads=2" cannot do this as this generate some concurrent download issues + ff-maven: "4.0.0-beta-3" # Maven version for fail-fast-build jdk-distribution-matrix: '[ "temurin", "zulu", "microsoft", "adopt-openj9" ]' - jdk-matrix: '[ "8", "11", "17", "21" ]' - matrix-exclude: '[ - { "jdk": "8", "distribution": "microsoft" }, - { "jdk": "8", "distribution": "temurin", "os": "macos-latest" }, - { "jdk": "8", "distribution": "adopt-openj9", "os": "macos-latest" } - ]' - maven-matrix: '[ "3.6.3", "3.8.8", "3.9.6" ]' # Maven versions matrix for verify builds + jdk-matrix: '[ "17", "21" ]' + maven-matrix: '[ "4.0.0-beta-3" ]' # Maven versions matrix for verify builds diff --git a/pom.xml b/pom.xml index 5a83a207..6afc21e0 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ under the License. maven-compiler-plugin - 3.13.1-SNAPSHOT + 4.0.0-SNAPSHOT maven-plugin Apache Maven Compiler Plugin @@ -67,48 +67,75 @@ under the License. - 3.6.3 + 4.0.0-beta-3 + + 3.13.1 + 4.0.0-SNAPSHOT 2.15.0 2.4.21 3.7.0 2.5.14-02 - 1.2.0 - 8 + 1.1.1 + 4.0.1 + 17 + 5.10.1 + 4.11.0 + 3.2.1 + 4.0.0-alpha-3-SNAPSHOT + 3.7.0 false 2024-03-15T07:28:09Z org.apache.maven.plugins.compiler.its + + + + + com.thoughtworks.qdox + qdox + 2.0.1 + + + org.junit + junit-bom + ${junitVersion} + pom + import + + + org.mockito + mockito-bom + ${mockitoVersion} + pom + import + + + + - - org.apache.maven.plugin-tools - maven-plugin-annotations - provided - org.apache.maven - maven-plugin-api + maven-api-core ${mavenVersion} provided - org.apache.maven - maven-artifact - ${mavenVersion} - provided + org.slf4j + slf4j-api + 2.0.13 - org.apache.maven - maven-core - ${mavenVersion} - provided + org.codehaus.plexus + plexus-utils - org.apache.maven.shared - maven-shared-utils - 3.4.2 + org.codehaus.plexus + plexus-xml org.apache.maven.shared @@ -127,68 +154,114 @@ under the License. org.apache.maven.shared maven-shared-utils - - org.codehaus.plexus - plexus-component-annotations - + + org.apache.maven.shared + maven-shared-utils + 3.4.2 + + + org.eclipse.sisu + org.eclipse.sisu.plexus + 0.9.0.M2 + org.codehaus.plexus plexus-java ${plexus-java.version} + + org.ow2.asm + asm + 9.7 + org.codehaus.plexus plexus-compiler-api ${plexusCompilerVersion} + + + org.codehaus.plexus + plexus-component-api + + org.codehaus.plexus plexus-compiler-manager ${plexusCompilerVersion} + + + org.codehaus.plexus + plexus-component-api + + org.codehaus.plexus plexus-compiler-javac ${plexusCompilerVersion} - runtime + + + org.codehaus.plexus + plexus-component-api + + + - org.codehaus.plexus - plexus-utils + org.apache.maven + maven-core + ${mavenVersion} + test - - org.apache.maven.plugin-testing - maven-plugin-testing-harness - 4.0.0-alpha-2 + com.google.inject + guice + 6.0.0 test - - org.codehaus.plexus - plexus-xml + org.apache.maven + maven-model-builder + ${mavenVersion} + test + + + org.apache.maven + maven-xml-impl + ${mavenVersion} + test + + + org.apache.maven.plugin-testing + maven-plugin-testing-harness + ${mavenPluginTestingHarnessVersion} test org.mockito mockito-core - 4.8.0 test org.junit.jupiter - junit-jupiter-api - 5.10.2 + junit-jupiter test - org.junit.jupiter - junit-jupiter-params - 5.10.2 + junit + junit + 4.13.2 + test + + + org.slf4j + slf4j-simple + 2.0.13 test @@ -225,8 +298,24 @@ under the License. - org.eclipse.sisu - sisu-maven-plugin + org.codehaus.plexus + plexus-component-metadata + 2.1.1 + + + descriptors + + generate-metadata + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + **/*$*, unit/** + diff --git a/src/it/MCOMPILER-192/verify.groovy b/src/it/MCOMPILER-192/verify.groovy index a23fe871..e7f065a4 100644 --- a/src/it/MCOMPILER-192/verify.groovy +++ b/src/it/MCOMPILER-192/verify.groovy @@ -22,7 +22,7 @@ assert logFile.exists() def content = logFile.getText('UTF-8') def causedByExpected = content.contains ( 'Caused by: org.apache.maven.plugin.compiler.CompilationFailureException: Compilation failure' ) -def twoFilesBeingCompiled = content.contains ( '[INFO] Compiling 2 source files with javac ' ) +def twoFilesBeingCompiled = content.contains ( '[INFO] Compiling 2 source files ' ) def checkResult = content.contains ( '[INFO] BUILD FAILURE' ) def compilationFailure1 = content.contains( '[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:') diff --git a/src/it/MCOMPILER-525/invoker.properties b/src/it/MCOMPILER-525/invoker.properties index da0f0927..02bb1558 100644 --- a/src/it/MCOMPILER-525/invoker.properties +++ b/src/it/MCOMPILER-525/invoker.properties @@ -18,4 +18,5 @@ # NOTE: The first time, we run up to "integration-test" phase which includes the AntRun execution which saves the # timestamp of the first JAR for comparison with the timestamp of the JAR from the final "package" invocation. # Note: -invoker.goals = clean integration-test package -Dmaven.compiler.showCompilationChanges=true +invoker.goals.1 = clean integration-test +invoker.goals.2 = package -Dmaven.compiler.showCompilationChanges=true diff --git a/src/it/mcompiler-106/pom.xml b/src/it/mcompiler-106/pom.xml index edbafc0b..0cfe2b90 100644 --- a/src/it/mcompiler-106/pom.xml +++ b/src/it/mcompiler-106/pom.xml @@ -33,10 +33,10 @@ under the License. @project.version@ true - - - true - + + -Xlint + -Averbose=true + diff --git a/src/it/settings.xml b/src/it/settings.xml index 0a5c986f..f4333950 100644 --- a/src/it/settings.xml +++ b/src/it/settings.xml @@ -32,17 +32,21 @@ under the License. @localRepositoryUrl@ true + ignore true + ignore false + ignore true + ignore plexus-snapshots Plexus Snapshot Repository @@ -55,9 +59,11 @@ under the License. @localRepositoryUrl@ true + ignore true + ignore diff --git a/src/it/setup_annotation-verify-plugin/pom.xml b/src/it/setup_annotation-verify-plugin/pom.xml index 206df9b5..2408814d 100644 --- a/src/it/setup_annotation-verify-plugin/pom.xml +++ b/src/it/setup_annotation-verify-plugin/pom.xml @@ -32,7 +32,7 @@ org.apache.maven.plugin-tools maven-plugin-annotations - @version.maven-plugin-tools@ + @version.maven-plugin-tools-3.x@ provided @@ -99,7 +99,7 @@ org.apache.maven.plugins maven-plugin-plugin - @version.maven-plugin-tools@ + @version.maven-plugin-tools-3.x@ annotation-verify diff --git a/src/main/java/org/apache/maven/plugin/compiler/AbstractCompilerMojo.java b/src/main/java/org/apache/maven/plugin/compiler/AbstractCompilerMojo.java index 2dc4ddb8..95c81c54 100644 --- a/src/main/java/org/apache/maven/plugin/compiler/AbstractCompilerMojo.java +++ b/src/main/java/org/apache/maven/plugin/compiler/AbstractCompilerMojo.java @@ -26,12 +26,10 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.time.Instant; -import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; -import java.util.Comparator; -import java.util.Date; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; @@ -42,28 +40,22 @@ import java.util.Properties; import java.util.Set; import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.apache.maven.RepositoryUtils; -import org.apache.maven.artifact.handler.ArtifactHandler; -import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager; -import org.apache.maven.execution.MavenExecutionRequest; -import org.apache.maven.execution.MavenSession; -import org.apache.maven.model.Dependency; -import org.apache.maven.model.DependencyManagement; -import org.apache.maven.plugin.AbstractMojo; -import org.apache.maven.plugin.MojoExecution; -import org.apache.maven.plugin.MojoExecutionException; -import org.apache.maven.plugins.annotations.Component; -import org.apache.maven.plugins.annotations.Parameter; -import org.apache.maven.project.MavenProject; -import org.apache.maven.shared.incremental.IncrementalBuildHelper; -import org.apache.maven.shared.incremental.IncrementalBuildHelperRequest; -import org.apache.maven.shared.utils.StringUtils; -import org.apache.maven.shared.utils.logging.MessageBuilder; -import org.apache.maven.shared.utils.logging.MessageUtils; -import org.apache.maven.toolchain.Toolchain; -import org.apache.maven.toolchain.ToolchainManager; + +import org.apache.maven.api.*; +import org.apache.maven.api.di.Inject; +import org.apache.maven.api.plugin.Log; +import org.apache.maven.api.plugin.Mojo; +import org.apache.maven.api.plugin.MojoException; +import org.apache.maven.api.plugin.annotations.Parameter; +import org.apache.maven.api.services.ArtifactManager; +import org.apache.maven.api.services.DependencyCoordinateFactory; +import org.apache.maven.api.services.DependencyCoordinateFactoryRequest; +import org.apache.maven.api.services.DependencyResolver; +import org.apache.maven.api.services.DependencyResolverRequest; +import org.apache.maven.api.services.MessageBuilder; +import org.apache.maven.api.services.MessageBuilderFactory; +import org.apache.maven.api.services.ProjectManager; +import org.apache.maven.api.services.ToolchainManager; import org.codehaus.plexus.compiler.Compiler; import org.codehaus.plexus.compiler.CompilerConfiguration; import org.codehaus.plexus.compiler.CompilerException; @@ -79,16 +71,12 @@ import org.codehaus.plexus.compiler.util.scan.mapping.SuffixMapping; import org.codehaus.plexus.languages.java.jpms.JavaModuleDescriptor; import org.codehaus.plexus.languages.java.version.JavaVersion; +import org.codehaus.plexus.logging.AbstractLogger; +import org.codehaus.plexus.logging.LogEnabled; +import org.codehaus.plexus.logging.Logger; import org.codehaus.plexus.util.FileUtils; -import org.eclipse.aether.RepositorySystem; -import org.eclipse.aether.artifact.Artifact; -import org.eclipse.aether.artifact.ArtifactTypeRegistry; -import org.eclipse.aether.artifact.DefaultArtifact; -import org.eclipse.aether.collection.CollectRequest; -import org.eclipse.aether.graph.Exclusion; -import org.eclipse.aether.resolution.DependencyRequest; -import org.eclipse.aether.resolution.DependencyResult; -import org.eclipse.aether.util.artifact.JavaScopes; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.StringUtils; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Opcodes; @@ -102,7 +90,7 @@ * @author Trygve Laugstøl * @since 2.0 */ -public abstract class AbstractCompilerMojo extends AbstractMojo { +public abstract class AbstractCompilerMojo implements Mojo { protected static final String PS = System.getProperty("path.separator"); private static final String INPUT_FILES_LST_FILENAME = "inputFiles.lst"; @@ -111,6 +99,9 @@ public abstract class AbstractCompilerMojo extends AbstractMojo { static final String DEFAULT_TARGET = "1.8"; + // Used to compare with older targets + static final String MODULE_INFO_TARGET = "1.9"; + // ---------------------------------------------------------------------- // Configurables // ---------------------------------------------------------------------- @@ -121,7 +112,7 @@ public abstract class AbstractCompilerMojo extends AbstractMojo { * @since 2.0.2 */ @Parameter(property = "maven.compiler.failOnError", defaultValue = "true") - private boolean failOnError = true; + protected boolean failOnError = true; /** * Indicates whether the build will continue even if there are compilation warnings. @@ -129,7 +120,7 @@ public abstract class AbstractCompilerMojo extends AbstractMojo { * @since 3.6 */ @Parameter(property = "maven.compiler.failOnWarning", defaultValue = "false") - private boolean failOnWarning; + protected boolean failOnWarning; /** * Set to true to include debugging information in the compiled class files. @@ -137,7 +128,7 @@ public abstract class AbstractCompilerMojo extends AbstractMojo { * @see #debuglevel */ @Parameter(property = "maven.compiler.debug", defaultValue = "true") - private boolean debug = true; + protected boolean debug = true; /** * Set to true to generate metadata for reflection on method parameters. @@ -145,7 +136,7 @@ public abstract class AbstractCompilerMojo extends AbstractMojo { * @see javac -parameters */ @Parameter(property = "maven.compiler.parameters", defaultValue = "false") - private boolean parameters; + protected boolean parameters; /** * Set to true to enable preview language features of the java compiler @@ -153,7 +144,7 @@ public abstract class AbstractCompilerMojo extends AbstractMojo { * @see javac --enable-preview */ @Parameter(property = "maven.compiler.enablePreview", defaultValue = "false") - private boolean enablePreview; + protected boolean enablePreview; /** * Set to true to show messages about what the compiler is doing. @@ -161,13 +152,13 @@ public abstract class AbstractCompilerMojo extends AbstractMojo { * @see javac -verbose */ @Parameter(property = "maven.compiler.verbose", defaultValue = "false") - private boolean verbose; + protected boolean verbose; /** * Sets whether to show source locations where deprecated APIs are used. */ @Parameter(property = "maven.compiler.showDeprecation", defaultValue = "false") - private boolean showDeprecation; + protected boolean showDeprecation; /** * Set to true to optimize the compiled code using the compiler's optimization methods. @@ -175,13 +166,13 @@ public abstract class AbstractCompilerMojo extends AbstractMojo { */ @Deprecated @Parameter(property = "maven.compiler.optimize", defaultValue = "false") - private boolean optimize; + protected boolean optimize; /** * Set to false to disable warnings during compilation. */ @Parameter(property = "maven.compiler.showWarnings", defaultValue = "true") - private boolean showWarnings; + protected boolean showWarnings; /** *

The {@code -source} argument for the Java compiler.

@@ -224,21 +215,21 @@ public abstract class AbstractCompilerMojo extends AbstractMojo { * @see javac -encoding */ @Parameter(property = "encoding", defaultValue = "${project.build.sourceEncoding}") - private String encoding; + protected String encoding; /** * Sets the granularity in milliseconds of the last modification * date for testing whether a source needs recompilation. */ @Parameter(property = "lastModGranularityMs", defaultValue = "0") - private int staleMillis; + protected int staleMillis; /** * The compiler id of the compiler to use. See this * guide for more information. */ @Parameter(property = "maven.compiler.compilerId", defaultValue = "javac") - private String compilerId; + protected String compilerId; /** * Version of the compiler to use, ex. "1.3", "1.5", if {@link #fork} is set to true. @@ -247,14 +238,14 @@ public abstract class AbstractCompilerMojo extends AbstractMojo { */ @Deprecated @Parameter(property = "maven.compiler.compilerVersion") - private String compilerVersion; + protected String compilerVersion; /** * Allows running the compiler in a separate process. * If false it uses the built in compiler, while if true it will use an executable. */ @Parameter(property = "maven.compiler.fork", defaultValue = "false") - private boolean fork; + protected boolean fork; /** * Initial size, in megabytes, of the memory allocation pool, ex. "64", "64m" @@ -263,7 +254,7 @@ public abstract class AbstractCompilerMojo extends AbstractMojo { * @since 2.0.1 */ @Parameter(property = "maven.compiler.meminitial") - private String meminitial; + protected String meminitial; /** * Sets the maximum size, in megabytes, of the memory allocation pool, ex. "128", "128m" @@ -272,13 +263,13 @@ public abstract class AbstractCompilerMojo extends AbstractMojo { * @since 2.0.1 */ @Parameter(property = "maven.compiler.maxmem") - private String maxmem; + protected String maxmem; /** * Sets the executable of the compiler to use when {@link #fork} is true. */ @Parameter(property = "maven.compiler.executable") - private String executable; + protected String executable; /** *

@@ -299,7 +290,7 @@ public abstract class AbstractCompilerMojo extends AbstractMojo { * @see javac Annotation Processing */ @Parameter(property = "maven.compiler.proc") - private String proc; + protected String proc; /** *

@@ -312,7 +303,7 @@ public abstract class AbstractCompilerMojo extends AbstractMojo { * @see javac Annotation Processing */ @Parameter - private String[] annotationProcessors; + protected String[] annotationProcessors; /** *

@@ -353,7 +344,7 @@ public abstract class AbstractCompilerMojo extends AbstractMojo { * */ @Parameter - private List annotationProcessorPaths; + protected List annotationProcessorPaths; /** *

@@ -369,37 +360,7 @@ public abstract class AbstractCompilerMojo extends AbstractMojo { * @since 3.12.0 */ @Parameter(defaultValue = "false") - private boolean annotationProcessorPathsUseDepMgmt; - - /** - *

- * Sets the arguments to be passed to the compiler (prepending a dash). - *

- *

- * This is because the list of valid arguments passed to a Java compiler varies based on the compiler version. - *

- *

- * Note that {@code -J} options are only passed through if {@link #fork} is set to {@code true}. - *

- *

- * To pass -Xmaxerrs 1000 -Xlint -Xlint:-path -Averbose=true you should include the following: - *

- * - *
-     * <compilerArguments>
-     *   <Xmaxerrs>1000</Xmaxerrs>
-     *   <Xlint/>
-     *   <Xlint:-path/>
-     *   <Averbose>true</Averbose>
-     * </compilerArguments>
-     * 
- * - * @since 2.0.1 - * @deprecated use {@link #compilerArgs} instead. - */ - @Parameter - @Deprecated - protected Map compilerArguments; + protected boolean annotationProcessorPathsUseDepMgmt; /** *

@@ -468,13 +429,7 @@ public abstract class AbstractCompilerMojo extends AbstractMojo { * @see javac -implicit */ @Parameter(property = "maven.compiler.implicit") - private String implicit; - - /** - * - */ - @Component - private ToolchainManager toolchainManager; + protected String implicit; /** *

@@ -506,7 +461,7 @@ public abstract class AbstractCompilerMojo extends AbstractMojo { * @since 3.6 */ @Parameter - private Map jdkToolchain; + protected Map jdkToolchain; // ---------------------------------------------------------------------- // Read-only parameters @@ -515,33 +470,33 @@ public abstract class AbstractCompilerMojo extends AbstractMojo { /** * The directory to run the compiler from if fork is true. */ - @Parameter(defaultValue = "${basedir}", required = true, readonly = true) - private File basedir; + @Parameter(defaultValue = "${project.basedir}", required = true, readonly = true) + protected Path basedir; /** * The target directory of the compiler if fork is true. */ @Parameter(defaultValue = "${project.build.directory}", required = true, readonly = true) - private File buildDirectory; + protected Path buildDirectory; /** * Plexus compiler manager. */ - @Component - private CompilerManager compilerManager; + @Inject + protected CompilerManager compilerManager; /** * The current build session instance. This is used for toolchain manager API calls. */ - @Parameter(defaultValue = "${session}", readonly = true, required = true) - private MavenSession session; + @Inject + protected Session session; /** * The current project instance. This is used for propagating generated-sources paths as compile/testCompile source * roots. */ - @Parameter(defaultValue = "${project}", readonly = true, required = true) - private MavenProject project; + @Inject + protected Project project; /** * Strategy to re use javacc class created: @@ -557,23 +512,13 @@ public abstract class AbstractCompilerMojo extends AbstractMojo { * @since 2.5 */ @Parameter(defaultValue = "${reuseCreated}", property = "maven.compiler.compilerReuseStrategy") - private String compilerReuseStrategy = "reuseCreated"; + protected String compilerReuseStrategy = "reuseCreated"; /** * @since 2.5 */ @Parameter(defaultValue = "false", property = "maven.compiler.skipMultiThreadWarning") - private boolean skipMultiThreadWarning; - - /** - * Legacy parameter name of {@link #forceLegacyJavacApi}. Only considered if {@link #forceLegacyJavacApi} is - * not set or {@code false}. - * @since 3.0 - * @deprecated Use {@link #forceLegacyJavacApi} instead - */ - @Deprecated - @Parameter(defaultValue = "false", property = "maven.compiler.forceJavacCompilerUse") - private boolean forceJavacCompilerUse; + protected boolean skipMultiThreadWarning; /** * The underlying compiler now uses {@code javax.tools} API @@ -586,21 +531,22 @@ public abstract class AbstractCompilerMojo extends AbstractMojo { * @since 3.13 */ @Parameter(defaultValue = "false", property = "maven.compiler.forceLegacyJavacApi") - private boolean forceLegacyJavacApi; + protected boolean forceLegacyJavacApi; /** * @since 3.0 needed for storing the status for the incremental build support. */ - @Parameter(defaultValue = "${mojoExecution}", readonly = true, required = true) - private MojoExecution mojoExecution; + @Parameter(defaultValue = "maven-status/${mojo.plugin.descriptor.id}/${mojo.goal}/${mojo.executionId}") + protected String mojoStatusPath; /** * File extensions to check timestamp for incremental build. + * Default contains only class and jar. * * @since 3.1 */ - @Parameter(defaultValue = "class,jar") - private Set fileExtensions; + @Parameter + protected List fileExtensions; /** *

to enable/disable incremental compilation feature.

@@ -619,7 +565,7 @@ public abstract class AbstractCompilerMojo extends AbstractMojo { * @since 3.1 */ @Parameter(defaultValue = "true", property = "maven.compiler.useIncrementalCompilation") - private boolean useIncrementalCompilation = true; + protected boolean useIncrementalCompilation = true; /** * Package info source files that only contain javadoc and no annotation on the package @@ -631,10 +577,10 @@ public abstract class AbstractCompilerMojo extends AbstractMojo { * @since 3.10 */ @Parameter(defaultValue = "true", property = "maven.compiler.createMissingPackageInfoClass") - private boolean createMissingPackageInfoClass = true; + protected boolean createMissingPackageInfoClass = true; @Parameter(defaultValue = "false", property = "maven.compiler.showCompilationChanges") - private boolean showCompilationChanges = false; + protected boolean showCompilationChanges = false; /** * Timestamp for reproducible output archive entries, either formatted as ISO 8601 @@ -643,19 +589,22 @@ public abstract class AbstractCompilerMojo extends AbstractMojo { * @since 3.12.0 */ @Parameter(defaultValue = "${project.build.outputTimestamp}") - private String outputTimestamp; + protected String outputTimestamp; - /** - * Resolves the artifacts needed. - */ - @Component - private RepositorySystem repositorySystem; + @Inject + protected ProjectManager projectManager; - /** - * Artifact handler manager. - */ - @Component - private ArtifactHandlerManager artifactHandlerManager; + @Inject + protected ArtifactManager artifactManager; + + @Inject + protected ToolchainManager toolchainManager; + + @Inject + protected MessageBuilderFactory messageBuilderFactory; + + @Inject + protected Log logger; protected abstract SourceInclusionScanner getSourceInclusionScanner(int staleMillis); @@ -667,11 +616,11 @@ public abstract class AbstractCompilerMojo extends AbstractMojo { protected abstract Map getPathElements(); - protected abstract List getCompileSourceRoots(); + protected abstract List getCompileSourceRoots(); - protected abstract void preparePaths(Set sourceFiles); + protected abstract void preparePaths(Set sourceFiles); - protected abstract File getOutputDirectory(); + protected abstract Path getOutputDirectory(); protected abstract String getSource(); @@ -681,29 +630,18 @@ public abstract class AbstractCompilerMojo extends AbstractMojo { protected abstract String getCompilerArgument(); - protected abstract Map getCompilerArguments(); - - protected abstract File getGeneratedSourcesDirectory(); + protected abstract Path getGeneratedSourcesDirectory(); protected abstract String getDebugFileName(); - protected final MavenProject getProject() { + protected final Project getProject() { return project; } - protected final Optional getModuleDeclaration(final Set sourceFiles) { - for (File sourceFile : sourceFiles) { - if ("module-info.java".equals(sourceFile.getName())) { - return Optional.of(sourceFile.toPath()); - } - } - return Optional.empty(); - } - private boolean targetOrReleaseSet; @Override - public void execute() throws MojoExecutionException, CompilationFailureException { + public void execute() { // ---------------------------------------------------------------------- // Look up the compiler. This is done before other code than can // cause the mojo to return before the lookup is done possibly resulting @@ -716,38 +654,41 @@ public void execute() throws MojoExecutionException, CompilationFailureException try { compiler = compilerManager.getCompiler(compilerId); + if (compiler instanceof LogEnabled) { + ((LogEnabled) compiler).enableLogging(new MavenLogger()); + } } catch (NoSuchCompilerException e) { - throw new MojoExecutionException("No such compiler '" + e.getCompilerId() + "'.", e); + throw new MojoException("No such compiler '" + e.getCompilerId() + "'."); } // -----------toolchains start here ---------------------------------- // use the compilerId as identifier for toolchains as well. - Toolchain tc = getToolchain(); - if (tc != null) { + Optional tc = getToolchain(); + if (tc.isPresent()) { getLog().info("Toolchain in maven-compiler-plugin: " + tc); if (executable != null) { getLog().warn("Toolchains are ignored, 'executable' parameter is set to " + executable); } else { fork = true; // TODO somehow shaky dependency between compilerId and tool executable. - executable = tc.findTool(compilerId); + executable = tc.get().findTool(compilerId); } } // ---------------------------------------------------------------------- // // ---------------------------------------------------------------------- - List compileSourceRoots = removeEmptyCompileSourceRoots(getCompileSourceRoots()); + List compileSourceRoots = removeEmptyCompileSourceRoots(getCompileSourceRoots()); if (compileSourceRoots.isEmpty()) { getLog().info("No sources to compile"); - return; } // Verify that target or release is set if (!targetOrReleaseSet) { - MessageBuilder mb = MessageUtils.buffer() + MessageBuilder mb = messageBuilderFactory + .builder() .a("No explicit value set for target or release! ") .a("To ensure the same result even after upgrading this plugin, please add ") .newline() @@ -755,7 +696,7 @@ public void execute() throws MojoExecutionException, CompilationFailureException writePlugin(mb); - getLog().warn(mb.toString()); + getLog().warn(mb.build()); } // ---------------------------------------------------------------------- @@ -764,7 +705,8 @@ public void execute() throws MojoExecutionException, CompilationFailureException CompilerConfiguration compilerConfiguration = new CompilerConfiguration(); - compilerConfiguration.setOutputLocation(getOutputDirectory().getAbsolutePath()); + compilerConfiguration.setOutputLocation( + getOutputDirectory().toAbsolutePath().toString()); compilerConfiguration.setOptimize(optimize); @@ -774,7 +716,7 @@ public void execute() throws MojoExecutionException, CompilationFailureException compilerConfiguration.setImplicitOption(implicit); - if (debug && (debuglevel != null && !debuglevel.isEmpty())) { + if (debug && StringUtils.isNotEmpty(debuglevel)) { String[] split = StringUtils.split(debuglevel, ","); for (String aSplit : split) { if (!(aSplit.equalsIgnoreCase("none") @@ -798,11 +740,6 @@ public void execute() throws MojoExecutionException, CompilationFailureException compilerConfiguration.setFailOnWarning(failOnWarning); - if (failOnWarning && !showWarnings) { - getLog().warn("The property failOnWarning is set to true, but showWarnings is set to false."); - getLog().warn("With compiler's warnings silenced the failOnWarning has no effect."); - } - compilerConfiguration.setShowDeprecation(showDeprecation); compilerConfiguration.setSourceVersion(getSource()); @@ -813,39 +750,40 @@ public void execute() throws MojoExecutionException, CompilationFailureException compilerConfiguration.setProc(proc); - File generatedSourcesDirectory = getGeneratedSourcesDirectory(); + Path generatedSourcesDirectory = getGeneratedSourcesDirectory(); compilerConfiguration.setGeneratedSourcesDirectory( - generatedSourcesDirectory != null ? generatedSourcesDirectory.getAbsoluteFile() : null); + generatedSourcesDirectory != null + ? generatedSourcesDirectory.toFile().getAbsoluteFile() + : null); if (generatedSourcesDirectory != null) { - if (!generatedSourcesDirectory.exists()) { - generatedSourcesDirectory.mkdirs(); + if (!Files.exists(generatedSourcesDirectory)) { + try { + Files.createDirectories(generatedSourcesDirectory); + } catch (IOException e) { + throw new MojoException("Unable to create directory: " + generatedSourcesDirectory, e); + } } - String generatedSourcesPath = generatedSourcesDirectory.getAbsolutePath(); + Path generatedSourcesPath = generatedSourcesDirectory.toAbsolutePath(); compileSourceRoots.add(generatedSourcesPath); - if (isTestCompile()) { - getLog().debug("Adding " + generatedSourcesPath + " to test-compile source roots:\n " - + StringUtils.join(project.getTestCompileSourceRoots().iterator(), "\n ")); + ProjectScope scope = isTestCompile() ? ProjectScope.TEST : ProjectScope.MAIN; - project.addTestCompileSourceRoot(generatedSourcesPath); + getLog().debug("Adding " + generatedSourcesPath + " to " + scope.id() + "-compile source roots:\n " + + StringUtils.join( + projectManager.getCompileSourceRoots(project, scope).iterator(), "\n ")); - getLog().debug("New test-compile source roots:\n " - + StringUtils.join(project.getTestCompileSourceRoots().iterator(), "\n ")); - } else { - getLog().debug("Adding " + generatedSourcesPath + " to compile source roots:\n " - + StringUtils.join(project.getCompileSourceRoots().iterator(), "\n ")); + projectManager.addCompileSourceRoot(project, scope, generatedSourcesPath); - project.addCompileSourceRoot(generatedSourcesPath); - - getLog().debug("New compile source roots:\n " - + StringUtils.join(project.getCompileSourceRoots().iterator(), "\n ")); - } + getLog().debug("New " + scope.id() + "-compile source roots:\n " + + StringUtils.join( + projectManager.getCompileSourceRoots(project, scope).iterator(), "\n ")); } - compilerConfiguration.setSourceLocations(compileSourceRoots); + compilerConfiguration.setSourceLocations( + compileSourceRoots.stream().map(Path::toString).collect(Collectors.toList())); compilerConfiguration.setAnnotationProcessors(annotationProcessors); @@ -856,7 +794,7 @@ public void execute() throws MojoExecutionException, CompilationFailureException compilerConfiguration.setFork(fork); if (fork) { - if (!(meminitial == null || meminitial.isEmpty())) { + if (!StringUtils.isEmpty(meminitial)) { String value = getMemoryValue(meminitial); if (value != null) { @@ -866,7 +804,7 @@ public void execute() throws MojoExecutionException, CompilationFailureException } } - if (!(maxmem == null || maxmem.isEmpty())) { + if (!StringUtils.isEmpty(maxmem)) { String value = getMemoryValue(maxmem); if (value != null) { @@ -879,11 +817,11 @@ public void execute() throws MojoExecutionException, CompilationFailureException compilerConfiguration.setExecutable(executable); - compilerConfiguration.setWorkingDirectory(basedir); + compilerConfiguration.setWorkingDirectory(basedir.toFile()); compilerConfiguration.setCompilerVersion(compilerVersion); - compilerConfiguration.setBuildDirectory(buildDirectory); + compilerConfiguration.setBuildDirectory(buildDirectory.toFile()); compilerConfiguration.setOutputFileName(outputFileName); @@ -911,15 +849,13 @@ public void execute() throws MojoExecutionException, CompilationFailureException getLog().debug("CompilerReuseStrategy: " + compilerConfiguration.getCompilerReuseStrategy().getStrategy()); - compilerConfiguration.setForceJavacCompilerUse(forceLegacyJavacApi || forceJavacCompilerUse); + compilerConfiguration.setForceJavacCompilerUse(forceLegacyJavacApi); boolean canUpdateTarget; - IncrementalBuildHelper incrementalBuildHelper = new IncrementalBuildHelper(mojoExecution, session); - - final Set sources; + IncrementalBuildHelper incrementalBuildHelper = null; - IncrementalBuildHelperRequest incrementalBuildHelperRequest = null; + final Set sources; if (useIncrementalCompilation) { getLog().debug("useIncrementalCompilation enabled"); @@ -930,36 +866,45 @@ public void execute() throws MojoExecutionException, CompilationFailureException preparePaths(sources); - incrementalBuildHelperRequest = new IncrementalBuildHelperRequest().inputFiles(sources); - - // Strategies used to detect modifications. - String immutableOutputFile = (compiler.getCompilerOutputStyle() - .equals(CompilerOutputStyle.ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES) - && !canUpdateTarget) - ? "immutable single output file" - : null; - String dependencyChanged = isDependencyChanged() ? "changed dependency" : null; - String sourceChanged = isSourceChanged(compilerConfiguration, compiler) ? "changed source code" : null; - String inputFileTreeChanged = hasInputFileTreeChanged(incrementalBuildHelper, sources) - ? "added or removed source files" - : null; - - // Get the first cause for the rebuild compilation detection. - String cause = Stream.of(immutableOutputFile, dependencyChanged, sourceChanged, inputFileTreeChanged) - .filter(Objects::nonNull) - .findFirst() - .orElse(null); - - if (cause != null) { - getLog().info("Recompiling the module because of " - + MessageUtils.buffer().strong(cause) + "."); - compilerConfiguration.setSourceFiles(sources); + incrementalBuildHelper = + new IncrementalBuildHelper(mojoStatusPath, sources, buildDirectory, getOutputDirectory()); + + List added = new ArrayList<>(); + List removed = new ArrayList<>(); + boolean immutableOutputFile = compiler.getCompilerOutputStyle() + .equals(CompilerOutputStyle.ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES) + && !canUpdateTarget; + boolean dependencyChanged = isDependencyChanged(); + boolean sourceChanged = isSourceChanged(compilerConfiguration, compiler); + boolean inputFileTreeChanged = incrementalBuildHelper.inputFileTreeChanged(added, removed); + // CHECKSTYLE_OFF: LineLength + if (immutableOutputFile || dependencyChanged || sourceChanged || inputFileTreeChanged) + // CHECKSTYLE_ON: LineLength + { + String cause = immutableOutputFile + ? "immutable single output file" + : (dependencyChanged + ? "changed dependency" + : (sourceChanged ? "changed source code" : "added or removed source files")); + getLog().info("Recompiling the module because of " + cause + "."); + if (showCompilationChanges) { + for (String fileAdded : added) { + getLog().info("\t+ " + fileAdded); + } + for (String fileRemoved : removed) { + getLog().info("\t- " + fileRemoved); + } + } + + compilerConfiguration.setSourceFiles( + sources.stream().map(Path::toFile).collect(Collectors.toSet())); } else { getLog().info("Nothing to compile - all classes are up to date."); + return; } } catch (CompilerException e) { - throw new MojoExecutionException("Error while computing stale sources.", e); + throw new MojoException("Error while computing stale sources.", e); } } else { getLog().debug("useIncrementalCompilation disabled"); @@ -982,11 +927,12 @@ public void execute() throws MojoExecutionException, CompilationFailureException } } catch (CompilerException e) { - throw new MojoExecutionException("Error while computing stale sources.", e); + throw new MojoException("Error while computing stale sources.", e); } if (staleSources.isEmpty()) { getLog().info("Nothing to compile - all classes are up to date."); + return; } @@ -998,14 +944,14 @@ public void execute() throws MojoExecutionException, CompilationFailureException if (getLog().isDebugEnabled()) { getLog().debug("#sources: " + sources.size()); - for (File file : sources) { - getLog().debug(file.getPath()); + for (Path file : sources) { + getLog().debug(file.toString()); } } preparePaths(sources); } catch (CompilerException e) { - throw new MojoExecutionException("Error while computing stale sources.", e); + throw new MojoException("Error while computing stale sources.", e); } } @@ -1018,27 +964,10 @@ public void execute() throws MojoExecutionException, CompilationFailureException compilerConfiguration.setExcludes(getExcludes()); - Map effectiveCompilerArguments = getCompilerArguments(); - String effectiveCompilerArgument = getCompilerArgument(); - if ((effectiveCompilerArguments != null) || (effectiveCompilerArgument != null) || (compilerArgs != null)) { - if (effectiveCompilerArguments != null) { - for (Map.Entry me : effectiveCompilerArguments.entrySet()) { - String key = me.getKey(); - String value = me.getValue(); - if (!key.startsWith("-")) { - key = "-" + key; - } - - if (key.startsWith("-A") && (value != null && !value.isEmpty())) { - compilerConfiguration.addCompilerCustomArgument(key + "=" + value, null); - } else { - compilerConfiguration.addCompilerCustomArgument(key, value); - } - } - } - if (!(effectiveCompilerArgument == null || effectiveCompilerArgument.isEmpty())) { + if ((effectiveCompilerArgument != null) || (compilerArgs != null)) { + if (!StringUtils.isEmpty(effectiveCompilerArgument)) { compilerConfiguration.addCompilerCustomArgument(effectiveCompilerArgument, null); } if (compilerArgs != null) { @@ -1067,7 +996,7 @@ public void execute() throws MojoExecutionException, CompilationFailureException getLog().debug("Source roots:"); - for (String root : getCompileSourceRoots()) { + for (Path root : getCompileSourceRoots()) { getLog().debug(" " + root); } @@ -1088,10 +1017,10 @@ public void execute() throws MojoExecutionException, CompilationFailureException sb.append(cl[i]); } getLog().debug("Command line options:"); - getLog().debug(sb); + getLog().debug(sb.toString()); } } catch (CompilerException ce) { - getLog().debug(ce); + getLog().debug("Compilation error", ce); } } @@ -1129,18 +1058,15 @@ public void execute() throws MojoExecutionException, CompilationFailureException patchModule.append('='); Set patchModules = new LinkedHashSet<>(); - Set sourceRoots = new HashSet<>(getCompileSourceRoots().size()); - for (String sourceRoot : getCompileSourceRoots()) { - sourceRoots.add(Paths.get(sourceRoot)); - } + Set sourceRoots = new HashSet<>(getCompileSourceRoots()); String[] files = values[1].split(PS); for (String file : files) { Path filePath = Paths.get(file); - if (getOutputDirectory().toPath().equals(filePath)) { + if (getOutputDirectory().equals(filePath)) { patchModules.add("_"); // this jar - } else if (getOutputDirectory().toPath().startsWith(filePath)) { + } else if (getOutputDirectory().startsWith(filePath)) { // multirelease, can be ignored continue; } else if (sourceRoots.contains(filePath)) { @@ -1178,7 +1104,7 @@ public void execute() throws MojoExecutionException, CompilationFailureException } if (!jpmsLines.isEmpty()) { - Path jpmsArgs = Paths.get(getOutputDirectory().getAbsolutePath(), "META-INF/jpms.args"); + Path jpmsArgs = getOutputDirectory().toAbsolutePath().resolve("META-INF/jpms.args"); try { Files.createDirectories(jpmsArgs.getParent()); @@ -1193,31 +1119,14 @@ public void execute() throws MojoExecutionException, CompilationFailureException // ---------------------------------------------------------------------- if (StringUtils.isEmpty(compilerConfiguration.getSourceEncoding())) { - getLog().warn("File encoding has not been set, using platform encoding " - + MessageUtils.buffer().strong(Charset.defaultCharset()) + getLog().warn("File encoding has not been set, using platform encoding " + ReaderFactory.FILE_ENCODING + ", i.e. build is platform dependent!"); } CompilerResult compilerResult; if (useIncrementalCompilation) { - incrementalBuildHelperRequest.outputDirectory(getOutputDirectory()); - - // MCOMPILER-333: Cleanup the generated source files created by annotation processing - // to avoid issues with `javac` compiler when the source code is rebuild. - if (getGeneratedSourcesDirectory() != null) { - try (Stream walk = - Files.walk(getGeneratedSourcesDirectory().toPath())) { - walk.sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete); - // MCOMPILER-567: The directory must already exist because javac does not create it. - Files.createDirectories(getGeneratedSourcesDirectory().toPath()); - } catch (IOException ex) { - getLog().warn("I/O error deleting the annotation processing generated files: " + ex.getMessage()); - } - } - - incrementalBuildHelper.beforeRebuildExecution(incrementalBuildHelperRequest); - + incrementalBuildHelper.beforeRebuildExecution(); getLog().debug("incrementalBuildHelper#beforeRebuildExecution"); } @@ -1225,7 +1134,7 @@ public void execute() throws MojoExecutionException, CompilationFailureException compilerResult = compiler.performCompile(compilerConfiguration); } catch (Exception e) { // TODO: don't catch Exception - throw new MojoExecutionException("Fatal error compiling", e); + throw new MojoException("Fatal error compiling", e); } if (createMissingPackageInfoClass @@ -1245,10 +1154,10 @@ public void execute() throws MojoExecutionException, CompilationFailureException } if (useIncrementalCompilation) { - if (incrementalBuildHelperRequest.getOutputDirectory().exists()) { + if (Files.exists(getOutputDirectory())) { getLog().debug("incrementalBuildHelper#afterRebuildExecution"); // now scan the same directory again and create a diff - incrementalBuildHelper.afterRebuildExecution(incrementalBuildHelperRequest); + incrementalBuildHelper.afterRebuildExecution(); } else { getLog().debug( "skip incrementalBuildHelper#afterRebuildExecution as the output directory doesn't exist"); @@ -1259,17 +1168,13 @@ public void execute() throws MojoExecutionException, CompilationFailureException List errors = new ArrayList<>(); List others = new ArrayList<>(); for (CompilerMessage message : compilerResult.getCompilerMessages()) { - switch (message.getKind()) { - case ERROR: - errors.add(message); - break; - case WARNING: - case MANDATORY_WARNING: - warnings.add(message); - break; - default: - others.add(message); - break; + if (message.getKind() == CompilerMessage.Kind.ERROR) { + errors.add(message); + } else if (message.getKind() == CompilerMessage.Kind.WARNING + || message.getKind() == CompilerMessage.Kind.MANDATORY_WARNING) { + warnings.add(message); + } else { + others.add(message); } } @@ -1314,9 +1219,11 @@ public void execute() throws MojoExecutionException, CompilationFailureException case OTHER: getLog().info(message.toString()); break; + case ERROR: getLog().error(message.toString()); break; + case MANDATORY_WARNING: case WARNING: default: @@ -1328,16 +1235,17 @@ public void execute() throws MojoExecutionException, CompilationFailureException } private void createMissingPackageInfoClasses( - CompilerConfiguration compilerConfiguration, SourceMapping sourceMapping, Set sources) + CompilerConfiguration compilerConfiguration, SourceMapping sourceMapping, Set sources) throws InclusionScanException, IOException { - for (File source : sources) { + for (Path source : sources) { String path = source.toString(); if (path.endsWith(File.separator + "package-info.java")) { - for (String root : getCompileSourceRoots()) { - root = root + File.separator; + for (Path rootPath : getCompileSourceRoots()) { + String root = rootPath.toString() + File.separator; if (path.startsWith(root)) { String rel = path.substring(root.length()); - Set files = sourceMapping.getTargetFiles(getOutputDirectory(), rel); + Set files = sourceMapping.getTargetFiles( + getOutputDirectory().toFile(), rel); for (File file : files) { if (!file.exists()) { File parentFile = file.getParentFile(); @@ -1399,10 +1307,10 @@ protected boolean isTestCompile() { /** * @return all source files for the compiler */ - private Set getCompileSources(Compiler compiler, CompilerConfiguration compilerConfiguration) - throws MojoExecutionException, CompilerException { + private Set getCompileSources(Compiler compiler, CompilerConfiguration compilerConfiguration) + throws MojoException, CompilerException { String inputFileEnding = compiler.getInputFileEnding(compilerConfiguration); - if (inputFileEnding == null || inputFileEnding.isEmpty()) { + if (StringUtils.isEmpty(inputFileEnding)) { // see MCOMPILER-199 GroovyEclipseCompiler doesn't set inputFileEnding // so we can presume it's all files from the source directory inputFileEnding = ".*"; @@ -1413,20 +1321,18 @@ private Set getCompileSources(Compiler compiler, CompilerConfiguration com scanner.addSourceMapping(mapping); - Set compileSources = new HashSet<>(); + Set compileSources = new HashSet<>(); - for (String sourceRoot : getCompileSourceRoots()) { - File rootFile = new File(sourceRoot); - - if (!rootFile.isDirectory() - || rootFile.getAbsoluteFile().equals(compilerConfiguration.getGeneratedSourcesDirectory())) { + for (Path sourceRoot : getCompileSourceRoots()) { + if (!Files.isDirectory(sourceRoot) + || sourceRoot.toFile().equals(compilerConfiguration.getGeneratedSourcesDirectory())) { continue; } try { - compileSources.addAll(scanner.getIncludedSources(rootFile, null)); + scanner.getIncludedSources(sourceRoot.toFile(), null).forEach(f -> compileSources.add(f.toPath())); } catch (InclusionScanException e) { - throw new MojoExecutionException( + throw new MojoException( "Error scanning source root: '" + sourceRoot + "' for stale files to recompile.", e); } } @@ -1441,21 +1347,20 @@ private Set getCompileSources(Compiler compiler, CompilerConfiguration com /** * @param compilerConfiguration * @param compiler - * @return {@code true} if at least a single source file is newer than it's class file + * @return true if at least a single source file is newer than it's class file */ - private boolean isSourceChanged(CompilerConfiguration compilerConfiguration, Compiler compiler) { - Set staleSources = Collections.emptySet(); - try { - staleSources = computeStaleSources(compilerConfiguration, compiler, getSourceInclusionScanner(staleMillis)); - } catch (MojoExecutionException | CompilerException ex) { - // we cannot detect Stale Sources, so don't do anything beside logging - getLog().warn("Cannot detect stale sources."); - return false; - } + private boolean isSourceChanged(CompilerConfiguration compilerConfiguration, Compiler compiler) + throws CompilerException, MojoException { + Set staleSources = + computeStaleSources(compilerConfiguration, compiler, getSourceInclusionScanner(staleMillis)); if (getLog().isDebugEnabled() || showCompilationChanges) { for (File f : staleSources) { - getLog().info("\tStale source detected: " + f.getAbsolutePath()); + if (showCompilationChanges) { + getLog().info("Stale source detected: " + f.getAbsolutePath()); + } else { + getLog().debug("Stale source detected: " + f.getAbsolutePath()); + } } } return !staleSources.isEmpty(); @@ -1467,18 +1372,11 @@ private boolean isSourceChanged(CompilerConfiguration compilerConfiguration, Com * @return number of thread for this build or 1 if not multi-thread build */ protected int getRequestThreadCount() { - return session.getRequest().getDegreeOfConcurrency(); - } - - protected Date getBuildStartTime() { - return getBuildStartTimeInstant().map(Date::from).orElseGet(Date::new); + return session.getDegreeOfConcurrency(); } - private Optional getBuildStartTimeInstant() { - return Optional.ofNullable(session.getRequest()) - .map(MavenExecutionRequest::getStartTime) - .map(Date::toInstant) - .map(i -> i.truncatedTo(ChronoUnit.MILLIS)); + protected Instant getBuildStartTime() { + return session.getStartTime(); } private String getMemoryValue(String setting) { @@ -1494,21 +1392,14 @@ private String getMemoryValue(String setting) { return value; } - protected final Toolchain getToolchain() { - Toolchain tc = null; - + protected final Optional getToolchain() { if (jdkToolchain != null) { List tcs = toolchainManager.getToolchains(session, "jdk", jdkToolchain); if (tcs != null && !tcs.isEmpty()) { - tc = tcs.get(0); + return Optional.of(tcs.get(0)); } } - - if (tc == null) { - tc = toolchainManager.getToolchainFromBuildContext("jdk", session); - } - - return tc; + return toolchainManager.getToolchainFromBuildContext(session, "jdk"); } private boolean isDigits(String string) { @@ -1522,10 +1413,10 @@ private boolean isDigits(String string) { private Set computeStaleSources( CompilerConfiguration compilerConfiguration, Compiler compiler, SourceInclusionScanner scanner) - throws MojoExecutionException, CompilerException { + throws MojoException, CompilerException { SourceMapping mapping = getSourceMapping(compilerConfiguration, compiler); - File outputDirectory; + Path outputDirectory; CompilerOutputStyle outputStyle = compiler.getCompilerOutputStyle(); if (outputStyle == CompilerOutputStyle.ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES) { outputDirectory = buildDirectory; @@ -1537,17 +1428,15 @@ private Set computeStaleSources( Set staleSources = new HashSet<>(); - for (String sourceRoot : getCompileSourceRoots()) { - File rootFile = new File(sourceRoot); - - if (!rootFile.isDirectory()) { + for (Path sourceRoot : getCompileSourceRoots()) { + if (!Files.isDirectory(sourceRoot)) { continue; } try { - staleSources.addAll(scanner.getIncludedSources(rootFile, outputDirectory)); + staleSources.addAll(scanner.getIncludedSources(sourceRoot.toFile(), outputDirectory.toFile())); } catch (InclusionScanException e) { - throw new MojoExecutionException( + throw new MojoException( "Error scanning source root: \'" + sourceRoot + "\' for stale files to recompile.", e); } } @@ -1556,7 +1445,7 @@ private Set computeStaleSources( } private SourceMapping getSourceMapping(CompilerConfiguration compilerConfiguration, Compiler compiler) - throws CompilerException, MojoExecutionException { + throws CompilerException, MojoException { CompilerOutputStyle outputStyle = compiler.getCompilerOutputStyle(); SourceMapping mapping; @@ -1569,7 +1458,7 @@ private SourceMapping getSourceMapping(CompilerConfiguration compilerConfigurati compiler.getInputFileEnding(compilerConfiguration), compiler.getOutputFile(compilerConfiguration)); } else { - throw new MojoExecutionException("Unknown compiler output style: '" + outputStyle + "'."); + throw new MojoException("Unknown compiler output style: '" + outputStyle + "'."); } return mapping; } @@ -1578,17 +1467,12 @@ private SourceMapping getSourceMapping(CompilerConfiguration compilerConfigurati * @todo also in ant plugin. This should be resolved at some point so that it does not need to * be calculated continuously - or should the plugins accept empty source roots as is? */ - private static List removeEmptyCompileSourceRoots(List compileSourceRootsList) { - List newCompileSourceRootsList = new ArrayList<>(); + private static List removeEmptyCompileSourceRoots(List compileSourceRootsList) { if (compileSourceRootsList != null) { - // copy as I may be modifying it - for (String srcDir : compileSourceRootsList) { - if (!newCompileSourceRootsList.contains(srcDir) && new File(srcDir).exists()) { - newCompileSourceRootsList.add(srcDir); - } - } + return compileSourceRootsList.stream().filter(Files::exists).collect(Collectors.toList()); + } else { + return new ArrayList<>(); } - return newCompileSourceRootsList; } /** @@ -1596,41 +1480,36 @@ private static List removeEmptyCompileSourceRoots(List compileSo * generated classes and if we got a file which is >= the build-started timestamp, then we caught a file which * got changed during this build. * - * @return {@code true} if at least one single dependency has changed. + * @return true if at least one single dependency has changed. */ protected boolean isDependencyChanged() { - final Instant buildStartTime = getBuildStartTimeInstant().orElse(null); - if (buildStartTime == null) { + if (session == null) { // we just cannot determine it, so don't do anything beside logging - getLog().debug("Cannot determine build start time, skipping incremental build detection."); + getLog().info("Cannot determine build start date, skipping incremental build detection."); return false; } if (fileExtensions == null || fileExtensions.isEmpty()) { - fileExtensions = new HashSet<>(Arrays.asList("class", "jar")); + fileExtensions = Collections.unmodifiableList(Arrays.asList("class", "jar")); } + Instant buildStartTime = getBuildStartTime(); + List pathElements = new ArrayList<>(); pathElements.addAll(getClasspathElements()); pathElements.addAll(getModulepathElements()); for (String pathElement : pathElements) { - Path artifactPath = Paths.get(pathElement); - - // Search files only on dependencies (other modules), not on the current project, - if (Files.isDirectory(artifactPath) - && !artifactPath.equals(getOutputDirectory().toPath())) { - try (Stream walk = Files.walk(artifactPath)) { - if (walk.anyMatch(p -> hasNewFile(p, buildStartTime))) { - return true; + File artifactPath = new File(pathElement); + if (artifactPath.isDirectory() || artifactPath.isFile()) { + if (hasNewFile(artifactPath, buildStartTime)) { + if (showCompilationChanges) { + getLog().info("New dependency detected: " + artifactPath.getAbsolutePath()); + } else { + getLog().debug("New dependency detected: " + artifactPath.getAbsolutePath()); } - } catch (IOException ex) { - // we just cannot determine it, so don't do anything beside logging - getLog().warn("I/O error walking the path: " + ex.getMessage()); - return false; + return true; } - } else if (hasNewFile(artifactPath, buildStartTime)) { - return true; } } @@ -1639,85 +1518,99 @@ protected boolean isDependencyChanged() { } /** - * @param file entry to check + * @param classPathEntry entry to check * @param buildStartTime time build start * @return if any changes occurred */ - private boolean hasNewFile(Path file, Instant buildStartTime) { - if (Files.isRegularFile(file) - && fileExtensions.contains( - FileUtils.extension(file.getFileName().toString()))) { - try { - Instant lastModifiedTime = Files.getLastModifiedTime(file) - .toInstant() - .minusMillis(staleMillis) - .truncatedTo(ChronoUnit.MILLIS); - boolean hasChanged = lastModifiedTime.isAfter(buildStartTime); - if (hasChanged && (getLog().isDebugEnabled() || showCompilationChanges)) { - getLog().info("\tNew dependency detected: " + file.toAbsolutePath()); - } - return hasChanged; - } catch (IOException ex) { - // we just cannot determine it, so don't do anything beside logging - getLog().warn("I/O error reading the lastModifiedTime: " + ex.getMessage()); + private boolean hasNewFile(File classPathEntry, Instant buildStartTime) { + // TODO: rewrite with NIO api + if (!classPathEntry.exists()) { + return false; + } + + if (classPathEntry.isFile()) { + return classPathEntry.lastModified() >= buildStartTime.toEpochMilli() + && fileExtensions.contains(FileUtils.getExtension(classPathEntry.getName())); + } + + File[] children = classPathEntry.listFiles(); + + for (File child : children) { + if (hasNewFile(child, buildStartTime)) { + return true; } } return false; } - private List resolveProcessorPathEntries() throws MojoExecutionException { + private List resolveProcessorPathEntries() throws MojoException { if (annotationProcessorPaths == null || annotationProcessorPaths.isEmpty()) { return null; } try { - List dependencies = convertToDependencies(annotationProcessorPaths); - List managedDependencies = - getManagedDependenciesForAnnotationProcessorPaths(); - CollectRequest collectRequest = - new CollectRequest(dependencies, managedDependencies, project.getRemoteProjectRepositories()); - DependencyRequest dependencyRequest = new DependencyRequest(); - dependencyRequest.setCollectRequest(collectRequest); - DependencyResult dependencyResult = - repositorySystem.resolveDependencies(session.getRepositorySession(), dependencyRequest); - - return dependencyResult.getArtifactResults().stream() - .map(resolved -> resolved.getArtifact().getFile().getAbsolutePath()) + Session session = this.session.withRemoteRepositories(projectManager.getRemoteProjectRepositories(project)); + List coords = + annotationProcessorPaths.stream().map(this::toCoordinate).collect(Collectors.toList()); + return session + .getService(DependencyResolver.class) + .resolve(DependencyResolverRequest.builder() + .session(session) + .dependencies(coords) + .managedDependencies(project.getManagedDependencies()) + .pathScope(PathScope.MAIN_RUNTIME) + .build()) + .getPaths() + .stream() + .map(Path::toString) .collect(Collectors.toList()); } catch (Exception e) { - throw new MojoExecutionException( - "Resolution of annotationProcessorPath dependencies failed: " + e.getLocalizedMessage(), e); + throw new MojoException("Resolution of annotationProcessorPath dependencies failed: " + e.getMessage(), e); } } - private List convertToDependencies( - List annotationProcessorPaths) throws MojoExecutionException { - List dependencies = new ArrayList<>(); - for (DependencyCoordinate annotationProcessorPath : annotationProcessorPaths) { - ArtifactHandler handler = artifactHandlerManager.getArtifactHandler(annotationProcessorPath.getType()); - String version = getAnnotationProcessorPathVersion(annotationProcessorPath); - Artifact artifact = new DefaultArtifact( - annotationProcessorPath.getGroupId(), - annotationProcessorPath.getArtifactId(), - annotationProcessorPath.getClassifier(), - handler.getExtension(), - version); - Set exclusions = convertToAetherExclusions(annotationProcessorPath.getExclusions()); - dependencies.add(new org.eclipse.aether.graph.Dependency(artifact, JavaScopes.RUNTIME, false, exclusions)); + private org.apache.maven.api.DependencyCoordinate toCoordinate(DependencyCoordinate coord) { + return session.getService(DependencyCoordinateFactory.class) + .create(DependencyCoordinateFactoryRequest.builder() + .session(session) + .groupId(coord.getGroupId()) + .artifactId(coord.getArtifactId()) + .classifier(coord.getClassifier()) + .type(coord.getType()) + .version(getAnnotationProcessorPathVersion(coord)) + .exclusions(toExclusions(coord.getExclusions())) + .build()); + } + + private Collection toExclusions(Set exclusions) { + if (exclusions == null || exclusions.isEmpty()) { + return List.of(); } - return dependencies; + return exclusions.stream() + .map(e -> (Exclusion) new Exclusion() { + @Override + public String getGroupId() { + return e.getGroupId(); + } + + @Override + public String getArtifactId() { + return e.getArtifactId(); + } + }) + .toList(); } private String getAnnotationProcessorPathVersion(DependencyCoordinate annotationProcessorPath) - throws MojoExecutionException { + throws MojoException { String configuredVersion = annotationProcessorPath.getVersion(); if (configuredVersion != null) { return configuredVersion; } else { - List managedDependencies = getProjectManagedDependencies(); + List managedDependencies = project.getManagedDependencies(); return findManagedVersion(annotationProcessorPath, managedDependencies) - .orElseThrow(() -> new MojoExecutionException(String.format( + .orElseThrow(() -> new MojoException(String.format( "Cannot find version for annotation processor path '%s'. The version needs to be either" + " provided directly in the plugin configuration or via dependency management.", annotationProcessorPath))); @@ -1725,51 +1618,15 @@ private String getAnnotationProcessorPathVersion(DependencyCoordinate annotation } private Optional findManagedVersion( - DependencyCoordinate dependencyCoordinate, List managedDependencies) { + DependencyCoordinate dependencyCoordinate, + List managedDependencies) { return managedDependencies.stream() .filter(dep -> Objects.equals(dep.getGroupId(), dependencyCoordinate.getGroupId()) && Objects.equals(dep.getArtifactId(), dependencyCoordinate.getArtifactId()) && Objects.equals(dep.getClassifier(), dependencyCoordinate.getClassifier()) - && Objects.equals(dep.getType(), dependencyCoordinate.getType())) + && Objects.equals(dep.getType().id(), dependencyCoordinate.getType())) .findAny() - .map(org.apache.maven.model.Dependency::getVersion); - } - - private List getManagedDependenciesForAnnotationProcessorPaths() { - if (!annotationProcessorPathsUseDepMgmt) { - return Collections.emptyList(); - } - List projectManagedDependencies = getProjectManagedDependencies(); - ArtifactTypeRegistry artifactTypeRegistry = - session.getRepositorySession().getArtifactTypeRegistry(); - - return projectManagedDependencies.stream() - .map(dep -> RepositoryUtils.toDependency(dep, artifactTypeRegistry)) - .collect(Collectors.toList()); - } - - private List getProjectManagedDependencies() { - DependencyManagement dependencyManagement = project.getDependencyManagement(); - if (dependencyManagement == null || dependencyManagement.getDependencies() == null) { - return Collections.emptyList(); - } - return dependencyManagement.getDependencies(); - } - - private Set convertToAetherExclusions(Set exclusions) { - if (exclusions == null || exclusions.isEmpty()) { - return Collections.emptySet(); - } - Set aetherExclusions = new HashSet<>(); - for (DependencyExclusion exclusion : exclusions) { - Exclusion aetherExclusion = new Exclusion( - exclusion.getGroupId(), - exclusion.getArtifactId(), - exclusion.getClassifier(), - exclusion.getExtension()); - aetherExclusions.add(aetherExclusion); - } - return aetherExclusions; + .map(d -> d.getVersion().asString()); } private void writePlugin(MessageBuilder mb) { @@ -1789,7 +1646,7 @@ private void writePlugin(MessageBuilder mb) { private void writeConfig(MessageBuilder mb) { mb.a(" ").newline(); - if (release != null) { + if (release != null && !release.isEmpty()) { mb.a(" ").a(release).a("").newline(); } else if (JavaVersion.JAVA_VERSION.isAtLeast("9")) { String rls = target.replaceAll(".\\.", ""); @@ -1817,52 +1674,6 @@ private String getMavenCompilerPluginVersion() { return pomProperties.getProperty("version"); } - private boolean hasInputFileTreeChanged(IncrementalBuildHelper ibh, Set inputFiles) { - Path mojoConfigBase; - try { - mojoConfigBase = ibh.getMojoStatusDirectory().toPath(); - } catch (MojoExecutionException e) { - // we cannot get the mojo status dir, so don't do anything beside logging - getLog().warn("Error reading mojo status directory."); - return false; - } - Path mojoConfigFile = mojoConfigBase.resolve(INPUT_FILES_LST_FILENAME); - - List oldInputFiles = Collections.emptyList(); - if (Files.isRegularFile(mojoConfigFile)) { - try { - oldInputFiles = Files.readAllLines(mojoConfigFile); - } catch (IOException e) { - // we cannot read the mojo config file, so don't do anything beside logging - getLog().warn("Error while reading old mojo status: " + mojoConfigFile); - return false; - } - } - - List newInputFiles = - inputFiles.stream().sorted().map(File::getAbsolutePath).collect(Collectors.toList()); - - try { - Files.write(mojoConfigFile, newInputFiles); - } catch (IOException e) { - // we cannot write the mojo config file, so don't do anything beside logging - getLog().warn("Error while writing new mojo status: " + mojoConfigFile); - return false; - } - - DeltaList inputTreeChanges = new DeltaList<>(oldInputFiles, newInputFiles); - if (getLog().isDebugEnabled() || showCompilationChanges) { - for (String fileAdded : inputTreeChanges.getAdded()) { - getLog().info("\tInput tree files (+): " + fileAdded); - } - for (String fileRemoved : inputTreeChanges.getRemoved()) { - getLog().info("\tInput tree files (-): " + fileRemoved); - } - } - - return inputTreeChanges.hasChanged(); - } - public void setTarget(String target) { this.target = target; targetOrReleaseSet = true; @@ -1886,9 +1697,9 @@ final String getImplicit() { * @see MCOMPILER-542 * @see JDK-8318913 */ - private void patchJdkModuleVersion(CompilerResult compilerResult, Set sources) throws MojoExecutionException { + private void patchJdkModuleVersion(CompilerResult compilerResult, Set sources) throws MojoException { if (compilerResult.isSuccess() && getModuleDeclaration(sources).isPresent()) { - Path moduleDescriptor = getOutputDirectory().toPath().resolve("module-info.class"); + Path moduleDescriptor = getOutputDirectory().resolve("module-info.class"); if (Files.isRegularFile(moduleDescriptor)) { try { final byte[] descriptorOriginal = Files.readAllBytes(moduleDescriptor); @@ -1898,9 +1709,83 @@ private void patchJdkModuleVersion(CompilerResult compilerResult, Set sour Files.write(moduleDescriptor, descriptorMod); } } catch (IOException ex) { - throw new MojoExecutionException("Error reading or writing module-info.class", ex); + throw new MojoException("Error reading or writing module-info.class", ex); } } } } + + protected final Optional getModuleDeclaration(final Set sourceFiles) { + for (Path sourceFile : sourceFiles) { + if ("module-info.java".equals(sourceFile.getFileName().toString())) { + return Optional.of(sourceFile); + } + } + return Optional.empty(); + } + + protected Log getLog() { + return logger; + } + + class MavenLogger extends AbstractLogger { + MavenLogger() { + super(0, AbstractCompilerMojo.this.getClass().getName()); + } + + @Override + public void debug(String message, Throwable throwable) { + logger.debug(message, throwable); + } + + @Override + public boolean isDebugEnabled() { + return logger.isDebugEnabled(); + } + + @Override + public void info(String message, Throwable throwable) { + logger.info(message, throwable); + } + + @Override + public boolean isInfoEnabled() { + return logger.isInfoEnabled(); + } + + @Override + public void warn(String message, Throwable throwable) { + logger.warn(message, throwable); + } + + @Override + public boolean isWarnEnabled() { + return logger.isWarnEnabled(); + } + + @Override + public void error(String message, Throwable throwable) { + logger.error(message, throwable); + } + + @Override + public boolean isErrorEnabled() { + return logger.isErrorEnabled(); + } + + @Override + public void fatalError(String message, Throwable throwable) { + logger.error(message, throwable); + } + + @Override + public boolean isFatalErrorEnabled() { + return isFatalErrorEnabled(); + } + + @Override + public Logger getChildLogger(String name) { + return this; + } + } } diff --git a/src/main/java/org/apache/maven/plugin/compiler/CompilationFailureException.java b/src/main/java/org/apache/maven/plugin/compiler/CompilationFailureException.java index fa55b16e..9efc5d8c 100644 --- a/src/main/java/org/apache/maven/plugin/compiler/CompilationFailureException.java +++ b/src/main/java/org/apache/maven/plugin/compiler/CompilationFailureException.java @@ -20,7 +20,7 @@ import java.util.List; -import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.api.plugin.MojoException; import org.codehaus.plexus.compiler.CompilerMessage; /** @@ -28,7 +28,7 @@ * @since 2.0 */ @SuppressWarnings("serial") -public class CompilationFailureException extends MojoFailureException { +public class CompilationFailureException extends MojoException { private static final String LS = System.getProperty("line.separator"); /** diff --git a/src/main/java/org/apache/maven/plugin/compiler/CompilerMojo.java b/src/main/java/org/apache/maven/plugin/compiler/CompilerMojo.java index 52da7ca8..eded2bfc 100644 --- a/src/main/java/org/apache/maven/plugin/compiler/CompilerMojo.java +++ b/src/main/java/org/apache/maven/plugin/compiler/CompilerMojo.java @@ -20,7 +20,9 @@ import java.io.File; import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -29,21 +31,16 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; -import java.util.Objects; import java.util.Optional; import java.util.Set; - -import org.apache.maven.artifact.Artifact; -import org.apache.maven.plugin.MojoExecutionException; -import org.apache.maven.plugins.annotations.LifecyclePhase; -import org.apache.maven.plugins.annotations.Mojo; -import org.apache.maven.plugins.annotations.Parameter; -import org.apache.maven.plugins.annotations.ResolutionScope; -import org.apache.maven.project.MavenProject; -import org.apache.maven.shared.utils.StringUtils; -import org.apache.maven.shared.utils.logging.MessageUtils; -import org.apache.maven.toolchain.Toolchain; -import org.apache.maven.toolchain.java.DefaultJavaToolChain; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.apache.maven.api.*; +import org.apache.maven.api.plugin.MojoException; +import org.apache.maven.api.plugin.annotations.Mojo; +import org.apache.maven.api.plugin.annotations.Parameter; +import org.apache.maven.api.services.MessageBuilderFactory; import org.codehaus.plexus.compiler.util.scan.SimpleSourceInclusionScanner; import org.codehaus.plexus.compiler.util.scan.SourceInclusionScanner; import org.codehaus.plexus.compiler.util.scan.StaleSourceScanner; @@ -52,6 +49,7 @@ import org.codehaus.plexus.languages.java.jpms.ModuleNameSource; import org.codehaus.plexus.languages.java.jpms.ResolvePathsRequest; import org.codehaus.plexus.languages.java.jpms.ResolvePathsResult; +import org.codehaus.plexus.util.StringUtils; /** * Compiles application sources. @@ -63,81 +61,64 @@ * @since 2.0 * @see javac Command */ -@Mojo( - name = "compile", - defaultPhase = LifecyclePhase.COMPILE, - threadSafe = true, - requiresDependencyResolution = ResolutionScope.COMPILE) +@Mojo(name = "compile", defaultPhase = "compile") public class CompilerMojo extends AbstractCompilerMojo { /** * The source directories containing the sources to be compiled. */ - @Parameter(defaultValue = "${project.compileSourceRoots}", readonly = false, required = true) - private List compileSourceRoots; + @Parameter + protected List compileSourceRoots; /** - * The directory for compiled classes. - *

- * This parameter should only be modified in special cases. One example is creating - * a multi-release jar with a lower bytecode level (i.e. setting it to - * {@code ${project.build.outputDirectory}/META-INF/versions/21} or similar) in an additional - * execution. - *

- * When the required bytecode level is available though an installed JDK or toolchain, - * it is recommended to use the {@code } property - * in conjunction with the ${multiReleaseOutput} parameter instead. + * Projects main artifact. */ - @Parameter( - property = "maven.compiler.outputDirectory", - defaultValue = "${project.build.outputDirectory}", - required = true, - readonly = false) - private File outputDirectory; + @Parameter(defaultValue = "${project.mainArtifact}", readonly = true, required = true) + protected Artifact projectArtifact; /** - * Projects main artifact. - * - * @todo this is an export variable, really + * The directory for compiled classes. */ - @Parameter(defaultValue = "${project.artifact}", readonly = true, required = true) - private Artifact projectArtifact; + @Parameter(defaultValue = "${project.build.outputDirectory}", required = true, readonly = true) + protected Path outputDirectory; /** * A list of inclusion filters for the compiler. */ @Parameter - private Set includes = new HashSet<>(); + protected Set includes = new HashSet<>(); /** * A list of exclusion filters for the compiler. */ @Parameter - private Set excludes = new HashSet<>(); + protected Set excludes = new HashSet<>(); /** * A list of exclusion filters for the incremental calculation. * @since 3.11 */ @Parameter - private Set incrementalExcludes = new HashSet<>(); + protected Set incrementalExcludes = new HashSet<>(); /** + *

* Specify where to place generated source files created by annotation processing. Only applies to JDK 1.6+ + *

* * @since 2.2 */ @Parameter(defaultValue = "${project.build.directory}/generated-sources/annotations") - private File generatedSourcesDirectory; + protected Path generatedSourcesDirectory; /** * Set this to {@code true} to bypass compilation of main sources. Its use is NOT RECOMMENDED, but quite convenient on * occasion. */ @Parameter(property = "maven.main.skip") - private boolean skipMain; + protected boolean skipMain; - @Parameter(defaultValue = "${project.compileClasspathElements}", readonly = true, required = true) - private List compilePath; + @Parameter + protected List compilePath; /** *

@@ -152,7 +133,7 @@ public class CompilerMojo extends AbstractCompilerMojo { * @since 3.7.1 */ @Parameter - private boolean multiReleaseOutput; + protected boolean multiReleaseOutput; /** * When both {@link AbstractCompilerMojo#fork} and {@link AbstractCompilerMojo#debug} are enabled the commandline arguments used @@ -160,7 +141,7 @@ public class CompilerMojo extends AbstractCompilerMojo { * @since 3.10.0 */ @Parameter(defaultValue = "javac") - private String debugFileName; + protected String debugFileName; final LocationManager locationManager = new LocationManager(); @@ -170,9 +151,12 @@ public class CompilerMojo extends AbstractCompilerMojo { private Map pathElements; - @Override - protected List getCompileSourceRoots() { - return compileSourceRoots; + protected List getCompileSourceRoots() { + if (compileSourceRoots == null || compileSourceRoots.isEmpty()) { + return projectManager.getCompileSourceRoots(getProject(), ProjectScope.MAIN); + } else { + return compileSourceRoots.stream().map(Paths::get).collect(Collectors.toList()); + } } @Override @@ -190,36 +174,30 @@ protected Map getPathElements() { return pathElements; } - @Override - protected File getOutputDirectory() { - File dir; + protected Path getOutputDirectory() { + Path dir; if (!multiReleaseOutput) { dir = outputDirectory; } else { - dir = new File(outputDirectory, "META-INF/versions/" + release); + dir = outputDirectory.resolve("META-INF/versions/" + release); } return dir; } - @Override - public void execute() throws MojoExecutionException, CompilationFailureException { + public void execute() throws MojoException { if (skipMain) { getLog().info("Not compiling main sources"); return; } if (multiReleaseOutput && release == null) { - throw new MojoExecutionException("When using 'multiReleaseOutput' the release must be set"); + throw new MojoException("When using 'multiReleaseOutput' the release must be set"); } super.execute(); - if (outputDirectory.isDirectory()) { - File artifactFile = projectArtifact.getFile(); - if (artifactFile != null && !Objects.equals(artifactFile, outputDirectory)) { - getLog().warn("Overwriting artifact's file from " + artifactFile + " to " + outputDirectory); - } - projectArtifact.setFile(outputDirectory); + if (Files.isDirectory(outputDirectory) && projectArtifact != null) { + artifactManager.setPath(projectArtifact, outputDirectory); } } @@ -234,12 +212,28 @@ protected Set getExcludes() { } @Override - protected void preparePaths(Set sourceFiles) { + protected void preparePaths(Set sourceFiles) { // assert compilePath != null; + List compilePath = this.compilePath; + if (compilePath == null) { + Stream s1 = Stream.of(getOutputDirectory().toString()); + Stream s2 = session.resolveDependencies(getProject(), PathScope.MAIN_COMPILE).stream() + .map(Path::toString); + compilePath = Stream.concat(s1, s2).collect(Collectors.toList()); + } + + Path moduleDescriptorPath = null; - Optional moduleDeclaration = getModuleDeclaration(sourceFiles); + boolean hasModuleDescriptor = false; + for (Path sourceFile : sourceFiles) { + if ("module-info.java".equals(sourceFile.getFileName().toString())) { + moduleDescriptorPath = sourceFile; + hasModuleDescriptor = true; + break; + } + } - if (moduleDeclaration.isPresent()) { + if (hasModuleDescriptor) { // For now only allow named modules. Once we can create a graph with ASM we can specify exactly the modules // and we can detect if auto modules are used. In that case, MavenProject.setFile() should not be used, so // you cannot depend on this project and so it won't be distributed. @@ -250,15 +244,17 @@ protected void preparePaths(Set sourceFiles) { ResolvePathsResult resolvePathsResult; try { - Collection dependencyArtifacts = getCompileClasspathElements(getProject()); + Collection dependencyArtifacts = getCompileClasspathElements(getProject()).stream() + .map(Path::toFile) + .collect(Collectors.toList()); ResolvePathsRequest request = ResolvePathsRequest.ofFiles(dependencyArtifacts) .setIncludeStatic(true) - .setMainModuleDescriptor(moduleDeclaration.get().toFile()); + .setMainModuleDescriptor(moduleDescriptorPath.toFile()); - Toolchain toolchain = getToolchain(); - if (toolchain instanceof DefaultJavaToolChain) { - request.setJdkHome(new File(((DefaultJavaToolChain) toolchain).getJavaHome())); + Optional toolchain = getToolchain(); + if (toolchain.isPresent() && toolchain.get() instanceof JavaToolchain) { + request.setJdkHome(new File(((JavaToolchain) toolchain.get()).getJavaHome())); } resolvePathsResult = locationManager.resolvePaths(request); @@ -290,7 +286,7 @@ protected void preparePaths(Set sourceFiles) { classpathElements.add(file.getPath()); if (multiReleaseOutput) { - if (getOutputDirectory().toPath().startsWith(file.getPath())) { + if (getOutputDirectory().startsWith(file.getPath())) { compilerArgs.add("--patch-module"); compilerArgs.add(String.format("%s=%s", moduleDescriptor.name(), file.getPath())); } @@ -309,11 +305,10 @@ protected void preparePaths(Set sourceFiles) { } } else { classpathElements = new ArrayList<>(); - for (File element : getCompileClasspathElements(getProject())) { - classpathElements.add(element.getPath()); + for (Path element : getCompileClasspathElements(getProject())) { + classpathElements.add(element.toString()); } modulepathElements = Collections.emptyList(); - pathElements = Collections.emptyMap(); } } @@ -342,33 +337,31 @@ private void detectFilenameBasedAutomodules( } } - private List getCompileClasspathElements(MavenProject project) { + private List getCompileClasspathElements(Project project) { + List artifacts = session.resolveDependencies(project, PathScope.MAIN_COMPILE); + // 3 is outputFolder + 2 preserved for multirelease - List list = new ArrayList<>(project.getArtifacts().size() + 3); + List list = new ArrayList<>(artifacts.size() + 3); if (multiReleaseOutput) { - File versionsFolder = new File(project.getBuild().getOutputDirectory(), "META-INF/versions"); + Path versionsFolder = outputDirectory.resolve("META-INF/versions"); // in reverse order for (int version = Integer.parseInt(getRelease()) - 1; version >= 9; version--) { - File versionSubFolder = new File(versionsFolder, String.valueOf(version)); - if (versionSubFolder.exists()) { + Path versionSubFolder = versionsFolder.resolve(String.valueOf(version)); + if (Files.exists(versionSubFolder)) { list.add(versionSubFolder); } } } - list.add(new File(project.getBuild().getOutputDirectory())); + list.add(outputDirectory); + + list.addAll(artifacts); - for (Artifact a : project.getArtifacts()) { - if (a.getArtifactHandler().isAddedToClasspath()) { - list.add(a.getFile()); - } - } return list; } - @Override protected SourceInclusionScanner getSourceInclusionScanner(int staleMillis) { if (includes.isEmpty() && excludes.isEmpty() && incrementalExcludes.isEmpty()) { return new StaleSourceScanner(staleMillis); @@ -383,7 +376,6 @@ protected SourceInclusionScanner getSourceInclusionScanner(int staleMillis) { return new StaleSourceScanner(staleMillis, includes, excludesIncr); } - @Override protected SourceInclusionScanner getSourceInclusionScanner(String inputFileEnding) { // it's not defined if we get the ending with or without the dot '.' String defaultIncludePattern = "**/*" + (inputFileEnding.startsWith(".") ? "" : ".") + inputFileEnding; @@ -416,13 +408,7 @@ protected String getCompilerArgument() { return compilerArgument; } - @Override - protected Map getCompilerArguments() { - return compilerArguments; - } - - @Override - protected File getGeneratedSourcesDirectory() { + protected Path getGeneratedSourcesDirectory() { return generatedSourcesDirectory; } @@ -434,7 +420,14 @@ protected String getDebugFileName() { private void writeBoxedWarning(String message) { String line = StringUtils.repeat("*", message.length() + 4); getLog().warn(line); - getLog().warn("* " + MessageUtils.buffer().strong(message) + " *"); + getLog().warn("* " + strong(message) + " *"); getLog().warn(line); } + + private String strong(String message) { + return session.getService(MessageBuilderFactory.class) + .builder() + .strong(message) + .build(); + } } diff --git a/src/main/java/org/apache/maven/plugin/compiler/DeltaList.java b/src/main/java/org/apache/maven/plugin/compiler/DeltaList.java deleted file mode 100644 index 18943fab..00000000 --- a/src/main/java/org/apache/maven/plugin/compiler/DeltaList.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 - * - * http://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.apache.maven.plugin.compiler; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -/** - * Show the modifications between two lists. - */ -final class DeltaList { - - private final List added; - private final List removed; - private final boolean hasChanged; - - DeltaList(Collection oldList, Collection newList) { - this.added = new ArrayList<>(newList); - this.removed = new ArrayList<>(oldList); - added.removeAll(oldList); - removed.removeAll(newList); - this.hasChanged = !added.isEmpty() || !removed.isEmpty(); - } - - Collection getAdded() { - return Collections.unmodifiableCollection(added); - } - - Collection getRemoved() { - return Collections.unmodifiableCollection(removed); - } - - boolean hasChanged() { - return hasChanged; - } -} diff --git a/src/main/java/org/apache/maven/plugin/compiler/IncrementalBuildHelper.java b/src/main/java/org/apache/maven/plugin/compiler/IncrementalBuildHelper.java new file mode 100644 index 00000000..3dc0e2db --- /dev/null +++ b/src/main/java/org/apache/maven/plugin/compiler/IncrementalBuildHelper.java @@ -0,0 +1,204 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.plugin.compiler; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.apache.maven.api.plugin.MojoException; + +/** + * Various helper methods to support incremental builds + */ +public class IncrementalBuildHelper { + /** + * the root directory to store status information about Maven executions in. + */ + private static final String MAVEN_STATUS_ROOT = "maven-status"; + + public static final String CREATED_FILES_LST_FILENAME = "createdFiles.lst"; + private static final String INPUT_FILES_LST_FILENAME = "inputFiles.lst"; + + /** + * Needed for storing the status for the incremental build support. + */ + private final String mojoStatusPath; + + private final Set sources; + + private final Path directory; + + private final Path outputDirectory; + + /** + * Once the {@link #beforeRebuildExecution()} got + * called, this will contain the list of files in the build directory. + */ + private List filesBeforeAction = Collections.emptyList(); + + public IncrementalBuildHelper(String mojoStatusPath, Set sources, Path directory, Path outputDirectory) { + if (mojoStatusPath == null) { + throw new IllegalArgumentException("MojoExecution must not be null!"); + } + + this.mojoStatusPath = mojoStatusPath; + this.sources = sources; + this.directory = directory; + this.outputDirectory = outputDirectory; + } + + /** + * We use a specific status directory for each Mojo execution to store state + * which is needed during the next build invocation run. + * @return the directory for storing status information of the current Mojo execution. + */ + public Path getMojoStatusDirectory() throws MojoException { + // X TODO the executionId contains -cli and -mojoname + // X we should remove those postfixes as it should not make + // X any difference whether being run on the cli or via build + Path mojoStatusDir = directory.resolve(mojoStatusPath); + + try { + Files.createDirectories(mojoStatusDir); + } catch (IOException e) { + throw new MojoException("Unable to create directory: " + mojoStatusDir, e); + } + + return mojoStatusDir; + } + + /** + * Detect whether the list of detected files has changed since the last build. + * We simply load the list of files for the previous build from a status file + * and compare it with the new list. Afterwards we store the new list in the status file. + * + * @return true if the set of inputFiles got changed since the last build. + */ + public boolean inputFileTreeChanged(List added, List removed) { + Path mojoConfigBase = getMojoStatusDirectory(); + Path mojoConfigFile = mojoConfigBase.resolve(INPUT_FILES_LST_FILENAME); + + List oldInputFiles = Collections.emptyList(); + + if (Files.exists(mojoConfigFile)) { + try { + oldInputFiles = Files.readAllLines(mojoConfigFile); + } catch (IOException e) { + throw new MojoException("Error reading old mojo status " + mojoConfigFile, e); + } + } + + List newFiles = + sources.stream().map(Path::toAbsolutePath).map(Path::toString).collect(Collectors.toList()); + + List previousFiles = oldInputFiles; + newFiles.stream().filter(s -> !previousFiles.contains(s)).forEach(added::add); + previousFiles.stream().filter(s -> !newFiles.contains(s)).forEach(removed::add); + try { + Files.write(mojoConfigFile, added); + } catch (IOException e) { + throw new MojoException("Error while storing the mojo status", e); + } + + return added.size() + removed.size() > 0; + } + + /** + *

+ * This method shall get invoked before the actual Mojo task gets triggered, e.g. the actual compile in + * maven-compiler-plugin. + *

+ *

+ * Attention: This method shall only get invoked if the plugin re-creates all the output. + *

+ *

+ * It first picks up the list of files created in the previous build and delete them. This step is necessary to + * prevent left-overs. After that we take a 'directory snapshot' (list of all files which exist in the + * outputDirectory after the clean). + *

+ *

+ * After the actual Mojo task got executed you should invoke the method + * {@link #afterRebuildExecution()} to collect the + * list of files which got changed by this task. + *

+ */ + public void beforeRebuildExecution() { + Path mojoConfigBase = getMojoStatusDirectory(); + Path mojoConfigFile = mojoConfigBase.resolve(CREATED_FILES_LST_FILENAME); + + try { + if (Files.exists(mojoConfigFile)) { + for (String oldFileName : Files.readAllLines(mojoConfigFile)) { + Path oldFile = outputDirectory.resolve(oldFileName); + Files.deleteIfExists(oldFile); + } + } + + // we remember all files which currently exist in the output directory + if (Files.exists(outputDirectory)) { + try (Stream walk = Files.walk(outputDirectory)) { + filesBeforeAction = walk.filter(Files::isRegularFile).collect(Collectors.toList()); + } + } + } catch (IOException e) { + throw new MojoException("Error reading old mojo status", e); + } + } + + /** + *

This method collects and stores all information about files changed since the + * call to {@link #beforeRebuildExecution()}.

+ * + *

Attention: This method shall only get invoked if the plugin re-creates all the output.

+ */ + public void afterRebuildExecution() { + Path mojoConfigBase = getMojoStatusDirectory(); + Path mojoConfigFile = mojoConfigBase.resolve(CREATED_FILES_LST_FILENAME); + + try { + try (Stream walk = Files.walk(outputDirectory)) { + List added = walk.filter(Files::isRegularFile) + .filter(p -> !filesBeforeAction.contains(p)) + .map(Path::toString) + .collect(Collectors.toList()); + + Files.write(mojoConfigFile, added); + } + } catch (IOException e) { + throw new MojoException("Error while storing the mojo status", e); + } + + // in case of clean compile the file is not created so next compile won't see it + // we mus create it here + mojoConfigFile = mojoConfigBase.resolve(INPUT_FILES_LST_FILENAME); + if (!Files.exists(mojoConfigFile)) { + try { + Files.write(mojoConfigFile, sources.stream().map(Path::toString).collect(Collectors.toList())); + } catch (IOException e) { + throw new MojoException("Error while storing the mojo status", e); + } + } + } +} diff --git a/src/main/java/org/apache/maven/plugin/compiler/ModuleInfoTransformer.java b/src/main/java/org/apache/maven/plugin/compiler/ModuleInfoTransformer.java index d03eff3f..79607a3b 100644 --- a/src/main/java/org/apache/maven/plugin/compiler/ModuleInfoTransformer.java +++ b/src/main/java/org/apache/maven/plugin/compiler/ModuleInfoTransformer.java @@ -23,7 +23,7 @@ import java.util.List; import java.util.Set; -import org.apache.maven.plugin.logging.Log; +import org.apache.maven.api.plugin.Log; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; diff --git a/src/main/java/org/apache/maven/plugin/compiler/Providers.java b/src/main/java/org/apache/maven/plugin/compiler/Providers.java new file mode 100644 index 00000000..c02156b8 --- /dev/null +++ b/src/main/java/org/apache/maven/plugin/compiler/Providers.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.plugin.compiler; + +import java.lang.reflect.Field; +import java.util.Map; + +import org.apache.maven.api.Session; +import org.apache.maven.api.di.Named; +import org.apache.maven.api.di.Provides; +import org.apache.maven.api.services.*; +import org.codehaus.plexus.compiler.Compiler; +import org.codehaus.plexus.compiler.javac.JavacCompiler; +import org.codehaus.plexus.compiler.javac.JavaxToolsCompiler; +import org.codehaus.plexus.compiler.manager.CompilerManager; +import org.codehaus.plexus.compiler.manager.NoSuchCompilerException; + +@Named +class Providers { + + @Provides + static ToolchainManager toolchainManager(Session session) { + return session.getService(ToolchainManager.class); + } + + @Provides + static ArtifactManager artifactManager(Session session) { + return session.getService(ArtifactManager.class); + } + + @Provides + static ProjectManager projectManager(Session session) { + return session.getService(ProjectManager.class); + } + + @Provides + static MessageBuilderFactory messageBuilderFactory(Session session) { + return session.getService(MessageBuilderFactory.class); + } + + @Provides + static CompilerManager compilerManager(Map compilers) { + return compilerId -> { + Compiler compiler = compilers.get(compilerId); + if (compiler == null) { + throw new NoSuchCompilerException(compilerId); + } else { + return compiler; + } + }; + } + + @Provides + @Named("javac") + static Compiler javacCompiler() throws Exception { + JavacCompiler compiler = new JavacCompiler(); + Field ipc = JavacCompiler.class.getDeclaredField("inProcessCompiler"); + ipc.setAccessible(true); + ipc.set(compiler, new JavaxToolsCompiler()); + return compiler; + } +} diff --git a/src/main/java/org/apache/maven/plugin/compiler/TestCompilerMojo.java b/src/main/java/org/apache/maven/plugin/compiler/TestCompilerMojo.java index b8290377..d9b9cdc5 100644 --- a/src/main/java/org/apache/maven/plugin/compiler/TestCompilerMojo.java +++ b/src/main/java/org/apache/maven/plugin/compiler/TestCompilerMojo.java @@ -18,28 +18,29 @@ */ package org.apache.maven.plugin.compiler; -import java.io.File; import java.io.IOException; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Optional; import java.util.Set; - -import org.apache.maven.plugin.MojoExecutionException; -import org.apache.maven.plugins.annotations.LifecyclePhase; -import org.apache.maven.plugins.annotations.Mojo; -import org.apache.maven.plugins.annotations.Parameter; -import org.apache.maven.plugins.annotations.ResolutionScope; -import org.apache.maven.shared.utils.StringUtils; -import org.apache.maven.toolchain.Toolchain; -import org.apache.maven.toolchain.java.DefaultJavaToolChain; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.apache.maven.api.JavaToolchain; +import org.apache.maven.api.PathScope; +import org.apache.maven.api.ProjectScope; +import org.apache.maven.api.Toolchain; +import org.apache.maven.api.plugin.MojoException; +import org.apache.maven.api.plugin.annotations.Mojo; +import org.apache.maven.api.plugin.annotations.Parameter; import org.codehaus.plexus.compiler.util.scan.SimpleSourceInclusionScanner; import org.codehaus.plexus.compiler.util.scan.SourceInclusionScanner; import org.codehaus.plexus.compiler.util.scan.StaleSourceScanner; @@ -58,11 +59,7 @@ * @since 2.0 * @see javac Command */ -@Mojo( - name = "testCompile", - defaultPhase = LifecyclePhase.TEST_COMPILE, - threadSafe = true, - requiresDependencyResolution = ResolutionScope.TEST) +@Mojo(name = "testCompile", defaultPhase = "test-compile") public class TestCompilerMojo extends AbstractCompilerMojo { /** * Set this to 'true' to bypass compilation of test sources. @@ -74,9 +71,15 @@ public class TestCompilerMojo extends AbstractCompilerMojo { /** * The source directories containing the test-source to be compiled. */ - @Parameter(defaultValue = "${project.testCompileSourceRoots}", readonly = false, required = true) + @Parameter private List compileSourceRoots; + /** + * The directory where compiled test classes go. + */ + @Parameter(defaultValue = "${project.build.outputDirectory}", required = true) + private Path mainOutputDirectory; + /** * The directory where compiled test classes go. *

@@ -85,8 +88,8 @@ public class TestCompilerMojo extends AbstractCompilerMojo { * * @see CompilerMojo#outputDirectory */ - @Parameter(defaultValue = "${project.build.testOutputDirectory}", required = true, readonly = false) - private File outputDirectory; + @Parameter(defaultValue = "${project.build.testOutputDirectory}", required = true) + private Path outputDirectory; /** * A list of inclusion filters for the compiler. @@ -168,7 +171,7 @@ public class TestCompilerMojo extends AbstractCompilerMojo { * @since 2.2 */ @Parameter(defaultValue = "${project.build.directory}/generated-test-sources/test-annotations") - private File generatedTestSourcesDirectory; + private Path generatedTestSourcesDirectory; /** *

@@ -182,7 +185,7 @@ public class TestCompilerMojo extends AbstractCompilerMojo { @Parameter(defaultValue = "true") private boolean useModulePath; - @Parameter(defaultValue = "${project.testClasspathElements}", readonly = true) + @Parameter private List testPath; /** @@ -196,11 +199,11 @@ public class TestCompilerMojo extends AbstractCompilerMojo { private Map pathElements; - private Collection classpathElements; + private List classpathElements; - private Collection modulepathElements; + private List modulepathElements; - public void execute() throws MojoExecutionException, CompilationFailureException { + public void execute() throws MojoException { if (skip) { getLog().info("Not compiling test sources"); return; @@ -208,8 +211,12 @@ public void execute() throws MojoExecutionException, CompilationFailureException super.execute(); } - protected List getCompileSourceRoots() { - return compileSourceRoots; + protected List getCompileSourceRoots() { + if (compileSourceRoots == null || compileSourceRoots.isEmpty()) { + return projectManager.getCompileSourceRoots(getProject(), ProjectScope.TEST); + } else { + return compileSourceRoots.stream().map(Paths::get).collect(Collectors.toList()); + } } @Override @@ -218,49 +225,58 @@ protected Map getPathElements() { } protected List getClasspathElements() { - return new ArrayList<>(classpathElements); + return classpathElements; } @Override protected List getModulepathElements() { - return new ArrayList<>(modulepathElements); + return modulepathElements; } - protected File getOutputDirectory() { + protected Path getOutputDirectory() { return outputDirectory; } @Override - protected void preparePaths(Set sourceFiles) { - File mainOutputDirectory = new File(getProject().getBuild().getOutputDirectory()); + protected void preparePaths(Set sourceFiles) { + List testPath = this.testPath; + if (testPath == null) { + Stream s1 = Stream.of(outputDirectory.toString(), mainOutputDirectory.toString()); + Stream s2 = session.resolveDependencies(getProject(), PathScope.TEST_COMPILE).stream() + .map(Path::toString); + testPath = Stream.concat(s1, s2).collect(Collectors.toList()); + } + + Path mainOutputDirectory = Paths.get(getProject().getBuild().getOutputDirectory()); - File mainModuleDescriptorClassFile = new File(mainOutputDirectory, "module-info.class"); + Path mainModuleDescriptorClassFile = mainOutputDirectory.resolve("module-info.class"); JavaModuleDescriptor mainModuleDescriptor = null; - File testModuleDescriptorJavaFile = new File("module-info.java"); + Path testModuleDescriptorJavaFile = Paths.get("module-info.java"); JavaModuleDescriptor testModuleDescriptor = null; // Go through the source files to respect includes/excludes - for (File sourceFile : sourceFiles) { + for (Path sourceFile : sourceFiles) { // @todo verify if it is the root of a sourcedirectory? - if ("module-info.java".equals(sourceFile.getName())) { + if ("module-info.java".equals(sourceFile.getFileName().toString())) { testModuleDescriptorJavaFile = sourceFile; break; } } // Get additional information from the main module descriptor, if available - if (mainModuleDescriptorClassFile.exists()) { + if (Files.exists(mainModuleDescriptorClassFile)) { ResolvePathsResult result; try { ResolvePathsRequest request = ResolvePathsRequest.ofStrings(testPath) .setIncludeStatic(true) - .setMainModuleDescriptor(mainModuleDescriptorClassFile.getAbsolutePath()); + .setMainModuleDescriptor( + mainModuleDescriptorClassFile.toAbsolutePath().toString()); - Toolchain toolchain = getToolchain(); - if (toolchain instanceof DefaultJavaToolChain) { - request.setJdkHome(((DefaultJavaToolChain) toolchain).getJavaHome()); + Optional toolchain = getToolchain(); + if (toolchain.isPresent() && toolchain.get() instanceof JavaToolchain) { + request.setJdkHome(((JavaToolchain) toolchain.get()).getJavaHome()); } result = locationManager.resolvePaths(request); @@ -284,21 +300,22 @@ protected void preparePaths(Set sourceFiles) { pathElements = new LinkedHashMap<>(result.getPathElements().size()); pathElements.putAll(result.getPathElements()); - modulepathElements = result.getModulepathElements().keySet(); - classpathElements = result.getClasspathElements(); + modulepathElements = new ArrayList<>(result.getModulepathElements().keySet()); + classpathElements = new ArrayList<>(result.getClasspathElements()); } // Get additional information from the test module descriptor, if available - if (testModuleDescriptorJavaFile.exists()) { + if (Files.exists(testModuleDescriptorJavaFile)) { ResolvePathsResult result; try { ResolvePathsRequest request = ResolvePathsRequest.ofStrings(testPath) - .setMainModuleDescriptor(testModuleDescriptorJavaFile.getAbsolutePath()); + .setMainModuleDescriptor( + testModuleDescriptorJavaFile.toAbsolutePath().toString()); - Toolchain toolchain = getToolchain(); - if (toolchain instanceof DefaultJavaToolChain) { - request.setJdkHome(((DefaultJavaToolChain) toolchain).getJavaHome()); + Optional toolchain = getToolchain(); + if (toolchain.isPresent() && toolchain.get() instanceof JavaToolchain) { + request.setJdkHome(((JavaToolchain) toolchain.get()).getJavaHome()); } result = locationManager.resolvePaths(request); @@ -309,20 +326,14 @@ protected void preparePaths(Set sourceFiles) { testModuleDescriptor = result.getMainModuleDescriptor(); } - if (!useModulePath) { - pathElements = Collections.emptyMap(); - modulepathElements = Collections.emptyList(); - classpathElements = testPath; - return; - } - if (StringUtils.isNotEmpty(getRelease())) { - if (isOlderThanJDK9(getRelease())) { + if (release != null && !release.isEmpty()) { + if (Integer.parseInt(release) < 9) { pathElements = Collections.emptyMap(); modulepathElements = Collections.emptyList(); classpathElements = testPath; return; } - } else if (isOlderThanJDK9(getTarget())) { + } else if (Double.parseDouble(getTarget()) < Double.parseDouble(MODULE_INFO_TARGET)) { pathElements = Collections.emptyMap(); modulepathElements = Collections.emptyList(); classpathElements = testPath; @@ -350,8 +361,8 @@ protected void preparePaths(Set sourceFiles) { patchModuleValue.append(testModuleDescriptor.name()); patchModuleValue.append('='); - for (String root : getProject().getCompileSourceRoots()) { - if (Files.exists(Paths.get(root))) { + for (Path root : projectManager.getCompileSourceRoots(getProject(), ProjectScope.MAIN)) { + if (Files.exists(root)) { patchModuleValue.append(root).append(PS); } } @@ -362,7 +373,7 @@ protected void preparePaths(Set sourceFiles) { } } else { // No main binaries available? Means we're a test-only project. - if (!mainOutputDirectory.exists()) { + if (!Files.exists(mainOutputDirectory)) { return; } // very odd @@ -383,7 +394,7 @@ protected void preparePaths(Set sourceFiles) { .append('=') .append(mainOutputDirectory) .append(PS); - for (String root : compileSourceRoots) { + for (Path root : getCompileSourceRoots()) { patchModuleValue.append(root).append(PS); } @@ -436,14 +447,6 @@ protected SourceInclusionScanner getSourceInclusionScanner(String inputFileEndin return scanner; } - static boolean isOlderThanJDK9(String version) { - if (version.startsWith("1.")) { - return Integer.parseInt(version.substring(2)) < 9; - } - - return Integer.parseInt(version) < 9; - } - protected String getSource() { return testSource == null ? source : testSource; } @@ -461,11 +464,7 @@ protected String getCompilerArgument() { return testCompilerArgument == null ? compilerArgument : testCompilerArgument; } - protected Map getCompilerArguments() { - return testCompilerArguments == null ? compilerArguments : testCompilerArguments; - } - - protected File getGeneratedSourcesDirectory() { + protected Path getGeneratedSourcesDirectory() { return generatedTestSourcesDirectory; } diff --git a/src/test/java/org/apache/maven/plugin/compiler/CompilerMojoTest.java b/src/test/java/org/apache/maven/plugin/compiler/CompilerMojoTest.java deleted file mode 100644 index 8d852ba1..00000000 --- a/src/test/java/org/apache/maven/plugin/compiler/CompilerMojoTest.java +++ /dev/null @@ -1,301 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 - * - * http://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.apache.maven.plugin.compiler; - -import java.io.File; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -import org.apache.maven.artifact.Artifact; -import org.apache.maven.plugin.compiler.stubs.CompilerManagerStub; -import org.apache.maven.plugin.logging.Log; -import org.apache.maven.plugin.testing.junit5.InjectMojo; -import org.apache.maven.plugin.testing.junit5.MojoTest; -import org.apache.maven.plugin.testing.stubs.ArtifactStub; -import org.junit.jupiter.api.Test; - -import static org.apache.maven.plugin.compiler.MojoTestUtils.getMockMavenProject; -import static org.apache.maven.plugin.compiler.MojoTestUtils.getMockMavenSession; -import static org.apache.maven.plugin.compiler.MojoTestUtils.getMockMojoExecution; -import static org.apache.maven.plugin.compiler.MojoTestUtils.getVariableValueFromObject; -import static org.apache.maven.plugin.compiler.MojoTestUtils.setVariableValueToObject; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.startsWith; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; - -@MojoTest -class CompilerMojoTest { - - private static final String COMPILE = "compile"; - - /** - * tests the ability of the plugin to compile a basic file - * - * @throws Exception - */ - @Test - @InjectMojo(goal = COMPILE, pom = "classpath:/unit/compiler-basic-test/plugin-config.xml") - void testCompilerBasic(CompilerMojo compilerMojo) throws Exception { - setUpCompilerMojoTestEnv(compilerMojo); - - Log log = mock(Log.class); - - compilerMojo.setLog(log); - - setVariableValueToObject(compilerMojo, "targetOrReleaseSet", false); - compilerMojo.execute(); - - Artifact projectArtifact = getVariableValueFromObject(compilerMojo, "projectArtifact"); - assertNotNull( - projectArtifact.getFile(), - "MCOMPILER-94: artifact file should only be null if there is nothing to compile"); - - File testClass = new File(compilerMojo.getOutputDirectory(), "foo/TestCompile0.class"); - - verify(log).warn(startsWith("No explicit value set for target or release!")); - - assertTrue(testClass::exists); - } - - @Test - @InjectMojo(goal = COMPILE, pom = "classpath:/unit/compiler-basic-sourcetarget/plugin-config.xml") - void testCompilerBasicSourceTarget(CompilerMojo compilerMojo) throws Exception { - setUpCompilerMojoTestEnv(compilerMojo); - - Log log = mock(Log.class); - - compilerMojo.setLog(log); - - compilerMojo.execute(); - - verify(log, never()).warn(startsWith("No explicit value set for target or release!")); - } - - /** - * tests the ability of the plugin to respond to empty source - * - * @throws Exception - */ - @Test - @InjectMojo(goal = COMPILE, pom = "classpath:/unit/compiler-empty-source-test/plugin-config.xml") - void testCompilerEmptySource(CompilerMojo compilerMojo) throws Exception { - setUpCompilerMojoTestEnv(compilerMojo); - - compilerMojo.execute(); - - assertFalse(compilerMojo.getOutputDirectory().exists()); - - Artifact projectArtifact = getVariableValueFromObject(compilerMojo, "projectArtifact"); - assertNull( - projectArtifact.getFile(), "MCOMPILER-94: artifact file should be null if there is nothing to compile"); - } - - /** - * tests the ability of the plugin to respond to includes and excludes correctly - * - * @throws Exception - */ - @Test - @InjectMojo(goal = COMPILE, pom = "classpath:/unit/compiler-includes-excludes-test/plugin-config.xml") - void testCompilerIncludesExcludes(CompilerMojo compilerMojo) throws Exception { - setUpCompilerMojoTestEnv(compilerMojo); - - Set includes = new HashSet<>(); - includes.add("**/TestCompile4*.java"); - setVariableValueToObject(compilerMojo, "includes", includes); - - Set excludes = new HashSet<>(); - excludes.add("**/TestCompile2*.java"); - excludes.add("**/TestCompile3*.java"); - setVariableValueToObject(compilerMojo, "excludes", excludes); - - compilerMojo.execute(); - - File testClass = new File(compilerMojo.getOutputDirectory(), "foo/TestCompile2.class"); - assertFalse(testClass.exists()); - - testClass = new File(compilerMojo.getOutputDirectory(), "foo/TestCompile3.class"); - assertFalse(testClass.exists()); - - testClass = new File(compilerMojo.getOutputDirectory(), "foo/TestCompile4.class"); - assertTrue(testClass.exists()); - } - - /** - * tests the ability of the plugin to fork and successfully compile - * - * @throws Exception - */ - @Test - @InjectMojo(goal = COMPILE, pom = "classpath:/unit/compiler-fork-test/plugin-config.xml") - void testCompilerFork(CompilerMojo compilerMojo) throws Exception { - setUpCompilerMojoTestEnv(compilerMojo); - // JAVA_HOME doesn't have to be on the PATH. - setVariableValueToObject( - compilerMojo, "executable", new File(System.getenv("JAVA_HOME"), "bin/javac").getPath()); - - compilerMojo.execute(); - - File testClass = new File(compilerMojo.getOutputDirectory(), "foo/TestCompile1.class"); - assertTrue(testClass.exists()); - } - - @Test - @InjectMojo(goal = COMPILE, pom = "classpath:/unit/compiler-one-output-file-test/plugin-config.xml") - void testOneOutputFileForAllInput(CompilerMojo compilerMojo) throws Exception { - setUpCompilerMojoTestEnv(compilerMojo); - - setVariableValueToObject(compilerMojo, "compilerManager", new CompilerManagerStub()); - - compilerMojo.execute(); - - File testClass = new File(compilerMojo.getOutputDirectory(), "compiled.class"); - assertTrue(testClass.exists()); - } - - @Test - @InjectMojo(goal = COMPILE, pom = "classpath:/unit/compiler-args-test/plugin-config.xml") - void testCompilerArgs(CompilerMojo compilerMojo) throws Exception { - setUpCompilerMojoTestEnv(compilerMojo); - - setVariableValueToObject(compilerMojo, "compilerManager", new CompilerManagerStub()); - - compilerMojo.execute(); - - File testClass = new File(compilerMojo.getOutputDirectory(), "compiled.class"); - assertTrue(testClass.exists()); - assertEquals( - Arrays.asList("key1=value1", "-Xlint", "-my&special:param-with+chars/not>allowed_in_XML_element_names"), - compilerMojo.compilerArgs); - } - - @Test - @InjectMojo(goal = COMPILE, pom = "classpath:/unit/compiler-implicit-test/plugin-config-none.xml") - void testImplicitFlagNone(CompilerMojo compilerMojo) throws Exception { - setUpCompilerMojoTestEnv(compilerMojo); - - assertEquals("none", compilerMojo.getImplicit()); - } - - @Test - @InjectMojo(goal = COMPILE, pom = "classpath:/unit/compiler-implicit-test/plugin-config-not-set.xml") - void testImplicitFlagNotSet(CompilerMojo compilerMojo) throws Exception { - setUpCompilerMojoTestEnv(compilerMojo); - - assertNull(compilerMojo.getImplicit()); - } - - @Test - @InjectMojo(goal = COMPILE, pom = "classpath:/unit/compiler-one-output-file-test2/plugin-config.xml") - void testOneOutputFileForAllInput2(CompilerMojo compilerMojo) throws Exception { - setUpCompilerMojoTestEnv(compilerMojo); - - setVariableValueToObject(compilerMojo, "compilerManager", new CompilerManagerStub()); - - Set includes = new HashSet<>(); - includes.add("**/TestCompile4*.java"); - setVariableValueToObject(compilerMojo, "includes", includes); - - Set excludes = new HashSet<>(); - excludes.add("**/TestCompile2*.java"); - excludes.add("**/TestCompile3*.java"); - setVariableValueToObject(compilerMojo, "excludes", excludes); - - compilerMojo.execute(); - - File testClass = new File(compilerMojo.getOutputDirectory(), "compiled.class"); - assertTrue(testClass.exists()); - } - - @Test - @InjectMojo(goal = COMPILE, pom = "classpath:/unit/compiler-fail-test/plugin-config.xml") - void testCompileFailure(CompilerMojo compilerMojo) throws Exception { - setUpCompilerMojoTestEnv(compilerMojo); - - setVariableValueToObject(compilerMojo, "compilerManager", new CompilerManagerStub(true)); - - try { - compilerMojo.execute(); - - fail("Should throw an exception"); - } catch (CompilationFailureException e) { - // expected - } - } - - @Test - @InjectMojo(goal = COMPILE, pom = "classpath:/unit/compiler-failonerror-test/plugin-config.xml") - void testCompileFailOnError(CompilerMojo compilerMojo) throws Exception { - setUpCompilerMojoTestEnv(compilerMojo); - - setVariableValueToObject(compilerMojo, "compilerManager", new CompilerManagerStub(true)); - - try { - compilerMojo.execute(); - assertTrue(true); - } catch (CompilationFailureException e) { - fail("The compilation error should have been consumed because failOnError = false"); - } - } - - /** - * Tests that setting 'skipMain' to true skips compilation of the main Java source files, but that test Java source - * files are still compiled. - * @throws Exception - */ - @Test - @InjectMojo(goal = COMPILE, pom = "classpath:/unit/compiler-skip-main/plugin-config.xml") - void testCompileSkipMain(CompilerMojo compilerMojo) throws Exception { - setUpCompilerMojoTestEnv(compilerMojo); - setVariableValueToObject(compilerMojo, "skipMain", true); - compilerMojo.execute(); - File testClass = new File(compilerMojo.getOutputDirectory(), "foo/TestSkipMainCompile0.class"); - assertFalse(testClass.exists()); - } - - /** - * Tests that setting 'skip' to true skips compilation of the test Java source files, but that main Java source - * files are still compiled. - * @throws Exception - */ - @Test - @InjectMojo(goal = COMPILE, pom = "classpath:/unit/compiler-skip-test/plugin-config.xml") - void testCompileSkipTest(CompilerMojo compilerMojo) throws Exception { - setUpCompilerMojoTestEnv(compilerMojo); - - compilerMojo.execute(); - - File testClass = new File(compilerMojo.getOutputDirectory(), "foo/TestSkipTestCompile0.class"); - assertTrue(testClass.exists()); - } - - private void setUpCompilerMojoTestEnv(CompilerMojo mojo) throws Exception { - setVariableValueToObject(mojo, "projectArtifact", new ArtifactStub()); - setVariableValueToObject(mojo, "compilePath", Collections.EMPTY_LIST); - setVariableValueToObject(mojo, "session", getMockMavenSession()); - setVariableValueToObject(mojo, "project", getMockMavenProject()); - setVariableValueToObject(mojo, "mojoExecution", getMockMojoExecution()); - setVariableValueToObject(mojo, "source", AbstractCompilerMojo.DEFAULT_SOURCE); - setVariableValueToObject(mojo, "target", AbstractCompilerMojo.DEFAULT_TARGET); - } -} diff --git a/src/test/java/org/apache/maven/plugin/compiler/CompilerMojoTestCase.java b/src/test/java/org/apache/maven/plugin/compiler/CompilerMojoTestCase.java new file mode 100644 index 00000000..8d6c532b --- /dev/null +++ b/src/test/java/org/apache/maven/plugin/compiler/CompilerMojoTestCase.java @@ -0,0 +1,671 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.maven.plugin.compiler; + +import java.io.File; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.maven.api.Artifact; +import org.apache.maven.api.PathScope; +import org.apache.maven.api.Project; +import org.apache.maven.api.Session; +import org.apache.maven.api.di.Inject; +import org.apache.maven.api.di.Provides; +import org.apache.maven.api.di.Singleton; +import org.apache.maven.api.model.Build; +import org.apache.maven.api.model.Model; +import org.apache.maven.api.plugin.Log; +import org.apache.maven.api.plugin.Mojo; +import org.apache.maven.api.plugin.testing.Basedir; +import org.apache.maven.api.plugin.testing.InjectMojo; +import org.apache.maven.api.plugin.testing.MojoExtension; +import org.apache.maven.api.plugin.testing.MojoParameter; +import org.apache.maven.api.plugin.testing.MojoTest; +import org.apache.maven.api.plugin.testing.stubs.ArtifactStub; +import org.apache.maven.api.plugin.testing.stubs.ProjectStub; +import org.apache.maven.api.plugin.testing.stubs.SessionMock; +import org.apache.maven.api.services.ArtifactManager; +import org.apache.maven.api.services.MessageBuilderFactory; +import org.apache.maven.api.services.ProjectManager; +import org.apache.maven.api.services.ToolchainManager; +import org.apache.maven.internal.impl.DefaultMessageBuilderFactory; +import org.apache.maven.internal.impl.InternalSession; +import org.apache.maven.plugin.compiler.stubs.CompilerManagerStub; +import org.codehaus.plexus.languages.java.version.JavaVersion; +import org.junit.jupiter.api.Test; + +import static org.apache.maven.api.plugin.testing.MojoExtension.getBasedir; +import static org.apache.maven.api.plugin.testing.MojoExtension.getVariableValueFromObject; +import static org.apache.maven.api.plugin.testing.MojoExtension.setVariableValueToObject; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.startsWith; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +@MojoTest +public class CompilerMojoTestCase { + + private static final String LOCAL_REPO = "/target/local-repo"; + + @Inject + private Session session; + + /** + * tests the ability of the plugin to compile a basic file + */ + @Test + @Basedir("${basedir}/target/test-classes/unit/compiler-basic-test") + public void testCompilerBasic( + @InjectMojo(goal = "compile", pom = "plugin-config.xml") + @MojoParameter(name = "mojoStatusPath", value = "maven-status/compile") + CompilerMojo compileMojo, + @InjectMojo(goal = "testCompile", pom = "plugin-config.xml") + @MojoParameter(name = "compileSourceRoots", value = "${project.basedir}/src/test/java") + @MojoParameter(name = "mojoStatusPath", value = "maven-status/testCompile") + TestCompilerMojo testCompileMojo) + throws Exception { + Log log = setMockLogger(compileMojo); + + setVariableValueToObject(compileMojo, "targetOrReleaseSet", Boolean.FALSE); + execute(compileMojo); + + verify(log).warn(startsWith("No explicit value set for target or release!")); + + Path testClass = compileMojo.getOutputDirectory().resolve("foo/TestCompile0.class"); + assertTrue(Files.exists(testClass)); + Artifact projectArtifact = (Artifact) getVariableValueFromObject(compileMojo, "projectArtifact"); + assertNotNull( + session.getArtifactPath(projectArtifact).orElse(null), + "MCOMPILER-94: artifact file should only be null if there is nothing to compile"); + + execute(testCompileMojo); + + testClass = testCompileMojo.getOutputDirectory().resolve("foo/TestCompile0Test.class"); + assertTrue(Files.exists(testClass)); + } + + @Test + @Basedir("${basedir}/target/test-classes/unit/compiler-basic-sourcetarget") + public void testCompilerBasicSourceTarget( + @InjectMojo(goal = "compile", pom = "plugin-config.xml") + @MojoParameter(name = "mojoStatusPath", value = "maven-status/compile") + CompilerMojo compileMojo) + throws Exception { + Log log = setMockLogger(compileMojo); + + execute(compileMojo); + + verify(log, never()).warn(startsWith("No explicit value set for target or release!")); + } + + /** + * tests the ability of the plugin to respond to empty source + */ + @Test + @Basedir("${basedir}/target/test-classes/unit/compiler-empty-source-test") + public void testCompilerEmptySource( + @InjectMojo(goal = "compile", pom = "plugin-config.xml") + @MojoParameter(name = "mojoStatusPath", value = "maven-status/compile") + CompilerMojo compileMojo, + @InjectMojo(goal = "testCompile", pom = "plugin-config.xml") + @MojoParameter(name = "compileSourceRoots", value = "${basedir}/src/test/java") + @MojoParameter(name = "mojoStatusPath", value = "maven-status/testCompile") + TestCompilerMojo testCompileMojo) + throws Exception { + execute(compileMojo); + + assertFalse(Files.exists(compileMojo.getOutputDirectory())); + Artifact projectArtifact = (Artifact) getVariableValueFromObject(compileMojo, "projectArtifact"); + assertNull( + session.getArtifactPath(projectArtifact).orElse(null), + "MCOMPILER-94: artifact file should be null if there is nothing to compile"); + + execute(testCompileMojo); + + assertFalse(Files.exists(testCompileMojo.getOutputDirectory())); + } + + /** + * tests the ability of the plugin to respond to includes and excludes correctly + */ + @Test + @Basedir("${basedir}/target/test-classes/unit/compiler-includes-excludes-test") + public void testCompilerIncludesExcludes( + @InjectMojo(goal = "compile", pom = "plugin-config.xml") + @MojoParameter(name = "mojoStatusPath", value = "maven-status/compile") + CompilerMojo compileMojo, + @InjectMojo(goal = "testCompile", pom = "plugin-config.xml") + @MojoParameter(name = "compileSourceRoots", value = "${project.basedir}/src/test/java") + @MojoParameter(name = "mojoStatusPath", value = "maven-status/testCompile") + TestCompilerMojo testCompileMojo) + throws Exception { + Set includes = new HashSet<>(); + includes.add("**/TestCompile4*.java"); + setVariableValueToObject(compileMojo, "includes", includes); + + Set excludes = new HashSet<>(); + excludes.add("**/TestCompile2*.java"); + excludes.add("**/TestCompile3*.java"); + setVariableValueToObject(compileMojo, "excludes", excludes); + + execute(compileMojo); + + Path testClass = compileMojo.getOutputDirectory().resolve("foo/TestCompile2.class"); + assertFalse(Files.exists(testClass)); + + testClass = compileMojo.getOutputDirectory().resolve("foo/TestCompile3.class"); + assertFalse(Files.exists(testClass)); + + testClass = compileMojo.getOutputDirectory().resolve("foo/TestCompile4.class"); + assertTrue(Files.exists(testClass)); + + setVariableValueToObject(testCompileMojo, "testIncludes", includes); + setVariableValueToObject(testCompileMojo, "testExcludes", excludes); + + execute(testCompileMojo); + + testClass = testCompileMojo.getOutputDirectory().resolve("foo/TestCompile2TestCase.class"); + assertFalse(Files.exists(testClass)); + + testClass = testCompileMojo.getOutputDirectory().resolve("foo/TestCompile3TestCase.class"); + assertFalse(Files.exists(testClass)); + + testClass = testCompileMojo.getOutputDirectory().resolve("foo/TestCompile4TestCase.class"); + assertTrue(Files.exists(testClass)); + } + + /** + * tests the ability of the plugin to fork and successfully compile + */ + @Test + @Basedir("${basedir}/target/test-classes/unit/compiler-fork-test") + public void testCompilerFork( + @InjectMojo(goal = "compile", pom = "plugin-config.xml") + @MojoParameter(name = "mojoStatusPath", value = "maven-status/compile") + CompilerMojo compileMojo, + @InjectMojo(goal = "testCompile", pom = "plugin-config.xml") + @MojoParameter(name = "compileSourceRoots", value = "${project.basedir}/src/test/java") + @MojoParameter(name = "mojoStatusPath", value = "maven-status/testCompile") + TestCompilerMojo testCompileMojo) + throws Exception { + // JAVA_HOME doesn't have to be on the PATH. + setVariableValueToObject( + compileMojo, "executable", new File(System.getenv("JAVA_HOME"), "bin/javac").getPath()); + + execute(compileMojo); + + Path testClass = compileMojo.getOutputDirectory().resolve("foo/TestCompile1.class"); + assertTrue(Files.exists(testClass)); + + // JAVA_HOME doesn't have to be on the PATH. + setVariableValueToObject( + testCompileMojo, "executable", new File(System.getenv("JAVA_HOME"), "bin/javac").getPath()); + + execute(testCompileMojo); + + testClass = testCompileMojo.getOutputDirectory().resolve("foo/TestCompile1TestCase.class"); + assertTrue(Files.exists(testClass)); + } + + @Test + @Basedir("${basedir}/target/test-classes/unit/compiler-one-output-file-test") + public void testOneOutputFileForAllInput( + @InjectMojo(goal = "compile", pom = "plugin-config.xml") + @MojoParameter(name = "mojoStatusPath", value = "maven-status/compile") + CompilerMojo compileMojo, + @InjectMojo(goal = "testCompile", pom = "plugin-config.xml") + @MojoParameter(name = "mojoStatusPath", value = "maven-status/testCompile") + TestCompilerMojo testCompileMojo) + throws Exception { + setVariableValueToObject(compileMojo, "compilerManager", new CompilerManagerStub()); + + execute(compileMojo); + + Path testClass = compileMojo.getOutputDirectory().resolve("compiled.class"); + assertTrue(Files.exists(testClass)); + + setVariableValueToObject(testCompileMojo, "compilerManager", new CompilerManagerStub()); + + setVariableValueToObject( + testCompileMojo, + "compileSourceRoots", + Collections.singletonList( + Paths.get(getBasedir(), "src/test/java").toString())); + execute(testCompileMojo); + + testClass = testCompileMojo.getOutputDirectory().resolve("compiled.class"); + assertTrue(Files.exists(testClass)); + } + + @Test + @Basedir("${basedir}/target/test-classes/unit/compiler-args-test") + public void testCompilerArgs( + @InjectMojo(goal = "compile", pom = "plugin-config.xml") + @MojoParameter(name = "mojoStatusPath", value = "maven-status/compile") + CompilerMojo compileMojo) + throws Exception { + setVariableValueToObject(compileMojo, "compilerManager", new CompilerManagerStub()); + + execute(compileMojo); + + Path testClass = compileMojo.getOutputDirectory().resolve("compiled.class"); + assertTrue(Files.exists(testClass)); + assertEquals( + Arrays.asList("key1=value1", "-Xlint", "-my&special:param-with+chars/not>allowed_in_XML_element_names"), + compileMojo.compilerArgs); + } + + @Test + @Basedir("${basedir}/target/test-classes/unit/compiler-implicit-test") + public void testImplicitFlagNone( + @InjectMojo(goal = "compile", pom = "plugin-config-none.xml") + @MojoParameter(name = "mojoStatusPath", value = "maven-status/compile") + CompilerMojo compileMojo) + throws Exception { + assertEquals("none", compileMojo.getImplicit()); + } + + @Test + @Basedir("${basedir}/target/test-classes/unit/compiler-implicit-test") + public void testImplicitFlagNotSet( + @InjectMojo(goal = "compile", pom = "plugin-config-not-set.xml") + @MojoParameter(name = "mojoStatusPath", value = "maven-status/compile") + CompilerMojo compileMojo) + throws Exception { + assertNull(compileMojo.getImplicit()); + } + + @Test + @Basedir("${basedir}/target/test-classes/unit/compiler-one-output-file-test2") + public void testOneOutputFileForAllInput2( + @InjectMojo(goal = "compile", pom = "plugin-config.xml") + @MojoParameter(name = "mojoStatusPath", value = "maven-status/compile") + CompilerMojo compileMojo, + @InjectMojo(goal = "testCompile", pom = "plugin-config.xml") + @MojoParameter(name = "mojoStatusPath", value = "maven-status/testCompile") + TestCompilerMojo testCompileMojo) + throws Exception { + setVariableValueToObject(compileMojo, "compilerManager", new CompilerManagerStub()); + + Set includes = new HashSet<>(); + includes.add("**/TestCompile4*.java"); + setVariableValueToObject(compileMojo, "includes", includes); + + Set excludes = new HashSet<>(); + excludes.add("**/TestCompile2*.java"); + excludes.add("**/TestCompile3*.java"); + setVariableValueToObject(compileMojo, "excludes", excludes); + + execute(compileMojo); + + Path testClass = compileMojo.getOutputDirectory().resolve("compiled.class"); + assertTrue(Files.exists(testClass)); + + setVariableValueToObject(testCompileMojo, "compilerManager", new CompilerManagerStub()); + setVariableValueToObject(testCompileMojo, "testIncludes", includes); + setVariableValueToObject(testCompileMojo, "testExcludes", excludes); + + setVariableValueToObject( + testCompileMojo, + "compileSourceRoots", + Collections.singletonList( + Paths.get(getBasedir(), "src/test/java").toString())); + execute(testCompileMojo); + + testClass = testCompileMojo.getOutputDirectory().resolve("compiled.class"); + assertTrue(Files.exists(testClass)); + } + + @Test + @Basedir("${basedir}/target/test-classes/unit/compiler-fail-test") + public void testCompileFailure( + @InjectMojo(goal = "compile", pom = "plugin-config.xml") + @MojoParameter(name = "mojoStatusPath", value = "maven-status/compile") + CompilerMojo compileMojo) + throws Exception { + setVariableValueToObject(compileMojo, "compilerManager", new CompilerManagerStub(true)); + + assertThrows(CompilationFailureException.class, compileMojo::execute, "Should throw an exception"); + } + + @Test + @Basedir("${basedir}/target/test-classes/unit/compiler-failonerror-test") + public void testCompileFailOnError( + @InjectMojo(goal = "compile", pom = "plugin-config.xml") + @MojoParameter(name = "mojoStatusPath", value = "maven-status/compile") + CompilerMojo compileMojo) + throws Exception { + setVariableValueToObject(compileMojo, "compilerManager", new CompilerManagerStub(true)); + + try { + execute(compileMojo); + assertTrue(true); + } catch (CompilationFailureException e) { + fail("The compilation error should have been consumed because failOnError = false"); + } + } + + /** + * Tests that setting 'skipMain' to true skips compilation of the main Java source files, but that test Java source + * files are still compiled. + */ + @Test + @Basedir("${basedir}/target/test-classes/unit/compiler-skip-main") + public void testCompileSkipMain( + @InjectMojo(goal = "compile", pom = "plugin-config.xml") + @MojoParameter(name = "mojoStatusPath", value = "maven-status/compile") + CompilerMojo compileMojo, + @InjectMojo(goal = "testCompile", pom = "plugin-config.xml") + @MojoParameter(name = "mojoStatusPath", value = "maven-status/testCompile") + TestCompilerMojo testCompileMojo) + throws Exception { + setVariableValueToObject(compileMojo, "skipMain", true); + execute(compileMojo); + Path testClass = compileMojo.getOutputDirectory().resolve("foo/TestSkipMainCompile0.class"); + assertFalse(Files.exists(testClass)); + + setVariableValueToObject( + testCompileMojo, + "compileSourceRoots", + Collections.singletonList( + Paths.get(getBasedir(), "src/test/java").toString())); + execute(testCompileMojo); + testClass = testCompileMojo.getOutputDirectory().resolve("foo/TestSkipMainCompile0Test.class"); + assertTrue(Files.exists(testClass)); + } + + /** + * Tests that setting 'skip' to true skips compilation of the test Java source files, but that main Java source + * files are still compiled. + */ + @Test + @Basedir("${basedir}/target/test-classes/unit/compiler-skip-test") + public void testCompileSkipTest( + @InjectMojo(goal = "compile", pom = "plugin-config.xml") + @MojoParameter(name = "mojoStatusPath", value = "maven-status/compile") + CompilerMojo compileMojo, + @InjectMojo(goal = "testCompile", pom = "plugin-config.xml") + @MojoParameter(name = "mojoStatusPath", value = "maven-status/testCompile") + TestCompilerMojo testCompileMojo) + throws Exception { + execute(compileMojo); + Path testClass = compileMojo.getOutputDirectory().resolve("foo/TestSkipTestCompile0.class"); + assertTrue(Files.exists(testClass)); + + setVariableValueToObject(testCompileMojo, "skip", true); + setVariableValueToObject( + testCompileMojo, + "compileSourceRoots", + Collections.singletonList( + Paths.get(getBasedir(), "src/test/java").toString())); + execute(testCompileMojo); + testClass = testCompileMojo.getOutputDirectory().resolve("foo/TestSkipTestCompile0Test.class"); + assertFalse(Files.exists(testClass)); + } + + @Provides + @Singleton + @SuppressWarnings("unused") + private static InternalSession createSession() { + InternalSession session = SessionMock.getMockSession(getBasedir() + LOCAL_REPO); + + ToolchainManager toolchainManager = mock(ToolchainManager.class); + doReturn(toolchainManager).when(session).getService(ToolchainManager.class); + + doAnswer(iom -> Instant.now().minus(200, ChronoUnit.MILLIS)) + .when(session) + .getStartTime(); + + Artifact junit = new ArtifactStub("junit", "junit", null, "3.8.1", "jar"); + + MessageBuilderFactory messageBuilderFactory = new DefaultMessageBuilderFactory(); + doReturn(messageBuilderFactory).when(session).getService(MessageBuilderFactory.class); + + String source = AbstractCompilerMojo.DEFAULT_SOURCE; + String target = AbstractCompilerMojo.DEFAULT_TARGET; + String javaSpec = System.getProperty("java.specification.version"); + // It is needed to set target/source to JDK 7 for JDK12+ and JDK 8 for JDK17+ + // because this is the lowest version which is supported by those JDK's. + // The default source/target "6" is not supported anymore. + if (JavaVersion.parse(javaSpec).isAtLeast("17")) { + source = "8"; + target = "8"; + } else if (JavaVersion.parse(javaSpec).isAtLeast("12")) { + source = "7"; + target = "7"; + } + + Map props = new HashMap<>(); + props.put("basedir", MojoExtension.getBasedir()); + props.put("maven.compiler.source", source); + props.put("maven.compiler.target", target); + doReturn(props).when(session).getUserProperties(); + + List artifacts = new ArrayList<>(); + try { + Path artifactFile; + String localRepository = System.getProperty("localRepository"); + if (localRepository != null) { + artifactFile = Paths.get(localRepository, "junit/junit/3.8.1/junit-3.8.1.jar"); + } else { + // for IDE + String junitURI = junit.framework.Test.class + .getResource("Test.class") + .toURI() + .toString(); + junitURI = junitURI.substring("jar:".length(), junitURI.indexOf('!')); + artifactFile = new File(URI.create(junitURI)).toPath(); + } + ArtifactManager artifactManager = session.getService(ArtifactManager.class); + artifactManager.setPath(junit, artifactFile); + artifacts.add(artifactFile); + } catch (Exception e) { + throw new RuntimeException("Unable to setup junit jar path", e); + } + + ProjectManager projectManager = session.getService(ProjectManager.class); + doAnswer(iom -> Collections.emptyList()).when(session).resolveDependencies(any(), eq(PathScope.MAIN_COMPILE)); + doAnswer(iom -> artifacts).when(session).resolveDependencies(any(), eq(PathScope.TEST_COMPILE)); + + return session; + } + + @Provides + @Singleton + @SuppressWarnings("unused") + private static Project createProject() { + ProjectStub stub = new ProjectStub(); + ArtifactStub artifact = new ArtifactStub("myGroupId", "myArtifactId", null, "1.0-SNAPSHOT", "jar"); + stub.setMainArtifact(artifact); + stub.setModel(Model.newBuilder() + .groupId(artifact.getGroupId()) + .artifactId(artifact.getArtifactId()) + .version(artifact.getVersion().asString()) + .build(Build.newBuilder() + .directory(MojoExtension.getBasedir() + "/target") + .outputDirectory(MojoExtension.getBasedir() + "/target/classes") + .sourceDirectory(MojoExtension.getBasedir() + "/src/main/java") + .testOutputDirectory(MojoExtension.getBasedir() + "/target/test-classes") + .build()) + .build()); + stub.setBasedir(Paths.get(MojoExtension.getBasedir())); + return stub; + } + + // @Provides + // @SuppressWarnings("unused") + // ProjectManager createProjectManager(InternalSession session) { + // return session.getService(ProjectManager.class); + // } + + // @Provides + // @SuppressWarnings("unused") + // ArtifactManager createArtifactManager(InternalSession session) { + // return session.getService(ArtifactManager.class); + // } + + private Log setMockLogger(AbstractCompilerMojo mojo) throws IllegalAccessException { + Log log = mock(Log.class); + setVariableValueToObject(mojo, "logger", log); + return log; + } + + private static void execute(Mojo mojo) { + try { + mojo.execute(); + } catch (CompilationFailureException e) { + throw new RuntimeException(e.getLongMessage(), e); + } + } + + /* + private CompilerMojo getCompilerMojo( String pomXml ) + throws Exception + { + File testPom = new File( getBasedir(), pomXml ); + + CompilerMojo mojo = (CompilerMojo) lookupMojo( "compile", testPom ); + + setVariableValueToObject( mojo, "projectArtifact", new ArtifactStub() ); + setVariableValueToObject( mojo, "compilePath", Collections.EMPTY_LIST ); + setVariableValueToObject( mojo, "session", getMockMavenSession() ); + setVariableValueToObject( mojo, "project", getMockMavenProject() ); + setVariableValueToObject( mojo, "mojoExecution", getMockMojoExecution() ); + setVariableValueToObject( mojo, "source", source ); + setVariableValueToObject( mojo, "target", target ); + + return mojo; + } + + private TestCompilerMojo getTestCompilerMojo( CompilerMojo compilerMojo, String pomXml ) + throws Exception + { + File testPom = new File( getBasedir(), pomXml ); + + TestCompilerMojo mojo = (TestCompilerMojo) lookupMojo( "testCompile", testPom ); + + setVariableValueToObject( mojo, "log", new DebugEnabledLog() ); + + File buildDir = (File) getVariableValueFromObject( compilerMojo, "buildDirectory" ); + File testClassesDir = new File( buildDir, "test-classes" ); + setVariableValueToObject( mojo, "outputDirectory", testClassesDir ); + + List testClasspathList = new ArrayList<>(); + + Artifact junitArtifact = mock( Artifact.class ); + ArtifactHandler handler = mock( ArtifactHandler.class ); + when( handler.isAddedToClasspath() ).thenReturn( true ); + when( junitArtifact.getArtifactHandler() ).thenReturn( handler ); + + File artifactFile; + String localRepository = System.getProperty( "localRepository" ); + if ( localRepository != null ) + { + artifactFile = new File( localRepository, "junit/junit/3.8.1/junit-3.8.1.jar" ); + } + else + { + // for IDE + String junitURI = org.junit.Test.class.getResource( "Test.class" ).toURI().toString(); + junitURI = junitURI.substring( "jar:".length(), junitURI.indexOf( '!' ) ); + artifactFile = new File( URI.create( junitURI ) ); + } + when ( junitArtifact.getFile() ).thenReturn( artifactFile ); + + testClasspathList.add( artifactFile.getAbsolutePath() ); + testClasspathList.add( compilerMojo.getOutputDirectory().toString() ); + + String testSourceRoot = testPom.getParent() + "/src/test/java"; + setVariableValueToObject( mojo, "compileSourceRoots", Collections.singletonList( testSourceRoot ) ); + + Project project = getMockMavenProject(); + project.setFile( testPom ); + project.addCompileSourceRoot("/src/main/java" ); + project.setArtifacts( Collections.singleton( junitArtifact ) ); + project.getBuild().setOutputDirectory( new File( buildDir, "classes" ).getAbsolutePath() ); + setVariableValueToObject( mojo, "project", project ); + setVariableValueToObject( mojo, "testPath", testClasspathList ); + setVariableValueToObject( mojo, "session", getMockMavenSession() ); + setVariableValueToObject( mojo, "mojoExecution", getMockMojoExecution() ); + setVariableValueToObject( mojo, "source", source ); + setVariableValueToObject( mojo, "target", target ); + + return mojo; + } + + + private MavenProject getMockMavenProject() + { + MavenProject mp = new MavenProject(); + mp.getBuild().setDirectory( "target" ); + mp.getBuild().setOutputDirectory( "target/classes" ); + mp.getBuild().setSourceDirectory( "src/main/java" ); + mp.getBuild().setTestOutputDirectory( "target/test-classes" ); + return mp; + } + + private MavenSession getMockMavenSession() + { + MavenSession session = mock( MavenSession.class ); + // when( session.getPluginContext( isA(PluginDescriptor.class), isA(MavenProject.class) ) ).thenReturn( + // Collections.emptyMap() ); + when( session.getCurrentProject() ).thenReturn( getMockMavenProject() ); + return session; + } + + private MojoExecution getMockMojoExecution() + { + MojoDescriptor md = new MojoDescriptor(); + md.setGoal( "compile" ); + + MojoExecution me = new MojoExecution( md ); + + PluginDescriptor pd = new PluginDescriptor(); + pd.setArtifactId( "maven-compiler-plugin" ); + md.setPluginDescriptor( pd ); + + return me; + } + */ + +} diff --git a/src/test/java/org/apache/maven/plugin/compiler/MojoTestUtils.java b/src/test/java/org/apache/maven/plugin/compiler/MojoTestUtils.java deleted file mode 100644 index 7173d647..00000000 --- a/src/test/java/org/apache/maven/plugin/compiler/MojoTestUtils.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 - * - * http://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.apache.maven.plugin.compiler; - -import java.lang.reflect.Field; - -import org.apache.maven.execution.MavenSession; -import org.apache.maven.plugin.MojoExecution; -import org.apache.maven.plugin.descriptor.MojoDescriptor; -import org.apache.maven.plugin.descriptor.PluginDescriptor; -import org.apache.maven.project.MavenProject; -import org.codehaus.plexus.util.ReflectionUtils; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class MojoTestUtils { - - public static T getVariableValueFromObject(Object object, String variable) throws IllegalAccessException { - Field field = ReflectionUtils.getFieldByNameIncludingSuperclasses(variable, object.getClass()); - field.setAccessible(true); - return (T) field.get(object); - } - - public static void setVariableValueToObject(Object object, String variable, T value) - throws IllegalAccessException { - Field field = ReflectionUtils.getFieldByNameIncludingSuperclasses(variable, object.getClass()); - field.setAccessible(true); - field.set(object, value); - } - - public static MavenProject getMockMavenProject() { - MavenProject mp = new MavenProject(); - mp.getBuild().setDirectory("target"); - mp.getBuild().setOutputDirectory("target/classes"); - mp.getBuild().setSourceDirectory("src/main/java"); - mp.getBuild().setTestOutputDirectory("target/test-classes"); - return mp; - } - - public static MavenSession getMockMavenSession() { - MavenSession session = mock(MavenSession.class); - // when( session.getPluginContext( isA(PluginDescriptor.class), isA(MavenProject.class) ) ).thenReturn( - // Collections.emptyMap() ); - when(session.getCurrentProject()).thenReturn(getMockMavenProject()); - return session; - } - - public static MojoExecution getMockMojoExecution() { - MojoDescriptor md = new MojoDescriptor(); - MojoExecution me = new MojoExecution(md); - - PluginDescriptor pd = new PluginDescriptor(); - pd.setArtifactId("maven-compiler-plugin"); - md.setPluginDescriptor(pd); - - return me; - } -} diff --git a/src/test/java/org/apache/maven/plugin/compiler/TestCompilerMojoTest.java b/src/test/java/org/apache/maven/plugin/compiler/TestCompilerMojoTest.java deleted file mode 100644 index 31a2e2a3..00000000 --- a/src/test/java/org/apache/maven/plugin/compiler/TestCompilerMojoTest.java +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 - * - * http://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.apache.maven.plugin.compiler; - -import java.io.File; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Stream; - -import org.apache.maven.plugin.compiler.stubs.CompilerManagerStub; -import org.apache.maven.plugin.testing.junit5.InjectMojo; -import org.apache.maven.plugin.testing.junit5.MojoTest; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; - -import static org.apache.maven.plugin.compiler.MojoTestUtils.getMockMavenProject; -import static org.apache.maven.plugin.compiler.MojoTestUtils.getMockMavenSession; -import static org.apache.maven.plugin.compiler.MojoTestUtils.getMockMojoExecution; -import static org.apache.maven.plugin.compiler.MojoTestUtils.getVariableValueFromObject; -import static org.apache.maven.plugin.compiler.MojoTestUtils.setVariableValueToObject; -import static org.junit.jupiter.api.Assertions.*; - -@MojoTest -class TestCompilerMojoTest { - - private static final String TEST_COMPILE = "testCompile"; - - @Test - @InjectMojo(goal = TEST_COMPILE, pom = "classpath:/unit/compiler-basic-test/plugin-config.xml") - void testCompilerBasic(TestCompilerMojo testCompilerMojo) throws Exception { - setUpCompilerMojoTestEnv(testCompilerMojo); - - testCompilerMojo.execute(); - - File testClass = new File(testCompilerMojo.getOutputDirectory(), "foo/TestCompile0Test.class"); - - assertTrue(testClass::exists); - } - - @Test - @InjectMojo(goal = TEST_COMPILE, pom = "classpath:/unit/compiler-empty-source-test/plugin-config.xml") - public void testCompilerEmptySource(TestCompilerMojo testCompilerMojo) throws Exception { - setUpCompilerMojoTestEnv(testCompilerMojo); - - testCompilerMojo.execute(); - - assertFalse(testCompilerMojo.getOutputDirectory().exists()); - } - - @Test - @InjectMojo(goal = TEST_COMPILE, pom = "classpath:/unit/compiler-includes-excludes-test/plugin-config.xml") - void testCompilerIncludesExcludes(TestCompilerMojo testCompilerMojo) throws Exception { - setUpCompilerMojoTestEnv(testCompilerMojo); - - Set includes = new HashSet<>(); - includes.add("**/TestCompile4*.java"); - setVariableValueToObject(testCompilerMojo, "testIncludes", includes); - - Set excludes = new HashSet<>(); - excludes.add("**/TestCompile2*.java"); - excludes.add("**/TestCompile3*.java"); - setVariableValueToObject(testCompilerMojo, "testExcludes", excludes); - - testCompilerMojo.execute(); - - File testClass = new File(testCompilerMojo.getOutputDirectory(), "foo/TestCompile2TestCase.class"); - assertFalse(testClass.exists()); - - testClass = new File(testCompilerMojo.getOutputDirectory(), "foo/TestCompile3TestCase.class"); - assertFalse(testClass.exists()); - - testClass = new File(testCompilerMojo.getOutputDirectory(), "foo/TestCompile4TestCase.class"); - assertTrue(testClass.exists()); - } - - @Test - @InjectMojo(goal = TEST_COMPILE, pom = "classpath:/unit/compiler-fork-test/plugin-config.xml") - void testCompilerFork(TestCompilerMojo testCompilerMojo) throws Exception { - setUpCompilerMojoTestEnv(testCompilerMojo); - - // JAVA_HOME doesn't have to be on the PATH. - setVariableValueToObject( - testCompilerMojo, "executable", new File(System.getenv("JAVA_HOME"), "bin/javac").getPath()); - - testCompilerMojo.execute(); - - File testClass = new File(testCompilerMojo.getOutputDirectory(), "foo/TestCompile1TestCase.class"); - assertTrue(testClass.exists()); - } - - @Test - @InjectMojo(goal = TEST_COMPILE, pom = "classpath:/unit/compiler-one-output-file-test/plugin-config.xml") - void testOneOutputFileForAllInput(TestCompilerMojo testCompilerMojo) throws Exception { - setUpCompilerMojoTestEnv(testCompilerMojo); - - setVariableValueToObject(testCompilerMojo, "compilerManager", new CompilerManagerStub()); - - testCompilerMojo.execute(); - - File testClass = new File(testCompilerMojo.getOutputDirectory(), "compiled.class"); - assertTrue(testClass.exists()); - } - - @Test - @InjectMojo(goal = TEST_COMPILE, pom = "classpath:/unit/compiler-one-output-file-test2/plugin-config.xml") - void testOneOutputFileForAllInput2(TestCompilerMojo testCompilerMojo) throws Exception { - setUpCompilerMojoTestEnv(testCompilerMojo); - - setVariableValueToObject(testCompilerMojo, "compilerManager", new CompilerManagerStub()); - - Set includes = new HashSet<>(); - includes.add("**/TestCompile4*.java"); - setVariableValueToObject(testCompilerMojo, "testIncludes", includes); - - Set excludes = new HashSet<>(); - excludes.add("**/TestCompile2*.java"); - excludes.add("**/TestCompile3*.java"); - setVariableValueToObject(testCompilerMojo, "testExcludes", excludes); - - testCompilerMojo.execute(); - - File testClass = new File(testCompilerMojo.getOutputDirectory(), "compiled.class"); - assertTrue(testClass.exists()); - } - - @Test - @InjectMojo(goal = TEST_COMPILE, pom = "classpath:/unit/compiler-skip-main/plugin-config.xml") - void testCompileSkipMain(TestCompilerMojo testCompilerMojo) throws Exception { - setUpCompilerMojoTestEnv(testCompilerMojo); - - testCompilerMojo.execute(); - - File testClass = new File(testCompilerMojo.getOutputDirectory(), "foo/TestSkipMainCompile0Test.class"); - assertTrue(testClass.exists()); - } - - @Test - @InjectMojo(goal = TEST_COMPILE, pom = "classpath:/unit/compiler-skip-test/plugin-config.xml") - void testCompileSkipTest(TestCompilerMojo testCompilerMojo) throws Exception { - setUpCompilerMojoTestEnv(testCompilerMojo); - - setVariableValueToObject(testCompilerMojo, "skip", true); - - testCompilerMojo.execute(); - - File testClass = new File(testCompilerMojo.getOutputDirectory(), "foo/TestSkipTestCompile0Test.class"); - assertFalse(testClass.exists()); - } - - private void setUpCompilerMojoTestEnv(TestCompilerMojo mojo) throws Exception { - File buildDir = getVariableValueFromObject(mojo, "buildDirectory"); - File testClassesDir = new File(buildDir, "test-classes"); - setVariableValueToObject(mojo, "outputDirectory", testClassesDir); - - setVariableValueToObject(mojo, "project", getMockMavenProject()); - - Path baseDir = mojo.getOutputDirectory() - .toPath() - .resolveSibling(Paths.get("..", "..", "..", "..", "test-classes")) - .normalize(); - Path subpath = mojo.getOutputDirectory().toPath().subpath(baseDir.getNameCount(), baseDir.getNameCount() + 2); - String sourceRoot = baseDir.resolve(subpath) + "/src/main/java"; - String testSourceRoot = baseDir.resolve(subpath) + "/src/test/java"; - - setVariableValueToObject(mojo, "compileSourceRoots", Arrays.asList(sourceRoot, testSourceRoot)); - - setVariableValueToObject(mojo, "session", getMockMavenSession()); - setVariableValueToObject(mojo, "mojoExecution", getMockMojoExecution()); - setVariableValueToObject(mojo, "source", AbstractCompilerMojo.DEFAULT_SOURCE); - setVariableValueToObject(mojo, "target", AbstractCompilerMojo.DEFAULT_TARGET); - } - - static Stream olderThanJDK9() { - return Stream.of( - Arguments.of("1.8", true), - Arguments.of("8", true), - Arguments.of("1.9", false), - Arguments.of("1.9", false), - Arguments.of("9", false), - Arguments.of("11", false)); - } - - @ParameterizedTest - @MethodSource - void olderThanJDK9(String version, boolean expected) { - assertEquals(expected, TestCompilerMojo.isOlderThanJDK9(version)); - } -} diff --git a/src/test/java/org/apache/maven/plugin/compiler/stubs/DebugEnabledLog.java b/src/test/java/org/apache/maven/plugin/compiler/stubs/DebugEnabledLog.java deleted file mode 100644 index 4663fa18..00000000 --- a/src/test/java/org/apache/maven/plugin/compiler/stubs/DebugEnabledLog.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 - * - * http://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.apache.maven.plugin.compiler.stubs; - -import org.apache.maven.plugin.logging.SystemStreamLog; - -/** - * @author Edwin Punzalan - */ -public class DebugEnabledLog extends SystemStreamLog { - public DebugEnabledLog() { - super(); - } - - public boolean isDebugEnabled() { - return true; - } -} diff --git a/src/test/resources/unit/compiler-args-test/plugin-config.xml b/src/test/resources/unit/compiler-args-test/plugin-config.xml index ea541356..d6d6d17f 100644 --- a/src/test/resources/unit/compiler-args-test/plugin-config.xml +++ b/src/test/resources/unit/compiler-args-test/plugin-config.xml @@ -24,12 +24,13 @@ maven-compiler-plugin - ${basedir}/target/test-classes/unit/compiler-args-test/src/main/java + ${project.basedir}/src/main/java javac true - ${basedir}/target/test/unit/compiler-args-test/target/classes - ${basedir}/target/test/unit/compiler-args-test/target + ${project.basedir}/target + ${project.basedir}/target/classes + ${project.basedir}/target/test-classes value1 diff --git a/src/test/resources/unit/compiler-basic-sourcetarget/plugin-config.xml b/src/test/resources/unit/compiler-basic-sourcetarget/plugin-config.xml index b4059e06..cf09fbc8 100644 --- a/src/test/resources/unit/compiler-basic-sourcetarget/plugin-config.xml +++ b/src/test/resources/unit/compiler-basic-sourcetarget/plugin-config.xml @@ -24,14 +24,12 @@ maven-compiler-plugin - ${basedir}/target/test-classes/unit/compiler-basic-test/src/main/java + ${project.basedir}/src/main/java javac true - ${basedir}/target/test/unit/compiler-basic-test/target/classes - ${basedir}/target/test/unit/compiler-basic-test/target - 1.6 - 1.6 + ${project.basedir}/target + ${project.basedir}/target/classes diff --git a/src/test/resources/unit/compiler-basic-test/plugin-config.xml b/src/test/resources/unit/compiler-basic-test/plugin-config.xml index 5ed0fece..13c4512e 100644 --- a/src/test/resources/unit/compiler-basic-test/plugin-config.xml +++ b/src/test/resources/unit/compiler-basic-test/plugin-config.xml @@ -19,17 +19,18 @@ + ${project.basedir}/target + ${project.basedir}/target/classes + ${project.basedir}/target/test-classes maven-compiler-plugin - ${basedir}/target/test-classes/unit/compiler-basic-test/src/main/java + ${project.basedir}/src/main/java javac true - ${basedir}/target/test/unit/compiler-basic-test/target/classes - ${basedir}/target/test/unit/compiler-basic-test/target diff --git a/src/test/resources/unit/compiler-empty-source-test/plugin-config.xml b/src/test/resources/unit/compiler-empty-source-test/plugin-config.xml index 7e47c2f1..7d7a2461 100644 --- a/src/test/resources/unit/compiler-empty-source-test/plugin-config.xml +++ b/src/test/resources/unit/compiler-empty-source-test/plugin-config.xml @@ -24,13 +24,15 @@ maven-compiler-plugin - ${basedir}/target/test-classes/unit/compiler-empty-source-test/src/main/java - + ${project.basedir}/src/main/java javac true - ${basedir}/target/test/unit/compiler-empty-source-test/target/classes - ${basedir}/target/test/unit/compiler-empty-source-test/target + ${project.basedir}/target + ${project.basedir}/target/classes + ${project.basedir}/target/test-classes + 8 + 8 diff --git a/src/test/resources/unit/compiler-fail-test/plugin-config.xml b/src/test/resources/unit/compiler-fail-test/plugin-config.xml index 7f24e86e..13c4512e 100644 --- a/src/test/resources/unit/compiler-fail-test/plugin-config.xml +++ b/src/test/resources/unit/compiler-fail-test/plugin-config.xml @@ -19,17 +19,18 @@ + ${project.basedir}/target + ${project.basedir}/target/classes + ${project.basedir}/target/test-classes maven-compiler-plugin - ${basedir}/target/test-classes/unit/compiler-fail-test/src/main/java + ${project.basedir}/src/main/java javac true - ${basedir}/target/test/unit/compiler-fail-test/target/classes - ${basedir}/target/test/unit/compiler-fail-test/target diff --git a/src/test/resources/unit/compiler-failonerror-test/plugin-config.xml b/src/test/resources/unit/compiler-failonerror-test/plugin-config.xml index 512d56dc..34467014 100644 --- a/src/test/resources/unit/compiler-failonerror-test/plugin-config.xml +++ b/src/test/resources/unit/compiler-failonerror-test/plugin-config.xml @@ -25,12 +25,13 @@ false - ${basedir}/target/test-classes/unit/compiler-fail-test/src/main/java + ${project.basedir}/src/main/java javac true - ${basedir}/target/test/unit/compiler-fail-test/target/classes - ${basedir}/target/test/unit/compiler-fail-test/target + ${project.basedir}/target + ${project.basedir}/target/classes + ${project.basedir}/target/test-classes diff --git a/src/test/resources/unit/compiler-fork-test/plugin-config.xml b/src/test/resources/unit/compiler-fork-test/plugin-config.xml index 99bd0364..f9459426 100644 --- a/src/test/resources/unit/compiler-fork-test/plugin-config.xml +++ b/src/test/resources/unit/compiler-fork-test/plugin-config.xml @@ -24,16 +24,18 @@ maven-compiler-plugin true + 8 + 8 - ${basedir} + ${project.basedir} 64m 128m true true - ${basedir}/target/test-classes/unit/compiler-fork-test/src/main/java + ${project.basedir}/src/main/java javac true @@ -43,8 +45,9 @@ This value would have been better, but ${env.JAVA_HOME} can't be resolved. So it is adjusted in org.apache.maven.plugin.compiler.CompilerMojoTestCase.testCompilerFork() --> - ${basedir}/target/test/unit/compiler-fork-test/target/classes - ${basedir}/target/test/unit/compiler-fork-test/target + ${project.basedir}/target + ${project.basedir}/target/classes + ${project.basedir}/target/test-classes diff --git a/src/test/resources/unit/compiler-implicit-test/plugin-config-none.xml b/src/test/resources/unit/compiler-implicit-test/plugin-config-none.xml index faf33341..c90ee61b 100644 --- a/src/test/resources/unit/compiler-implicit-test/plugin-config-none.xml +++ b/src/test/resources/unit/compiler-implicit-test/plugin-config-none.xml @@ -24,12 +24,13 @@ maven-compiler-plugin - ${basedir}/target/test-classes/unit/compiler-implicit-test/src/main/java + ${project.basedir}/src/main/java javac true - ${basedir}/target/test/unit/compiler-implicit-test/target/classes - ${basedir}/target/test/unit/compiler-implicit-test/target + ${project.basedir}/target + ${project.basedir}/target/classes + ${project.basedir}/target/test-classes none diff --git a/src/test/resources/unit/compiler-implicit-test/plugin-config-not-set.xml b/src/test/resources/unit/compiler-implicit-test/plugin-config-not-set.xml index 1ad147b6..64e1e7fb 100644 --- a/src/test/resources/unit/compiler-implicit-test/plugin-config-not-set.xml +++ b/src/test/resources/unit/compiler-implicit-test/plugin-config-not-set.xml @@ -24,12 +24,13 @@ maven-compiler-plugin - ${basedir}/target/test-classes/unit/compiler-implicit-test/src/main/java + ${project.basedir}/src/main/java javac true - ${basedir}/target/test/unit/compiler-implicit-test/target/classes - ${basedir}/target/test/unit/compiler-implicit-test/target + ${project.basedir}/target + ${project.basedir}/target/classes + ${project.basedir}/target/test-classes diff --git a/src/test/resources/unit/compiler-includes-excludes-test/plugin-config.xml b/src/test/resources/unit/compiler-includes-excludes-test/plugin-config.xml index a773b718..d248ec8d 100644 --- a/src/test/resources/unit/compiler-includes-excludes-test/plugin-config.xml +++ b/src/test/resources/unit/compiler-includes-excludes-test/plugin-config.xml @@ -19,13 +19,15 @@ + ${project.basedir}/target + ${project.basedir}/target/classes + ${project.basedir}/target/test-classes maven-compiler-plugin - ${basedir}/target/test-classes/unit/compiler-includes-excludes-test/src/main/java - + ${project.basedir}/src/main/java