Skip to content

Commit 846045a

Browse files
committed
Allow incremental restore in case of plugin parameter mismatch
1 parent c2e4c0b commit 846045a

File tree

10 files changed

+256
-14
lines changed

10 files changed

+256
-14
lines changed

src/main/java/org/apache/maven/buildcache/BuildCacheMojosExecutionStrategy.java

+59-11
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,13 @@
2424

2525
import java.io.File;
2626
import java.nio.file.Path;
27+
import java.util.Collection;
2728
import java.util.HashSet;
2829
import java.util.List;
2930
import java.util.Map;
3031
import java.util.Set;
32+
import java.util.stream.Collectors;
33+
import java.util.stream.Stream;
3134

3235
import org.apache.commons.lang3.ArrayUtils;
3336
import org.apache.commons.lang3.StringUtils;
@@ -130,14 +133,14 @@ public void execute(
130133
}
131134

132135
boolean restorable = result.isSuccess() || result.isPartialSuccess();
133-
boolean restored = false; // if partially restored need to save increment
136+
CacheRestorationStatus restorationStatus =
137+
CacheRestorationStatus.FAILURE; // if partially restored need to save increment
134138
if (restorable) {
135-
CacheRestorationStatus cacheRestorationStatus =
136-
restoreProject(result, mojoExecutions, mojoExecutionRunner, cacheConfig);
137-
restored = CacheRestorationStatus.SUCCESS == cacheRestorationStatus;
138-
executeExtraCleanPhaseIfNeeded(cacheRestorationStatus, cleanPhase, mojoExecutionRunner);
139+
restorationStatus = restoreProject(result, mojoExecutions, mojoExecutionRunner, cacheConfig);
140+
executeExtraCleanPhaseIfNeeded(restorationStatus, cleanPhase, mojoExecutionRunner);
139141
}
140-
if (!restored) {
142+
if (restorationStatus != CacheRestorationStatus.SUCCESS
143+
&& restorationStatus != CacheRestorationStatus.INCREMENTAL_SUCCESS) {
141144
for (MojoExecution mojoExecution : mojoExecutions) {
142145
if (source == Source.CLI
143146
|| mojoExecution.getLifecyclePhase() == null
@@ -147,7 +150,8 @@ public void execute(
147150
}
148151
}
149152

150-
if (cacheState == INITIALIZED && (!result.isSuccess() || !restored)) {
153+
if (cacheState == INITIALIZED
154+
&& (!result.isSuccess() || restorationStatus != CacheRestorationStatus.SUCCESS)) {
151155
if (cacheConfig.isSkipSave()) {
152156
LOGGER.info("Cache saving is disabled.");
153157
} else if (cacheConfig.isMandatoryClean()
@@ -228,20 +232,35 @@ private CacheRestorationStatus restoreProject(
228232
// Verify cache consistency for cached mojos
229233
LOGGER.debug("Verify consistency on cached mojos");
230234
Set<MojoExecution> forcedExecutionMojos = new HashSet<>();
235+
Set<MojoExecution> reconciliationExecutionMojos = new HashSet<>();
231236
for (MojoExecution cacheCandidate : cachedSegment) {
232237
if (cacheController.isForcedExecution(project, cacheCandidate)) {
233238
forcedExecutionMojos.add(cacheCandidate);
234239
} else {
240+
if (!reconciliationExecutionMojos.isEmpty()) {
241+
reconciliationExecutionMojos.add(cacheCandidate);
242+
continue;
243+
}
235244
if (!verifyCacheConsistency(
236245
cacheCandidate, build, project, session, mojoExecutionRunner, cacheConfig)) {
237-
LOGGER.info("A cached mojo is not consistent, continuing with non cached build");
238-
return CacheRestorationStatus.FAILURE;
246+
if (!cacheConfig.isIncrementalReconciliationOnParameterMismatch()) {
247+
LOGGER.info("A cached mojo is not consistent, continuing with non cached build");
248+
return CacheRestorationStatus.FAILURE;
249+
} else {
250+
LOGGER.info("A cached mojo is not consistent, will reconciliate from here");
251+
reconciliationExecutionMojos.add(cacheCandidate);
252+
}
239253
}
240254
}
241255
}
242256

257+
Set<MojoExecution> plannedExecutions = Stream.concat(
258+
forcedExecutionMojos.stream(), reconciliationExecutionMojos.stream())
259+
.collect(Collectors.toSet());
243260
// Restore project artifacts
244-
ArtifactRestorationReport restorationReport = cacheController.restoreProjectArtifacts(cacheResult);
261+
ArtifactRestorationReport restorationReport = cacheController.restoreProjectArtifacts(
262+
cacheResult,
263+
!containsExecution(plannedExecutions, "org.apache.maven.plugins", "maven-jar-plugin", "jar"));
245264
if (!restorationReport.isSuccess()) {
246265
LOGGER.info("Cannot restore project artifacts, continuing with non cached build");
247266
return restorationReport.isRestoredFilesInProjectDirectory()
@@ -267,6 +286,12 @@ private CacheRestorationStatus restoreProject(
267286
// mojoExecutionScope.seed(
268287
// org.apache.maven.api.MojoExecution.class, new DefaultMojoExecution(cacheCandidate));
269288
mojoExecutionRunner.run(cacheCandidate);
289+
} else if (reconciliationExecutionMojos.contains(cacheCandidate)) {
290+
LOGGER.info(
291+
"Mojo execution is needed for reconciliation: {}",
292+
cacheCandidate.getMojoDescriptor().getFullGoalName());
293+
mojoExecutionScope.seed(MojoExecution.class, cacheCandidate);
294+
mojoExecutionRunner.run(cacheCandidate);
270295
} else {
271296
LOGGER.info(
272297
"Skipping plugin execution (cached): {}",
@@ -295,12 +320,34 @@ private CacheRestorationStatus restoreProject(
295320
for (MojoExecution mojoExecution : postCachedSegment) {
296321
mojoExecutionRunner.run(mojoExecution);
297322
}
298-
return CacheRestorationStatus.SUCCESS;
323+
324+
if (reconciliationExecutionMojos.isEmpty()) {
325+
return CacheRestorationStatus.SUCCESS;
326+
} else {
327+
return CacheRestorationStatus.INCREMENTAL_SUCCESS;
328+
}
299329
} finally {
300330
mojoExecutionScope.exit();
301331
}
302332
}
303333

334+
private boolean containsExecution(
335+
Collection<MojoExecution> executions, String groupId, String artifactId, String goal) {
336+
for (MojoExecution execution : executions) {
337+
if (!groupId.equals(execution.getGroupId())) {
338+
continue;
339+
}
340+
if (!artifactId.equals(execution.getArtifactId())) {
341+
continue;
342+
}
343+
if (!goal.equals(execution.getGoal())) {
344+
continue;
345+
}
346+
return true;
347+
}
348+
return false;
349+
}
350+
304351
private boolean verifyCacheConsistency(
305352
MojoExecution cacheCandidate,
306353
Build cachedBuild,
@@ -433,6 +480,7 @@ private static String normalizedPath(Path path, Path baseDirPath) {
433480

434481
private enum CacheRestorationStatus {
435482
SUCCESS,
483+
INCREMENTAL_SUCCESS,
436484
FAILURE,
437485
FAILURE_NEEDS_CLEAN
438486
}

src/main/java/org/apache/maven/buildcache/CacheController.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public interface CacheController {
3535
CacheResult findCachedBuild(
3636
MavenSession session, MavenProject project, List<MojoExecution> mojoExecutions, boolean skipCache);
3737

38-
ArtifactRestorationReport restoreProjectArtifacts(CacheResult cacheResult);
38+
ArtifactRestorationReport restoreProjectArtifacts(CacheResult cacheResult, boolean setProjectArtifact);
3939

4040
void save(
4141
CacheResult cacheResult,

src/main/java/org/apache/maven/buildcache/CacheControllerImpl.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,7 @@ private void verifyRestorationInsideProject(final MavenProject project, Path pat
345345
}
346346

347347
@Override
348-
public ArtifactRestorationReport restoreProjectArtifacts(CacheResult cacheResult) {
348+
public ArtifactRestorationReport restoreProjectArtifacts(CacheResult cacheResult, boolean setProjectArtifact) {
349349

350350
LOGGER.debug("Restore project artifacts");
351351
final Build build = cacheResult.getBuildInfo();
@@ -412,7 +412,9 @@ public ArtifactRestorationReport restoreProjectArtifacts(CacheResult cacheResult
412412
// Actually modify project at the end in case something went wrong during restoration,
413413
// in which case, the project is unmodified and we continue with normal build.
414414
if (restoredProjectArtifact != null) {
415-
project.setArtifact(restoredProjectArtifact);
415+
if (setProjectArtifact) {
416+
project.setArtifact(restoredProjectArtifact);
417+
}
416418
// need to include package lifecycle to save build info for incremental builds
417419
if (!project.hasLifecyclePhase("package")) {
418420
project.addLifecyclePhase("package");

src/main/java/org/apache/maven/buildcache/xml/CacheConfig.java

+2
Original file line numberDiff line numberDiff line change
@@ -152,4 +152,6 @@ public interface CacheConfig {
152152
* Flag to save in cache only if a build went through the clean lifecycle
153153
*/
154154
boolean isMandatoryClean();
155+
156+
boolean isIncrementalReconciliationOnParameterMismatch();
155157
}

src/main/java/org/apache/maven/buildcache/xml/CacheConfigImpl.java

+9
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ public class CacheConfigImpl implements org.apache.maven.buildcache.xml.CacheCon
9595
public static final String RESTORE_GENERATED_SOURCES_PROPERTY_NAME = "maven.build.cache.restoreGeneratedSources";
9696
public static final String ALWAYS_RUN_PLUGINS = "maven.build.cache.alwaysRunPlugins";
9797
public static final String MANDATORY_CLEAN = "maven.build.cache.mandatoryClean";
98+
public static final String INCREMENTAL_RECONCILIATION_ON_PARAMETER_MISMATCH =
99+
"maven.build.cache.incrementalReconciliationOnParameterMismatch";
98100

99101
/**
100102
* Flag to control if we should skip lookup for cached artifacts globally or for a particular project even if
@@ -538,6 +540,13 @@ public boolean isMandatoryClean() {
538540
return getProperty(MANDATORY_CLEAN, getConfiguration().isMandatoryClean());
539541
}
540542

543+
@Override
544+
public boolean isIncrementalReconciliationOnParameterMismatch() {
545+
return getProperty(
546+
INCREMENTAL_RECONCILIATION_ON_PARAMETER_MISMATCH,
547+
getConfiguration().isIncrementalReconciliationOnParameterMismatch());
548+
}
549+
541550
@Override
542551
public String getId() {
543552
checkInitializedState();

src/main/mdo/build-cache-config.mdo

+8
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,14 @@ under the License.
241241
<description>FileHash (causes file hash is saved in build metadata) or
242242
EffectivePom (causes effective pom info is saved in build metadata)</description>
243243
</field>
244+
<field>
245+
<name>incrementalReconciliationOnParameterMismatch</name>
246+
<type>boolean</type>
247+
<defaultValue>false</defaultValue>
248+
<description>If true, on plugin execution parameter mismatch, the build will try to complete by
249+
running the mismatched execution and executions that follow. If false, on plugin execution parameter
250+
mismatch, a full build will be performed.</description>
251+
</field>
244252
</fields>
245253
</class>
246254

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.maven.buildcache.its;
20+
21+
import java.util.Arrays;
22+
23+
import org.apache.maven.buildcache.its.junit.IntegrationTest;
24+
import org.apache.maven.it.VerificationException;
25+
import org.apache.maven.it.Verifier;
26+
import org.junit.jupiter.api.Assertions;
27+
import org.junit.jupiter.api.Test;
28+
29+
import static org.apache.maven.buildcache.util.LogFileUtils.findFirstLineContainingTextsInLogs;
30+
31+
/**
32+
* @author Réda Housni Alaoui
33+
*/
34+
@IntegrationTest("src/test/projects/mbuildcache-103")
35+
class Issue103Test {
36+
37+
private static final String PROJECT_NAME = "org.apache.maven.caching.test.mbuildcache-103:simple";
38+
39+
@Test
40+
void simple(Verifier verifier) throws VerificationException {
41+
verifier.setAutoclean(false);
42+
43+
verifier.getCliOptions().clear();
44+
verifier.addCliOption("-Dmaven.build.cache.incrementalReconciliationOnParameterMismatch");
45+
verifier.addCliOption("-DskipTests");
46+
verifier.setLogFileName("../log-1.txt");
47+
verifier.executeGoals(Arrays.asList("clean", "verify"));
48+
verifier.verifyErrorFreeLog();
49+
50+
verifier.getCliOptions().clear();
51+
verifier.addCliOption("-Dmaven.build.cache.incrementalReconciliationOnParameterMismatch");
52+
verifier.setLogFileName("../log-2.txt");
53+
verifier.executeGoals(Arrays.asList("clean", "verify"));
54+
verifier.verifyErrorFreeLog();
55+
verifier.verifyTextInLog("No tests to run.");
56+
verifier.verifyTextInLog("Building jar");
57+
verifier.verifyTextInLog("Saved Build to local file");
58+
verifyNoTextInLog(verifier, "A cached mojo is not consistent, continuing with non cached build");
59+
60+
verifier.getCliOptions().clear();
61+
verifier.addCliOption("-Dmaven.build.cache.incrementalReconciliationOnParameterMismatch");
62+
verifier.setLogFileName("../log-3.txt");
63+
verifier.executeGoals(Arrays.asList("clean", "verify"));
64+
verifier.verifyErrorFreeLog();
65+
verifier.verifyTextInLog("Found cached build, restoring " + PROJECT_NAME + " from cache by");
66+
verifier.verifyTextInLog("Skipping plugin execution (cached): surefire:test");
67+
}
68+
69+
private static void verifyNoTextInLog(Verifier verifier, String text) throws VerificationException {
70+
Assertions.assertNull(findFirstLineContainingTextsInLogs(verifier, text));
71+
}
72+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
3+
<!--
4+
Licensed to the Apache Software Foundation (ASF) under one
5+
or more contributor license agreements. See the NOTICE file
6+
distributed with this work for additional information
7+
regarding copyright ownership. The ASF licenses this file
8+
to you under the Apache License, Version 2.0 (the
9+
"License"); you may not use this file except in compliance
10+
with the License. You may obtain a copy of the License at
11+
12+
http://www.apache.org/licenses/LICENSE-2.0
13+
14+
Unless required by applicable law or agreed to in writing,
15+
software distributed under the License is distributed on an
16+
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17+
KIND, either express or implied. See the License for the
18+
specific language governing permissions and limitations
19+
under the License.
20+
-->
21+
22+
<cache xmlns="http://maven.apache.org/BUILD-CACHE-CONFIG/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
23+
xsi:schemaLocation="http://maven.apache.org/BUILD-CACHE-CONFIG/1.0.0 https://maven.apache.org/xsd/build-cache-config-1.0.0.xsd">
24+
<executionControl>
25+
<reconcile>
26+
<plugins>
27+
<plugin artifactId="maven-surefire-plugin" goal="test">
28+
<reconciles>
29+
<reconcile propertyName="skipTests" skipValue="true"/>
30+
</reconciles>
31+
</plugin>
32+
</plugins>
33+
</reconcile>
34+
</executionControl>
35+
</cache>
+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<!--
2+
3+
Copyright 2021 the original author or authors.
4+
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
17+
-->
18+
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
19+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
20+
21+
<modelVersion>4.0.0</modelVersion>
22+
<groupId>org.apache.maven.caching.test.mbuildcache-103</groupId>
23+
<artifactId>simple</artifactId>
24+
<version>0.0.1-SNAPSHOT</version>
25+
<packaging>jar</packaging>
26+
27+
<properties>
28+
<maven.compiler.source>1.8</maven.compiler.source>
29+
<maven.compiler.target>1.8</maven.compiler.target>
30+
</properties>
31+
32+
<build>
33+
<extensions>
34+
<extension>
35+
<groupId>org.apache.maven.extensions</groupId>
36+
<artifactId>maven-build-cache-extension</artifactId>
37+
<version>${projectVersion}</version>
38+
</extension>
39+
</extensions>
40+
</build>
41+
42+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.maven.buildcache;
20+
21+
class Test
22+
{
23+
24+
}

0 commit comments

Comments
 (0)