Skip to content

Commit 8742639

Browse files
committed
Apply test reachability metadata to dedicated layers
1 parent e18b29e commit 8742639

2 files changed

Lines changed: 94 additions & 6 deletions

File tree

docs/tck.md

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -104,12 +104,17 @@ test classes. The application invocation uses the coordinate's real unique-ID
104104
files to discover and register those classes. The written test code therefore
105105
remains exclusively in the final application layer.
106106
The base build uses the same resolved Native Image configuration directories as
107-
the final test image, including JUnit metadata and generated test resources, so
108-
`typeReached` and class-initialization state remain stable across the layer
109-
boundary. Gradle tracks the coordinate, Native Image version and arguments,
110-
configuration files, and complete base-analysis classpath, rebuilding the layer
111-
when an input changes. The final test image retains the complete
112-
standalone test runtime classpath while using the coordinate-specific layer.
107+
the final test image. For both builds, it stages test-scoped
108+
`reachability-metadata.json` without conditions because LayerCreate does not
109+
preserve runtime `typeReached` tracking for selected JAR types. The normal test
110+
lane still validates the original conditions; the dedicated lane applies the
111+
same metadata unconditionally in both analyses to keep class-initialization
112+
policy stable without including test classes or unrelated test resources in the
113+
base layer.
114+
Gradle rebuilds the layer when its coordinate, Native Image settings,
115+
configuration, or base-analysis classpath changes. The final test image retains
116+
the complete standalone test runtime classpath while using the
117+
coordinate-specific layer.
113118
CI supplies `-Ptck.layered.deleteDedicatedLayerAfterTest=true` to delete each
114119
large coordinate layer after its test; local runs retain layers for reuse unless
115120
they explicitly request the same cleanup behavior.

tests/tck-build-logic/src/main/groovy/org.graalvm.internal.tck.gradle

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
* You should have received a copy of the CC0 legalcode along with this
55
* work. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
66
*/
7+
import groovy.json.JsonOutput
78
import groovy.json.JsonSlurper
89
import org.graalvm.internal.tck.utils.DynamicAccessUtils
910
import org.graalvm.internal.tck.utils.BaseLayerUtils
@@ -398,6 +399,17 @@ Provider<Directory> dedicatedLayerJUnitTestListDirectory = layout.buildDirectory
398399
"native/generated/dedicatedLayerJUnitTestList")
399400
Provider<Directory> dedicatedLayerConfigurationDirectory = layout.buildDirectory.dir(
400401
"native/generated/dedicatedLayerConfiguration")
402+
Provider<Directory> dedicatedLayerReachabilityMetadataDirectory = layout.buildDirectory.dir(
403+
"native/generated/dedicatedLayerReachabilityMetadata")
404+
405+
if (dedicatedLayer) {
406+
tasks.named("processTestResources").configure { task ->
407+
task.exclude("META-INF/native-image/**/reachability-metadata.json")
408+
}
409+
tasks.named("generateTestResourcesConfigFile").configure { task ->
410+
task.dependsOn(tasks.named("processTestResources"))
411+
}
412+
}
401413

402414
tasks.register("generateDedicatedLayerJUnitTestList") { task ->
403415
task.setDescription("Generates engine-root JUnit selectors for the dedicated layer")
@@ -419,10 +431,70 @@ tasks.register("generateDedicatedLayerJUnitTestList") { task ->
419431
}
420432
}
421433

434+
Closure<List<File>> resolveTestReachabilityMetadataFiles = {
435+
return sourceSets.test.resources.srcDirs.collectMany { File resourceDirectory ->
436+
fileTree(resourceDirectory) {
437+
include("META-INF/native-image/**/reachability-metadata.json")
438+
}.files
439+
}.unique().sort { File file -> file.absolutePath }
440+
}
441+
442+
tasks.register("prepareDedicatedLayerReachabilityMetadata") { task ->
443+
task.setDescription("Prepares test-scoped reachability metadata for dedicated-layer builds")
444+
task.setGroup("build")
445+
task.inputs.files(providers.provider { resolveTestReachabilityMetadataFiles() })
446+
.withPropertyName("testReachabilityMetadata")
447+
.withPathSensitivity(PathSensitivity.RELATIVE)
448+
task.outputs.dir(dedicatedLayerReachabilityMetadataDirectory)
449+
task.doLast {
450+
File outputDirectory = dedicatedLayerReachabilityMetadataDirectory.get().asFile
451+
delete(outputDirectory)
452+
if (!outputDirectory.mkdirs()) {
453+
throw new GradleException("Failed to create dedicated-layer reachability metadata directory: " +
454+
outputDirectory.absolutePath)
455+
}
456+
resolveTestReachabilityMetadataFiles().eachWithIndex { File metadataFile, int index ->
457+
File metadataDirectory = new File(outputDirectory, "entry-${index}")
458+
if (!metadataDirectory.mkdirs()) {
459+
throw new GradleException("Failed to create dedicated-layer metadata entry: " +
460+
metadataDirectory.absolutePath)
461+
}
462+
Closure<Object> withoutConditions
463+
withoutConditions = { Object value ->
464+
if (value instanceof Map) {
465+
Map<String, Object> result = new LinkedHashMap<>()
466+
value.each { Object key, Object nestedValue ->
467+
if (key != "condition") {
468+
result.put(key.toString(), withoutConditions(nestedValue))
469+
}
470+
}
471+
return result
472+
}
473+
if (value instanceof List) {
474+
return value.collect { Object nestedValue -> withoutConditions(nestedValue) }
475+
}
476+
return value
477+
}
478+
Object metadata = new JsonSlurper().parse(metadataFile)
479+
String json = JsonOutput.prettyPrint(JsonOutput.toJson(withoutConditions(metadata)))
480+
new File(metadataDirectory, "reachability-metadata.json").text =
481+
json + System.lineSeparator()
482+
}
483+
}
484+
}
485+
486+
Closure<List<File>> resolveDedicatedLayerReachabilityMetadataDirectories = {
487+
File outputDirectory = dedicatedLayerReachabilityMetadataDirectory.get().asFile
488+
return resolveTestReachabilityMetadataFiles().indices.collect { int index ->
489+
new File(outputDirectory, "entry-${index}")
490+
}
491+
}
492+
422493
Closure<List<File>> resolveNativeTestConfigurationDirectories = {
423494
Set<File> directories = new LinkedHashSet<>(nativeTestCompileTask.get()
424495
.options.get().configurationFileDirectories.files)
425496
directories.add(coordinateMetadataDirectory)
497+
directories.addAll(resolveDedicatedLayerReachabilityMetadataDirectories())
426498
return directories.findAll { File directory -> directory.isDirectory() }
427499
.toList()
428500
.sort { File directory -> directory.absolutePath }
@@ -432,6 +504,10 @@ tasks.register("prepareDedicatedLayerConfiguration") { task ->
432504
task.setDescription("Prepares Native Image configuration for the dedicated layer")
433505
task.setGroup("build")
434506
task.dependsOn(tasks.named("generateTestResourcesConfigFile"))
507+
task.dependsOn(tasks.named("prepareDedicatedLayerReachabilityMetadata"))
508+
task.inputs.dir(dedicatedLayerReachabilityMetadataDirectory)
509+
.withPropertyName("dedicatedLayerReachabilityMetadata")
510+
.withPathSensitivity(PathSensitivity.RELATIVE)
435511
task.outputs.dir(dedicatedLayerConfigurationDirectory)
436512
task.outputs.upToDateWhen { false }
437513
task.doLast {
@@ -812,6 +888,13 @@ graalvmNative {
812888
buildArgs.addAll(nativeTraceBuildArgs)
813889
if (dedicatedLayer) {
814890
buildArgs.add(coordinateMetadataArgument)
891+
buildArgs.addAll(providers.provider {
892+
List<File> metadataDirectories = resolveDedicatedLayerReachabilityMetadataDirectories()
893+
return metadataDirectories.isEmpty() ? [] : [
894+
"-H:ConfigurationFileDirectories=" +
895+
metadataDirectories.collect { File file -> file.absolutePath }.join(",")
896+
]
897+
})
815898
}
816899
if (layeredNativeImage && !dedicatedLayer) {
817900
buildArgs.addAll([

0 commit comments

Comments
 (0)