Skip to content

Commit 73a945b

Browse files
committed
Add ReachabilityMetadataCopyTask to Gradle Plugin
Update the gradle plugin with a new `ReachabilityMetadataCopyTask` class that can be used to copy hints obtained from the metadata repository.
1 parent fda6ab5 commit 73a945b

File tree

8 files changed

+337
-23
lines changed

8 files changed

+337
-23
lines changed

docs/src/docs/asciidoc/gradle-plugin.adoc

+26
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,32 @@ include::../snippets/gradle/groovy/build.gradle[tags=specify-metadata-version-fo
411411
include::../snippets/gradle/kotlin/build.gradle.kts[tags=specify-metadata-version-for-library]
412412
----
413413

414+
=== Including metadata repository files
415+
416+
By default, reachability metadata will be used only when your native image is generated.
417+
In some situations, you may want a copy of the reachability metadata to use directly.
418+
419+
For example, copying the reachability metadata into your jar can be useful when some other process is responsible for converting your jar into a native image.
420+
You might be generating a shaded jar and using a https://paketo.io/[Paketo buildpack] to convert it to a native image.
421+
422+
To download a copy of the metadata into the `build/native-reachability-metadata` directory you can the `collectReachabilityMetadata` task.
423+
Files will be downloaded into `META-INF/native-image/<groupId>/<versionId>` subdirectories.
424+
425+
To include metadata repository inside your jar you can link to the task using the `jar` DSL `from` directive:
426+
427+
.Including metadata repository files
428+
[source, groovy, role="multi-language-sample"]
429+
----
430+
include::../snippets/gradle/groovy/build.gradle[tags=include-metadata]
431+
----
432+
433+
[source, kotlin, role="multi-language-sample"]
434+
----
435+
include::../snippets/gradle/kotlin/build.gradle.kts[tags=include-metadata]
436+
----
437+
438+
For more advanced configurations you can declare a `org.graalvm.buildtools.gradle.tasks.CollectReachabilityMetadata` task and set the appropriate properties.
439+
414440
[[plugin-configurations]]
415441
== Configurations defined by the plugin
416442

docs/src/docs/snippets/gradle/groovy/build.gradle

+6
Original file line numberDiff line numberDiff line change
@@ -207,3 +207,9 @@ graalvmNative {
207207
}
208208
}
209209
// end::specify-metadata-version-for-library[]
210+
211+
// tag::include-metadata[]
212+
tasks.named("jar") {
213+
from collectReachabilityMetadata
214+
}
215+
// end::include-metadata[]

docs/src/docs/snippets/gradle/kotlin/build.gradle.kts

+6
Original file line numberDiff line numberDiff line change
@@ -221,3 +221,9 @@ graalvmNative {
221221
}
222222
}
223223
// end::specify-metadata-version-for-library[]
224+
225+
// tag::include-metadata[]
226+
tasks.named("jar", Jar) {
227+
from(collectReachabilityMetadata)
228+
}
229+
// end::include-metadata[]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* The Universal Permissive License (UPL), Version 1.0
6+
*
7+
* Subject to the condition set forth below, permission is hereby granted to any
8+
* person obtaining a copy of this software, associated documentation and/or
9+
* data (collectively the "Software"), free of charge and under any and all
10+
* copyright rights in the Software, and any and all patent rights owned or
11+
* freely licensable by each licensor hereunder covering either (i) the
12+
* unmodified Software as contributed to or provided by such licensor, or (ii)
13+
* the Larger Works (as defined below), to deal in both
14+
*
15+
* (a) the Software, and
16+
*
17+
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
18+
* one is included with the Software each a "Larger Work" to which the Software
19+
* is contributed by such licensors),
20+
*
21+
* without restriction, including without limitation the rights to copy, create
22+
* derivative works of, display, perform, and distribute the Software and make,
23+
* use, sell, offer for sale, import, export, have made, and have sold the
24+
* Software and the Larger Work(s), and to sublicense the foregoing rights on
25+
* either these or other terms.
26+
*
27+
* This license is subject to the following condition:
28+
*
29+
* The above copyright notice and either this complete permission notice or at a
30+
* minimum a reference to the UPL must be included in all copies or substantial
31+
* portions of the Software.
32+
*
33+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
39+
* SOFTWARE.
40+
*/
41+
42+
package org.graalvm.buildtools.gradle
43+
44+
import org.graalvm.buildtools.gradle.fixtures.AbstractFunctionalTest
45+
import org.gradle.api.logging.LogLevel
46+
import spock.lang.Unroll
47+
48+
class ReachabilityMetadataFunctionalTest extends AbstractFunctionalTest {
49+
50+
def "the application runs when using the official metadata repository"() {
51+
given:
52+
withSample("metadata-repo-integration")
53+
54+
when:
55+
run 'jar', "-D${NativeImagePlugin.CONFIG_REPO_LOGLEVEL}=${LogLevel.LIFECYCLE}"
56+
57+
then:
58+
tasks {
59+
succeeded ':jar', ':reachabilityMetadata'
60+
}
61+
62+
and: "has copied metadata file"
63+
file("build/native-reachability-metadata/META-INF/native-image/com.h2database/h2/2.1.210/resource-config.json").text.trim() == '''{
64+
"bundles": [],
65+
"resources": {
66+
"includes": [
67+
{
68+
"condition": {
69+
"typeReachable": "org.h2.util.Utils"
70+
},
71+
"pattern": "\\\\Qorg/h2/util/data.zip\\\\E"
72+
}
73+
]
74+
}
75+
}'''
76+
}
77+
78+
}

native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/NativeImagePlugin.java

+45-23
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
import org.graalvm.buildtools.gradle.internal.NativeConfigurations;
6060
import org.graalvm.buildtools.gradle.internal.agent.AgentConfigurationFactory;
6161
import org.graalvm.buildtools.gradle.tasks.BuildNativeImageTask;
62+
import org.graalvm.buildtools.gradle.tasks.CollectReachabilityMetadata;
6263
import org.graalvm.buildtools.gradle.tasks.GenerateResourcesConfigFile;
6364
import org.graalvm.buildtools.gradle.tasks.MetadataCopyTask;
6465
import org.graalvm.buildtools.gradle.tasks.NativeRunTask;
@@ -99,6 +100,7 @@
99100
import org.gradle.api.tasks.OutputDirectory;
100101
import org.gradle.api.tasks.SourceSet;
101102
import org.gradle.api.tasks.SourceSetContainer;
103+
import org.gradle.api.tasks.TaskCollection;
102104
import org.gradle.api.tasks.TaskContainer;
103105
import org.gradle.api.tasks.TaskProvider;
104106
import org.gradle.api.tasks.bundling.AbstractArchiveTask;
@@ -271,6 +273,26 @@ private void configureJavaProject(Project project, Provider<NativeImageService>
271273
task.getMergeWithExisting().set(graalExtension.getAgent().getMetadataCopy().getMergeWithExisting());
272274
task.getToolchainDetection().set(graalExtension.getToolchainDetection());
273275
});
276+
277+
project.getTasks().register("collectReachabilityMetadata", CollectReachabilityMetadata.class, task -> {
278+
task.setGroup(LifecycleBasePlugin.BUILD_GROUP);
279+
task.setDescription("Obtains native reachability metdata for the runtime classpath configuration");
280+
});
281+
282+
GraalVMReachabilityMetadataRepositoryExtension metadataRepositoryExtension = reachabilityExtensionOn(graalExtension);
283+
TaskCollection<CollectReachabilityMetadata> reachabilityMetadataCopyTasks = project.getTasks()
284+
.withType(CollectReachabilityMetadata.class);
285+
reachabilityMetadataCopyTasks.configureEach(task -> {
286+
Provider<GraalVMReachabilityMetadataService> reachabilityMetadataService = graalVMReachabilityMetadataService(
287+
project, metadataRepositoryExtension);
288+
task.getMetadataService().set(reachabilityMetadataService);
289+
task.usesService(reachabilityMetadataService);
290+
task.getUri().convention(task.getVersion().map(this::getReachabilityMetadataRepositoryUrlForVersion)
291+
.orElse(metadataRepositoryExtension.getUri()));
292+
task.getExcludedModules().convention(metadataRepositoryExtension.getExcludedModules());
293+
task.getModuleToConfigVersion().convention(metadataRepositoryExtension.getModuleToConfigVersion());
294+
task.getInto().convention(project.getLayout().getBuildDirectory().dir("native-reachability-metadata"));
295+
});
274296
}
275297

276298
private void configureAutomaticTaskCreation(Project project,
@@ -320,14 +342,7 @@ private void configureAutomaticTaskCreation(Project project,
320342

321343
private void configureJvmReachabilityConfigurationDirectories(Project project, GraalVMExtension graalExtension, NativeImageOptions options, SourceSet sourceSet) {
322344
GraalVMReachabilityMetadataRepositoryExtension repositoryExtension = reachabilityExtensionOn(graalExtension);
323-
Provider<GraalVMReachabilityMetadataService> serviceProvider = project.getGradle()
324-
.getSharedServices()
325-
.registerIfAbsent("nativeConfigurationService", GraalVMReachabilityMetadataService.class, spec -> {
326-
LogLevel logLevel = determineLogLevel();
327-
spec.getParameters().getLogLevel().set(logLevel);
328-
spec.getParameters().getUri().set(repositoryExtension.getUri());
329-
spec.getParameters().getCacheDir().set(new File(project.getGradle().getGradleUserHomeDir(), "native-build-tools/repositories"));
330-
});
345+
Provider<GraalVMReachabilityMetadataService> serviceProvider = graalVMReachabilityMetadataService(project, repositoryExtension);
331346
options.getConfigurationFileDirectories().from(repositoryExtension.getEnabled().flatMap(enabled -> {
332347
if (enabled) {
333348
if (repositoryExtension.getUri().isPresent()) {
@@ -358,14 +373,7 @@ private void configureJvmReachabilityConfigurationDirectories(Project project, G
358373

359374
private void configureJvmReachabilityExcludeConfigArgs(Project project, GraalVMExtension graalExtension, NativeImageOptions options, SourceSet sourceSet) {
360375
GraalVMReachabilityMetadataRepositoryExtension repositoryExtension = reachabilityExtensionOn(graalExtension);
361-
Provider<GraalVMReachabilityMetadataService> serviceProvider = project.getGradle()
362-
.getSharedServices()
363-
.registerIfAbsent("nativeConfigurationService", GraalVMReachabilityMetadataService.class, spec -> {
364-
LogLevel logLevel = determineLogLevel();
365-
spec.getParameters().getLogLevel().set(logLevel);
366-
spec.getParameters().getUri().set(repositoryExtension.getUri());
367-
spec.getParameters().getCacheDir().set(new File(project.getGradle().getGradleUserHomeDir(), "native-build-tools/repositories"));
368-
});
376+
Provider<GraalVMReachabilityMetadataService> serviceProvider = graalVMReachabilityMetadataService(project, repositoryExtension);
369377
options.getExcludeConfig().putAll(repositoryExtension.getEnabled().flatMap(enabled -> {
370378
if (enabled) {
371379
if (repositoryExtension.getUri().isPresent()) {
@@ -397,6 +405,18 @@ private void configureJvmReachabilityExcludeConfigArgs(Project project, GraalVME
397405
}));
398406
}
399407

408+
private Provider<GraalVMReachabilityMetadataService> graalVMReachabilityMetadataService(Project project,
409+
GraalVMReachabilityMetadataRepositoryExtension repositoryExtension) {
410+
return project.getGradle()
411+
.getSharedServices()
412+
.registerIfAbsent("nativeConfigurationService", GraalVMReachabilityMetadataService.class, spec -> {
413+
LogLevel logLevel = determineLogLevel();
414+
spec.getParameters().getLogLevel().set(logLevel);
415+
spec.getParameters().getUri().set(repositoryExtension.getUri());
416+
spec.getParameters().getCacheDir().set(new File(project.getGradle().getGradleUserHomeDir(), "native-build-tools/repositories"));
417+
});
418+
}
419+
400420
private static LogLevel determineLogLevel() {
401421
LogLevel logLevel = LogLevel.DEBUG;
402422
String loggingProperty = System.getProperty(CONFIG_REPO_LOGLEVEL);
@@ -475,17 +495,19 @@ private void configureNativeConfigurationRepo(ExtensionAware graalvmNative) {
475495
GraalVMReachabilityMetadataRepositoryExtension configurationRepository = graalvmNative.getExtensions().create("metadataRepository", GraalVMReachabilityMetadataRepositoryExtension.class);
476496
configurationRepository.getEnabled().convention(false);
477497
configurationRepository.getVersion().convention(SharedConstants.METADATA_REPO_DEFAULT_VERSION);
478-
configurationRepository.getUri().convention(configurationRepository.getVersion().map(v -> {
479-
try {
480-
return new URI(String.format(METADATA_REPO_URL_TEMPLATE, v));
481-
} catch (URISyntaxException e) {
482-
throw new RuntimeException(e);
483-
}
484-
}));
498+
configurationRepository.getUri().convention(configurationRepository.getVersion().map(this::getReachabilityMetadataRepositoryUrlForVersion));
485499
configurationRepository.getExcludedModules().convention(Collections.emptySet());
486500
configurationRepository.getModuleToConfigVersion().convention(Collections.emptyMap());
487501
}
488502

503+
private URI getReachabilityMetadataRepositoryUrlForVersion(String version) {
504+
try {
505+
return new URI(String.format(METADATA_REPO_URL_TEMPLATE, version));
506+
} catch (URISyntaxException e) {
507+
throw new RuntimeException(e);
508+
}
509+
}
510+
489511
private TaskProvider<GenerateResourcesConfigFile> registerResourcesConfigTask(Provider<Directory> generatedDir,
490512
NativeImageOptions options,
491513
TaskContainer tasks,

native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/internal/GraalVMReachabilityMetadataService.java

+19
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import org.graalvm.reachability.GraalVMReachabilityMetadataRepository;
4646
import org.graalvm.reachability.Query;
4747
import org.graalvm.reachability.internal.FileSystemRepository;
48+
import org.gradle.api.artifacts.ModuleVersionIdentifier;
4849
import org.gradle.api.file.ArchiveOperations;
4950
import org.gradle.api.file.DirectoryProperty;
5051
import org.gradle.api.file.FileSystemOperations;
@@ -66,6 +67,8 @@
6667
import java.nio.file.Files;
6768
import java.nio.file.Path;
6869
import java.util.Collection;
70+
import java.util.Map;
71+
import java.util.Objects;
6972
import java.util.Set;
7073
import java.util.function.Consumer;
7174
import java.util.function.Supplier;
@@ -208,4 +211,20 @@ public Set<DirectoryConfiguration> findConfigurationsFor(String gavCoordinates)
208211
public Set<DirectoryConfiguration> findConfigurationsFor(Collection<String> modules) {
209212
return repository.findConfigurationsFor(modules);
210213
}
214+
215+
public Set<DirectoryConfiguration> findConfigurationsFor(Set<String> excludedModules, Map<String, String> forcedVersions, ModuleVersionIdentifier moduleVersion) {
216+
Objects.requireNonNull(moduleVersion);
217+
String groupAndArtifact = moduleVersion.getGroup() + ":" + moduleVersion.getName();
218+
return findConfigurationsFor(query -> {
219+
if (!excludedModules.contains(groupAndArtifact)) {
220+
query.forArtifact(artifact -> {
221+
artifact.gav(groupAndArtifact + ":" + moduleVersion.getVersion());
222+
if (forcedVersions.containsKey(groupAndArtifact)) {
223+
artifact.forceConfigVersion(forcedVersions.get(groupAndArtifact));
224+
}
225+
});
226+
}
227+
query.useLatestConfigWhenVersionIsUntested();
228+
});
229+
}
211230
}

0 commit comments

Comments
 (0)