Skip to content

Commit 246ad04

Browse files
authored
ValueSource exclusionList (#137)
1 parent bca0a5e commit 246ad04

File tree

6 files changed

+169
-18
lines changed

6 files changed

+169
-18
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## Version 1.8
44
* [#127](https://github.com/gradlex-org/java-module-dependencies/issues/127) Less configuration cache misses when modifying `module-info.java` (Thanks [TheGoesen](https://github.com/TheGoesen))
5+
* [#128](https://github.com/gradlex-org/java-module-dependencies/issues/128) Less configuration cache misses when using Settings plugin (Thanks [TheGoesen](https://github.com/TheGoesen))
56

67
## Version 1.7.1
78
* Update module name mappings

README.MD

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@ javaModules { // use instead of 'include(...)'
119119
group = "org.example" // group for all Modules
120120
plugin("java-library") // apply plugin to all Modules' subprojects
121121
module("app") { ... } // individualise Module (only if needed)
122+
123+
// To optimze Configuration Cache hits:
124+
exclusions.add("_.*") // do not inspect certain folders (regex)
125+
requiresBuildFile // only look at folder containing a build.gradle(.kts)
122126
}
123127

124128
versions("gradle/versions") // subproject configured as Platform Project

src/main/java/org/gradlex/javamodule/dependencies/initialization/Directory.java

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@
2020
import org.gradle.api.model.ObjectFactory;
2121
import org.gradle.api.provider.ListProperty;
2222
import org.gradle.api.provider.Property;
23+
import org.gradle.api.provider.Provider;
2324

2425
import javax.inject.Inject;
2526
import java.io.File;
27+
import java.util.Arrays;
2628
import java.util.LinkedHashMap;
2729
import java.util.Map;
2830

@@ -41,14 +43,17 @@ public abstract class Directory {
4143
*/
4244
public abstract ListProperty<String> getPlugins();
4345

44-
@Inject
45-
protected abstract ObjectFactory getObjects();
4646

4747
@Inject
4848
public Directory(File root) {
4949
this.root = root;
50+
getExclusions().convention(Arrays.asList("build", "\\..*"));
51+
getRequiresBuildFile().convention(false);
5052
}
5153

54+
@Inject
55+
protected abstract ObjectFactory getObjects();
56+
5257
/**
5358
* {@link Module#plugin(String)}
5459
*/
@@ -81,4 +86,19 @@ Module addModule(String subDirectory) {
8186
module.getPlugins().addAll(getPlugins());
8287
return module;
8388
}
89+
90+
/**
91+
* Configure which folders should be ignored when searching for Modules.
92+
* This can be tweaked to optimize the configuration cache hit ratio.
93+
* Defaults to: 'build', '.*'
94+
*/
95+
public abstract ListProperty<String> getExclusions();
96+
97+
/**
98+
* Configure if only folders that contain a 'build.gradle' or 'build.gradle.kts'
99+
* should be considered when searching for Modules.
100+
* Setting this to true may improve configuration cache hit ratio if you know
101+
* that all modules have build files in addition to the 'module-info.java' files.
102+
*/
103+
public abstract Property<Boolean> getRequiresBuildFile();
84104
}

src/main/java/org/gradlex/javamodule/dependencies/initialization/JavaModulesExtension.java

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,14 @@
2727
import org.gradle.api.plugins.JavaApplication;
2828
import org.gradle.api.plugins.JavaPlatformExtension;
2929
import org.gradle.api.plugins.JavaPlatformPlugin;
30+
import org.gradle.api.provider.Provider;
31+
import org.gradle.api.provider.ProviderFactory;
3032
import org.gradlex.javamodule.dependencies.JavaModuleDependenciesExtension;
3133
import org.gradlex.javamodule.dependencies.JavaModuleDependenciesPlugin;
3234
import org.gradlex.javamodule.dependencies.JavaModuleVersionsPlugin;
3335
import org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo;
3436
import org.gradlex.javamodule.dependencies.internal.utils.ModuleInfoCache;
37+
import org.gradlex.javamodule.dependencies.internal.utils.ValueModuleDirectoryListing;
3538

3639
import javax.annotation.Nullable;
3740
import javax.inject.Inject;
@@ -47,6 +50,9 @@ public abstract class JavaModulesExtension {
4750
@Inject
4851
public abstract ObjectFactory getObjects();
4952

53+
@Inject
54+
public abstract ProviderFactory getProviders();
55+
5056
@Inject
5157
public JavaModulesExtension(Settings settings) {
5258
this.settings = settings;
@@ -85,22 +91,21 @@ public void directory(String directory, Action<Directory> action) {
8591
Directory moduleDirectory = getObjects().newInstance(Directory.class, modulesDirectory);
8692
action.execute(moduleDirectory);
8793

88-
File[] projectDirs = modulesDirectory.listFiles();
89-
if (projectDirs == null) {
90-
throw new RuntimeException("Failed to inspect: " + modulesDirectory);
91-
}
92-
9394
for (Module module : moduleDirectory.customizedModules.values()) {
9495
includeModule(module, new File(modulesDirectory, module.getDirectory().get()));
9596
}
96-
97-
for (File projectDir : projectDirs) {
98-
if (!moduleDirectory.customizedModules.containsKey(projectDir.getName())) {
99-
Module module = moduleDirectory.addModule(projectDir.getName());
100-
if (!module.getModuleInfoPaths().get().isEmpty()) {
101-
// only auto-include if there is at least one module-info.java
102-
includeModule(module, projectDir);
103-
}
97+
Provider<List<String>> listProvider = getProviders().of(ValueModuleDirectoryListing.class, spec -> {
98+
spec.getParameters().getExclusions().set(moduleDirectory.getExclusions());
99+
spec.getParameters().getExplicitlyConfiguredFolders().set(moduleDirectory.customizedModules.keySet());
100+
spec.getParameters().getDir().set(modulesDirectory);
101+
spec.getParameters().getRequiresBuildFile().set(moduleDirectory.getRequiresBuildFile());
102+
});
103+
104+
for (String projectDir : listProvider.get()) {
105+
Module module = moduleDirectory.addModule(projectDir);
106+
if (!module.getModuleInfoPaths().get().isEmpty()) {
107+
// only auto-include if there is at least one module-info.java
108+
includeModule(module, new File(modulesDirectory, projectDir));
104109
}
105110
}
106111
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright the GradleX team.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.gradlex.javamodule.dependencies.internal.utils;
18+
19+
import org.gradle.api.provider.Property;
20+
import org.gradle.api.provider.SetProperty;
21+
import org.gradle.api.provider.ValueSource;
22+
import org.gradle.api.provider.ValueSourceParameters;
23+
24+
import java.io.File;
25+
import java.io.IOException;
26+
import java.nio.file.Files;
27+
import java.nio.file.Path;
28+
import java.util.List;
29+
import java.util.stream.Collectors;
30+
import java.util.stream.Stream;
31+
32+
public abstract class ValueModuleDirectoryListing implements ValueSource<List<String>, ValueModuleDirectoryListing.Parameter> {
33+
34+
public interface Parameter extends ValueSourceParameters {
35+
Property<File> getDir();
36+
SetProperty<String> getExplicitlyConfiguredFolders();
37+
SetProperty<String> getExclusions();
38+
Property<Boolean> getRequiresBuildFile();
39+
}
40+
41+
@Override
42+
public List<String> obtain() {
43+
Path path = getParameters().getDir().get().toPath();
44+
try (Stream<Path> directoryStream = Files.find(path, 1, (unused, basicFileAttributes) -> basicFileAttributes.isDirectory())) {
45+
return directoryStream
46+
.filter(x -> !getParameters().getExplicitlyConfiguredFolders().get().contains(x.getFileName().toString()))
47+
.filter(x -> getParameters().getExclusions().get().stream().noneMatch(r -> x.getFileName().toString().matches(r)))
48+
.filter(x -> checkBuildFile(x, getParameters()))
49+
.map(x -> x.getFileName().toString())
50+
.sorted()
51+
.collect(Collectors.toList());
52+
53+
} catch (IOException e) {
54+
throw new RuntimeException("Failed to inspect: " + path, e);
55+
}
56+
}
57+
58+
private boolean checkBuildFile(Path x, Parameter parameters) {
59+
if (!parameters.getRequiresBuildFile().get()) {
60+
return true;
61+
}
62+
return Files.isRegularFile(x.resolve("build.gradle.kts")) || Files.isRegularFile(x.resolve("build.gradle"));
63+
}
64+
}

src/test/groovy/org/gradlex/javamodule/dependencies/test/initialization/SettingsPluginTest.groovy

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,13 +90,72 @@ class SettingsPluginTest extends Specification {
9090
result.getOutput().contains("Calculating task graph as no cached configuration is available for tasks: :app:compileJava")
9191

9292
when:
93-
runner.build() // https://github.com/gradlex-org/java-module-dependencies/issues/128
9493
result = runner.build()
9594

9695
then:
9796
result.getOutput().contains("Reusing configuration cache.")
9897
}
9998

99+
def "configurationCacheHitExtraDir"() {
100+
given:
101+
settingsFile << '''
102+
javaModules {
103+
directory(".") { plugin("java-library") }
104+
}
105+
'''
106+
libModuleInfoFile << 'module abc.lib { }'
107+
appModuleInfoFile << '''
108+
module org.gradlex.test.app {
109+
requires abc.lib;
110+
}
111+
'''
112+
113+
def runner = runner(':app:compileJava')
114+
when:
115+
def result = runner.build()
116+
117+
then:
118+
result.getOutput().contains("Calculating task graph as no cached configuration is available for tasks: :app:compileJava")
119+
120+
when:
121+
new File(settingsFile.parentFile, ".thisShallBeIgnored").mkdir()
122+
123+
result = runner.build()
124+
125+
then:
126+
result.getOutput().contains("Reusing configuration cache.")
127+
}
128+
129+
def "configurationCacheHitExtraNotIgnored"() {
130+
given:
131+
settingsFile << '''
132+
javaModules {
133+
directory(".") { plugin("java-library") }
134+
}
135+
'''
136+
libModuleInfoFile << 'module abc.lib { }'
137+
appModuleInfoFile << '''
138+
module org.gradlex.test.app {
139+
requires abc.lib;
140+
}
141+
'''
142+
143+
def runner = runner(':app:compileJava')
144+
when:
145+
def result = runner.build()
146+
147+
then:
148+
result.getOutput().contains("Calculating task graph as no cached configuration is available for tasks: :app:compileJava")
149+
150+
when:
151+
new File(settingsFile.parentFile, "thisShallNotBeIgnored").mkdir()
152+
153+
result = runner.build()
154+
155+
then:
156+
result.getOutput().contains("Calculating task graph as configuration cache cannot be reused because a build logic input of type 'ValueModuleDirectoryListing' has changed.")
157+
}
158+
100159
def "configurationCacheHitIrrelevantChange"() {
101160
given:
102161
settingsFile << '''
@@ -119,7 +178,6 @@ class SettingsPluginTest extends Specification {
119178
result.getOutput().contains("Calculating task graph as no cached configuration is available for tasks: :app:compileJava")
120179

121180
when:
122-
runner.build() // https://github.com/gradlex-org/java-module-dependencies/issues/128
123181
appModuleInfoFile.write('''
124182
module org.gradlex.test.app {
125183
requires abc.lib; //This is a comment and should not break the configurationCache
@@ -153,7 +211,6 @@ class SettingsPluginTest extends Specification {
153211
result.getOutput().contains("Calculating task graph as no cached configuration is available for tasks: :app:compileJava")
154212

155213
when:
156-
runner.build() // https://github.com/gradlex-org/java-module-dependencies/issues/128
157214
appModuleInfoFile.write('''
158215
module org.gradlex.test.app {
159216
//dependency removed; so thats indeed a configuration change

0 commit comments

Comments
 (0)