diff --git a/.asf.yaml b/.asf.yaml index a95f99ea..c87faefa 100644 --- a/.asf.yaml +++ b/.asf.yaml @@ -13,6 +13,8 @@ github: rebase: true autolink_jira: - MBUILDCACHE + del_branch_on_merge: true + notifications: commits: commits@maven.apache.org issues: issues@maven.apache.org diff --git a/.github/workflows/maven-verify-3.9.x.yml b/.github/workflows/maven-verify-3.9.x.yml index 10dab2d3..3b4b9d09 100644 --- a/.github/workflows/maven-verify-3.9.x.yml +++ b/.github/workflows/maven-verify-3.9.x.yml @@ -24,11 +24,11 @@ on: jobs: build: name: Verify - uses: apache/maven-gh-actions-shared/.github/workflows/maven-verify.yml@v3 + uses: apache/maven-gh-actions-shared/.github/workflows/maven-verify.yml@v4 with: maven-args: '-D"maven.test.redirectTestOutputToFile=false -Pmaven3"' - maven-matrix: '[ "3.9.5", "4.0.0-alpha-8" ]' - jdk-matrix: '[ "11", "17" ]' + maven-matrix: '[ "3.9.9" ]' + jdk-matrix: '[ "11", "17", "21" ]' ff-goal: '-P run-its verify javadoc:jar' verify-goal: '-P run-its verify javadoc:jar' diff --git a/.github/workflows/maven-verify.yml b/.github/workflows/maven-verify.yml index 0665f5c2..247b1cf1 100644 --- a/.github/workflows/maven-verify.yml +++ b/.github/workflows/maven-verify.yml @@ -24,10 +24,11 @@ on: jobs: build: name: Verify - uses: apache/maven-gh-actions-shared/.github/workflows/maven-verify.yml@v3 + uses: apache/maven-gh-actions-shared/.github/workflows/maven-verify.yml@v4 with: maven-args: '-D"maven.test.redirectTestOutputToFile=false"' - maven-matrix: '[ "3.9.5", "4.0.0-alpha-8" ]' - jdk-matrix: '[ "11", "17" ]' + maven-matrix: '[ "3.9.9", "4.0.0-rc-3" ]' + maven4-enabled: true + matrix-exclude: '[ {"jdk": "8"} ]' ff-goal: '-P run-its verify javadoc:jar' - verify-goal: '-P run-its verify javadoc:jar' \ No newline at end of file + verify-goal: '-P run-its verify javadoc:jar' diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index b44872cf..1049eaa3 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -22,4 +22,4 @@ on: - master jobs: update_release_draft: - uses: apache/maven-gh-actions-shared/.github/workflows/release-drafter.yml@v3 + uses: apache/maven-gh-actions-shared/.github/workflows/release-drafter.yml@v4 diff --git a/Jenkinsfile b/Jenkinsfile index bcfdaeb8..079fa482 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -17,4 +17,4 @@ * under the License. */ -asfMavenTlpStdBuild( 'jdks' : [ "11", "17" ] ) +asfMavenTlpStdBuild( 'jdks' : [ "11", "17", "21" ] ) diff --git a/pom.xml b/pom.xml index 4ea7d506..b2502e66 100644 --- a/pom.xml +++ b/pom.xml @@ -29,7 +29,7 @@ under the License. org.apache.maven.extensions maven-build-cache-extension - 1.1.1-SNAPSHOT + 1.2.1-SNAPSHOT jar Apache Maven Build Cache Extension @@ -40,7 +40,7 @@ under the License. scm:git:https://github.com/apache/maven-build-cache-extension.git scm:git:https://github.com/apache/maven-build-cache-extension.git - maven-build-cache-extension-1.0.0 + HEAD https://github.com/apache/maven-build-cache-extension/tree/${project.scm.tag} @@ -59,7 +59,7 @@ under the License. - 2023-11-27T01:58:16Z + 2024-05-09T00:27:12Z 8 3.9.0 11 @@ -71,18 +71,17 @@ under the License. ${project.build.directory}/${maven.dir} 1.4 - 3.14.0 - 5.10.1 + 3.17.0 + 5.12.2 4.11.0 - 3.5.1 3.4.3 2.0 2.0 1.11 1.3 - 1.9.18 + 1.9.22 1.7.36 - 2.35.1 + 2.35.2 2.6.4 true extensions-archives/${project.artifactId}-LATEST @@ -93,7 +92,7 @@ under the License. org.testcontainers testcontainers-bom - 1.19.4 + 1.21.0 pom import @@ -114,12 +113,6 @@ under the License. ${mavenVersion} provided - - org.apache.maven - maven-compat - ${mavenVersion} - provided - org.apache.maven.resolver maven-resolver-transport-http @@ -129,12 +122,12 @@ under the License. net.openhft zero-allocation-hashing - 0.16 + 0.27ea0 com.github.albfernandez juniversalchardet - 2.4.0 + 2.5.0 org.apache.commons @@ -144,7 +137,7 @@ under the License. commons-io commons-io - 2.15.1 + 2.19.0 javax.annotation @@ -154,7 +147,10 @@ under the License. org.codehaus.plexus plexus-utils - ${plexusUtilsVersion} + + + org.codehaus.plexus + plexus-xml com.google.code.findbugs @@ -221,7 +217,7 @@ under the License. org.assertj assertj-core - 3.25.2 + 3.27.3 test @@ -307,7 +303,7 @@ under the License. org.apache.maven.plugins maven-invoker-plugin - 3.6.0 + 3.9.0 org.apache.rat @@ -338,7 +334,7 @@ under the License. src/main/mdo/build-cache-diff.mdo src/main/mdo/build-cache-report.mdo - 1.0.0 + 1.2.0 @@ -373,7 +369,7 @@ under the License. org.codehaus.mojo build-helper-maven-plugin - 3.5.0 + 3.6.0 add-resources @@ -394,7 +390,7 @@ under the License. org.apache.maven.plugins maven-dependency-plugin - 3.6.1 + 3.8.1 copy-maven-distribution @@ -418,6 +414,19 @@ under the License. + + + org.eclipse.sisu + sisu-maven-plugin + + + generate-index + + main-index + + + + @@ -456,7 +465,7 @@ under the License. maven3 - 3.9.5 + 3.9.9 maven3 ${project.build.directory}/${maven.dir} diff --git a/src/main/java/org/apache/maven/buildcache/BuildCacheMojosExecutionStrategy.java b/src/main/java/org/apache/maven/buildcache/BuildCacheMojosExecutionStrategy.java index 6112e293..5790d19f 100644 --- a/src/main/java/org/apache/maven/buildcache/BuildCacheMojosExecutionStrategy.java +++ b/src/main/java/org/apache/maven/buildcache/BuildCacheMojosExecutionStrategy.java @@ -79,7 +79,7 @@ public class BuildCacheMojosExecutionStrategy implements MojosExecutionStrategy private final MojoParametersListener mojoListener; private final LifecyclePhasesHelper lifecyclePhasesHelper; private final MavenPluginManager mavenPluginManager; - private MojoExecutionScope mojoExecutionScope; + private final MojoExecutionScope mojoExecutionScope; @Inject public BuildCacheMojosExecutionStrategy( @@ -130,11 +130,11 @@ public void execute( } boolean restorable = result.isSuccess() || result.isPartialSuccess(); - boolean restored = result.isSuccess(); // if partially restored need to save increment + boolean restored = false; // if partially restored need to save increment if (restorable) { CacheRestorationStatus cacheRestorationStatus = restoreProject(result, mojoExecutions, mojoExecutionRunner, cacheConfig); - restored &= CacheRestorationStatus.SUCCESS == cacheRestorationStatus; + restored = CacheRestorationStatus.SUCCESS == cacheRestorationStatus; executeExtraCleanPhaseIfNeeded(cacheRestorationStatus, cleanPhase, mojoExecutionRunner); } if (!restored) { @@ -147,9 +147,18 @@ public void execute( } } - if (cacheState == INITIALIZED && (!restorable || !restored)) { - final Map executionEvents = mojoListener.getProjectExecutions(project); - cacheController.save(result, mojoExecutions, executionEvents); + if (cacheState == INITIALIZED && (!result.isSuccess() || !restored)) { + if (cacheConfig.isSkipSave()) { + LOGGER.info("Cache saving is disabled."); + } else if (cacheConfig.isMandatoryClean() + && lifecyclePhasesHelper + .getCleanSegment(project, mojoExecutions) + .isEmpty()) { + LOGGER.info("Cache storing is skipped since there was no \"clean\" phase."); + } else { + final Map executionEvents = mojoListener.getProjectExecutions(project); + cacheController.save(result, mojoExecutions, executionEvents); + } } if (cacheConfig.isFailFast() && !result.isSuccess() && !skipCache && !forkedExecution) { @@ -262,6 +271,20 @@ private CacheRestorationStatus restoreProject( LOGGER.info( "Skipping plugin execution (cached): {}", cacheCandidate.getMojoDescriptor().getFullGoalName()); + // Need to populate cached candidate executions for the build cache save result + Mojo mojo = null; + try { + mojo = mavenPluginManager.getConfiguredMojo(Mojo.class, session, cacheCandidate); + MojoExecutionEvent mojoExecutionEvent = + new MojoExecutionEvent(session, project, cacheCandidate, mojo); + mojoListener.beforeMojoExecution(mojoExecutionEvent); + } catch (PluginConfigurationException | PluginContainerException e) { + throw new RuntimeException(e); + } finally { + if (mojo != null) { + mavenPluginManager.releaseMojo(mojo, cacheCandidate); + } + } } } @@ -411,6 +434,6 @@ private static String normalizedPath(Path path, Path baseDirPath) { private enum CacheRestorationStatus { SUCCESS, FAILURE, - FAILURE_NEEDS_CLEAN; + FAILURE_NEEDS_CLEAN } } diff --git a/src/main/java/org/apache/maven/buildcache/CacheControllerImpl.java b/src/main/java/org/apache/maven/buildcache/CacheControllerImpl.java index 9779a482..5a774735 100644 --- a/src/main/java/org/apache/maven/buildcache/CacheControllerImpl.java +++ b/src/main/java/org/apache/maven/buildcache/CacheControllerImpl.java @@ -34,9 +34,11 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.SimpleFileVisitor; +import java.nio.file.StandardCopyOption; import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -46,20 +48,20 @@ import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.FutureTask; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Function; import java.util.regex.Pattern; -import org.apache.commons.io.FileUtils; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.mutable.MutableBoolean; import org.apache.maven.SessionScoped; -import org.apache.maven.artifact.InvalidArtifactRTException; import org.apache.maven.artifact.handler.ArtifactHandler; import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager; import org.apache.maven.buildcache.artifact.ArtifactRestorationReport; +import org.apache.maven.buildcache.artifact.OutputType; import org.apache.maven.buildcache.artifact.RestoredArtifact; import org.apache.maven.buildcache.checksum.MavenProjectInput; import org.apache.maven.buildcache.hash.HashAlgorithm; @@ -74,6 +76,7 @@ import org.apache.maven.buildcache.xml.build.DigestItem; import org.apache.maven.buildcache.xml.build.ProjectsInputInfo; import org.apache.maven.buildcache.xml.build.Scm; +import org.apache.maven.buildcache.xml.config.DirName; import org.apache.maven.buildcache.xml.config.PropertyName; import org.apache.maven.buildcache.xml.config.TrackedProperty; import org.apache.maven.buildcache.xml.diff.Diff; @@ -85,15 +88,14 @@ import org.apache.maven.plugin.descriptor.Parameter; import org.apache.maven.project.MavenProject; import org.apache.maven.project.MavenProjectHelper; -import org.apache.maven.repository.RepositorySystem; import org.codehaus.plexus.util.ReflectionUtils; +import org.eclipse.aether.RepositorySystem; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static java.nio.file.StandardOpenOption.CREATE; import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING; import static org.apache.commons.lang3.StringUtils.isNotBlank; -import static org.apache.commons.lang3.StringUtils.replace; import static org.apache.commons.lang3.StringUtils.split; import static org.apache.maven.buildcache.CacheResult.empty; import static org.apache.maven.buildcache.CacheResult.failure; @@ -112,13 +114,10 @@ @SuppressWarnings("unused") public class CacheControllerImpl implements CacheController { - public static final String FILE_SEPARATOR_SUBST = "_"; - /** - * Prefix for generated sources stored as a separate artifact in cache - */ - private static final String BUILD_PREFIX = "build" + FILE_SEPARATOR_SUBST; - private static final Logger LOGGER = LoggerFactory.getLogger(CacheControllerImpl.class); + private static final String DEFAULT_FILE_GLOB = "*"; + public static final String ERROR_MSG_RESTORATION_OUTSIDE_PROJECT = + "Blocked an attempt to restore files outside of a project directory: "; private final MavenProjectHelper projectHelper; private final ArtifactHandlerManager artifactHandlerManager; @@ -133,6 +132,15 @@ public class CacheControllerImpl implements CacheController { private final RestoredArtifactHandler restoreArtifactHandler; private volatile Scm scm; + /** + * A map dedicated to store the base path of resources stored to the cache which are not original artifacts + * (ex : generated source basedir). + * Used to link the resource to its path on disk + */ + private final Map attachedResourcesPathsById = new HashMap<>(); + + private int attachedResourceCounter = 0; + // CHECKSTYLE_OFF: ParameterNumber @Inject public CacheControllerImpl( MavenProjectHelper projectHelper, @@ -146,6 +154,7 @@ public CacheControllerImpl( RestoredArtifactHandler restoreArtifactHandler, LifecyclePhasesHelper lifecyclePhasesHelper, MavenSession session) { + // CHECKSTYLE_OFF: ParameterNumber this.projectHelper = projectHelper; this.localCache = localCache; this.remoteCache = remoteCache; @@ -297,6 +306,45 @@ private boolean canIgnoreMissingSegment(MavenProject project, Build info, List createRestorationToDiskConsumer(final MavenProject project, final Artifact artifact) { + + if (cacheConfig.isRestoreOnDiskArtifacts() && MavenProjectInput.isRestoreOnDiskArtifacts(project)) { + + Path restorationPath = project.getBasedir().toPath().resolve(artifact.getFilePath()); + final AtomicBoolean restored = new AtomicBoolean(false); + return file -> { + // Set to restored even if it fails later, we don't want multiple try + if (restored.compareAndSet(false, true)) { + verifyRestorationInsideProject(project, restorationPath); + try { + Files.createDirectories(restorationPath.getParent()); + Files.copy(file.toPath(), restorationPath, StandardCopyOption.REPLACE_EXISTING); + } catch (IOException e) { + LOGGER.error("Cannot restore file " + artifact.getFileName(), e); + throw new RuntimeException(e); + } + LOGGER.debug("Restored file on disk ({} to {})", artifact.getFileName(), restorationPath); + } + return restorationPath.toFile(); + }; + } + // Return a consumer doing nothing + return file -> file; + } + + private boolean isPathInsideProject(final MavenProject project, Path path) { + Path restorationPath = path.toAbsolutePath().normalize(); + return restorationPath.startsWith(project.getBasedir().toPath()); + } + + private void verifyRestorationInsideProject(final MavenProject project, Path path) { + if (!isPathInsideProject(project, path)) { + Path normalized = path.toAbsolutePath().normalize(); + LOGGER.error(ERROR_MSG_RESTORATION_OUTSIDE_PROJECT + normalized); + throw new RuntimeException(ERROR_MSG_RESTORATION_OUTSIDE_PROJECT + normalized); + } + } + @Override public ArtifactRestorationReport restoreProjectArtifacts(CacheResult cacheResult) { @@ -318,15 +366,23 @@ public ArtifactRestorationReport restoreProjectArtifacts(CacheResult cacheResult final Future downloadTask = createDownloadTask(cacheResult, context, project, artifactInfo, originalVersion); restoredProjectArtifact = restoredArtifact( - project.getArtifact(), artifactInfo.getType(), artifactInfo.getClassifier(), downloadTask); + project.getArtifact(), + artifactInfo.getType(), + artifactInfo.getClassifier(), + downloadTask, + createRestorationToDiskConsumer(project, artifactInfo)); + if (!cacheConfig.isLazyRestore()) { + restoredProjectArtifact.getFile(); + } } for (Artifact attachedArtifactInfo : build.getAttachedArtifacts()) { String originalVersion = attachedArtifactInfo.getVersion(); attachedArtifactInfo.setVersion(project.getVersion()); if (isNotBlank(attachedArtifactInfo.getFileName())) { - if (StringUtils.startsWith(attachedArtifactInfo.getClassifier(), BUILD_PREFIX)) { - // restoring generated sources might be unnecessary in CI, could be disabled for + OutputType outputType = OutputType.fromClassifier(attachedArtifactInfo.getClassifier()); + if (OutputType.ARTIFACT != outputType) { + // restoring generated sources / extra output might be unnecessary in CI, could be disabled for // performance reasons // it may also be disabled on a per-project level (defaults to true - enable) if (cacheConfig.isRestoreGeneratedSources() @@ -345,7 +401,11 @@ public ArtifactRestorationReport restoreProjectArtifacts(CacheResult cacheResult restoredProjectArtifact == null ? project.getArtifact() : restoredProjectArtifact, attachedArtifactInfo.getType(), attachedArtifactInfo.getClassifier(), - downloadTask); + downloadTask, + createRestorationToDiskConsumer(project, attachedArtifactInfo)); + if (!cacheConfig.isLazyRestore()) { + restoredAttachedArtifact.getFile(); + } restoredAttachedArtifacts.add(restoredAttachedArtifact); } } @@ -354,6 +414,10 @@ public ArtifactRestorationReport restoreProjectArtifacts(CacheResult cacheResult // in which case, the project is unmodified and we continue with normal build. if (restoredProjectArtifact != null) { project.setArtifact(restoredProjectArtifact); + // need to include package lifecycle to save build info for incremental builds + if (!project.hasLifecyclePhase("package")) { + project.addLifecyclePhase("package"); + } } restoredAttachedArtifacts.forEach(project::addAttachedArtifact); restorationReport.setSuccess(true); @@ -371,7 +435,8 @@ private RestoredArtifact restoredArtifact( org.apache.maven.artifact.Artifact parent, String artifactType, String artifactClassifier, - Future artifactFile) { + Future artifactFile, + Function restoreToDiskConsumer) { ArtifactHandler handler = null; if (artifactType != null) { @@ -383,8 +448,8 @@ private RestoredArtifact restoredArtifact( } // todo: probably need update download url to cache - RestoredArtifact artifact = - new RestoredArtifact(parent, artifactFile, artifactType, artifactClassifier, handler); + RestoredArtifact artifact = new RestoredArtifact( + parent, artifactFile, artifactType, artifactClassifier, handler, restoreToDiskConsumer); artifact.setResolved(true); return artifact; @@ -399,36 +464,17 @@ private Future createDownloadTask( final FutureTask downloadTask = new FutureTask<>(() -> { LOGGER.debug("Downloading artifact {}", artifact.getArtifactId()); final Path artifactFile = localCache.getArtifactFile(context, cacheResult.getSource(), artifact); + if (!Files.exists(artifactFile)) { throw new FileNotFoundException("Missing file for cached build, cannot restore. File: " + artifactFile); } - LOGGER.debug("Downloaded artifact " + artifact.getArtifactId() + " to: " + artifactFile); + LOGGER.debug("Downloaded artifact {} to: {}", artifact.getArtifactId(), artifactFile); return restoreArtifactHandler .adjustArchiveArtifactVersion(project, originalVersion, artifactFile) .toFile(); }); if (!cacheConfig.isLazyRestore()) { downloadTask.run(); - try { - downloadTask.get(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new InvalidArtifactRTException( - artifact.getGroupId(), - artifact.getArtifactId(), - artifact.getVersion(), - artifact.getType(), - RestoredArtifact.MSG_INTERRUPTED_WHILE_RETRIEVING_ARTIFACT_FILE, - e); - } catch (ExecutionException e) { - throw new InvalidArtifactRTException( - artifact.getGroupId(), - artifact.getArtifactId(), - artifact.getVersion(), - artifact.getType(), - RestoredArtifact.MSG_ERROR_RETRIEVING_ARTIFACT_FILE, - e.getCause()); - } } return downloadTask; } @@ -460,8 +506,8 @@ public void save( attachedArtifacts = project.getAttachedArtifacts() != null ? project.getAttachedArtifacts() : Collections.emptyList(); - attachedArtifactDtos = artifactDtos(attachedArtifacts, algorithm); - projectArtifactDto = artifactDto(project.getArtifact(), algorithm); + attachedArtifactDtos = artifactDtos(attachedArtifacts, algorithm, project); + projectArtifactDto = artifactDto(project.getArtifact(), algorithm, project); } else { attachedArtifacts = Collections.emptyList(); attachedArtifactDtos = new ArrayList<>(); @@ -485,7 +531,6 @@ public void save( // if package phase presence means new artifacts were packaged if (project.hasLifecyclePhase("package")) { - localCache.saveBuildInfo(cacheResult, build); if (projectArtifact.getFile() != null) { localCache.saveArtifactFile(cacheResult, projectArtifact); } @@ -503,10 +548,10 @@ public void save( } } } - } else { - localCache.saveBuildInfo(cacheResult, build); } + localCache.saveBuildInfo(cacheResult, build); + if (cacheConfig.isBaselineDiffEnabled()) { produceDiffReport(cacheResult, build); } @@ -575,24 +620,35 @@ public void produceDiffReport(CacheResult cacheResult, Build build) { } private List artifactDtos( - List attachedArtifacts, HashAlgorithm digest) throws IOException { + List attachedArtifacts, HashAlgorithm digest, MavenProject project) + throws IOException { List result = new ArrayList<>(); for (org.apache.maven.artifact.Artifact attachedArtifact : attachedArtifacts) { if (attachedArtifact.getFile() != null && isOutputArtifact(attachedArtifact.getFile().getName())) { - result.add(artifactDto(attachedArtifact, digest)); + result.add(artifactDto(attachedArtifact, digest, project)); } } return result; } - private Artifact artifactDto(org.apache.maven.artifact.Artifact projectArtifact, HashAlgorithm algorithm) + private Artifact artifactDto( + org.apache.maven.artifact.Artifact projectArtifact, HashAlgorithm algorithm, MavenProject project) throws IOException { final Artifact dto = DtoUtils.createDto(projectArtifact); if (projectArtifact.getFile() != null && projectArtifact.getFile().isFile()) { final Path file = projectArtifact.getFile().toPath(); dto.setFileHash(algorithm.hash(file)); dto.setFileSize(Files.size(file)); + + // Get the relative path of any extra zip directory added to the cache + Path relativePath = attachedResourcesPathsById.get(projectArtifact.getClassifier()); + if (relativePath == null) { + // If the path was not a member of this map, we are in presence of an original artifact. + // we get its location on the disk + relativePath = project.getBasedir().toPath().relativize(file.toAbsolutePath()); + } + dto.setFilePath(FilenameUtils.separatorsToUnix(relativePath.toString())); } return dto; } @@ -817,36 +873,23 @@ private void populateGitInfo(Build build, MavenSession session) { build.getDto().setScm(scm); } - private void zipAndAttachArtifact(MavenProject project, Path dir, String classifier) throws IOException { - Path temp = Files.createTempFile("maven-incremental", project.getArtifactId()); + private boolean zipAndAttachArtifact(MavenProject project, Path dir, String classifier, final String glob) + throws IOException { + Path temp = Files.createTempFile("maven-incremental-", project.getArtifactId()); temp.toFile().deleteOnExit(); - CacheUtils.zip(dir, temp); - projectHelper.attachArtifact(project, "zip", classifier, temp.toFile()); - } - - private String pathToClassifier(Path relative) { - final int nameCount = relative.getNameCount(); - List segments = new ArrayList<>(nameCount + 1); - for (int i = 0; i < nameCount; i++) { - segments.add(relative.getName(i).toFile().getName()); + boolean hasFile = CacheUtils.zip(dir, temp, glob); + if (hasFile) { + projectHelper.attachArtifact(project, "zip", classifier, temp.toFile()); } - // todo handle _ in file names - return BUILD_PREFIX + StringUtils.join(segments.iterator(), FILE_SEPARATOR_SUBST); - } - - private Path classifierToPath(Path outputDir, String classifier) { - classifier = StringUtils.removeStart(classifier, BUILD_PREFIX); - final String relPath = replace(classifier, FILE_SEPARATOR_SUBST, File.separator); - return outputDir.resolve(relPath); + return hasFile; } private void restoreGeneratedSources(Artifact artifact, Path artifactFilePath, MavenProject project) throws IOException { - final Path targetDir = Paths.get(project.getBuild().getDirectory()); - final Path outputDir = classifierToPath(targetDir, artifact.getClassifier()); - if (Files.exists(outputDir)) { - FileUtils.cleanDirectory(outputDir.toFile()); - } else { + final Path baseDir = project.getBasedir().toPath(); + final Path outputDir = baseDir.resolve(FilenameUtils.separatorsToSystem(artifact.getFilePath())); + verifyRestorationInsideProject(project, outputDir); + if (!Files.exists(outputDir)) { Files.createDirectories(outputDir); } CacheUtils.unzip(artifactFilePath, outputDir); @@ -857,10 +900,11 @@ public void attachGeneratedSources(MavenProject project) throws IOException { final Path targetDir = Paths.get(project.getBuild().getDirectory()); final Path generatedSourcesDir = targetDir.resolve("generated-sources"); - attachDirIfNotEmpty(generatedSourcesDir, targetDir, project); + attachDirIfNotEmpty(generatedSourcesDir, targetDir, project, OutputType.GENERATED_SOURCE, DEFAULT_FILE_GLOB); final Path generatedTestSourcesDir = targetDir.resolve("generated-test-sources"); - attachDirIfNotEmpty(generatedTestSourcesDir, targetDir, project); + attachDirIfNotEmpty( + generatedTestSourcesDir, targetDir, project, OutputType.GENERATED_SOURCE, DEFAULT_FILE_GLOB); Set sourceRoots = new TreeSet<>(); if (project.getCompileSourceRoots() != null) { @@ -876,26 +920,40 @@ public void attachGeneratedSources(MavenProject project) throws IOException { && sourceRootPath.startsWith(targetDir) && !(sourceRootPath.startsWith(generatedSourcesDir) || sourceRootPath.startsWith(generatedTestSourcesDir))) { // dir within target - attachDirIfNotEmpty(sourceRootPath, targetDir, project); + attachDirIfNotEmpty(sourceRootPath, targetDir, project, OutputType.GENERATED_SOURCE, DEFAULT_FILE_GLOB); } } } private void attachOutputs(MavenProject project) throws IOException { - final List attachedDirs = cacheConfig.getAttachedOutputs(); - for (String dir : attachedDirs) { + final List attachedDirs = cacheConfig.getAttachedOutputs(); + for (DirName dir : attachedDirs) { final Path targetDir = Paths.get(project.getBuild().getDirectory()); - final Path outputDir = targetDir.resolve(dir); - attachDirIfNotEmpty(outputDir, targetDir, project); + final Path outputDir = targetDir.resolve(dir.getValue()); + if (isPathInsideProject(project, outputDir)) { + attachDirIfNotEmpty(outputDir, targetDir, project, OutputType.EXTRA_OUTPUT, dir.getGlob()); + } else { + LOGGER.warn("Outside project output candidate directory discarded ({})", outputDir.normalize()); + } } } - private void attachDirIfNotEmpty(Path candidateSubDir, Path parentDir, MavenProject project) throws IOException { + private void attachDirIfNotEmpty( + Path candidateSubDir, + Path parentDir, + MavenProject project, + final OutputType attachedOutputType, + final String glob) + throws IOException { if (Files.isDirectory(candidateSubDir) && hasFiles(candidateSubDir)) { - final Path relativePath = parentDir.relativize(candidateSubDir); - final String classifier = pathToClassifier(relativePath); - zipAndAttachArtifact(project, candidateSubDir, classifier); - LOGGER.debug("Attached directory: {}", candidateSubDir); + final Path relativePath = project.getBasedir().toPath().relativize(candidateSubDir); + attachedResourceCounter++; + final String classifier = attachedOutputType.getClassifierPrefix() + attachedResourceCounter; + boolean success = zipAndAttachArtifact(project, candidateSubDir, classifier, glob); + if (success) { + attachedResourcesPathsById.put(classifier, relativePath); + LOGGER.debug("Attached directory: {}", candidateSubDir); + } } } diff --git a/src/main/java/org/apache/maven/buildcache/CacheDiff.java b/src/main/java/org/apache/maven/buildcache/CacheDiff.java index 4c9c63b5..a378804c 100644 --- a/src/main/java/org/apache/maven/buildcache/CacheDiff.java +++ b/src/main/java/org/apache/maven/buildcache/CacheDiff.java @@ -68,6 +68,7 @@ public Diff compare() { compareExecutions(current.getExecutions(), baseline.getExecutions()); compareFiles(current.getProjectsInputInfo(), baseline.getProjectsInputInfo()); compareDependencies(current.getProjectsInputInfo(), baseline.getProjectsInputInfo()); + comparePluginDependencies(current.getProjectsInputInfo(), baseline.getProjectsInputInfo()); final Diff buildDiffType = new Diff(); buildDiffType.getMismatches().addAll(report); @@ -157,20 +158,40 @@ private void compareFiles(ProjectsInputInfo current, ProjectsInputInfo baseline) } private void compareDependencies(ProjectsInputInfo current, ProjectsInputInfo baseline) { - final Map currentDependencies = current.getItems().stream() - .filter(item -> "dependency".equals(item.getType())) - .collect(Collectors.toMap(DigestItem::getValue, item -> item)); + compareDependencies( + "dependencies files", + current.getItems().stream() + .filter(item -> "dependency".equals(item.getType())) + .collect(Collectors.toList()), + baseline.getItems().stream() + .filter(item -> "dependency".equals(item.getType())) + .collect(Collectors.toList())); + } - final Map baselineDependencies = baseline.getItems().stream() - .filter(item -> "dependency".equals(item.getType())) - .collect(Collectors.toMap(DigestItem::getValue, item -> item)); + private void comparePluginDependencies(ProjectsInputInfo current, ProjectsInputInfo baseline) { + compareDependencies( + "plugin dependencies files", + current.getItems().stream() + .filter(item -> "pluginDependency".equals(item.getType())) + .collect(Collectors.toList()), + baseline.getItems().stream() + .filter(item -> "pluginDependency".equals(item.getType())) + .collect(Collectors.toList())); + } + + private void compareDependencies(String property, List current, List baseline) { + final Map currentDependencies = + current.stream().collect(Collectors.toMap(DigestItem::getValue, item -> item)); + + final Map baselineDependencies = + baseline.stream().collect(Collectors.toMap(DigestItem::getValue, item -> item)); if (!Objects.equals(currentDependencies.keySet(), baselineDependencies.keySet())) { Set currentVsBaseline = diff(currentDependencies.keySet(), baselineDependencies.keySet()); Set baselineVsCurrent = diff(baselineDependencies.keySet(), currentDependencies.keySet()); addNewMismatch( - "dependencies files", + property, "Remote and local builds contain different sets of dependencies and cannot be matched. " + "Added dependencies: " + currentVsBaseline + ". Removed dependencies: " + baselineVsCurrent, diff --git a/src/main/java/org/apache/maven/buildcache/CacheUtils.java b/src/main/java/org/apache/maven/buildcache/CacheUtils.java index 2396a0bc..61b9bc55 100644 --- a/src/main/java/org/apache/maven/buildcache/CacheUtils.java +++ b/src/main/java/org/apache/maven/buildcache/CacheUtils.java @@ -21,10 +21,13 @@ import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.nio.file.FileSystems; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.PathMatcher; import java.nio.file.SimpleFileVisitor; +import java.nio.file.StandardCopyOption; import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.FileTime; import java.util.Arrays; @@ -37,6 +40,7 @@ import java.util.zip.ZipOutputStream; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.mutable.MutableBoolean; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.handler.ArtifactHandler; import org.apache.maven.buildcache.xml.build.Scm; @@ -160,21 +164,39 @@ public static boolean isArchive(File file) { return StringUtils.endsWithAny(fileName, ".jar", ".zip", ".war", ".ear"); } - public static void zip(Path dir, Path zip) throws IOException { + /** + * Put every matching files of a directory in a zip. + * @param dir directory to zip + * @param zip zip to populate + * @param glob glob to apply to filenames + * @return true if at least one file has been included in the zip. + * @throws IOException + */ + public static boolean zip(final Path dir, final Path zip, final String glob) throws IOException { + final MutableBoolean hasFiles = new MutableBoolean(); try (ZipOutputStream zipOutputStream = new ZipOutputStream(Files.newOutputStream(zip))) { + + PathMatcher matcher = + "*".equals(glob) ? null : FileSystems.getDefault().getPathMatcher("glob:" + glob); Files.walkFileTree(dir, new SimpleFileVisitor() { @Override public FileVisitResult visitFile(Path path, BasicFileAttributes basicFileAttributes) throws IOException { - final ZipEntry zipEntry = new ZipEntry(dir.relativize(path).toString()); - zipOutputStream.putNextEntry(zipEntry); - Files.copy(path, zipOutputStream); - zipOutputStream.closeEntry(); + + if (matcher == null || matcher.matches(path.getFileName())) { + final ZipEntry zipEntry = + new ZipEntry(dir.relativize(path).toString()); + zipOutputStream.putNextEntry(zipEntry); + Files.copy(path, zipOutputStream); + hasFiles.setTrue(); + zipOutputStream.closeEntry(); + } return FileVisitResult.CONTINUE; } }); } + return hasFiles.booleanValue(); } public static void unzip(Path zip, Path out) throws IOException { @@ -190,7 +212,7 @@ public static void unzip(Path zip, Path out) throws IOException { } else { Path parent = file.getParent(); Files.createDirectories(parent); - Files.copy(zis, file); + Files.copy(zis, file, StandardCopyOption.REPLACE_EXISTING); } Files.setLastModifiedTime(file, FileTime.fromMillis(entry.getTime())); entry = zis.getNextEntry(); diff --git a/src/main/java/org/apache/maven/buildcache/DefaultMultiModuleSupport.java b/src/main/java/org/apache/maven/buildcache/DefaultMultiModuleSupport.java index 7693b3d6..41cb0217 100644 --- a/src/main/java/org/apache/maven/buildcache/DefaultMultiModuleSupport.java +++ b/src/main/java/org/apache/maven/buildcache/DefaultMultiModuleSupport.java @@ -24,6 +24,7 @@ import java.io.File; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -132,7 +133,7 @@ private synchronized void buildModel(MavenSession session) { File multiModulePomFile = getMultiModulePomFile(session); ProjectBuildingRequest projectBuildingRequest = currentProject.getProjectBuildingRequest(); - boolean profilesMatched = projectBuildingRequest.getActiveProfileIds().containsAll(scanProfiles); + boolean profilesMatched = new HashSet<>(projectBuildingRequest.getActiveProfileIds()).containsAll(scanProfiles); // we are building from root with the same profiles, no need to re-scan the whole multi-module project if (currentProject.getFile().getAbsolutePath().equals(multiModulePomFile.getAbsolutePath()) diff --git a/src/main/java/org/apache/maven/buildcache/DefaultProjectInputCalculator.java b/src/main/java/org/apache/maven/buildcache/DefaultProjectInputCalculator.java index 2a1217fd..3e974fc0 100644 --- a/src/main/java/org/apache/maven/buildcache/DefaultProjectInputCalculator.java +++ b/src/main/java/org/apache/maven/buildcache/DefaultProjectInputCalculator.java @@ -27,13 +27,14 @@ import java.util.concurrent.ConcurrentMap; import org.apache.maven.SessionScoped; +import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager; import org.apache.maven.buildcache.checksum.MavenProjectInput; import org.apache.maven.buildcache.xml.CacheConfig; import org.apache.maven.buildcache.xml.build.ProjectsInputInfo; import org.apache.maven.execution.MavenSession; import org.apache.maven.lifecycle.internal.builder.BuilderCommon; import org.apache.maven.project.MavenProject; -import org.apache.maven.repository.RepositorySystem; +import org.eclipse.aether.RepositorySystem; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -49,6 +50,7 @@ public class DefaultProjectInputCalculator implements ProjectInputCalculator { private final RepositorySystem repoSystem; private final NormalizedModelProvider normalizedModelProvider; private final MultiModuleSupport multiModuleSupport; + private final ArtifactHandlerManager artifactHandlerManager; private final ConcurrentMap checkSumMap = new ConcurrentHashMap<>(); @@ -61,13 +63,15 @@ public DefaultProjectInputCalculator( CacheConfig cacheConfig, RepositorySystem repoSystem, NormalizedModelProvider rawModelProvider, - MultiModuleSupport multiModuleSupport) { + MultiModuleSupport multiModuleSupport, + ArtifactHandlerManager artifactHandlerManager) { this.mavenSession = mavenSession; this.remoteCache = remoteCache; this.cacheConfig = cacheConfig; this.repoSystem = repoSystem; this.normalizedModelProvider = rawModelProvider; this.multiModuleSupport = multiModuleSupport; + this.artifactHandlerManager = artifactHandlerManager; } @Override @@ -108,7 +112,8 @@ private ProjectsInputInfo calculateInputInternal(String key, MavenProject projec mavenSession, cacheConfig, repoSystem, - remoteCache); + remoteCache, + artifactHandlerManager); return input.calculateChecksum(); } catch (Exception e) { throw new RuntimeException("Failed to calculate checksums for " + project.getArtifactId(), e); diff --git a/src/main/java/org/apache/maven/buildcache/DefaultRestoredArtifactHandler.java b/src/main/java/org/apache/maven/buildcache/DefaultRestoredArtifactHandler.java index 0742a304..96af8d90 100644 --- a/src/main/java/org/apache/maven/buildcache/DefaultRestoredArtifactHandler.java +++ b/src/main/java/org/apache/maven/buildcache/DefaultRestoredArtifactHandler.java @@ -24,10 +24,10 @@ import java.io.BufferedOutputStream; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Enumeration; @@ -96,7 +96,7 @@ public Path adjustArchiveArtifactVersion(MavenProject project, String originalAr String pomPropsVersion = "version=" + currentVersion; try (JarFile jarFile = new JarFile(artifactFile.toFile())) { try (JarOutputStream jos = - new JarOutputStream(new BufferedOutputStream(new FileOutputStream(tmpJarFile)))) { + new JarOutputStream(new BufferedOutputStream(Files.newOutputStream(tmpJarFile.toPath())))) { // Copy original jar file to the temporary one. Enumeration jarEntries = jarFile.entries(); byte[] buffer = new byte[1024]; @@ -140,7 +140,7 @@ public Path adjustArchiveArtifactVersion(MavenProject project, String originalAr private static void replaceEntry( JarFile jarFile, JarEntry entry, String toReplace, String replacement, JarOutputStream jos) throws IOException { - String fullManifest = IOUtils.toString(jarFile.getInputStream(entry), StandardCharsets.UTF_8.name()); + String fullManifest = IOUtils.toString(jarFile.getInputStream(entry), StandardCharsets.UTF_8); String modified = fullManifest.replaceAll(toReplace, replacement); byte[] bytes = modified.getBytes(StandardCharsets.UTF_8); diff --git a/src/main/java/org/apache/maven/buildcache/MojoParametersListener.java b/src/main/java/org/apache/maven/buildcache/MojoParametersListener.java index 97036ba5..edc0e22d 100644 --- a/src/main/java/org/apache/maven/buildcache/MojoParametersListener.java +++ b/src/main/java/org/apache/maven/buildcache/MojoParametersListener.java @@ -18,6 +18,7 @@ */ package org.apache.maven.buildcache; +import javax.inject.Inject; import javax.inject.Named; import javax.inject.Singleton; @@ -25,6 +26,9 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import org.apache.maven.buildcache.checksum.MavenProjectInput; +import org.apache.maven.buildcache.xml.CacheConfig; +import org.apache.maven.buildcache.xml.CacheState; import org.apache.maven.execution.MojoExecutionEvent; import org.apache.maven.execution.MojoExecutionListener; import org.apache.maven.plugin.MojoExecutionException; @@ -32,6 +36,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static org.apache.maven.buildcache.xml.CacheState.DISABLED; +import static org.apache.maven.buildcache.xml.CacheState.INITIALIZED; + /** * MojoParametersListener */ @@ -45,6 +52,13 @@ public class MojoParametersListener implements MojoExecutionListener { private final ConcurrentMap> projectExecutions = new ConcurrentHashMap<>(); + private final CacheConfig cacheConfig; + + @Inject + public MojoParametersListener(CacheConfig cacheConfig) { + this.cacheConfig = cacheConfig; + } + @Override public void beforeMojoExecution(MojoExecutionEvent event) { final String executionKey = CacheUtils.mojoExecutionKey(event.getExecution()); @@ -53,15 +67,23 @@ public void beforeMojoExecution(MojoExecutionEvent event) { executionKey, event.getMojo().getClass()); final MavenProject project = event.getProject(); - Map projectEvents = projectExecutions.get(project); - if (projectEvents == null) { - Map candidate = new ConcurrentHashMap<>(); - projectEvents = projectExecutions.putIfAbsent(project, candidate); + CacheState cacheState = DISABLED; + boolean cacheIsDisabled = MavenProjectInput.isCacheDisabled(project); + if (!cacheIsDisabled) { + cacheState = cacheConfig.initialize(); + } + LOGGER.debug("cacheState: {}", cacheState); + if (cacheState == INITIALIZED) { + Map projectEvents = projectExecutions.get(project); if (projectEvents == null) { - projectEvents = candidate; + Map candidate = new ConcurrentHashMap<>(); + projectEvents = projectExecutions.putIfAbsent(project, candidate); + if (projectEvents == null) { + projectEvents = candidate; + } } + projectEvents.put(executionKey, event); } - projectEvents.put(executionKey, event); } @Override diff --git a/src/main/java/org/apache/maven/buildcache/RemoteCacheRepositoryNoOp.java b/src/main/java/org/apache/maven/buildcache/RemoteCacheRepositoryNoOp.java index 3808696b..e66d0e52 100644 --- a/src/main/java/org/apache/maven/buildcache/RemoteCacheRepositoryNoOp.java +++ b/src/main/java/org/apache/maven/buildcache/RemoteCacheRepositoryNoOp.java @@ -58,7 +58,6 @@ public boolean getArtifactContent( return false; } - @Nonnull @Override public String getResourceUrl(CacheContext context, String filename) { return null; diff --git a/src/main/java/org/apache/maven/buildcache/artifact/OutputType.java b/src/main/java/org/apache/maven/buildcache/artifact/OutputType.java new file mode 100644 index 00000000..0a557f4d --- /dev/null +++ b/src/main/java/org/apache/maven/buildcache/artifact/OutputType.java @@ -0,0 +1,49 @@ +/* + * 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.buildcache.artifact; + +public enum OutputType { + // generated project artifact + ARTIFACT(""), + // generated source (regular and test) + GENERATED_SOURCE("mvn-cache-ext-generated-source-"), + // any unclassified output added by configuration + EXTRA_OUTPUT("mvn-cache-ext-extra-output-"); + + private String classifierPrefix; + + OutputType(String getClassifierPrefix) { + this.classifierPrefix = getClassifierPrefix; + } + + public String getClassifierPrefix() { + return classifierPrefix; + } + + public static OutputType fromClassifier(String classifier) { + if (classifier != null) { + if (classifier.startsWith(GENERATED_SOURCE.classifierPrefix)) { + return GENERATED_SOURCE; + } else if (classifier.startsWith(EXTRA_OUTPUT.classifierPrefix)) { + return EXTRA_OUTPUT; + } + } + return ARTIFACT; + } +} diff --git a/src/main/java/org/apache/maven/buildcache/artifact/RestoredArtifact.java b/src/main/java/org/apache/maven/buildcache/artifact/RestoredArtifact.java index a74dce35..596fb1f7 100644 --- a/src/main/java/org/apache/maven/buildcache/artifact/RestoredArtifact.java +++ b/src/main/java/org/apache/maven/buildcache/artifact/RestoredArtifact.java @@ -23,6 +23,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.RunnableFuture; +import java.util.function.Function; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.DefaultArtifact; @@ -46,8 +47,15 @@ public class RestoredArtifact extends DefaultArtifact { private volatile Future fileFuture; + private Function restoreToDiskConsumer; + public RestoredArtifact( - Artifact parent, Future fileFuture, String type, String classifier, ArtifactHandler handler) { + Artifact parent, + Future fileFuture, + String type, + String classifier, + ArtifactHandler handler, + Function restoreToDiskConsumer) { super( parent.getGroupId(), parent.getArtifactId(), @@ -58,6 +66,7 @@ public RestoredArtifact( handler, parent.isOptional()); this.fileFuture = requireNonNull(fileFuture, "fileFuture == null"); + this.restoreToDiskConsumer = restoreToDiskConsumer; } /** @@ -89,7 +98,8 @@ public File getFile() { } try { - return fileFuture.get(); + File file = fileFuture.get(); + return restoreToDiskConsumer.apply(file); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new InvalidArtifactRTException( diff --git a/src/main/java/org/apache/maven/buildcache/checksum/DigestUtils.java b/src/main/java/org/apache/maven/buildcache/checksum/DigestUtils.java index da0f3f26..ee787330 100644 --- a/src/main/java/org/apache/maven/buildcache/checksum/DigestUtils.java +++ b/src/main/java/org/apache/maven/buildcache/checksum/DigestUtils.java @@ -56,6 +56,7 @@ public static DigestItem pom(HashChecksum checksum, String effectivePom) { public static DigestItem file(HashChecksum checksum, Path basedir, Path file) throws IOException { byte[] content = Files.readAllBytes(file); String normalized = normalize(basedir, file); + checksum.update(normalized.getBytes(UTF_8)); DigestItem item = item("file", normalized, checksum.update(content)); try { populateContentDetails(file, content, item); @@ -126,6 +127,10 @@ public static DigestItem dependency(HashChecksum checksum, String key, String ha return item("dependency", key, checksum.update(hash)); } + public static DigestItem pluginDependency(HashChecksum checksum, String key, String hash) { + return item("pluginDependency", key, checksum.update(hash)); + } + private static String normalize(Path basedirPath, Path file) { return FilenameUtils.separatorsToUnix(relativize(basedirPath, file).toString()); } diff --git a/src/main/java/org/apache/maven/buildcache/checksum/MavenProjectInput.java b/src/main/java/org/apache/maven/buildcache/checksum/MavenProjectInput.java index b6df5ec6..a3cb065e 100644 --- a/src/main/java/org/apache/maven/buildcache/checksum/MavenProjectInput.java +++ b/src/main/java/org/apache/maven/buildcache/checksum/MavenProjectInput.java @@ -35,6 +35,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -45,12 +46,17 @@ import java.util.SortedSet; import java.util.TreeMap; import java.util.TreeSet; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Predicate; import org.apache.commons.lang3.StringUtils; import org.apache.maven.artifact.Artifact; -import org.apache.maven.artifact.resolver.ArtifactResolutionRequest; -import org.apache.maven.artifact.resolver.ArtifactResolutionResult; +import org.apache.maven.artifact.DefaultArtifact; +import org.apache.maven.artifact.handler.ArtifactHandler; +import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager; +import org.apache.maven.artifact.resolver.filter.ExcludesArtifactFilter; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.artifact.versioning.VersionRange; import org.apache.maven.buildcache.CacheUtils; import org.apache.maven.buildcache.MultiModuleSupport; import org.apache.maven.buildcache.NormalizedModelProvider; @@ -69,15 +75,19 @@ import org.apache.maven.buildcache.xml.config.Include; import org.apache.maven.execution.MavenSession; import org.apache.maven.model.Dependency; +import org.apache.maven.model.Exclusion; import org.apache.maven.model.Model; import org.apache.maven.model.Plugin; import org.apache.maven.model.PluginExecution; import org.apache.maven.model.Resource; import org.apache.maven.model.io.xpp3.MavenXpp3Writer; import org.apache.maven.project.MavenProject; -import org.apache.maven.repository.RepositorySystem; -import org.codehaus.plexus.util.IOUtil; import org.codehaus.plexus.util.WriterFactory; +import org.eclipse.aether.RepositorySystem; +import org.eclipse.aether.artifact.DefaultArtifactType; +import org.eclipse.aether.resolution.ArtifactRequest; +import org.eclipse.aether.resolution.ArtifactResolutionException; +import org.eclipse.aether.resolution.ArtifactResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -93,6 +103,7 @@ import static org.apache.maven.buildcache.xml.CacheConfigImpl.CACHE_ENABLED_PROPERTY_NAME; import static org.apache.maven.buildcache.xml.CacheConfigImpl.CACHE_SKIP; import static org.apache.maven.buildcache.xml.CacheConfigImpl.RESTORE_GENERATED_SOURCES_PROPERTY_NAME; +import static org.apache.maven.buildcache.xml.CacheConfigImpl.RESTORE_ON_DISK_ARTIFACTS_PROPERTY_NAME; /** * MavenProjectInput @@ -100,9 +111,9 @@ public class MavenProjectInput { /** - * Version of hashing algorithm implementation. It is recommended to change to simplify remote cache maintenance + * Version of cache implementation. It is recommended to change to simplify remote cache maintenance */ - public static final String CACHE_IMPLEMENTATION_VERSION = "v1"; + public static final String CACHE_IMPLEMENTATION_VERSION = "v1.1"; /** * property name to pass glob value. The glob to be used to list directory files in plugins scanning @@ -130,6 +141,8 @@ public class MavenProjectInput { private final MultiModuleSupport multiModuleSupport; private final ProjectInputCalculator projectInputCalculator; private final Path baseDirPath; + private final ArtifactHandlerManager artifactHandlerManager; + /** * The project glob to use every time there is no override */ @@ -149,7 +162,8 @@ public MavenProjectInput( MavenSession session, CacheConfig config, RepositorySystem repoSystem, - RemoteCacheRepository remoteCache) { + RemoteCacheRepository remoteCache, + ArtifactHandlerManager artifactHandlerManager) { this.project = project; this.normalizedModelProvider = normalizedModelProvider; this.multiModuleSupport = multiModuleSupport; @@ -168,6 +182,7 @@ public MavenProjectInput( this.exclusionResolver = new ExclusionResolver(project, config); this.fileComparator = new PathIgnoringCaseComparator(); + this.artifactHandlerManager = artifactHandlerManager; } public ProjectsInputInfo calculateChecksum() throws IOException { @@ -176,14 +191,16 @@ public ProjectsInputInfo calculateChecksum() throws IOException { final String effectivePom = getEffectivePom(normalizedModelProvider.normalizedModel(project)); final SortedSet inputFiles = isPom(project) ? Collections.emptySortedSet() : getInputFiles(); final SortedMap dependenciesChecksum = getMutableDependencies(); + final SortedMap pluginDependenciesChecksum = getMutablePluginDependencies(); final long t1 = System.currentTimeMillis(); - // hash items: effective pom + version + input files + dependencies + // hash items: effective pom + version + input files paths + input files contents + dependencies final int count = 1 + (config.calculateProjectVersionChecksum() ? 1 : 0) - + inputFiles.size() - + dependenciesChecksum.size(); + + 2 * inputFiles.size() + + dependenciesChecksum.size() + + pluginDependenciesChecksum.size(); final List items = new ArrayList<>(count); final HashChecksum checksum = config.getHashFactory().createChecksum(count); @@ -236,6 +253,19 @@ public ProjectsInputInfo calculateChecksum() throws IOException { LOGGER.info("Dependencies: {}", dependenciesMatched ? "MATCHED" : "OUT OF DATE"); } + boolean pluginDependenciesMatched = true; + for (Map.Entry entry : pluginDependenciesChecksum.entrySet()) { + DigestItem dependencyDigest = DigestUtils.pluginDependency(checksum, entry.getKey(), entry.getValue()); + items.add(dependencyDigest); + if (compareWithBaseline) { + pluginDependenciesMatched &= checkItemMatchesBaseline(baselineHolder.get(), dependencyDigest); + } + } + + if (compareWithBaseline) { + LOGGER.info("Plugin dependencies: {}", pluginDependenciesMatched ? "MATCHED" : "OUT OF DATE"); + } + final ProjectsInputInfo projectsInputInfoType = new ProjectsInputInfo(); projectsInputInfoType.setChecksum(checksum.digest()); projectsInputInfoType.getItems().addAll(items); @@ -313,19 +343,13 @@ private boolean checkItemMatchesBaseline(ProjectsInputInfo baselineBuild, Digest private String getEffectivePom(Model prototype) throws IOException { ByteArrayOutputStream output = new ByteArrayOutputStream(); - Writer writer = null; - try { - writer = WriterFactory.newXmlWriter(output); + try (Writer writer = WriterFactory.newXmlWriter(output)) { new MavenXpp3Writer().write(writer, prototype); // normalize env specifics final String[] searchList = {baseDirPath.toString(), "\\", "windows", "linux"}; final String[] replacementList = {"", "/", "os.classifier", "os.classifier"}; - return replaceEachRepeatedly(output.toString(), searchList, replacementList); - - } finally { - IOUtil.close(writer); } } @@ -489,7 +513,7 @@ public FileVisitResult preVisitDirectory(Path path, BasicFileAttributes basicFil return FileVisitResult.SKIP_SUBTREE; } - walkDirectoryFiles(path, collectedFiles, key.getGlob(), entry -> exclusionResolver.excludesPath(entry)); + walkDirectoryFiles(path, collectedFiles, key.getGlob(), exclusionResolver::excludesPath); if (!key.isRecursive()) { LOGGER.debug("Skipping subtree (non recursive): {}", path); @@ -615,9 +639,137 @@ private static boolean isReadable(Path entry) throws IOException { } private SortedMap getMutableDependencies() throws IOException { + return getMutableDependenciesHashes("", project.getDependencies()); + } + + private SortedMap getMutablePluginDependencies() throws IOException { + Map keyPrefixOccurrenceIndex = new HashMap<>(); + SortedMap fullMap = new TreeMap<>(); + for (Plugin plugin : project.getBuildPlugins()) { + if (config.isPluginDependenciesExcluded(plugin)) { + continue; + } + + String rawKeyPrefix = KeyUtils.getVersionlessArtifactKey(createPluginArtifact(plugin)); + int occurrenceIndex = keyPrefixOccurrenceIndex + .computeIfAbsent(rawKeyPrefix, k -> new AtomicInteger()) + .getAndIncrement(); + fullMap.putAll( + getMutableDependenciesHashes(rawKeyPrefix + "|" + occurrenceIndex + "|", plugin.getDependencies())); + } + return fullMap; + } + + public Artifact createPluginArtifact(Plugin plugin) { + + VersionRange versionRange; + try { + versionRange = VersionRange.createFromVersionSpec(plugin.getVersion()); + } catch (InvalidVersionSpecificationException e) { + LOGGER.error( + String.format( + "Invalid version specification '%s' creating plugin artifact '%s'.", + plugin.getVersion(), plugin), + e); + // should not happen here + throw new RuntimeException(e); + } + + return createArtifact( + plugin.getGroupId(), + plugin.getArtifactId(), + versionRange, + "maven-plugin", + null, + Artifact.SCOPE_RUNTIME, + null, + false); + } + // CHECKSTYLE_OFF: ParameterNumber + private Artifact createArtifact( + String groupId, + String artifactId, + VersionRange versionRange, + String type, + String classifier, + String scope, + String inheritedScope, + boolean optional) { + // CHECKSTYLE_OFF: ParameterNumber + String desiredScope = Artifact.SCOPE_RUNTIME; + + if (inheritedScope == null) { + desiredScope = scope; + } else if (Artifact.SCOPE_TEST.equals(scope) || Artifact.SCOPE_PROVIDED.equals(scope)) { + return null; + } else if (Artifact.SCOPE_COMPILE.equals(scope) && Artifact.SCOPE_COMPILE.equals(inheritedScope)) { + // added to retain compile artifactScope. Remove if you want compile inherited as runtime + desiredScope = Artifact.SCOPE_COMPILE; + } + + if (Artifact.SCOPE_TEST.equals(inheritedScope)) { + desiredScope = Artifact.SCOPE_TEST; + } + + if (Artifact.SCOPE_PROVIDED.equals(inheritedScope)) { + desiredScope = Artifact.SCOPE_PROVIDED; + } + + if (Artifact.SCOPE_SYSTEM.equals(scope)) { + // system scopes come through unchanged... + desiredScope = Artifact.SCOPE_SYSTEM; + } + ArtifactHandler handler = artifactHandlerManager.getArtifactHandler(type); + + return new DefaultArtifact( + groupId, artifactId, versionRange, desiredScope, type, classifier, handler, optional); + } + + public Artifact createDependencyArtifact(Dependency d) { + VersionRange versionRange; + try { + versionRange = VersionRange.createFromVersionSpec(d.getVersion()); + } catch (InvalidVersionSpecificationException e) { + LOGGER.error( + String.format( + "Invalid version specification '%s' creating dependency artifact '%s'.", d.getVersion(), d), + e); + // should not happen here ? + throw new RuntimeException(e); + } + + Artifact artifact = createArtifact( + d.getGroupId(), + d.getArtifactId(), + versionRange, + d.getType(), + d.getClassifier(), + d.getScope(), + null, + d.isOptional()); + + if (Artifact.SCOPE_SYSTEM.equals(d.getScope()) && d.getSystemPath() != null) { + artifact.setFile(new File(d.getSystemPath())); + } + + if (!d.getExclusions().isEmpty()) { + List exclusions = new ArrayList<>(); + + for (Exclusion exclusion : d.getExclusions()) { + exclusions.add(exclusion.getGroupId() + ':' + exclusion.getArtifactId()); + } + + artifact.setDependencyFilter(new ExcludesArtifactFilter(exclusions)); + } + + return artifact; + } + + private SortedMap getMutableDependenciesHashes(String keyPrefix, List dependencies) + throws IOException { SortedMap result = new TreeMap<>(); - for (Dependency dependency : project.getDependencies()) { + for (Dependency dependency : dependencies) { if (CacheUtils.isPom(dependency)) { // POM dependency will be resolved by maven system to actual dependencies @@ -643,50 +795,58 @@ private SortedMap getMutableDependencies() throws IOException { projectInputCalculator.calculateInput(dependencyProject).getChecksum(); } else // this is a snapshot dependency { - DigestItem resolved = resolveArtifact(repoSystem.createDependencyArtifact(dependency), false); + DigestItem resolved = null; + try { + resolved = resolveArtifact(dependency); + } catch (ArtifactResolutionException | InvalidVersionSpecificationException e) { + throw new IOException(e); + } projectHash = resolved.getHash(); } result.put( - KeyUtils.getVersionlessArtifactKey(repoSystem.createDependencyArtifact(dependency)), projectHash); + keyPrefix + KeyUtils.getVersionlessArtifactKey(createDependencyArtifact(dependency)), projectHash); } return result; } @Nonnull - private DigestItem resolveArtifact(final Artifact dependencyArtifact, boolean isOffline) throws IOException { - ArtifactResolutionRequest request = new ArtifactResolutionRequest() - .setArtifact(dependencyArtifact) - .setResolveRoot(true) - .setResolveTransitively(false) - .setLocalRepository(session.getLocalRepository()) - .setRemoteRepositories(project.getRemoteArtifactRepositories()) - .setOffline(session.isOffline() || isOffline) - .setForceUpdate(session.getRequest().isUpdateSnapshots()) - .setServers(session.getRequest().getServers()) - .setMirrors(session.getRequest().getMirrors()) - .setProxies(session.getRequest().getProxies()); - - final ArtifactResolutionResult result = repoSystem.resolve(request); - - if (!result.isSuccess()) { + private DigestItem resolveArtifact(final Dependency dependency) + throws IOException, ArtifactResolutionException, InvalidVersionSpecificationException { + + org.eclipse.aether.artifact.Artifact dependencyArtifact = new org.eclipse.aether.artifact.DefaultArtifact( + dependency.getGroupId(), + dependency.getArtifactId(), + dependency.getClassifier(), + null, + dependency.getVersion(), + new DefaultArtifactType(dependency.getType())); + ArtifactRequest artifactRequest = new ArtifactRequest().setArtifact(dependencyArtifact); + + ArtifactResult result = repoSystem.resolveArtifact(session.getRepositorySession(), artifactRequest); + + if (!result.isResolved()) { throw new DependencyNotResolvedException("Cannot resolve in-project dependency: " + dependencyArtifact); } - if (!result.getMissingArtifacts().isEmpty()) { - throw new DependencyNotResolvedException( - "Cannot resolve artifact: " + dependencyArtifact + ", missing: " + result.getMissingArtifacts()); + if (result.isMissing()) { + throw new DependencyNotResolvedException("Cannot resolve missing artifact: " + dependencyArtifact); } - if (result.getArtifacts().size() != 1) { - throw new IllegalStateException("Unexpected number of artifacts returned. Requested: " + dependencyArtifact - + ", expected: 1, actual: " + result.getArtifacts()); - } + org.eclipse.aether.artifact.Artifact resolved = result.getArtifact(); - final Artifact resolved = result.getArtifacts().iterator().next(); + Artifact artifact = createArtifact( + resolved.getGroupId(), + resolved.getArtifactId(), + VersionRange.createFromVersionSpec(resolved.getVersion()), + dependency.getType(), + resolved.getClassifier(), + dependency.getType(), + dependency.getScope(), + false); final HashAlgorithm algorithm = config.getHashFactory().createAlgorithm(); final String hash = algorithm.hash(resolved.getFile().toPath()); - return DtoUtils.createDigestedFile(resolved, hash); + return DtoUtils.createDigestedFile(artifact, hash); } /** @@ -728,6 +888,18 @@ public static boolean isRestoreGeneratedSources(MavenProject project) { project.getProperties().getProperty(RESTORE_GENERATED_SOURCES_PROPERTY_NAME, "true")); } + /** + * Allow skipping artifacts restoration on a per-project level via a property (which defaults to true) + * e.g. {@code false}. + * + * @param project + * @return + */ + public static boolean isRestoreOnDiskArtifacts(MavenProject project) { + return Boolean.parseBoolean( + project.getProperties().getProperty(RESTORE_ON_DISK_ARTIFACTS_PROPERTY_NAME, "true")); + } + /** * Allow disabling caching entirely on a per-project level via a property - both artifact lookup and upload * Defaults to false diff --git a/src/main/java/org/apache/maven/buildcache/checksum/exclude/Exclusion.java b/src/main/java/org/apache/maven/buildcache/checksum/exclude/Exclusion.java index f89bc361..26988b39 100644 --- a/src/main/java/org/apache/maven/buildcache/checksum/exclude/Exclusion.java +++ b/src/main/java/org/apache/maven/buildcache/checksum/exclude/Exclusion.java @@ -146,12 +146,12 @@ public boolean excludesPath(Path parentPath, Path path) { public enum MatcherType { FILENAME, - PATH; + PATH } public enum EntryType { FILE, DIRECTORY, - ALL; + ALL } } diff --git a/src/main/java/org/apache/maven/buildcache/hash/CloseableBuffer.java b/src/main/java/org/apache/maven/buildcache/hash/CloseableBuffer.java index 791168de..7b7c60db 100644 --- a/src/main/java/org/apache/maven/buildcache/hash/CloseableBuffer.java +++ b/src/main/java/org/apache/maven/buildcache/hash/CloseableBuffer.java @@ -23,9 +23,7 @@ import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.FileChannel.MapMode; -import java.security.PrivilegedAction; -import static java.security.AccessController.doPrivileged; import static org.apache.maven.buildcache.hash.ReflectionUtils.getField; import static org.apache.maven.buildcache.hash.ReflectionUtils.getMethod; @@ -34,18 +32,16 @@ */ public class CloseableBuffer implements AutoCloseable { - private static final Cleaner CLEANER = doPrivileged(new PrivilegedAction() { + private static final Cleaner CLEANER = getJVMDependentCleaner(); - @Override - public Cleaner run() { - final String jsv = System.getProperty("java.specification.version", "9"); - if (jsv.startsWith("1.")) { - return DirectCleaner.isSupported() ? new DirectCleaner() : new NoopCleaner(); - } else { - return UnsafeCleaner.isSupported() ? new UnsafeCleaner() : new NoopCleaner(); - } + private static Cleaner getJVMDependentCleaner() { + final String jsv = System.getProperty("java.specification.version", "9"); + if (jsv.startsWith("1.")) { + return DirectCleaner.isSupported() ? new DirectCleaner() : new NoopCleaner(); + } else { + return UnsafeCleaner.isSupported() ? new UnsafeCleaner() : new NoopCleaner(); } - }); + } public static CloseableBuffer directBuffer(int capacity) { return new CloseableBuffer(ByteBuffer.allocateDirect(capacity)); @@ -74,14 +70,7 @@ public ByteBuffer getBuffer() { @Override public void close() { - // Java 8: () -> CLEANER.clean(buffer) - boolean done = doPrivileged(new PrivilegedAction() { - - @Override - public Boolean run() { - return CLEANER.clean(buffer); - } - }); + boolean done = CLEANER.clean(buffer); if (done) { buffer = null; } diff --git a/src/main/java/org/apache/maven/buildcache/xml/CacheConfig.java b/src/main/java/org/apache/maven/buildcache/xml/CacheConfig.java index 2569a167..d86b15d4 100644 --- a/src/main/java/org/apache/maven/buildcache/xml/CacheConfig.java +++ b/src/main/java/org/apache/maven/buildcache/xml/CacheConfig.java @@ -26,6 +26,7 @@ import org.apache.maven.buildcache.PluginScanConfig; import org.apache.maven.buildcache.hash.HashFactory; +import org.apache.maven.buildcache.xml.config.DirName; import org.apache.maven.buildcache.xml.config.Exclude; import org.apache.maven.buildcache.xml.config.Include; import org.apache.maven.buildcache.xml.config.MultiModule; @@ -57,6 +58,8 @@ public interface CacheConfig { @Nonnull List getEffectivePomExcludeProperties(Plugin plugin); + boolean isPluginDependenciesExcluded(Plugin plugin); + @Nullable MultiModule getMultiModule(); @@ -103,7 +106,7 @@ public interface CacheConfig { String getLocalRepositoryLocation(); - List getAttachedOutputs(); + List getAttachedOutputs(); boolean adjustMetaInfVersion(); @@ -133,5 +136,20 @@ public interface CacheConfig { */ boolean isRestoreGeneratedSources(); + /** + * Flag to restore (default) or not generated artifacts + */ + boolean isRestoreOnDiskArtifacts(); + String getAlwaysRunPlugins(); + + /** + * Flag to disable cache saving + */ + boolean isSkipSave(); + + /** + * Flag to save in cache only if a build went through the clean lifecycle + */ + boolean isMandatoryClean(); } diff --git a/src/main/java/org/apache/maven/buildcache/xml/CacheConfigImpl.java b/src/main/java/org/apache/maven/buildcache/xml/CacheConfigImpl.java index 75b16afc..566a067a 100644 --- a/src/main/java/org/apache/maven/buildcache/xml/CacheConfigImpl.java +++ b/src/main/java/org/apache/maven/buildcache/xml/CacheConfigImpl.java @@ -42,6 +42,7 @@ import org.apache.maven.buildcache.xml.config.CacheConfig; import org.apache.maven.buildcache.xml.config.Configuration; import org.apache.maven.buildcache.xml.config.CoordinatesBase; +import org.apache.maven.buildcache.xml.config.DirName; import org.apache.maven.buildcache.xml.config.Exclude; import org.apache.maven.buildcache.xml.config.Executables; import org.apache.maven.buildcache.xml.config.ExecutionConfigurationScan; @@ -90,8 +91,10 @@ public class CacheConfigImpl implements org.apache.maven.buildcache.xml.CacheCon public static final String FAIL_FAST_PROPERTY_NAME = "maven.build.cache.failFast"; public static final String BASELINE_BUILD_URL_PROPERTY_NAME = "maven.build.cache.baselineUrl"; public static final String LAZY_RESTORE_PROPERTY_NAME = "maven.build.cache.lazyRestore"; + public static final String RESTORE_ON_DISK_ARTIFACTS_PROPERTY_NAME = "maven.build.cache.restoreOnDiskArtifacts"; public static final String RESTORE_GENERATED_SOURCES_PROPERTY_NAME = "maven.build.cache.restoreGeneratedSources"; public static final String ALWAYS_RUN_PLUGINS = "maven.build.cache.alwaysRunPlugins"; + public static final String MANDATORY_CLEAN = "maven.build.cache.mandatoryClean"; /** * Flag to control if we should skip lookup for cached artifacts globally or for a particular project even if @@ -103,6 +106,11 @@ public class CacheConfigImpl implements org.apache.maven.buildcache.xml.CacheCon */ public static final String CACHE_SKIP = "maven.build.cache.skipCache"; + /** + * Flag to disable cache saving + */ + public static final String SKIP_SAVE = "maven.build.cache.skipSave"; + private static final Logger LOGGER = LoggerFactory.getLogger(CacheConfigImpl.class); private final XmlService xmlService; @@ -130,8 +138,9 @@ public CacheState initialize() { final boolean enabled = getProperty(CACHE_ENABLED_PROPERTY_NAME, true); if (!rtInfo.isMavenVersion("[3.9.0,)")) { - LOGGER.warn("Cache requires Maven >= 3.9, but version is " + rtInfo.getMavenVersion() - + ". Disabling cache."); + LOGGER.warn( + "Cache requires Maven >= 3.9, but version is {}. Disabling cache.", + rtInfo.getMavenVersion()); state = CacheState.DISABLED; } else if (!enabled) { LOGGER.info("Cache disabled by command line flag, project will be built fully and not cached"); @@ -296,6 +305,17 @@ public List getEffectivePomExcludeProperties(Plugin plugin) { return Collections.emptyList(); } + @Override + public boolean isPluginDependenciesExcluded(Plugin plugin) { + checkInitializedState(); + final PluginConfigurationScan pluginConfig = findPluginScanConfig(plugin); + + if (pluginConfig != null) { + return pluginConfig.isExcludeDependencies(); + } + return false; + } + @Nullable @Override public MultiModule getMultiModule() { @@ -498,11 +518,26 @@ public boolean isRestoreGeneratedSources() { return getProperty(RESTORE_GENERATED_SOURCES_PROPERTY_NAME, true); } + @Override + public boolean isRestoreOnDiskArtifacts() { + return getProperty(RESTORE_ON_DISK_ARTIFACTS_PROPERTY_NAME, true); + } + @Override public String getAlwaysRunPlugins() { return getProperty(ALWAYS_RUN_PLUGINS, null); } + @Override + public boolean isSkipSave() { + return getProperty(SKIP_SAVE, false); + } + + @Override + public boolean isMandatoryClean() { + return getProperty(MANDATORY_CLEAN, getConfiguration().isMandatoryClean()); + } + @Override public String getId() { checkInitializedState(); @@ -534,7 +569,7 @@ public String getLocalRepositoryLocation() { } @Override - public List getAttachedOutputs() { + public List getAttachedOutputs() { checkInitializedState(); final AttachedOutputs attachedOutputs = getConfiguration().getAttachedOutputs(); return attachedOutputs == null ? Collections.emptyList() : attachedOutputs.getDirNames(); diff --git a/src/main/mdo/build-cache-build.mdo b/src/main/mdo/build-cache-build.mdo index c9febc20..0e639352 100644 --- a/src/main/mdo/build-cache-build.mdo +++ b/src/main/mdo/build-cache-build.mdo @@ -240,6 +240,10 @@ under the License. fileSize long + + filePath + String + diff --git a/src/main/mdo/build-cache-config.mdo b/src/main/mdo/build-cache-config.mdo index 65f2ec7b..399c820e 100644 --- a/src/main/mdo/build-cache-config.mdo +++ b/src/main/mdo/build-cache-config.mdo @@ -160,8 +160,8 @@ under the License. 0.0.0+ - FileHash (causes file hash is saved in build metadata) or - EffectivePom (causes effective pom info is saved in build metadata) + FileHash (causes file hash to be saved in build metadata) or + EffectivePom (causes effective pom info to be saved in build metadata) @@ -194,6 +194,12 @@ under the License. TODO: not implemented + + mandatoryClean + boolean + false + Enable the cache storing ability only if a build went through the clean lifecycle. + multiModule @@ -232,8 +238,8 @@ under the License. String * - FileHash (causes file hash is saved in build metadata) or - EffectivePom (causes effective pom info is saved in build metadata) + FileHash (causes file hash to be saved in build metadata) or + EffectivePom (causes effective pom info to be saved in build metadata) @@ -368,14 +374,42 @@ under the License. --> AttachedOutputs + Section relative to outputs which are not artifacts but need to be saved/restored. dirNames - String + DirName * - Directory name in build output directory to attach to cached artifacts + Path to a directory containing files which needs to be saved/restored (relative to the build directory). + + + + + DirName + + Examples : +
    +
  • <dirName>classes</dirName> : files in ${project.basedir}/target/classes
  • +
  • <dirName glob="jacoco.xml"></dirName> : jacoco report files in ${project.basedir}/target
  • +
  • <dirName>../src/main/javagen</dirName> : files in ${project.basedir}/src/main/javagen (in this example, javagen is a folder saved in git but erased on clean)
  • +
+

+ + ]]>
+ + + value + String + Directory name in build output directory to attach to cached artifacts. + + + glob + String + * + Entries are filtered by matching this glob.
@@ -460,6 +494,12 @@ under the License. + + + 0.0.0+ + True to exclude plugin dependencies from the calculation rules. + + @@ -506,6 +546,11 @@ under the License. PluginConfigurationScan CoordinatesBase + + excludeDependencies + boolean + True to exclude plugin dependencies from the calculation rules + effectivePom @@ -874,7 +919,7 @@ under the License. Include - file or a directory path to add to checksum computation. Relative path are relative to each module basedir.

+ A file or a directory path to add to checksum computation. Relative paths are relative to each module basedir.

Include elements can also be added per project with the use of maven properties.

]]>
@@ -1260,7 +1305,7 @@ under the License. - Executions ids list with plugin identifier + Execution ids list with plugin identifier @@ -1280,7 +1325,7 @@ under the License. String *
- Executions ids list with plugin identifier + Execution ids list with plugin identifier
diff --git a/src/site/markdown/getting-started.md.vm b/src/site/markdown/getting-started.md.vm index c056afb8..8e7d3df6 100644 --- a/src/site/markdown/getting-started.md.vm +++ b/src/site/markdown/getting-started.md.vm @@ -28,7 +28,6 @@ To onboard the build-cache extension you need to complete several steps: #[[###]]# Declaring build cache extension ```xml - org.apache.maven.extensions maven-build-cache-extension @@ -78,6 +77,7 @@ by [Maven Resolver](https://maven.apache.org/resolver/) will suffice. In the sim server supporting get/put operations. Working examples: +* Sonatype Nexus Repository (raw repository) * Artifactory (generic repository) * [Nginx OSS](http://nginx.org/en/) with fs module diff --git a/src/site/markdown/how-to.md b/src/site/markdown/how-to.md index 879ccf2a..a4ebbb78 100644 --- a/src/site/markdown/how-to.md +++ b/src/site/markdown/how-to.md @@ -26,8 +26,8 @@ Minimal config ```xml - + true @@ -168,8 +168,8 @@ to `executionControl`: ```xml - + ... @@ -214,3 +214,16 @@ git reset --hard Solution: set `-Dmaven.build.cache.remote.save.final=true` to nodes that produce final builds. Such builds will not be overridden and eventually will replace all interim builds + +### I want to disable dependencies checksum calculation of one plugin + +Set attribute `excludeDependencies` to `true` in `input/plugins/plugin` section: + +```xml + + + + + + +``` diff --git a/src/site/markdown/parameters.md b/src/site/markdown/parameters.md index 16269f1a..e248b006 100644 --- a/src/site/markdown/parameters.md +++ b/src/site/markdown/parameters.md @@ -21,21 +21,24 @@ This document contains various configuration parameters supported by the cache e ### Command line flags -| Parameter | Description | Usage Scenario | -|------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------| -| `-Dmaven.build.cache.configPath=path to file` | Location of cache configuration file | Cache config is not in default location | -| `-Dmaven.build.cache.enabled=(true/false)` | Cache and associated features disabled/enabled | To remove noise from logs when the remote cache is not available | -| `-Dmaven.build.cache.remote.enabled=(true/false)` | Checks and downloads artifacts from the remote cache (overrides ) | To control remote cache access by node, if, say, some nodes lack reliable access | -| `-Dmaven.build.cache.remote.save.enabled=(true/false)` | Remote cache save allowed or not | To designate nodes which allowed to push in remote shared cache | -| `-Dmaven.build.cache.remote.save.final=(true/false)` | Prohibit to override remote cache | Prevents cache records from being overridden by subsequent builds | -| `-Dmaven.build.cache.remote.url=` | Url of the remote cache (overrides ) | To override url of remote cache from command line | -| `-Dmaven.build.cache.remote.server.id=` | Id of the remote cache server (overrides ) | To override id of remote cache server from command line | -| `-Dmaven.build.cache.failFast=(true/false)` | Fail on the first module which cannot be restored from cache | Remote cache setup/tuning/troubleshooting | -| `-Dmaven.build.cache.baselineUrl=` | Location of baseline build for comparison | Remote cache setup/tuning/troubleshooting | -| `-Dmaven.build.cache.lazyRestore=(true/false)` | Restore artifacts from remote cache lazily | Performance optimization | -| `-Dmaven.build.cache.restoreGeneratedSources=(true/false)` | Do not restore generated sources and directly attached files | Performance optimization | +| Parameter | Description | Usage Scenario | +|------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------| +| `-Dmaven.build.cache.configPath=path to file` | Location of cache configuration file | Cache config is not in default location | +| `-Dmaven.build.cache.enabled=(true/false)` | Cache and associated features disabled/enabled | To remove noise from logs when the remote cache is not available | +| `-Dmaven.build.cache.remote.enabled=(true/false)` | Checks and downloads artifacts from the remote cache (overrides ) | To control remote cache access by node, if, say, some nodes lack reliable access | +| `-Dmaven.build.cache.remote.save.enabled=(true/false)` | Remote cache save allowed or not | To designate nodes which allowed to push in remote shared cache | +| `-Dmaven.build.cache.remote.save.final=(true/false)` | Prohibit to override remote cache | Prevents cache records from being overridden by subsequent builds | +| `-Dmaven.build.cache.remote.url=` | Url of the remote cache (overrides ) | To override url of remote cache from command line | +| `-Dmaven.build.cache.remote.server.id=` | Id of the remote cache server (overrides ) | To override id of remote cache server from command line | +| `-Dmaven.build.cache.failFast=(true/false)` | Fail on the first module which cannot be restored from cache | Remote cache setup/tuning/troubleshooting | +| `-Dmaven.build.cache.baselineUrl=` | Location of baseline build for comparison | Remote cache setup/tuning/troubleshooting | +| `-Dmaven.build.cache.lazyRestore=(true/false)` | Restore artifacts from remote cache lazily | Performance optimization | +| `-Dmaven.build.cache.restoreGeneratedSources=(true/false)` | Restore generated sources and directly attached files in the corresponding project directories. (default is true) | Performance optimization | +| `-Dmaven.build.cache.restoreOnDiskArtifacts=(true/false)` | Restore generated artifacts in the project build directory. (default is true) | Performance optimization | | `-Dmaven.build.cache.alwaysRunPlugins=` | Comma separated list of plugins to always run regardless of cache state. An example: `maven-deploy-plugin:*,maven-install-plugin:install` | Remote cache setup/tuning/troubleshooting | | `-Dmaven.build.cache.skipCache=(true/false)` | Skip looking up artifacts in caches. Does not affect writing artifacts to caches, disables only reading when set to `true` | May be used to trigger a forced rebuild when matching artifacts do exist in caches | +| `-Dmaven.build.cache.skipSave=(true/false)` | Skip writing build result in caches. Does not affect reading from the cache. | Configuring MR builds to benefits from the cache, but restricting writes to the `master` branch | +| `-Dmaven.build.cache.mandatoryClean=(true/false)` | Enable or disable the necessity to execute the `clean` phase in order to store the build result in cache | Reducing the risk to save "wrong" files in cache in a local dev environnement | ### Project-level properties @@ -52,11 +55,13 @@ project properties: ``` -| Parameter | Description | -|---------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `maven.build.cache.input.glob` | Project specific glob to select sources. Overrides the global glob. | -| `maven.build.cache.input` | Additional inputs

Example :
```src/main/scala```
```assembly-conf``` | -| `maven.build.cache.exclude.xxx` | Additional exclusion.

Example :
```src/main/java/package-info.java```
```src/main/resources```
```*.md```
Produce two project exclusions :
```src/main/java/package-info.java```
```src/main/resources``` | -| `maven.build.cache.processPlugins` | Introspect plugins to find inputs or not. The default value is true. | -| `maven.build.cache.skipCache` | Skip looking up artifacts for a particular project in caches. The default value is false. | -| `maven.build.cache.restoreGeneratedSources` | Restore generated sources and directly attached files. The default value is true. | +| Parameter | Description | +|----------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `maven.build.cache.input.glob` | Project specific glob to select sources. Overrides the global glob. | +| `maven.build.cache.input` | Additional inputs

Example :
```src/main/scala```
```assembly-conf``` | +| `maven.build.cache.exclude.xxx` | Additional exclusion.

Example :
```src/main/java/package-info.java```
```src/main/resources```
```*.md```
Produce two project exclusions :
```src/main/java/package-info.java```
```src/main/resources``` | +| `maven.build.cache.processPlugins` | Introspect plugins to find inputs or not. The default value is true. | +| `maven.build.cache.skipCache` | Skip looking up artifacts for a particular project in caches. The default value is false. | +| `maven.build.cache.restoreGeneratedSources` | Restore generated sources and directly attached files in the corresponding project directories. The default value is true. | +| `maven.build.cache.restoreOnDiskArtifacts` | Restore generated artifacts in the project build directory. The default value is true. | + diff --git a/src/site/markdown/performance.md b/src/site/markdown/performance.md index 6031e908..ffd9ce41 100644 --- a/src/site/markdown/performance.md +++ b/src/site/markdown/performance.md @@ -23,7 +23,7 @@ Cache tuning could significantly reduce resource consumption and build execution ### Hash algorithm selection -By default, the cache uses the [XX](https://cyan4973.github.io/xxHash/) algorithm, which is a very fast hash algorithm and should be enough for most use cases. +By default, the cache uses the [XX](https://cyan4973.github.io/xxHash/) algorithm, which is a very fast hash algorithm and should be enough for most use cases. In projects with a large codebase, the performance of hash algorithms becomes more critical, and other algorithms like XXMM (XX with memory-mapped files) could provide better performance, depending on the environment. @@ -31,7 +31,7 @@ XXMM (XX with memory-mapped files) could provide better performance, depending o XX ``` -Also note that the usage of the XXMM or METRO+MM algorithms require the creation of a file `.mvn/jvm.config` in the +Also note that the usage of the XXMM or METRO+MM algorithms require the creation of a file `.mvn/jvm.config` in the top directory with the following line to run successfully on JDK >= 17. ``` --add-opens java.base/sun.nio.ch=ALL-UNNAMED @@ -47,7 +47,9 @@ EAR or ZIP archive locally is more efficient than writing to soring in cache bun - .*\.zip + + .*\.zip + @@ -81,7 +83,7 @@ The cache could be configured to auto-correct metadata (most notably [MANIFEST.M ... - + ... ... diff --git a/src/site/resources/maven-build-cache-config.xml b/src/site/resources/maven-build-cache-config.xml index 615cf384..1fb0ab97 100644 --- a/src/site/resources/maven-build-cache-config.xml +++ b/src/site/resources/maven-build-cache-config.xml @@ -15,8 +15,8 @@ See the License for the specific language governing permissions and limitations under the License. --> - + - + src/ @@ -56,7 +56,7 @@ - + 111 diff --git a/src/site/site.xml b/src/site/site.xml index 2325ecef..52ad71cf 100644 --- a/src/site/site.xml +++ b/src/site/site.xml @@ -34,7 +34,6 @@ under the License. - diff --git a/src/test/java/org/apache/maven/buildcache/its/BuildExtensionTest.java b/src/test/java/org/apache/maven/buildcache/its/BuildExtensionTest.java index e111fd4c..38788933 100644 --- a/src/test/java/org/apache/maven/buildcache/its/BuildExtensionTest.java +++ b/src/test/java/org/apache/maven/buildcache/its/BuildExtensionTest.java @@ -18,11 +18,20 @@ */ package org.apache.maven.buildcache.its; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + import org.apache.maven.buildcache.its.junit.IntegrationTest; import org.apache.maven.it.VerificationException; import org.apache.maven.it.Verifier; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import static org.apache.maven.buildcache.util.LogFileUtils.findFirstLineContainingTextsInLogs; +import static org.apache.maven.buildcache.xml.CacheConfigImpl.CACHE_LOCATION_PROPERTY_NAME; +import static org.apache.maven.buildcache.xml.CacheConfigImpl.SKIP_SAVE; + @IntegrationTest("src/test/projects/build-extension") public class BuildExtensionTest { @@ -41,4 +50,28 @@ void simple(Verifier verifier) throws VerificationException { verifier.verifyErrorFreeLog(); verifier.verifyTextInLog("Found cached build, restoring " + PROJECT_NAME + " from cache"); } + + @Test + void skipSaving(Verifier verifier) throws VerificationException, IOException { + verifier.setAutoclean(false); + Path tempDirectory = Files.createTempDirectory("skip-saving-test"); + verifier.getCliOptions().clear(); + verifier.addCliOption("-D" + CACHE_LOCATION_PROPERTY_NAME + "=" + tempDirectory.toAbsolutePath()); + verifier.addCliOption("-D" + SKIP_SAVE + "=true"); + + verifier.setLogFileName("../log-1.txt"); + verifier.executeGoal("verify"); + verifier.verifyTextInLog("Cache saving is disabled."); + verifier.verifyErrorFreeLog(); + + verifier.setLogFileName("../log-2.txt"); + verifier.executeGoal("verify"); + verifier.verifyErrorFreeLog(); + verifier.verifyTextInLog("Cache saving is disabled."); + verifyNoTextInLog(verifier, "Found cached build, restoring"); + } + + private static void verifyNoTextInLog(Verifier verifier, String text) throws VerificationException { + Assertions.assertNull(findFirstLineContainingTextsInLogs(verifier, text)); + } } diff --git a/src/test/java/org/apache/maven/buildcache/its/IncrementalRestoreTest.java b/src/test/java/org/apache/maven/buildcache/its/IncrementalRestoreTest.java new file mode 100644 index 00000000..5656fc04 --- /dev/null +++ b/src/test/java/org/apache/maven/buildcache/its/IncrementalRestoreTest.java @@ -0,0 +1,345 @@ +/* + * 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.buildcache.its; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.apache.maven.buildcache.its.junit.IntegrationTest; +import org.apache.maven.it.VerificationException; +import org.apache.maven.it.Verifier; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import static org.apache.maven.buildcache.util.LogFileUtils.findFirstLineContainingTextsInLogs; + +/** + * Check if a restoration restores build incrementally,i.e. package -> verify -> install -> deploy, + * so that the cached executions are not run again for builds with a higher goal. + */ +@IntegrationTest("src/test/projects/mbuildcache-incremental") +public class IncrementalRestoreTest { + + public static final String SAVED_BUILD_TO_LOCAL_FILE = "Saved Build to local file: "; + public static final String GENERATED_JAR = "target/mbuildcache-incremental-final.jar"; + public static final String GENERATED_SOURCES_JAR = "target/mbuildcache-incremental-final-sources.jar"; + public static final String GENERATED_JAVADOC_JAR = "target/mbuildcache-incremental-final-javadoc.jar"; + + public static final String EXTRA_OUTPUT_1 = + "target" + File.separatorChar + "extra-resources" + File.separatorChar + "extra-readme-1.md"; + public static final String EXTRA_OUTPUT_2 = "target/extra-resources/extra-readme-2.md"; + public static final String EXTRA_OUTPUT_3 = "target/extra-resources/other-readme-1.md"; + public static final String EXTRA_OUTPUT_4 = "target/other-resources/extra-readme-1.md"; + public static final String EXTRA_OUTPUT_5 = "target/other-resources/extra-readme-2.md"; + public static final String EXTRA_OUTPUT_6 = "target/other-resources/other-readme-1.md"; + public static final String SKIPPING_PLUGIN_EXECUTION_CACHED_RESOURCES_RESOURCES = + "Skipping plugin execution (cached): resources:resources"; + public static final String SKIPPING_PLUGIN_EXECUTION_CACHED_COMPILER_COMPILE = + "Skipping plugin execution (cached): compiler:compile"; + public static final String SKIPPING_PLUGIN_EXECUTION_CACHED_RESOURCES_TEST_RESOURCES = + "Skipping plugin execution (cached): resources:testResources"; + public static final String SKIPPING_PLUGIN_EXECUTION_CACHED_COMPILER_TEST_COMPILE = + "Skipping plugin execution (cached): compiler:testCompile"; + public static final String SKIPPING_PLUGIN_EXECUTION_CACHED_SUREFIRE_TEST = + "Skipping plugin execution (cached): surefire:test"; + public static final String SKIPPING_PLUGIN_EXECUTION_CACHED_JAR_JAR = "Skipping plugin execution (cached): jar:jar"; + public static final String SKIPPING_PLUGIN_EXECUTION_CACHED_RESOURCES_COPY = + "Skipping plugin execution (cached): resources:copy-resources"; + public static final String INSTALL_DEFAULT_INSTALL_MBUILDCACHE_INCREMENTAL = + "install (default-install) @ mbuildcache-incremental"; + public static final String SKIPPING_PLUGIN_EXECUTION_CACHED_INSTALL_INSTALL = + "Skipping plugin execution (cached): install:install"; + public static final String DEPLOY_DEFAULT_DEPLOY_MBUILDCACHE_INCREMENTAL = + "deploy (default-deploy) @ mbuildcache-incremental"; + public static final String LOCAL_BUILD_WAS_NOT_FOUND_BY_CHECKSUM = "Local build was not found by checksum"; + public static final String RESOURCES_DEFAULT_RESOURCES_MBUILDCACHE_INCREMENTAL = + "resources (default-resources) @ mbuildcache-incremental"; + public static final String COMPILE_DEFAULT_COMPILE_MBUILDCACHE_INCREMENTAL = + "compile (default-compile) @ mbuildcache-incremental"; + public static final String TEST_RESOURCES_DEFAULT_TEST_RESOURCES_MBUILDCACHE_INCREMENTAL = + "testResources (default-testResources) @ mbuildcache-incremental"; + public static final String TEST_COMPILE_DEFAULT_TEST_COMPILE_MBUILDCACHE_INCREMENTAL = + "testCompile (default-testCompile) @ mbuildcache-incremental"; + public static final String TEST_DEFAULT_TEST_MBUILDCACHE_INCREMENTAL = + "test (default-test) @ mbuildcache-incremental"; + public static final String JAR_DEFAULT_JAR_MBUILDCACHE_INCREMENTAL = "jar (default-jar) @ mbuildcache-incremental"; + public static final String + FOUND_CACHED_BUILD_RESTORING_ORG_APACHE_MAVEN_CACHING_TEST_MBUILDCACHE_INCREMENTAL_FROM_CACHE_BY_CHECKSUM = + "Found cached build, restoring org.apache.maven.caching.test:mbuildcache-incremental from cache by checksum"; + public static final String MBUILDCACHE_INCREMENTAL_JAR = "mbuildcache-incremental.jar"; + public static final String MBUILDCACHE_INCREMENTAL_SOURCES_JAR = "mbuildcache-incremental-sources.jar"; + public static final String MBUILDCACHE_INCREMENTAL_JAVADOC_JAR = "mbuildcache-incremental-javadoc.jar"; + public static final String INTEGRATION_TEST_DEFAULT_MBUILDCACHE_INCREMENTAL = + "integration-test (default) @ mbuildcache-incremental"; + public static final String VERIFY_DEFAULT_MBUILDCACHE_INCREMENTAL = "verify (default) @ mbuildcache-incremental"; + public static final String SKIPPING_PLUGIN_EXECUTION_CACHED_FAILSAFE_INTEGRATION_TEST = + "Skipping plugin execution (cached): failsafe:integration-test"; + public static final String SKIPPING_PLUGIN_EXECUTION_CACHED_FAILSAFE_VERIFY = + "Skipping plugin execution (cached): failsafe:verify"; + + @Test + void simple(Verifier verifier) throws VerificationException, IOException { + verifier.setAutoclean(false); + verifier.setMavenDebug(true); + + // First build, nothing in cache + verifier.setLogFileName("../log-package.txt"); + verifier.executeGoal("package"); + verifier.verifyErrorFreeLog(); + verifier.verifyTextInLog(LOCAL_BUILD_WAS_NOT_FOUND_BY_CHECKSUM); + verifier.verifyTextInLog(RESOURCES_DEFAULT_RESOURCES_MBUILDCACHE_INCREMENTAL); + verifier.verifyTextInLog(COMPILE_DEFAULT_COMPILE_MBUILDCACHE_INCREMENTAL); + verifier.verifyTextInLog(TEST_RESOURCES_DEFAULT_TEST_RESOURCES_MBUILDCACHE_INCREMENTAL); + verifier.verifyTextInLog(TEST_COMPILE_DEFAULT_TEST_COMPILE_MBUILDCACHE_INCREMENTAL); + verifier.verifyTextInLog(TEST_DEFAULT_TEST_MBUILDCACHE_INCREMENTAL); + verifier.verifyTextInLog(JAR_DEFAULT_JAR_MBUILDCACHE_INCREMENTAL); + verifier.verifyTextInLog(SAVED_BUILD_TO_LOCAL_FILE); + verifier.verifyFilePresent(GENERATED_JAR); + + // First build : all resources are present in the target folder + verifier.verifyFilePresent(EXTRA_OUTPUT_1); + verifier.verifyFilePresent(EXTRA_OUTPUT_2); + verifier.verifyFilePresent(EXTRA_OUTPUT_3); + verifier.verifyFilePresent(EXTRA_OUTPUT_4); + verifier.verifyFilePresent(EXTRA_OUTPUT_5); + verifier.verifyFilePresent(EXTRA_OUTPUT_6); + + Path buildInfoPath = getSavedBuildInfoPath(verifier); + Path jarCacheFile = buildInfoPath.getParent().resolve(MBUILDCACHE_INCREMENTAL_JAR); + Path jarSourcesCacheFile = buildInfoPath.getParent().resolve(MBUILDCACHE_INCREMENTAL_SOURCES_JAR); + Path jarJavadocCacheFile = buildInfoPath.getParent().resolve(MBUILDCACHE_INCREMENTAL_JAVADOC_JAR); + Assertions.assertTrue(Files.exists(jarCacheFile), "Expected artifact saved in build cache."); + Assertions.assertFalse( + Files.exists(jarSourcesCacheFile), "Not expected sources artifact saved in build cache."); + Assertions.assertFalse( + Files.exists(jarJavadocCacheFile), "Not expected javadoc artifact saved in build cache."); + + // Verify clean build, with the same goal should be fully restored + verifier.setMavenDebug(false); + verifier.setLogFileName("../log-clean.txt"); + verifier.executeGoal("clean"); + verifier.verifyFileNotPresent(GENERATED_JAR); + + verifier.setLogFileName("../log-package-2.txt"); + verifier.executeGoal("package"); + verifier.verifyTextInLog( + FOUND_CACHED_BUILD_RESTORING_ORG_APACHE_MAVEN_CACHING_TEST_MBUILDCACHE_INCREMENTAL_FROM_CACHE_BY_CHECKSUM); + verifier.verifyTextInLog( + "Found cached build, restoring org.apache.maven.caching.test:mbuildcache-incremental from cache by checksum"); + verifier.verifyErrorFreeLog(); + verifier.verifyTextInLog(SKIPPING_PLUGIN_EXECUTION_CACHED_RESOURCES_RESOURCES); + verifier.verifyTextInLog(SKIPPING_PLUGIN_EXECUTION_CACHED_COMPILER_COMPILE); + verifier.verifyTextInLog(SKIPPING_PLUGIN_EXECUTION_CACHED_RESOURCES_TEST_RESOURCES); + verifier.verifyTextInLog(SKIPPING_PLUGIN_EXECUTION_CACHED_COMPILER_TEST_COMPILE); + verifier.verifyTextInLog(SKIPPING_PLUGIN_EXECUTION_CACHED_SUREFIRE_TEST); + verifier.verifyTextInLog(SKIPPING_PLUGIN_EXECUTION_CACHED_JAR_JAR); + verifier.verifyTextInLog(SKIPPING_PLUGIN_EXECUTION_CACHED_RESOURCES_COPY); + verifier.verifyFilePresent(GENERATED_JAR); + // 2nd build with cache : only cached extra resources are present in the target folder + verifier.verifyFilePresent(EXTRA_OUTPUT_1); + verifier.verifyFilePresent(EXTRA_OUTPUT_2); + verifier.verifyFileNotPresent(EXTRA_OUTPUT_3); + verifier.verifyFileNotPresent(EXTRA_OUTPUT_4); + verifier.verifyFileNotPresent(EXTRA_OUTPUT_5); + verifier.verifyFilePresent(EXTRA_OUTPUT_6); + Assertions.assertTrue(Files.exists(jarCacheFile), "Expected artifact saved in build cache."); + + // Next step : verify + verifier.setMavenDebug(false); + verifier.setLogFileName("../log-clean.txt"); + verifier.executeGoal("clean"); + verifier.verifyFileNotPresent(GENERATED_JAR); + + verifier.setLogFileName("../log-verify.txt"); + verifier.executeGoal("verify"); + verifier.verifyTextInLog( + FOUND_CACHED_BUILD_RESTORING_ORG_APACHE_MAVEN_CACHING_TEST_MBUILDCACHE_INCREMENTAL_FROM_CACHE_BY_CHECKSUM); + verifier.verifyTextInLog( + "Project org.apache.maven.caching.test:mbuildcache-incremental restored partially. Highest cached goal: package, requested: verify"); + verifier.verifyErrorFreeLog(); + verifier.verifyTextInLog(SKIPPING_PLUGIN_EXECUTION_CACHED_RESOURCES_RESOURCES); + verifier.verifyTextInLog(SKIPPING_PLUGIN_EXECUTION_CACHED_COMPILER_COMPILE); + verifier.verifyTextInLog(SKIPPING_PLUGIN_EXECUTION_CACHED_RESOURCES_TEST_RESOURCES); + verifier.verifyTextInLog(SKIPPING_PLUGIN_EXECUTION_CACHED_COMPILER_TEST_COMPILE); + verifier.verifyTextInLog(SKIPPING_PLUGIN_EXECUTION_CACHED_SUREFIRE_TEST); + verifier.verifyTextInLog(SKIPPING_PLUGIN_EXECUTION_CACHED_JAR_JAR); + verifier.verifyTextInLog(SKIPPING_PLUGIN_EXECUTION_CACHED_RESOURCES_COPY); + verifier.verifyTextInLog(INTEGRATION_TEST_DEFAULT_MBUILDCACHE_INCREMENTAL); + verifier.verifyTextInLog(VERIFY_DEFAULT_MBUILDCACHE_INCREMENTAL); + verifier.verifyTextInLog(SAVED_BUILD_TO_LOCAL_FILE); + verifier.verifyFilePresent(GENERATED_JAR); + // only cached extra resources are present in the target folder + verifier.verifyFilePresent(EXTRA_OUTPUT_1); + verifier.verifyFilePresent(EXTRA_OUTPUT_2); + verifier.verifyFileNotPresent(EXTRA_OUTPUT_3); + verifier.verifyFileNotPresent(EXTRA_OUTPUT_4); + verifier.verifyFileNotPresent(EXTRA_OUTPUT_5); + verifier.verifyFilePresent(EXTRA_OUTPUT_6); + Assertions.assertTrue(Files.exists(jarCacheFile), "Expected artifact saved in build cache."); + + // Install with clean build, with a higher goal should restore cached mojo executions and apply increments + verifier.setMavenDebug(false); + verifier.setLogFileName("../log-clean.txt"); + verifier.executeGoal("clean"); + verifier.verifyFileNotPresent(GENERATED_JAR); + + verifier.setLogFileName("../log-install.txt"); + verifier.executeGoal("install"); + verifier.verifyErrorFreeLog(); + verifier.verifyTextInLog( + "Project org.apache.maven.caching.test:mbuildcache-incremental restored partially. Highest cached goal: verify, requested: install"); + verifier.verifyTextInLog(SKIPPING_PLUGIN_EXECUTION_CACHED_RESOURCES_RESOURCES); + verifier.verifyTextInLog(SKIPPING_PLUGIN_EXECUTION_CACHED_COMPILER_COMPILE); + verifier.verifyTextInLog(SKIPPING_PLUGIN_EXECUTION_CACHED_RESOURCES_TEST_RESOURCES); + verifier.verifyTextInLog(SKIPPING_PLUGIN_EXECUTION_CACHED_COMPILER_TEST_COMPILE); + verifier.verifyTextInLog(SKIPPING_PLUGIN_EXECUTION_CACHED_SUREFIRE_TEST); + verifier.verifyTextInLog(SKIPPING_PLUGIN_EXECUTION_CACHED_JAR_JAR); + verifier.verifyTextInLog(SKIPPING_PLUGIN_EXECUTION_CACHED_RESOURCES_COPY); + verifier.verifyTextInLog(SKIPPING_PLUGIN_EXECUTION_CACHED_FAILSAFE_INTEGRATION_TEST); + verifier.verifyTextInLog(SKIPPING_PLUGIN_EXECUTION_CACHED_FAILSAFE_VERIFY); + verifyNoTextInLog(verifier, RESOURCES_DEFAULT_RESOURCES_MBUILDCACHE_INCREMENTAL); + verifyNoTextInLog(verifier, COMPILE_DEFAULT_COMPILE_MBUILDCACHE_INCREMENTAL); + verifyNoTextInLog(verifier, TEST_RESOURCES_DEFAULT_TEST_RESOURCES_MBUILDCACHE_INCREMENTAL); + verifyNoTextInLog(verifier, TEST_COMPILE_DEFAULT_TEST_COMPILE_MBUILDCACHE_INCREMENTAL); + verifyNoTextInLog(verifier, TEST_DEFAULT_TEST_MBUILDCACHE_INCREMENTAL); + verifyNoTextInLog(verifier, JAR_DEFAULT_JAR_MBUILDCACHE_INCREMENTAL); + verifyNoTextInLog(verifier, INTEGRATION_TEST_DEFAULT_MBUILDCACHE_INCREMENTAL); + verifyNoTextInLog(verifier, VERIFY_DEFAULT_MBUILDCACHE_INCREMENTAL); + verifier.verifyTextInLog(INSTALL_DEFAULT_INSTALL_MBUILDCACHE_INCREMENTAL); + final String installToLocalRepoString = + "Installing " + verifier.getBasedir() + File.separatorChar + EXTRA_OUTPUT_1 + " to "; + verifier.verifyTextInLog(installToLocalRepoString); + verifier.verifyTextInLog(SAVED_BUILD_TO_LOCAL_FILE); + verifier.verifyFilePresent(GENERATED_JAR); + // only cached extra resources are present in the target folder + verifier.verifyFilePresent(EXTRA_OUTPUT_1); + verifier.verifyFilePresent(EXTRA_OUTPUT_2); + verifier.verifyFileNotPresent(EXTRA_OUTPUT_3); + verifier.verifyFileNotPresent(EXTRA_OUTPUT_4); + verifier.verifyFileNotPresent(EXTRA_OUTPUT_5); + verifier.verifyFilePresent(EXTRA_OUTPUT_6); + Assertions.assertTrue(Files.exists(jarCacheFile), "Expected artifact saved in build cache."); + + // Deploy with clean build, with a higher goal should restore cached mojo executions and apply increments + verifier.setMavenDebug(false); + verifier.setLogFileName("../log-clean.txt"); + verifier.executeGoal("clean"); + verifier.verifyFileNotPresent(GENERATED_JAR); + + verifier.setLogFileName("../log-deploy.txt"); + verifier.executeGoal("deploy"); + verifier.verifyErrorFreeLog(); + verifier.verifyTextInLog( + "Project org.apache.maven.caching.test:mbuildcache-incremental restored partially. Highest cached goal: install, requested: deploy"); + verifier.verifyTextInLog(SKIPPING_PLUGIN_EXECUTION_CACHED_RESOURCES_RESOURCES); + verifier.verifyTextInLog(SKIPPING_PLUGIN_EXECUTION_CACHED_COMPILER_COMPILE); + verifier.verifyTextInLog(SKIPPING_PLUGIN_EXECUTION_CACHED_RESOURCES_TEST_RESOURCES); + verifier.verifyTextInLog(SKIPPING_PLUGIN_EXECUTION_CACHED_COMPILER_TEST_COMPILE); + verifier.verifyTextInLog(SKIPPING_PLUGIN_EXECUTION_CACHED_SUREFIRE_TEST); + verifier.verifyTextInLog(SKIPPING_PLUGIN_EXECUTION_CACHED_JAR_JAR); + verifier.verifyTextInLog(SKIPPING_PLUGIN_EXECUTION_CACHED_RESOURCES_COPY); + verifier.verifyTextInLog(SKIPPING_PLUGIN_EXECUTION_CACHED_FAILSAFE_INTEGRATION_TEST); + verifier.verifyTextInLog(SKIPPING_PLUGIN_EXECUTION_CACHED_FAILSAFE_VERIFY); + verifier.verifyTextInLog(SKIPPING_PLUGIN_EXECUTION_CACHED_INSTALL_INSTALL); + verifyNoTextInLog(verifier, RESOURCES_DEFAULT_RESOURCES_MBUILDCACHE_INCREMENTAL); + verifyNoTextInLog(verifier, COMPILE_DEFAULT_COMPILE_MBUILDCACHE_INCREMENTAL); + verifyNoTextInLog(verifier, TEST_RESOURCES_DEFAULT_TEST_RESOURCES_MBUILDCACHE_INCREMENTAL); + verifyNoTextInLog(verifier, TEST_COMPILE_DEFAULT_TEST_COMPILE_MBUILDCACHE_INCREMENTAL); + verifyNoTextInLog(verifier, TEST_DEFAULT_TEST_MBUILDCACHE_INCREMENTAL); + verifyNoTextInLog(verifier, JAR_DEFAULT_JAR_MBUILDCACHE_INCREMENTAL); + verifyNoTextInLog(verifier, INTEGRATION_TEST_DEFAULT_MBUILDCACHE_INCREMENTAL); + verifyNoTextInLog(verifier, VERIFY_DEFAULT_MBUILDCACHE_INCREMENTAL); + verifyNoTextInLog(verifier, INSTALL_DEFAULT_INSTALL_MBUILDCACHE_INCREMENTAL); + verifier.verifyTextInLog(DEPLOY_DEFAULT_DEPLOY_MBUILDCACHE_INCREMENTAL); + verifier.verifyTextInLog("Using alternate deployment repository local::file:./target/staging"); + verifier.verifyTextInLog(SAVED_BUILD_TO_LOCAL_FILE); + verifier.verifyFilePresent(GENERATED_JAR); + verifier.verifyFilePresent(GENERATED_SOURCES_JAR); + verifier.verifyFilePresent(GENERATED_JAVADOC_JAR); + // only cached extra resources are present in the target folder + verifier.verifyFilePresent(EXTRA_OUTPUT_1); + verifier.verifyFilePresent(EXTRA_OUTPUT_2); + verifier.verifyFileNotPresent(EXTRA_OUTPUT_3); + verifier.verifyFileNotPresent(EXTRA_OUTPUT_4); + verifier.verifyFileNotPresent(EXTRA_OUTPUT_5); + verifier.verifyFilePresent(EXTRA_OUTPUT_6); + Assertions.assertTrue(Files.exists(jarCacheFile), "Expected artifact saved in build cache."); + Assertions.assertTrue(Files.exists(jarSourcesCacheFile), "Expected sources artifact saved in build cache."); + Assertions.assertTrue(Files.exists(jarJavadocCacheFile), "Expected javadoc artifact saved in build cache."); + + // Replay install with clean build, with a lower goal should only restore cached mojo executions + verifier.setLogFileName("../log-install-replay.txt"); + verifier.executeGoal("install"); + verifier.verifyErrorFreeLog(); + verifier.verifyTextInLog( + FOUND_CACHED_BUILD_RESTORING_ORG_APACHE_MAVEN_CACHING_TEST_MBUILDCACHE_INCREMENTAL_FROM_CACHE_BY_CHECKSUM); + verifier.verifyTextInLog(SKIPPING_PLUGIN_EXECUTION_CACHED_RESOURCES_RESOURCES); + verifier.verifyTextInLog(SKIPPING_PLUGIN_EXECUTION_CACHED_COMPILER_COMPILE); + verifier.verifyTextInLog(SKIPPING_PLUGIN_EXECUTION_CACHED_RESOURCES_TEST_RESOURCES); + verifier.verifyTextInLog(SKIPPING_PLUGIN_EXECUTION_CACHED_COMPILER_TEST_COMPILE); + verifier.verifyTextInLog(SKIPPING_PLUGIN_EXECUTION_CACHED_SUREFIRE_TEST); + verifier.verifyTextInLog(SKIPPING_PLUGIN_EXECUTION_CACHED_JAR_JAR); + verifier.verifyTextInLog(SKIPPING_PLUGIN_EXECUTION_CACHED_RESOURCES_COPY); + verifier.verifyTextInLog(SKIPPING_PLUGIN_EXECUTION_CACHED_FAILSAFE_INTEGRATION_TEST); + verifier.verifyTextInLog(SKIPPING_PLUGIN_EXECUTION_CACHED_FAILSAFE_VERIFY); + verifier.verifyTextInLog(SKIPPING_PLUGIN_EXECUTION_CACHED_INSTALL_INSTALL); + verifyNoTextInLog(verifier, DEPLOY_DEFAULT_DEPLOY_MBUILDCACHE_INCREMENTAL); + verifyNoTextInLog(verifier, RESOURCES_DEFAULT_RESOURCES_MBUILDCACHE_INCREMENTAL); + verifyNoTextInLog(verifier, COMPILE_DEFAULT_COMPILE_MBUILDCACHE_INCREMENTAL); + verifyNoTextInLog(verifier, TEST_RESOURCES_DEFAULT_TEST_RESOURCES_MBUILDCACHE_INCREMENTAL); + verifyNoTextInLog(verifier, TEST_COMPILE_DEFAULT_TEST_COMPILE_MBUILDCACHE_INCREMENTAL); + verifyNoTextInLog(verifier, TEST_DEFAULT_TEST_MBUILDCACHE_INCREMENTAL); + verifyNoTextInLog(verifier, JAR_DEFAULT_JAR_MBUILDCACHE_INCREMENTAL); + verifyNoTextInLog(verifier, INTEGRATION_TEST_DEFAULT_MBUILDCACHE_INCREMENTAL); + verifyNoTextInLog(verifier, VERIFY_DEFAULT_MBUILDCACHE_INCREMENTAL); + verifyNoTextInLog(verifier, INSTALL_DEFAULT_INSTALL_MBUILDCACHE_INCREMENTAL); + verifyNoTextInLog(verifier, installToLocalRepoString); + verifyNoTextInLog(verifier, SAVED_BUILD_TO_LOCAL_FILE, "Expected successful build cache restore."); + verifier.verifyFilePresent(GENERATED_JAR); + verifier.verifyFilePresent(GENERATED_SOURCES_JAR); + verifier.verifyFilePresent(GENERATED_JAVADOC_JAR); + // only cached extra resources are present in the target folder + verifier.verifyFilePresent(EXTRA_OUTPUT_1); + verifier.verifyFilePresent(EXTRA_OUTPUT_2); + verifier.verifyFileNotPresent(EXTRA_OUTPUT_3); + verifier.verifyFileNotPresent(EXTRA_OUTPUT_4); + verifier.verifyFileNotPresent(EXTRA_OUTPUT_5); + verifier.verifyFilePresent(EXTRA_OUTPUT_6); + Assertions.assertTrue(Files.exists(jarCacheFile), "Expected artifact saved in build cache."); + Assertions.assertTrue(Files.exists(jarSourcesCacheFile), "Expected sources artifact saved in build cache."); + Assertions.assertTrue(Files.exists(jarJavadocCacheFile), "Expected javadoc artifact saved in build cache."); + } + + private static void verifyNoTextInLog(Verifier verifier, String text, String message) throws VerificationException { + Assertions.assertNull(findFirstLineContainingTextsInLogs(verifier, text), message); + } + + private static void verifyNoTextInLog(Verifier verifier, String text) throws VerificationException { + Assertions.assertNull(findFirstLineContainingTextsInLogs(verifier, text)); + } + + private static Path getSavedBuildInfoPath(Verifier verifier) throws VerificationException { + String savedPathLogLine = findFirstLineContainingTextsInLogs(verifier, SAVED_BUILD_TO_LOCAL_FILE); + String[] array = savedPathLogLine.split(SAVED_BUILD_TO_LOCAL_FILE); + return Paths.get(array[array.length - 1]); + } +} diff --git a/src/test/java/org/apache/maven/buildcache/its/Issue67Test.java b/src/test/java/org/apache/maven/buildcache/its/Issue67Test.java index b04566a8..672622fc 100644 --- a/src/test/java/org/apache/maven/buildcache/its/Issue67Test.java +++ b/src/test/java/org/apache/maven/buildcache/its/Issue67Test.java @@ -57,7 +57,7 @@ void simple(Verifier verifier) throws VerificationException, IOException { String[] array = savedPathLogLine.split(SAVED_BUILD_TO_LOCAL_FILE); String jarCachePath = array[array.length - 1].replace("buildinfo.xml", "mbuildcache-67.jar"); - // We remove from the local cache repository the jar artefact. In order to launch a restoration error. + // We remove from the local cache repository the jar artifact. In order to launch a restoration error. Assertions.assertTrue( Files.deleteIfExists(Paths.get(jarCachePath)), "mbuildcache-67.jar was expected in the local cache"); diff --git a/src/test/java/org/apache/maven/buildcache/its/Issue87Test.java b/src/test/java/org/apache/maven/buildcache/its/Issue87Test.java new file mode 100644 index 00000000..66e90db8 --- /dev/null +++ b/src/test/java/org/apache/maven/buildcache/its/Issue87Test.java @@ -0,0 +1,133 @@ +/* + * 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.buildcache.its; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.List; + +import org.apache.maven.buildcache.its.junit.IntegrationTest; +import org.apache.maven.buildcache.util.LogFileUtils; +import org.apache.maven.buildcache.xml.XmlService; +import org.apache.maven.buildcache.xml.build.ProjectsInputInfo; +import org.apache.maven.it.VerificationException; +import org.apache.maven.it.Verifier; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@IntegrationTest("src/test/projects/mbuildcache-87") +public class Issue87Test { + + private static final String MODULE1_PROJECT_ARTIFACT = "org.apache.maven.caching.test.mbuildcache-87:module1:jar"; + private static final String MODULE2_PROJECT_NAME = "org.apache.maven.caching.test.mbuildcache-87:module2"; + private static final String FOUND_CACHED_RESTORING_MODULE2_MESSAGE = + "Found cached build, restoring " + MODULE2_PROJECT_NAME + " from cache"; + + @Test + void simple(Verifier verifier) throws VerificationException, IOException { + verifier.setLogFileName("../log-0.txt"); + verifier.executeGoals(Arrays.asList("-f", "external", "install")); + verifier.verifyErrorFreeLog(); + + verifier.setAutoclean(false); + + verifier.setLogFileName("../log-1.txt"); + verifier.executeGoals(Arrays.asList("-f", "top", "verify")); + verifier.verifyErrorFreeLog(); + + verifier.setLogFileName("../log-2.txt"); + verifier.executeGoals(Arrays.asList("-f", "top", "verify")); + verifier.verifyErrorFreeLog(); + verifier.verifyTextInLog(FOUND_CACHED_RESTORING_MODULE2_MESSAGE); + + // START: Modifying maven plugin reactor dependency makes the cache stale + verifier.writeFile("top/module1/src/main/resources/org/apache/maven/buildcache/test.properties", "foo=bar"); + verifier.setLogFileName("../log-3.txt"); + verifier.executeGoals(Arrays.asList("-f", "top", "verify")); + verifier.verifyErrorFreeLog(); + verifyTextNotInLog(verifier, FOUND_CACHED_RESTORING_MODULE2_MESSAGE); + // END: Modifying maven plugin reactor dependency makes the cache stale + + String buildInfoXmlLog = + LogFileUtils.findLinesContainingTextsInLogs(verifier, "Saved Build to local file: ").stream() + .filter(line -> line.contains("module2")) + .findFirst() + .orElseThrow( + () -> new VerificationException("Could not find module2 build info file location")); + Path buildInfoXmlLocation = Paths.get(buildInfoXmlLog.split(":\\s")[1]); + + ProjectsInputInfo projectsInputInfo = + new XmlService().loadBuild(buildInfoXmlLocation.toFile()).getProjectsInputInfo(); + + assertEquals( + 1, + projectsInputInfo.getItems().stream() + .filter(item -> "dependency".equals(item.getType())) + .filter(item -> MODULE1_PROJECT_ARTIFACT.equals(item.getValue())) + .count(), + "Expected artifact acting as plugin dependency and project dependency to be considered twice during checksum computation"); + assertEquals( + 1, + projectsInputInfo.getItems().stream() + .filter(item -> "pluginDependency".equals(item.getType())) + .filter(item -> ("org.apache.maven.plugins:maven-dependency-plugin:maven-plugin|0|" + + MODULE1_PROJECT_ARTIFACT) + .equals(item.getValue())) + .count(), + "Expected artifact acting as plugin dependency and project dependency to be considered twice during checksum computation"); + + assertEquals( + 1, + projectsInputInfo.getItems().stream() + .filter(item -> "pluginDependency".equals(item.getType())) + .filter(item -> + "org.apache.maven.plugins:maven-dependency-plugin:maven-plugin|0|org.apache.maven.caching.test.mbuildcache-87:external:jar" + .equals(item.getValue())) + .count(), + "Expected external snapshot plugin dependency to be included in the checksum computation"); + + assertEquals( + 0, + projectsInputInfo.getItems().stream() + .filter(item -> "pluginDependency".equals(item.getType())) + .filter(item -> item.getValue() + .startsWith("org.apache.maven.plugins:maven-compiler-plugin:maven-plugin|")) + .count(), + "Expected plugins having excludeDependencies=true to have their dependencies excluded"); + } + + private void verifyTextNotInLog(Verifier verifier, String text) throws VerificationException { + + List lines = verifier.loadFile(verifier.getBasedir(), verifier.getLogFileName(), false); + + boolean result = true; + for (String line : lines) { + if (Verifier.stripAnsi(line).contains(text)) { + result = false; + break; + } + } + if (!result) { + throw new VerificationException("Text found in log: " + text); + } + } +} diff --git a/src/test/java/org/apache/maven/buildcache/its/Issue99Test.java b/src/test/java/org/apache/maven/buildcache/its/Issue99Test.java new file mode 100644 index 00000000..4cfc125e --- /dev/null +++ b/src/test/java/org/apache/maven/buildcache/its/Issue99Test.java @@ -0,0 +1,71 @@ +/* + * 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.buildcache.its; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.List; + +import org.apache.maven.buildcache.its.junit.IntegrationTest; +import org.apache.maven.it.VerificationException; +import org.apache.maven.it.Verifier; +import org.junit.jupiter.api.Test; + +import static java.util.Arrays.asList; + +@IntegrationTest("src/test/projects/mbuildcache-99") +public class Issue99Test { + + @Test + void renamedFileInvalidatesCache(Verifier verifier) throws VerificationException, IOException { + verifier.setAutoclean(false); + + verifier.setLogFileName("../log-0.txt"); + verifier.executeGoals(asList("package")); + verifier.verifyErrorFreeLog(); + verifier.verifyTextInLog("Local build was not found"); + verifyTextNotInLog(verifier, "Found cached build"); + + verifier.setLogFileName("../log-1.txt"); + verifier.executeGoals(asList("package")); + verifier.verifyErrorFreeLog(); + verifier.verifyTextInLog("Found cached build"); + verifyTextNotInLog(verifier, "Local build was not found"); + + Files.move( + Paths.get(verifier.getBasedir(), "test-module/src/main/resources/test.properties"), + Paths.get(verifier.getBasedir(), "test-module/src/main/resources/test2.properties")); + + verifier.setLogFileName("../log-2.txt"); + verifier.executeGoals(asList("package")); + verifier.verifyErrorFreeLog(); + verifier.verifyTextInLog("Local build was not found"); + verifyTextNotInLog(verifier, "Found cached build"); + } + + private static void verifyTextNotInLog(Verifier verifier, String text) throws VerificationException { + List lines = verifier.loadFile(verifier.getBasedir(), verifier.getLogFileName(), false); + for (String line : lines) { + if (Verifier.stripAnsi(line).contains(text)) { + throw new VerificationException("Text found in log: " + text); + } + } + } +} diff --git a/src/test/java/org/apache/maven/buildcache/its/MandatoryCleanTest.java b/src/test/java/org/apache/maven/buildcache/its/MandatoryCleanTest.java new file mode 100644 index 00000000..72ded2c8 --- /dev/null +++ b/src/test/java/org/apache/maven/buildcache/its/MandatoryCleanTest.java @@ -0,0 +1,156 @@ +/* + * 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.buildcache.its; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.List; + +import org.apache.maven.buildcache.its.junit.IntegrationTest; +import org.apache.maven.buildcache.util.LogFileUtils; +import org.apache.maven.buildcache.xml.CacheConfigImpl; +import org.apache.maven.it.VerificationException; +import org.apache.maven.it.Verifier; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import static org.apache.maven.buildcache.xml.CacheConfigImpl.CACHE_LOCATION_PROPERTY_NAME; +import static org.junit.jupiter.api.Assertions.assertThrows; + +/** + * Test the "mandatoryClean" parameter : saving in cache should be done only if a clean phase has been executed. + */ +@IntegrationTest("src/test/projects/mandatory-clean") +public class MandatoryCleanTest { + + private static final String MODULE_NAME_1 = "org.apache.maven.caching.test.simple:non-forked-module"; + private static final String MODULE_NAME_2 = "org.apache.maven.caching.test.simple:forked-module"; + private static final String CACHE_BUILD_LOG = "Found cached build, restoring %s from cache"; + + @Test + void simple(Verifier verifier) throws VerificationException, IOException { + + verifier.setAutoclean(false); + Path tempDirectory = Files.createTempDirectory("simple-mandatory-clean"); + verifier.getCliOptions().clear(); + verifier.addCliOption("-D" + CACHE_LOCATION_PROPERTY_NAME + "=" + tempDirectory.toAbsolutePath()); + + verifier.setLogFileName("../log-1.txt"); + verifier.executeGoal("verify"); + verifier.verifyErrorFreeLog(); + List cacheSkippedBuild1 = LogFileUtils.findLinesContainingTextsInLogs( + verifier, "Cache storing is skipped since there was no \"clean\" phase."); + Assertions.assertEquals(2, cacheSkippedBuild1.size(), "Expected 2 skipped module caching"); + + assertThrows( + VerificationException.class, + () -> verifier.verifyTextInLog(String.format(CACHE_BUILD_LOG, MODULE_NAME_1)), + "not expected to be loaded from the cache"); + assertThrows( + VerificationException.class, + () -> verifier.verifyTextInLog(String.format(CACHE_BUILD_LOG, MODULE_NAME_2)), + "not expected to be loaded from the cache"); + + verifier.setLogFileName("../log-2.txt"); + verifier.executeGoals(Arrays.asList("clean", "verify")); + verifier.verifyErrorFreeLog(); + List cacheSkippedBuild2 = LogFileUtils.findLinesContainingTextsInLogs( + verifier, "Cache storing is skipped since there was no \"clean\" phase."); + Assertions.assertEquals(0, cacheSkippedBuild2.size(), "Expected 2 skipped module caching"); + assertThrows( + VerificationException.class, + () -> verifier.verifyTextInLog(String.format(CACHE_BUILD_LOG, MODULE_NAME_1)), + "not expected to be loaded from the cache"); + assertThrows( + VerificationException.class, + () -> verifier.verifyTextInLog(String.format(CACHE_BUILD_LOG, MODULE_NAME_2)), + "not expected to be loaded from the cache"); + + verifier.setLogFileName("../log-3.txt"); + verifier.executeGoal("verify"); + verifier.verifyErrorFreeLog(); + List cacheSkippedBuild3 = LogFileUtils.findLinesContainingTextsInLogs( + verifier, "Cache storing is skipped since there was no \"clean\" phase."); + Assertions.assertEquals(0, cacheSkippedBuild3.size(), "loading from cache, no more caching required"); + // Expect to find and restore cached project + verifier.verifyTextInLog(String.format(CACHE_BUILD_LOG, MODULE_NAME_1)); + verifier.verifyTextInLog(String.format(CACHE_BUILD_LOG, MODULE_NAME_2)); + } + + @Test + void disabledViaProperty(Verifier verifier) throws VerificationException { + + verifier.setAutoclean(false); + + verifier.setLogFileName("../log-1.txt"); + verifier.executeGoal("verify"); + verifier.verifyErrorFreeLog(); + List cacheSkippedBuild1 = LogFileUtils.findLinesContainingTextsInLogs( + verifier, "Cache storing is skipped since there was no \"clean\" phase."); + Assertions.assertEquals(2, cacheSkippedBuild1.size(), "Expected 2 skipped module caching"); + + assertThrows( + VerificationException.class, + () -> verifier.verifyTextInLog(String.format(CACHE_BUILD_LOG, MODULE_NAME_1)), + "not expected to be loaded from the cache"); + assertThrows( + VerificationException.class, + () -> verifier.verifyTextInLog(String.format(CACHE_BUILD_LOG, MODULE_NAME_2)), + "not expected to be loaded from the cache"); + + verifier.setLogFileName("../log-2.txt"); + verifier.getCliOptions().clear(); + // With "true", we do not change the initially expected behaviour + verifier.addCliOption("-D" + CacheConfigImpl.MANDATORY_CLEAN + "=true"); + verifier.executeGoal("verify"); + verifier.verifyErrorFreeLog(); + List cacheSkippedBuild2 = LogFileUtils.findLinesContainingTextsInLogs( + verifier, "Cache storing is skipped since there was no \"clean\" phase."); + Assertions.assertEquals(2, cacheSkippedBuild2.size(), "Expected 2 skipped module caching"); + + assertThrows( + VerificationException.class, + () -> verifier.verifyTextInLog(String.format(CACHE_BUILD_LOG, MODULE_NAME_1)), + "not expected to be loaded from the cache"); + assertThrows( + VerificationException.class, + () -> verifier.verifyTextInLog(String.format(CACHE_BUILD_LOG, MODULE_NAME_2)), + "not expected to be loaded from the cache"); + + // With "false", we remove the need for the clean phase + verifier.getCliOptions().clear(); + verifier.addCliOption("-D" + CacheConfigImpl.MANDATORY_CLEAN + "=false"); + verifier.setLogFileName("../log-3.txt"); + verifier.executeGoal("verify"); + verifier.verifyErrorFreeLog(); + List cacheSkippedBuild3 = LogFileUtils.findLinesContainingTextsInLogs( + verifier, "Cache storing is skipped since there was no \"clean\" phase."); + Assertions.assertEquals(0, cacheSkippedBuild3.size(), "Expected 2 skipped module caching"); + assertThrows( + VerificationException.class, + () -> verifier.verifyTextInLog(String.format(CACHE_BUILD_LOG, MODULE_NAME_1)), + "not expected to be loaded from the cache"); + assertThrows( + VerificationException.class, + () -> verifier.verifyTextInLog(String.format(CACHE_BUILD_LOG, MODULE_NAME_2)), + "not expected to be loaded from the cache"); + } +} diff --git a/src/test/java/org/apache/maven/buildcache/util/LogFileUtils.java b/src/test/java/org/apache/maven/buildcache/util/LogFileUtils.java index 58f65c9a..c7ac8843 100644 --- a/src/test/java/org/apache/maven/buildcache/util/LogFileUtils.java +++ b/src/test/java/org/apache/maven/buildcache/util/LogFileUtils.java @@ -21,6 +21,7 @@ import java.util.Arrays; import java.util.Iterator; import java.util.List; +import java.util.stream.Collectors; import org.apache.maven.it.VerificationException; import org.apache.maven.it.Verifier; @@ -60,4 +61,20 @@ public static String findFirstLineContainingTextsInLogs(final Verifier verifier, return null; } + + /** + * Find lines matching all the strings given as parameter in the log file attached to a verifier + * @param verifier the maven verifier instance + * @param texts all the matching strings to find + * @return a list of matching strings + * @throws VerificationException + */ + public static List findLinesContainingTextsInLogs(final Verifier verifier, final String... texts) + throws VerificationException { + List lines = verifier.loadFile(verifier.getBasedir(), verifier.getLogFileName(), false); + return lines.stream() + .map(s -> verifier.stripAnsi(s)) + .filter(s -> Arrays.stream(texts).allMatch(text -> s.contains(text))) + .collect(Collectors.toList()); + } } diff --git a/src/test/java/org/apache/maven/buildcache/xml/CacheConfigImplTest.java b/src/test/java/org/apache/maven/buildcache/xml/CacheConfigImplTest.java index 8b19884c..2e06907c 100644 --- a/src/test/java/org/apache/maven/buildcache/xml/CacheConfigImplTest.java +++ b/src/test/java/org/apache/maven/buildcache/xml/CacheConfigImplTest.java @@ -20,6 +20,8 @@ import java.io.File; import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.nio.file.AccessMode; import java.nio.file.FileSystem; import java.nio.file.Path; @@ -28,6 +30,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.Optional; import java.util.Properties; import java.util.stream.Collectors; @@ -47,6 +50,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentMatchers; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; @@ -122,11 +126,30 @@ private static void deepMockConfigFile(File mockFile, boolean exists) throws IOE when(mockPath.getFileSystem()).thenReturn(mockFileSystem); FileSystemProvider mockProvider = mock(FileSystemProvider.class); when(mockFileSystem.provider()).thenReturn(mockProvider); + + // Mock for java < 20. if (!exists) { doThrow(new IOException("mock IOException")) .when(mockProvider) .checkAccess(ArgumentMatchers.eq(mockPath), ArgumentMatchers.any(AccessMode.class)); } + + // Mock for java >= 20. Since the FileSystemProvider.exists method does not exist before v20, we use reflection + // to create the mock + Optional methodToMock = Arrays.stream(FileSystemProvider.class.getDeclaredMethods()) + .filter(method -> "exists".equals(method.getName())) + .findAny(); + if (methodToMock.isPresent()) { + Class[] paramTypes = methodToMock.get().getParameterTypes(); + Object[] params = Arrays.stream(paramTypes) + .map(paramType -> Mockito.any(paramType)) + .toArray(); + try { + Mockito.when(methodToMock.get().invoke(mockProvider, params)).thenReturn(exists); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException("Error while mocking the 'exists' method from FileSystemProvider", e); + } + } } private void assertDefaults() { diff --git a/src/test/projects/build-extension/.mvn/maven-build-cache-config.xml b/src/test/projects/build-extension/.mvn/maven-build-cache-config.xml index f23c4673..810f2212 100644 --- a/src/test/projects/build-extension/.mvn/maven-build-cache-config.xml +++ b/src/test/projects/build-extension/.mvn/maven-build-cache-config.xml @@ -19,6 +19,7 @@ specific language governing permissions and limitations under the License. --> - + diff --git a/src/test/projects/core-extension/.mvn/maven-build-cache-config.xml b/src/test/projects/core-extension/.mvn/maven-build-cache-config.xml index f23c4673..810f2212 100644 --- a/src/test/projects/core-extension/.mvn/maven-build-cache-config.xml +++ b/src/test/projects/core-extension/.mvn/maven-build-cache-config.xml @@ -19,6 +19,7 @@ specific language governing permissions and limitations under the License. --> - + diff --git a/src/test/projects/duplicate-goals/.mvn/maven-build-cache-config.xml b/src/test/projects/duplicate-goals/.mvn/maven-build-cache-config.xml index f23c4673..810f2212 100644 --- a/src/test/projects/duplicate-goals/.mvn/maven-build-cache-config.xml +++ b/src/test/projects/duplicate-goals/.mvn/maven-build-cache-config.xml @@ -19,6 +19,7 @@ specific language governing permissions and limitations under the License. --> - + diff --git a/src/test/projects/forked-executions-core-extension-remote/.mvn/maven-build-cache-config.xml b/src/test/projects/forked-executions-core-extension-remote/.mvn/maven-build-cache-config.xml index 573de403..48e0a9b9 100644 --- a/src/test/projects/forked-executions-core-extension-remote/.mvn/maven-build-cache-config.xml +++ b/src/test/projects/forked-executions-core-extension-remote/.mvn/maven-build-cache-config.xml @@ -19,7 +19,8 @@ specific language governing permissions and limitations under the License. --> - + http://to-be-overridden-on-cli-and-point-to-wiremock diff --git a/src/test/projects/forked-executions-core-extension/.mvn/maven-build-cache-config.xml b/src/test/projects/forked-executions-core-extension/.mvn/maven-build-cache-config.xml index f23c4673..810f2212 100644 --- a/src/test/projects/forked-executions-core-extension/.mvn/maven-build-cache-config.xml +++ b/src/test/projects/forked-executions-core-extension/.mvn/maven-build-cache-config.xml @@ -19,6 +19,7 @@ specific language governing permissions and limitations under the License. --> - + diff --git a/src/test/projects/include-exclude/.mvn/maven-build-cache-config.xml b/src/test/projects/include-exclude/.mvn/maven-build-cache-config.xml index 02a15bdd..cb59eb45 100644 --- a/src/test/projects/include-exclude/.mvn/maven-build-cache-config.xml +++ b/src/test/projects/include-exclude/.mvn/maven-build-cache-config.xml @@ -17,7 +17,8 @@ KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> - + diff --git a/src/test/projects/mandatory-clean/.mvn/extensions.xml b/src/test/projects/mandatory-clean/.mvn/extensions.xml new file mode 100644 index 00000000..8df78f8b --- /dev/null +++ b/src/test/projects/mandatory-clean/.mvn/extensions.xml @@ -0,0 +1,25 @@ + + + + + org.apache.maven.extensions + maven-build-cache-extension + ${projectVersion} + + diff --git a/src/test/projects/mandatory-clean/.mvn/maven-build-cache-config.xml b/src/test/projects/mandatory-clean/.mvn/maven-build-cache-config.xml new file mode 100644 index 00000000..2a06186e --- /dev/null +++ b/src/test/projects/mandatory-clean/.mvn/maven-build-cache-config.xml @@ -0,0 +1,29 @@ + + + + + + + + true + + + diff --git a/src/test/projects/mandatory-clean/forked-module/pom.xml b/src/test/projects/mandatory-clean/forked-module/pom.xml new file mode 100644 index 00000000..a20c2ed2 --- /dev/null +++ b/src/test/projects/mandatory-clean/forked-module/pom.xml @@ -0,0 +1,56 @@ + + + + + org.apache.maven.caching.test.simple + parent-multi-module + 0.0.1-SNAPSHOT + + 4.0.0 + org.apache.maven.caching.test.simple + forked-module + 0.0.1-SNAPSHOT + jar + + + + + org.apache.maven.plugins + maven-pmd-plugin + 3.19.0 + + + + check + pmd + + + + + + org.apache.maven.wagon + wagon-http + 3.5.2 + + + + + + \ No newline at end of file diff --git a/src/test/projects/mandatory-clean/forked-module/src/main/java/org/apache/maven/buildcache/ForkedExecutionClass.java b/src/test/projects/mandatory-clean/forked-module/src/main/java/org/apache/maven/buildcache/ForkedExecutionClass.java new file mode 100644 index 00000000..920b9f99 --- /dev/null +++ b/src/test/projects/mandatory-clean/forked-module/src/main/java/org/apache/maven/buildcache/ForkedExecutionClass.java @@ -0,0 +1,24 @@ +/* + * 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.buildcache; + +class ForkedExecutionClass +{ + +} \ No newline at end of file diff --git a/src/test/projects/mandatory-clean/non-forked-module/pom.xml b/src/test/projects/mandatory-clean/non-forked-module/pom.xml new file mode 100644 index 00000000..a9d9917b --- /dev/null +++ b/src/test/projects/mandatory-clean/non-forked-module/pom.xml @@ -0,0 +1,32 @@ + + + + + org.apache.maven.caching.test.simple + parent-multi-module + 0.0.1-SNAPSHOT + + 4.0.0 + org.apache.maven.caching.test.simple + non-forked-module + 0.0.1-SNAPSHOT + jar + + \ No newline at end of file diff --git a/src/test/projects/mandatory-clean/non-forked-module/src/main/java/org/apache/maven/buildcache/Test.java b/src/test/projects/mandatory-clean/non-forked-module/src/main/java/org/apache/maven/buildcache/Test.java new file mode 100644 index 00000000..03f66a82 --- /dev/null +++ b/src/test/projects/mandatory-clean/non-forked-module/src/main/java/org/apache/maven/buildcache/Test.java @@ -0,0 +1,24 @@ +/* + * 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.buildcache; + +class Test +{ + +} \ No newline at end of file diff --git a/src/test/projects/mandatory-clean/pom.xml b/src/test/projects/mandatory-clean/pom.xml new file mode 100644 index 00000000..c2390cdd --- /dev/null +++ b/src/test/projects/mandatory-clean/pom.xml @@ -0,0 +1,37 @@ + + + + 4.0.0 + org.apache.maven.caching.test.simple + parent-multi-module + 0.0.1-SNAPSHOT + pom + + + 1.8 + 1.8 + + + + non-forked-module + forked-module + + + \ No newline at end of file diff --git a/src/test/projects/mbuildcache-21/.mvn/maven-build-cache-config.xml b/src/test/projects/mbuildcache-21/.mvn/maven-build-cache-config.xml index f23c4673..810f2212 100644 --- a/src/test/projects/mbuildcache-21/.mvn/maven-build-cache-config.xml +++ b/src/test/projects/mbuildcache-21/.mvn/maven-build-cache-config.xml @@ -19,6 +19,7 @@ specific language governing permissions and limitations under the License. --> - + diff --git a/src/test/projects/mbuildcache-56-mojo-parameter-as-method/.mvn/maven-build-cache-config.xml b/src/test/projects/mbuildcache-56-mojo-parameter-as-method/.mvn/maven-build-cache-config.xml index 8132d1d7..fd6934e3 100644 --- a/src/test/projects/mbuildcache-56-mojo-parameter-as-method/.mvn/maven-build-cache-config.xml +++ b/src/test/projects/mbuildcache-56-mojo-parameter-as-method/.mvn/maven-build-cache-config.xml @@ -19,8 +19,8 @@ specific language governing permissions and limitations under the License. --> - + true diff --git a/src/test/projects/mbuildcache-67/.mvn/maven-build-cache-config.xml b/src/test/projects/mbuildcache-67/.mvn/maven-build-cache-config.xml index f23c4673..810f2212 100644 --- a/src/test/projects/mbuildcache-67/.mvn/maven-build-cache-config.xml +++ b/src/test/projects/mbuildcache-67/.mvn/maven-build-cache-config.xml @@ -19,6 +19,7 @@ specific language governing permissions and limitations under the License. --> - + diff --git a/src/test/projects/mbuildcache-74-clean-cache-any-artifact/.mvn/maven-build-cache-config.xml b/src/test/projects/mbuildcache-74-clean-cache-any-artifact/.mvn/maven-build-cache-config.xml index 1bbaaaba..f9926751 100644 --- a/src/test/projects/mbuildcache-74-clean-cache-any-artifact/.mvn/maven-build-cache-config.xml +++ b/src/test/projects/mbuildcache-74-clean-cache-any-artifact/.mvn/maven-build-cache-config.xml @@ -19,7 +19,8 @@ specific language governing permissions and limitations under the License. --> - + 1 diff --git a/src/test/projects/mbuildcache-76/.mvn/maven-build-cache-config.xml b/src/test/projects/mbuildcache-76/.mvn/maven-build-cache-config.xml index 67b961c0..bb61336f 100644 --- a/src/test/projects/mbuildcache-76/.mvn/maven-build-cache-config.xml +++ b/src/test/projects/mbuildcache-76/.mvn/maven-build-cache-config.xml @@ -19,7 +19,8 @@ specific language governing permissions and limitations under the License. --> - + diff --git a/src/test/projects/mbuildcache-87/.mvn/maven-build-cache-config.xml b/src/test/projects/mbuildcache-87/.mvn/maven-build-cache-config.xml new file mode 100644 index 00000000..80d7cd53 --- /dev/null +++ b/src/test/projects/mbuildcache-87/.mvn/maven-build-cache-config.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + diff --git a/src/test/projects/mbuildcache-87/external/pom.xml b/src/test/projects/mbuildcache-87/external/pom.xml new file mode 100644 index 00000000..3c0fac9e --- /dev/null +++ b/src/test/projects/mbuildcache-87/external/pom.xml @@ -0,0 +1,31 @@ + + + + 4.0.0 + org.apache.maven.caching.test.mbuildcache-87 + external + 0.0.1-SNAPSHOT + + + 1.8 + 1.8 + + + diff --git a/src/test/projects/mbuildcache-87/top/.mvn/extensions.xml b/src/test/projects/mbuildcache-87/top/.mvn/extensions.xml new file mode 100644 index 00000000..8df78f8b --- /dev/null +++ b/src/test/projects/mbuildcache-87/top/.mvn/extensions.xml @@ -0,0 +1,25 @@ + + + + + org.apache.maven.extensions + maven-build-cache-extension + ${projectVersion} + + diff --git a/src/test/projects/mbuildcache-87/top/module1/pom.xml b/src/test/projects/mbuildcache-87/top/module1/pom.xml new file mode 100644 index 00000000..b072a032 --- /dev/null +++ b/src/test/projects/mbuildcache-87/top/module1/pom.xml @@ -0,0 +1,33 @@ + + + + 4.0.0 + module1 + jar + + + org.apache.maven.caching.test.mbuildcache-87 + top + 0.0.1-SNAPSHOT + ../pom.xml + + + + diff --git a/src/test/projects/mbuildcache-87/top/module1/src/main/resources/org/apache/maven/buildcache/test.properties b/src/test/projects/mbuildcache-87/top/module1/src/main/resources/org/apache/maven/buildcache/test.properties new file mode 100644 index 00000000..71e666b7 --- /dev/null +++ b/src/test/projects/mbuildcache-87/top/module1/src/main/resources/org/apache/maven/buildcache/test.properties @@ -0,0 +1,16 @@ +# 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. diff --git a/src/test/projects/mbuildcache-87/top/module2/pom.xml b/src/test/projects/mbuildcache-87/top/module2/pom.xml new file mode 100644 index 00000000..fcc64bf7 --- /dev/null +++ b/src/test/projects/mbuildcache-87/top/module2/pom.xml @@ -0,0 +1,72 @@ + + + + 4.0.0 + module2 + jar + + + org.apache.maven.caching.test.mbuildcache-87 + top + 0.0.1-SNAPSHOT + ../pom.xml + + + + + ${project.groupId} + module1 + ${project.version} + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + ${project.groupId} + module1 + ${project.version} + + + ${project.groupId} + external + 0.0.1-SNAPSHOT + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + ${project.groupId} + module1 + ${project.version} + + + + + + diff --git a/src/test/projects/mbuildcache-87/top/module2/src/main/java/org/apache/maven/buildcache/Test2.java b/src/test/projects/mbuildcache-87/top/module2/src/main/java/org/apache/maven/buildcache/Test2.java new file mode 100644 index 00000000..03f66a82 --- /dev/null +++ b/src/test/projects/mbuildcache-87/top/module2/src/main/java/org/apache/maven/buildcache/Test2.java @@ -0,0 +1,24 @@ +/* + * 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.buildcache; + +class Test +{ + +} \ No newline at end of file diff --git a/src/test/projects/mbuildcache-87/top/pom.xml b/src/test/projects/mbuildcache-87/top/pom.xml new file mode 100644 index 00000000..1a5587f3 --- /dev/null +++ b/src/test/projects/mbuildcache-87/top/pom.xml @@ -0,0 +1,47 @@ + + + + 4.0.0 + org.apache.maven.caching.test.mbuildcache-87 + top + 0.0.1-SNAPSHOT + pom + + + 1.8 + 1.8 + + + + + + org.apache.maven.extensions + maven-build-cache-extension + ${projectVersion} + + + + + + module1 + module2 + + + diff --git a/src/test/projects/mbuildcache-99/.mvn/extensions.xml b/src/test/projects/mbuildcache-99/.mvn/extensions.xml new file mode 100644 index 00000000..8df78f8b --- /dev/null +++ b/src/test/projects/mbuildcache-99/.mvn/extensions.xml @@ -0,0 +1,25 @@ + + + + + org.apache.maven.extensions + maven-build-cache-extension + ${projectVersion} + + diff --git a/src/test/projects/mbuildcache-99/pom.xml b/src/test/projects/mbuildcache-99/pom.xml new file mode 100644 index 00000000..f8144592 --- /dev/null +++ b/src/test/projects/mbuildcache-99/pom.xml @@ -0,0 +1,36 @@ + + + + 4.0.0 + org.apache.maven.caching.test.mbuildcache-99 + test + 0.0.1-SNAPSHOT + pom + + + 1.8 + 1.8 + + + + test-module + + + diff --git a/src/test/projects/mbuildcache-99/test-module/pom.xml b/src/test/projects/mbuildcache-99/test-module/pom.xml new file mode 100644 index 00000000..ffaff30d --- /dev/null +++ b/src/test/projects/mbuildcache-99/test-module/pom.xml @@ -0,0 +1,33 @@ + + + + 4.0.0 + test-module + jar + + + org.apache.maven.caching.test.mbuildcache-99 + test + 0.0.1-SNAPSHOT + ../pom.xml + + + + diff --git a/src/test/projects/mbuildcache-99/test-module/src/main/resources/test.properties b/src/test/projects/mbuildcache-99/test-module/src/main/resources/test.properties new file mode 100644 index 00000000..1c6a1a9e --- /dev/null +++ b/src/test/projects/mbuildcache-99/test-module/src/main/resources/test.properties @@ -0,0 +1 @@ +a=b \ No newline at end of file diff --git a/src/test/projects/mbuildcache-incremental/.mvn/maven-build-cache-config.xml b/src/test/projects/mbuildcache-incremental/.mvn/maven-build-cache-config.xml new file mode 100644 index 00000000..d51859ed --- /dev/null +++ b/src/test/projects/mbuildcache-incremental/.mvn/maven-build-cache-config.xml @@ -0,0 +1,34 @@ + + + + + + + + + + extra-resources + other-resources + + + + + diff --git a/src/test/projects/mbuildcache-incremental/pom.xml b/src/test/projects/mbuildcache-incremental/pom.xml new file mode 100644 index 00000000..9dce3f1a --- /dev/null +++ b/src/test/projects/mbuildcache-incremental/pom.xml @@ -0,0 +1,148 @@ + + + + 4.0.0 + org.apache.maven.caching.test + mbuildcache-incremental + 0.0.1-SNAPSHOT + jar + + + 11 + 11 + local::file:./target/staging + UTF-8 + + + + ${artifactId}-final + + + org.apache.maven.extensions + maven-build-cache-extension + ${projectVersion} + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + 3.2.5 + + + + integration-test + verify + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.3.0 + + + attach-sources + deploy + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.6.3 + + + attach-javadocs + deploy + + jar + + + + + + maven-resources-plugin + 3.3.1 + + + copy-resources-1 + package + + copy-resources + + + ${basedir}/target/extra-resources + + + src/main/resources + true + + + + + + copy-resources-2 + package + + copy-resources + + + ${basedir}/target/other-resources + + + src/main/resources + true + + + + + + + + org.apache.maven.plugins + maven-install-plugin + + + secondary-install + install + + install-file + + + ${project.build.directory}/extra-resources/extra-readme-1.md + ${project.groupId} + ${project.artifactId} + ${project.version} + md + extra-readme-1 + + + + + + + + diff --git a/src/test/projects/mbuildcache-incremental/src/main/java/org/apache/maven/buildcache/Test.java b/src/test/projects/mbuildcache-incremental/src/main/java/org/apache/maven/buildcache/Test.java new file mode 100644 index 00000000..66837ac6 --- /dev/null +++ b/src/test/projects/mbuildcache-incremental/src/main/java/org/apache/maven/buildcache/Test.java @@ -0,0 +1,34 @@ +/* + * 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.buildcache; + +/** + * Test class + */ +public class Test +{ + + /** + * public constructor + */ + public Test() { + + } + +} diff --git a/src/test/projects/mbuildcache-incremental/src/main/resources/extra-readme-1.md b/src/test/projects/mbuildcache-incremental/src/main/resources/extra-readme-1.md new file mode 100644 index 00000000..77eadb74 --- /dev/null +++ b/src/test/projects/mbuildcache-incremental/src/main/resources/extra-readme-1.md @@ -0,0 +1,3 @@ +# Here is a readme + +Some text content \ No newline at end of file diff --git a/src/test/projects/mbuildcache-incremental/src/main/resources/extra-readme-2.md b/src/test/projects/mbuildcache-incremental/src/main/resources/extra-readme-2.md new file mode 100644 index 00000000..77eadb74 --- /dev/null +++ b/src/test/projects/mbuildcache-incremental/src/main/resources/extra-readme-2.md @@ -0,0 +1,3 @@ +# Here is a readme + +Some text content \ No newline at end of file diff --git a/src/test/projects/mbuildcache-incremental/src/main/resources/other-readme-1.md b/src/test/projects/mbuildcache-incremental/src/main/resources/other-readme-1.md new file mode 100644 index 00000000..630f2cfd --- /dev/null +++ b/src/test/projects/mbuildcache-incremental/src/main/resources/other-readme-1.md @@ -0,0 +1,3 @@ +# Here is a readme + +Other text content \ No newline at end of file diff --git a/src/test/projects/per-module-flags/.mvn/maven-build-cache-config.xml b/src/test/projects/per-module-flags/.mvn/maven-build-cache-config.xml index f23c4673..810f2212 100644 --- a/src/test/projects/per-module-flags/.mvn/maven-build-cache-config.xml +++ b/src/test/projects/per-module-flags/.mvn/maven-build-cache-config.xml @@ -19,6 +19,7 @@ specific language governing permissions and limitations under the License. --> - + diff --git a/src/test/projects/remote-cache-dav/.mvn/maven-build-cache-config.xml b/src/test/projects/remote-cache-dav/.mvn/maven-build-cache-config.xml index 7389571c..3a2f8749 100644 --- a/src/test/projects/remote-cache-dav/.mvn/maven-build-cache-config.xml +++ b/src/test/projects/remote-cache-dav/.mvn/maven-build-cache-config.xml @@ -14,7 +14,8 @@ limitations under the License. --> - + diff --git a/src/test/resources/org/apache/maven/buildcache/xml/build-cache-build-instance.xml b/src/test/resources/org/apache/maven/buildcache/xml/build-cache-build-instance.xml index 69ebb4a8..e0b8a8ce 100644 --- a/src/test/resources/org/apache/maven/buildcache/xml/build-cache-build-instance.xml +++ b/src/test/resources/org/apache/maven/buildcache/xml/build-cache-build-instance.xml @@ -19,9 +19,9 @@ specific language governing permissions and limitations under the License. --> - + xsi:schemaLocation="http://maven.apache.org/target/generated-resources/modello/build-cache-build-1.2.0.xsd"> v3 1980-03-23T10:20:15.000 my-server.com diff --git a/src/test/resources/org/apache/maven/buildcache/xml/build-cache-config-instance.xml b/src/test/resources/org/apache/maven/buildcache/xml/build-cache-config-instance.xml index d2d0d821..c5840103 100644 --- a/src/test/resources/org/apache/maven/buildcache/xml/build-cache-config-instance.xml +++ b/src/test/resources/org/apache/maven/buildcache/xml/build-cache-config-instance.xml @@ -19,9 +19,9 @@ specific language governing permissions and limitations under the License. --> - + xsi:schemaLocation="http://maven.apache.org/target/generated-resources/modello/build-cache-config-1.2.0.xsd"> true diff --git a/src/test/resources/org/apache/maven/buildcache/xml/build-cache-diff-instance.xml b/src/test/resources/org/apache/maven/buildcache/xml/build-cache-diff-instance.xml index 18ac7eda..50355ffe 100644 --- a/src/test/resources/org/apache/maven/buildcache/xml/build-cache-diff-instance.xml +++ b/src/test/resources/org/apache/maven/buildcache/xml/build-cache-diff-instance.xml @@ -19,9 +19,9 @@ specific language governing permissions and limitations under the License. --> - + xsi:schemaLocation="http://maven.apache.org/target/generated-resources/modello/build-cache-diff-1.2.0.xsd"> diff --git a/src/test/resources/org/apache/maven/buildcache/xml/build-cache-report-instance.xml b/src/test/resources/org/apache/maven/buildcache/xml/build-cache-report-instance.xml index 87257ec4..8c9e5851 100644 --- a/src/test/resources/org/apache/maven/buildcache/xml/build-cache-report-instance.xml +++ b/src/test/resources/org/apache/maven/buildcache/xml/build-cache-report-instance.xml @@ -19,9 +19,9 @@ specific language governing permissions and limitations under the License. --> - + xsi:schemaLocation="http://maven.apache.org/target/generated-resources/modello/build-cache-report-1.2.0.xsd">