Skip to content

Commit

Permalink
Fix compatibility with Gradle's configuration cache (#339)
Browse files Browse the repository at this point in the history
* Use serializable lambdas

This is an attempt to fix #338

* Fix compatibility with the configuration cache

In the process, this commit removes use of deprecated Gradle APIs
and therefore bumps the minimal version of Gradle to 7.4. The
configuration cache tests only pass on Gradle 7.5+, which is
reasonable given that it's an experimental feature.

* Disable test
  • Loading branch information
melix authored Mar 15, 2023
1 parent 199ce20 commit 5653022
Show file tree
Hide file tree
Showing 25 changed files with 138 additions and 84 deletions.
5 changes: 4 additions & 1 deletion .github/workflows/test-native-gradle-plugin.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ jobs:
strategy:
fail-fast: false
matrix:
gradle-version: ["current", "7.1"]
gradle-version: ["current", "7.4"]
gradle-config-cache-version: ["current", "7.5.1"]
# Following versions are disabled temporarily in order to speed up PR testing
# "7.3.3", "7.2", "7.1", "6.8.3"
graalvm-version: [ latest ] # dev
Expand All @@ -69,6 +70,8 @@ jobs:
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: "❓ Check and test the plugin"
run: ./gradlew :native-gradle-plugin:functionalTest -DgradleVersion=${{ matrix.gradle-version }}
- name: "❓ Check and test the plugin with configuration cache"
run: ./gradlew :native-gradle-plugin:configCacheFunctionalTest -DgradleVersion=${{ matrix.gradle-config-cache-version }}
- name: "📜 Upload functional tests results"
if: always()
uses: actions/upload-artifact@v2
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,12 +82,12 @@ def graalVm = javaToolchains.launcherFor {
def fullFunctionalTest = tasks.register("fullFunctionalTest")

['functionalTest', 'configCacheFunctionalTest'].each { baseName ->
["current", "7.3.3", "7.2", "7.1"].each { gradleVersion ->
["current", "7.4", "7.6.2"].each { gradleVersion ->
String taskName = gradleVersion == 'current' ? baseName : "gradle${gradleVersion}${baseName.capitalize()}"
// Add a task to run the functional tests
def testTask = tasks.register(taskName, Test) {
String effectiveVersion = gradleVersion
def versionProvider = providers.systemProperty('gradleVersion').forUseAtConfigurationTime()
def versionProvider = providers.systemProperty('gradleVersion')
if (effectiveVersion == 'current' && versionProvider.isPresent()) {
effectiveVersion = versionProvider.get()
}
Expand All @@ -99,7 +99,7 @@ def fullFunctionalTest = tasks.register("fullFunctionalTest")
systemProperty('common.repo.url', configurations.functionalTestCommonRepository.incoming.files.files[0])
systemProperty('gradle.test.version', effectiveVersion)
systemProperty('versions.junit', libs.versions.junitJupiter.get())
environment('GRAALVM_HOME', graalVm.forUseAtConfigurationTime().get().metadata.installationPath.asFile.absolutePath)
environment('GRAALVM_HOME', graalVm.get().metadata.installationPath.asFile.absolutePath)
testClassesDirs = sourceSets.functionalTest.output.classesDirs
classpath = sourceSets.functionalTest.runtimeClasspath
javaLauncher.set(graalVm)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ tasks.register("nativeTestCompile", Exec) {
argsProvider.classpath.from(test.classpath)
argsProvider.testIdsDir.set(testIdsDir)
argsProvider.agentOutputDir.set(agentOutput)
argsProvider.discovery.set(providers.systemProperty("testDiscovery").forUseAtConfigurationTime().map(v -> Boolean.valueOf(v)).orElse(false))
argsProvider.discovery.set(providers.systemProperty("testDiscovery").map(v -> Boolean.valueOf(v)).orElse(false))
argumentProviders.add(argsProvider)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ tasks.register("nativeTestCompile", Exec) {
argsProvider.classpath.from(test.classpath)
argsProvider.testIdsDir.set(testIdsDir)
argsProvider.agentOutputDir.set(agentOutput)
argsProvider.discovery.set(providers.systemProperty("testDiscovery").forUseAtConfigurationTime().map(v -> Boolean.valueOf(v)).orElse(false))
argsProvider.discovery.set(providers.systemProperty("testDiscovery").map(v -> Boolean.valueOf(v)).orElse(false))
argumentProviders.add(argsProvider)
}

Expand Down
1 change: 0 additions & 1 deletion docs/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ plugins {
}

version = providers.gradleProperty("publishVersion")
.forUseAtConfigurationTime()
.orElse(libs.versions.nativeBuildTools)
.get()

Expand Down
6 changes: 6 additions & 0 deletions docs/src/docs/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ If you are using alternative build systems, see <<alternative-build-systems.adoc

=== Release 0.9.21

==== Gradle plugin

- Bump minimal version of Gradle to 7.4
- Fix compatibility with Gradle's https://docs.gradle.org/8.0.2/userguide/configuration_cache.html#header[configuration cache] (requires Gradle 7.5+)
- Remove use of deprecated Gradle APIs

==== Maven plugin

- Add a new `native:write-args-file` goal that can be used to write the arguments passed to `native-image` to a file
Expand Down
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,10 @@
package org.graalvm.buildtools.gradle

import org.graalvm.buildtools.gradle.fixtures.AbstractFunctionalTest
import spock.lang.IgnoreIf

class DeprecatedExtensionFunctionalTest extends AbstractFunctionalTest {
@IgnoreIf({ Boolean.getBoolean("config.cache")} )
def "using a deprecated extension issues a warning"() {
given:
buildFile << """
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
package org.graalvm.buildtools.gradle

import org.graalvm.buildtools.gradle.fixtures.AbstractFunctionalTest
import org.gradle.util.GradleVersion
import spock.lang.Ignore
import spock.lang.Issue
import spock.lang.Requires

Expand Down Expand Up @@ -114,22 +114,20 @@ class JavaApplicationFunctionalTest extends AbstractFunctionalTest {
def graalvmHome = System.getenv("GRAALVM_HOME")
graalvmHome != null
})
@Ignore("Need to find another way to test this since toolchains will now always be evaluated early")
def "can override toolchain selection"() {
def nativeApp = file("build/native/nativeCompile/java-application")
boolean dummyToolchain = GradleVersion.version(gradleVersion).compareTo(GradleVersion.version("7.0")) >= 0

given:
withSample("java-application")

if (dummyToolchain) {
buildFile << """graalvmNative.binaries.configureEach {
javaLauncher.set(javaToolchains.launcherFor {
languageVersion.set(JavaLanguageVersion.of(JavaVersion.current().getMajorVersion()))
vendor.set(JvmVendorSpec.matching("non existing vendor"))
})
}"""
}

buildFile << """
}
tasks.withType(org.graalvm.buildtools.gradle.tasks.BuildNativeImageTask).configureEach {
disableToolchainDetection = true
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,12 @@ class JavaApplicationWithTestsFunctionalTest extends AbstractFunctionalTest {

then:
tasks {
upToDate(':test')
if (hasConfigurationCache) {
// because we run with --rerun-tasks when checking with config cache
succeeded(':test')
} else {
upToDate(':test')
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
import org.gradle.api.artifacts.Dependency;
import org.gradle.api.artifacts.ModuleVersionIdentifier;
import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
import org.gradle.api.artifacts.result.ResolutionResult;
import org.gradle.api.artifacts.result.ResolvedComponentResult;
import org.gradle.api.attributes.Attribute;
import org.gradle.api.attributes.AttributeContainer;
Expand Down Expand Up @@ -138,6 +139,7 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static org.graalvm.buildtools.gradle.internal.ConfigurationCacheSupport.serializableFunctionOf;
import static org.graalvm.buildtools.gradle.internal.ConfigurationCacheSupport.serializablePredicateOf;
import static org.graalvm.buildtools.gradle.internal.ConfigurationCacheSupport.serializableSupplierOf;
import static org.graalvm.buildtools.gradle.internal.ConfigurationCacheSupport.serializableTransformerOf;
Expand Down Expand Up @@ -293,7 +295,7 @@ private void configureJavaProject(Project project, Provider<NativeImageService>
project, metadataRepositoryExtension);
task.getMetadataService().set(reachabilityMetadataService);
task.usesService(reachabilityMetadataService);
task.getUri().convention(task.getVersion().map(this::getReachabilityMetadataRepositoryUrlForVersion)
task.getUri().convention(task.getVersion().map(serializableTransformerOf(this::getReachabilityMetadataRepositoryUrlForVersion))
.orElse(metadataRepositoryExtension.getUri()));
task.getExcludedModules().convention(metadataRepositoryExtension.getExcludedModules());
task.getModuleToConfigVersion().convention(metadataRepositoryExtension.getModuleToConfigVersion());
Expand Down Expand Up @@ -327,7 +329,7 @@ private void configureAutomaticTaskCreation(Project project,
tasks.register(runTaskName, NativeRunTask.class, task -> {
task.setGroup(LifecycleBasePlugin.BUILD_GROUP);
task.setDescription("Executes the " + options.getName() + " native binary");
task.getImage().convention(imageBuilder.map(t -> t.getOutputFile().get()));
task.getImage().convention(imageBuilder.flatMap(BuildNativeImageTask::getOutputFile));
task.getRuntimeArgs().convention(options.getRuntimeArgs());
});
configureClasspathJarFor(tasks, options, imageBuilder);
Expand All @@ -338,19 +340,26 @@ private void configureAutomaticTaskCreation(Project project,
tasks,
transitiveProjectArtifacts(project, sourceSet.getRuntimeClasspathConfigurationName()),
deriveTaskName(binaryName, "generate", "ResourcesConfigFile"));
options.getConfigurationFileDirectories().from(generateResourcesConfig.map(t ->
options.getConfigurationFileDirectories().from(generateResourcesConfig.map(serializableTransformerOf(t ->
t.getOutputFile().map(f -> f.getAsFile().getParentFile())
));
)));
configureJvmReachabilityConfigurationDirectories(project, graalExtension, options, sourceSet);
configureJvmReachabilityExcludeConfigArgs(project, graalExtension, options, sourceSet);
});
}

private void configureJvmReachabilityConfigurationDirectories(Project project, GraalVMExtension graalExtension,
NativeImageOptions options, SourceSet sourceSet) {
options.getConfigurationFileDirectories().from(graalVMReachabilityQuery(project, graalExtension, sourceSet,
configuration -> true, this::getConfigurationDirectory,
Collectors.toList()));
private void configureJvmReachabilityConfigurationDirectories(Project project,
GraalVMExtension graalExtension,
NativeImageOptions options,
SourceSet sourceSet) {
options.getConfigurationFileDirectories().from(
graalVMReachabilityQuery(project,
graalExtension,
sourceSet,
configuration -> true,
this::getConfigurationDirectory,
Collectors.toList())
);
}

private File getConfigurationDirectory(ModuleVersionIdentifier moduleVersion,
Expand All @@ -360,25 +369,32 @@ private File getConfigurationDirectory(ModuleVersionIdentifier moduleVersion,

private <T, A, R> Provider<R> graalVMReachabilityQuery(Project project, GraalVMExtension graalExtension,
SourceSet sourceSet, Predicate<DirectoryConfiguration> filter,
BiFunction<ModuleVersionIdentifier, DirectoryConfiguration, T> mapper, Collector<T, A, R> collector) {
BiFunction<ModuleVersionIdentifier, DirectoryConfiguration, T> mapper,
Collector<T, A, R> collector) {
GraalVMReachabilityMetadataRepositoryExtension extension = reachabilityExtensionOn(graalExtension);
return extension.getEnabled().flatMap(enabled -> {
Provider<GraalVMReachabilityMetadataService> metadataServiceProvider = graalVMReachabilityMetadataService(project, extension);
ResolutionResult resolutionResult = project.getConfigurations()
.getByName(sourceSet.getRuntimeClasspathConfigurationName())
.getIncoming()
.getResolutionResult();
return extension.getEnabled().flatMap(serializableTransformerOf(enabled -> {
if (enabled && extension.getUri().isPresent()) {
Configuration classpath = project.getConfigurations().getByName(sourceSet.getRuntimeClasspathConfigurationName());
Set<String> excludedModules = extension.getExcludedModules().getOrElse(Collections.emptySet());
Map<String, String> forcedVersions = extension.getModuleToConfigVersion().getOrElse(Collections.emptyMap());
return graalVMReachabilityMetadataService(project, extension).map(service -> {
Set<ResolvedComponentResult> components = classpath.getIncoming().getResolutionResult().getAllComponents();
Stream<T> mapped = components.stream().flatMap(component -> {
return metadataServiceProvider.map(serializableTransformerOf(service -> {
Set<ResolvedComponentResult> components = resolutionResult.getAllComponents();
Stream<T> mapped = components.stream().flatMap(serializableFunctionOf(component -> {
ModuleVersionIdentifier moduleVersion = component.getModuleVersion();
Set<DirectoryConfiguration> configurations = service.findConfigurationsFor(excludedModules, forcedVersions, moduleVersion);
return configurations.stream().filter(filter).map(configuration -> mapper.apply(moduleVersion, configuration));
});
return configurations.stream()
.filter(filter)
.map(serializableFunctionOf(configuration -> mapper.apply(moduleVersion, configuration)));
}));
return mapped.collect(collector);
});
}));
}
return project.getProviders().provider(() -> Stream.<T>empty().collect(collector));
});
}));
}

private Provider<GraalVMReachabilityMetadataService> graalVMReachabilityMetadataService(Project project,
Expand All @@ -389,7 +405,7 @@ private Provider<GraalVMReachabilityMetadataService> graalVMReachabilityMetadata
LogLevel logLevel = determineLogLevel();
spec.getMaxParallelUsages().set(1);
spec.getParameters().getLogLevel().set(logLevel);
spec.getParameters().getUri().set(repositoryExtension.getUri().map(configuredUri -> computeMetadataRepositoryUri(project, repositoryExtension, configuredUri, GraalVMLogger.of(project.getLogger()))));
spec.getParameters().getUri().set(repositoryExtension.getUri().map(serializableTransformerOf(configuredUri -> computeMetadataRepositoryUri(project, repositoryExtension, configuredUri, GraalVMLogger.of(project.getLogger())))));
spec.getParameters().getCacheDir().set(
new File(project.getGradle().getGradleUserHomeDir(), "native-build-tools/repositories"));
});
Expand Down Expand Up @@ -426,9 +442,14 @@ private static URI computeMetadataRepositoryUri(Project project,
}

private void configureJvmReachabilityExcludeConfigArgs(Project project, GraalVMExtension graalExtension, NativeImageOptions options, SourceSet sourceSet) {
options.getExcludeConfig().putAll(graalVMReachabilityQuery(project, graalExtension, sourceSet,
DirectoryConfiguration::isOverride, this::getExclusionConfig,
Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)));
options.getExcludeConfig().putAll(
graalVMReachabilityQuery(project,
graalExtension,
sourceSet,
DirectoryConfiguration::isOverride,
this::getExclusionConfig,
Collectors.<Map.Entry<String, List<String>>, String, List<String>>toMap(Map.Entry::getKey, Map.Entry::getValue))
);
GraalVMReachabilityMetadataRepositoryExtension repositoryExtension = reachabilityExtensionOn(graalExtension);
graalVMReachabilityMetadataService(project, repositoryExtension);
}
Expand Down Expand Up @@ -517,7 +538,7 @@ private void configureNativeConfigurationRepo(ExtensionAware graalvmNative) {
GraalVMReachabilityMetadataRepositoryExtension configurationRepository = graalvmNative.getExtensions().create("metadataRepository", GraalVMReachabilityMetadataRepositoryExtension.class);
configurationRepository.getEnabled().convention(false);
configurationRepository.getVersion().convention(VersionInfo.METADATA_REPO_VERSION);
configurationRepository.getUri().convention(configurationRepository.getVersion().map(this::getReachabilityMetadataRepositoryUrlForVersion));
configurationRepository.getUri().convention(configurationRepository.getVersion().map(serializableTransformerOf(this::getReachabilityMetadataRepositoryUrlForVersion)));
configurationRepository.getExcludedModules().convention(Collections.emptySet());
configurationRepository.getModuleToConfigVersion().convention(Collections.emptyMap());
}
Expand Down Expand Up @@ -612,7 +633,6 @@ public void registerTestBinary(Project project,
private static Provider<String> agentProperty(Project project, AgentOptions options) {
return project.getProviders()
.gradleProperty(AGENT_PROPERTY)
.forUseAtConfigurationTime()
.map(serializableTransformerOf(v -> {
if (!v.isEmpty()) {
return v;
Expand Down Expand Up @@ -714,7 +734,7 @@ private static void addExcludeConfigArg(List<String> args, Path jarPath, List<St
}

private static void setupExtensionConfigExcludes(NativeImageOptions options, NativeConfigurations configs) {
options.getExcludeConfigArgs().set(options.getExcludeConfig().map(excludedConfig -> {
options.getExcludeConfigArgs().set(options.getExcludeConfig().map(serializableTransformerOf(excludedConfig -> {
List<String> args = new ArrayList<>();
excludedConfig.forEach((entry, listOfResourcePatterns) -> {
if (entry instanceof File) {
Expand Down Expand Up @@ -744,7 +764,7 @@ private static void setupExtensionConfigExcludes(NativeImageOptions options, Nat
}
});
return args;
}));
})));
}

private static List<String> agentSessionDirectories(Directory outputDirectory) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,7 @@ public static Provider<NativeImageService> registerOn(Project project) {

private static Provider<Integer> maxParallelUsagesOf(ProviderFactory providers) {
return providers.gradleProperty(MAX_PARALLEL_SYSTEM_PROPERTY)
.forUseAtConfigurationTime()
.orElse(providers.environmentVariable(MAX_PARALLEL_ENV_VAR).forUseAtConfigurationTime())
.orElse(providers.environmentVariable(MAX_PARALLEL_ENV_VAR))
.map(Integer::parseInt)
.orElse(1 + Runtime.getRuntime().availableProcessors() / 16);
}
Expand Down
Loading

0 comments on commit 5653022

Please sign in to comment.