Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -930,7 +930,7 @@ public void attachGeneratedSources(MavenProject project) throws IOException {
}

private void attachOutputs(MavenProject project) throws IOException {
final List<DirName> attachedDirs = cacheConfig.getAttachedOutputs();
final List<DirName> attachedDirs = cacheConfig.getAttachedOutputs(project);
for (DirName dir : attachedDirs) {
final Path targetDir = Paths.get(project.getBuild().getDirectory());
final Path outputDir = targetDir.resolve(dir.getValue());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import org.apache.maven.model.Plugin;
import org.apache.maven.model.PluginExecution;
import org.apache.maven.plugin.MojoExecution;
import org.apache.maven.project.MavenProject;

/**
* A java interface to the information configured in the maven-build-cache-config.xml file
Expand Down Expand Up @@ -106,7 +107,7 @@ public interface CacheConfig {

String getLocalRepositoryLocation();

List<DirName> getAttachedOutputs();
List<DirName> getAttachedOutputs(MavenProject project);

boolean adjustMetaInfVersion();

Expand Down
45 changes: 43 additions & 2 deletions src/main/java/org/apache/maven/buildcache/xml/CacheConfigImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
import org.apache.maven.model.Plugin;
import org.apache.maven.model.PluginExecution;
import org.apache.maven.plugin.MojoExecution;
import org.apache.maven.project.MavenProject;
import org.apache.maven.rtinfo.RuntimeInformation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -95,6 +96,7 @@ public class CacheConfigImpl implements org.apache.maven.buildcache.xml.CacheCon
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 ATTACHED_OUTPUTS_ENABLED_PROPERTY_NAME = "maven.build.cache.attachedOutputs.enabled";
public static final String ALWAYS_RUN_PLUGINS = "maven.build.cache.alwaysRunPlugins";
public static final String MANDATORY_CLEAN = "maven.build.cache.mandatoryClean";

Expand Down Expand Up @@ -572,10 +574,49 @@ public String getLocalRepositoryLocation() {
}

@Override
public List<DirName> getAttachedOutputs() {
public List<DirName> getAttachedOutputs(MavenProject project) {
checkInitializedState();
final AttachedOutputs attachedOutputs = getConfiguration().getAttachedOutputs();
return attachedOutputs == null ? Collections.emptyList() : attachedOutputs.getDirNames();
if (attachedOutputs == null) {
return getDefaultAttachedOutputs(project);
}
return attachedOutputs.getDirNames();
}

private List<DirName> getDefaultAttachedOutputs(MavenProject project) {
boolean enabled = getProperty(ATTACHED_OUTPUTS_ENABLED_PROPERTY_NAME, true);
if (!enabled) {
return Collections.emptyList();
}

List<DirName> defaults = new ArrayList<>();

// Get output directories from project build configuration
String buildDirectory = project.getBuild().getDirectory();
String outputDirectory = project.getBuild().getOutputDirectory();
String testOutputDirectory = project.getBuild().getTestOutputDirectory();

// Compute relative paths from build directory
String classesRelative = getRelativePath(buildDirectory, outputDirectory);
String testClassesRelative = getRelativePath(buildDirectory, testOutputDirectory);

DirName classes = new DirName();
classes.setValue(classesRelative);
defaults.add(classes);

DirName testClasses = new DirName();
testClasses.setValue(testClassesRelative);
defaults.add(testClasses);

return defaults;
}

private String getRelativePath(String basePath, String fullPath) {
// Convert to Path objects and compute relative path
Path base = Paths.get(basePath);
Path full = Paths.get(fullPath);
Path relative = base.relativize(full);
return relative.toString();
}

@Override
Expand Down
6 changes: 4 additions & 2 deletions src/main/mdo/build-cache-config.mdo
Original file line number Diff line number Diff line change
Expand Up @@ -374,15 +374,17 @@ under the License.
-->
<class>
<name>AttachedOutputs</name>
<description>Section relative to outputs which are not artifacts but need to be saved/restored.</description>
<description>Section relative to outputs which are not artifacts but need to be saved/restored.
If not specified, defaults to caching 'classes' and 'test-classes' directories.</description>
<fields>
<field>
<name>dirNames</name>
<association>
<type>DirName</type>
<multiplicity>*</multiplicity>
</association>
<description>Path to a directory containing files which needs to be saved/restored (relative to the build directory).</description>
<description>Path to a directory containing files which needs to be saved/restored (relative to the build directory).
When omitted, the cache defaults to saving and restoring the 'classes' and 'test-classes' directories.</description>
</field>
</fields>
</class>
Expand Down
9 changes: 4 additions & 5 deletions src/site/markdown/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,7 @@ When a configuration is disabled by default in the config, it can be enabled via

Build cache extension is generally compatible with IDEs with one limitation:

* The cache doesn't restore the entire project state. Compiled classes, unpacked artifacts, and similar ones typically
will not be restored in the build directory (aka `target`). Configure your IDE to not use Maven
build (`target`) directories for compilation and execution. In that case, IDE will provide fast compilation using
native caches, and
the build cache will supplement that with fast builds.
* The cache restores `classes` and `test-classes` directories by default, but may not restore other artifacts like
unpacked dependencies or generated resources. Configure your IDE to not use Maven build (`target`) directories for
compilation and execution if you experience issues. In that case, IDE will provide fast compilation using
native caches, and the build cache will supplement that with fast builds.
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
Expand All @@ -39,13 +40,17 @@
import org.apache.commons.lang3.tuple.Pair;
import org.apache.maven.buildcache.DefaultPluginScanConfig;
import org.apache.maven.buildcache.hash.HashFactory;
import org.apache.maven.buildcache.xml.config.AttachedOutputs;
import org.apache.maven.buildcache.xml.config.Configuration;
import org.apache.maven.buildcache.xml.config.DirName;
import org.apache.maven.buildcache.xml.config.Remote;
import org.apache.maven.execution.MavenExecutionRequest;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.model.Build;
import org.apache.maven.model.Plugin;
import org.apache.maven.model.PluginExecution;
import org.apache.maven.plugin.MojoExecution;
import org.apache.maven.project.MavenProject;
import org.apache.maven.rtinfo.RuntimeInformation;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -169,7 +174,7 @@ private void assertDefaults(Map<String, Runnable> overrides) {
asserts.put("calculateProjectVersionChecksum", () -> assertFalse(testObject.calculateProjectVersionChecksum()));
asserts.put("canIgnore", () -> assertFalse(testObject.canIgnore(mock(MojoExecution.class))));
asserts.put("getAlwaysRunPlugins", () -> assertNull(testObject.getAlwaysRunPlugins()));
asserts.put("getAttachedOutputs", () -> assertEquals(Collections.emptyList(), testObject.getAttachedOutputs()));
// getAttachedOutputs removed - requires MavenProject parameter, tested separately
asserts.put("getBaselineCacheUrl", () -> assertNull(testObject.getBaselineCacheUrl()));
asserts.put("getDefaultGlob", () -> assertEquals("*", testObject.getDefaultGlob()));
asserts.put(
Expand Down Expand Up @@ -482,4 +487,111 @@ void testRemoveSaveFinalIgnoredWhenRemoteSaveDisabled() {
Pair.of("getUrl", () -> assertEquals("dummy.url.xyz", testObject.getUrl())),
Pair.of("isRemoteCacheEnabled", () -> assertTrue(testObject.isRemoteCacheEnabled())));
}

@Test
void testDefaultAttachedOutputsWhenNotConfigured() {
// When attachedOutputs is not configured in XML, should return default list
Configuration configuration = new Configuration();
// Deliberately not setting attachedOutputs
testCacheConfig.setConfiguration(configuration);

assertEquals(CacheState.INITIALIZED, testObject.initialize());

// Create mock project with default build configuration
MavenProject mockProject = mock(MavenProject.class);
Build mockBuild = mock(Build.class);
when(mockProject.getBuild()).thenReturn(mockBuild);
when(mockBuild.getDirectory()).thenReturn("/project/target");
when(mockBuild.getOutputDirectory()).thenReturn("/project/target/classes");
when(mockBuild.getTestOutputDirectory()).thenReturn("/project/target/test-classes");

List<DirName> attachedOutputs = testObject.getAttachedOutputs(mockProject);
assertEquals(2, attachedOutputs.size(), "Should have 2 default attached outputs");

List<String> dirNames = attachedOutputs.stream()
.map(DirName::getValue)
.collect(Collectors.toList());

assertTrue(dirNames.contains("classes"), "Should include 'classes' directory by default");
assertTrue(dirNames.contains("test-classes"), "Should include 'test-classes' directory by default");
}

@Test
void testExplicitAttachedOutputsOverridesDefaults() {
// When attachedOutputs is explicitly configured, should use those values instead of defaults
Configuration configuration = new Configuration();
AttachedOutputs attachedOutputs = new AttachedOutputs();

DirName customDir = new DirName();
customDir.setValue("custom-output");
attachedOutputs.addDirName(customDir);

configuration.setAttachedOutputs(attachedOutputs);
testCacheConfig.setConfiguration(configuration);

assertEquals(CacheState.INITIALIZED, testObject.initialize());

// Create mock project (not used when explicit config is set, but required by interface)
MavenProject mockProject = mock(MavenProject.class);
Build mockBuild = mock(Build.class);
when(mockProject.getBuild()).thenReturn(mockBuild);
when(mockBuild.getDirectory()).thenReturn("/project/target");
when(mockBuild.getOutputDirectory()).thenReturn("/project/target/classes");
when(mockBuild.getTestOutputDirectory()).thenReturn("/project/target/test-classes");

List<DirName> result = testObject.getAttachedOutputs(mockProject);
assertEquals(1, result.size(), "Should have 1 explicitly configured output");
assertEquals("custom-output", result.get(0).getValue(),
"Should use explicitly configured directory, not defaults");
}

@Test
void testDefaultAttachedOutputsDisabledViaProperty() {
// When attachedOutputs.enabled property is false, should return empty list
Configuration configuration = new Configuration();
testCacheConfig.setConfiguration(configuration);

when(mockProperties.getProperty(CacheConfigImpl.ATTACHED_OUTPUTS_ENABLED_PROPERTY_NAME))
.thenReturn("false");

assertEquals(CacheState.INITIALIZED, testObject.initialize());

// Create mock project
MavenProject mockProject = mock(MavenProject.class);
Build mockBuild = mock(Build.class);
when(mockProject.getBuild()).thenReturn(mockBuild);
when(mockBuild.getDirectory()).thenReturn("/project/target");
when(mockBuild.getOutputDirectory()).thenReturn("/project/target/classes");
when(mockBuild.getTestOutputDirectory()).thenReturn("/project/target/test-classes");

List<DirName> attachedOutputs = testObject.getAttachedOutputs(mockProject);
assertEquals(0, attachedOutputs.size(), "Should return empty list when disabled via property");
}

@Test
void testDefaultAttachedOutputsWithCustomDirectories() {
// When project has custom output directories, should use those
Configuration configuration = new Configuration();
testCacheConfig.setConfiguration(configuration);

assertEquals(CacheState.INITIALIZED, testObject.initialize());

// Create mock project with custom output directories
MavenProject mockProject = mock(MavenProject.class);
Build mockBuild = mock(Build.class);
when(mockProject.getBuild()).thenReturn(mockBuild);
when(mockBuild.getDirectory()).thenReturn("/project/build");
when(mockBuild.getOutputDirectory()).thenReturn("/project/build/custom-classes");
when(mockBuild.getTestOutputDirectory()).thenReturn("/project/build/custom-test-classes");

List<DirName> attachedOutputs = testObject.getAttachedOutputs(mockProject);
assertEquals(2, attachedOutputs.size(), "Should have 2 default attached outputs");

List<String> dirNames = attachedOutputs.stream()
.map(DirName::getValue)
.collect(Collectors.toList());

assertTrue(dirNames.contains("custom-classes"), "Should include custom output directory");
assertTrue(dirNames.contains("custom-test-classes"), "Should include custom test output directory");
}
}
Loading