Skip to content

Commit 46751a3

Browse files
committed
Refactoring guides according to demos
1 parent 67d287e commit 46751a3

File tree

6 files changed

+566
-289
lines changed

6 files changed

+566
-289
lines changed

docs/src/docs/asciidoc/end-to-end-gradle-guide.adoc

+182-90
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ The GraalVM team
66
This guide leads you through the process of integrating Native Gradle Plugin with your project.
77
It starts from adding the plugin to your project, goes through some of the main functionalities that you may use (like collecting the metadata),
88
all the way to some diagnostics tools that you can use to analyse native executables you have made.
9+
You can see an example of tests on which we tested whole guide <<example,here>>.
910

1011
In case you only want to see how a simple application works in practise, you can check <<quickstart-gradle-plugin.adoc#,our demo>>.
1112

@@ -178,10 +179,10 @@ Inside these blocks you can pass the following options:
178179
- `jvmArgs` - Passes the given argument directly to the JVM running the native image builder
179180
- `useFatJar` - Instead of passing each jar individually, builds a fat jar
180181

181-
You can also pass **build-time** and **run-time** options to the Native Image using:
182+
You can also pass **build-time** and **run-time** arguments:
182183

183-
- `buildArgs.add('<buildArg>')` - You can find more about possible build arguments https://www.graalvm.org/latest/reference-manual/native-image/overview/BuildConfiguration/[here]
184-
- `runtimeArgs.add('<runtimeArg>')` - You can find more about possible runtime arguments https://www.graalvm.org/latest/reference-manual/native-image/overview/Options/[here]
184+
- `buildArgs.add('<buildArg>')` - You can find more about possible build arguments https://www.graalvm.org/latest/reference-manual/native-image/overview/Options/[here] and https://www.graalvm.org/latest/reference-manual/native-image/overview/BuildConfiguration/[here]
185+
- `runtimeArgs.add('<runtimeArg>')` - Runtime arguments consumed by your application
185186

186187
Here is an example of additional options usage:
187188

@@ -258,9 +259,9 @@ The easiest way how this information can be passed to the Native Image is throug
258259
a single `reachability-metadata.json` file (for newer GraalVM versions) or multiple json files (`reflect-config.json`, `resource-config.json`, `proxy-config.json`, `serialization-config.json`, `jni-config.json`).
259260
To learn more about metadata that Native Image consumes, https://www.graalvm.org/latest/reference-manual/native-image/metadata/[see this].
260261

261-
For example, if you run the test that tries to load resource `resource.txt`, and you don't have entry for that resource in the metadata config file, the resource can't be loaded (will be null).
262+
For example, if you run the test that uses the reflection, and you don't have entry for that reflection call in the metadata config file, your test will fail.
262263

263-
To make your test/application work while using resources (like in this example) or other metadata, you should either generate metadata configurations or write them manually.
264+
To make your test/application work while using reflection (like in this example) or other metadata, you should either generate metadata configurations or write them manually.
264265
To generate metadata automatically, you can run your tests (or the main application) with the Native Image Agent, that will collect all the metadata your test/application require.
265266
To enable the agent (through Native Gradle Plugin) you should either:
266267

@@ -291,8 +292,6 @@ agent {
291292
[.underline]#To generate the metadata file(s) for your `application` just run:#
292293

293294
- `./gradlew run` if you added the agent block to the configuration or `./gradlew -Pagent run` if you didn't. This command runs on JVM with native-image-agent and collects the metadata.
294-
- `./gradlew nativeRun` if you added the agent block to the configuration or `./gradlew -Pagent nativeRun` if you didn't. This command runs on JVM with the native-image agent, collects the metadata and uses it for testing on native-image.
295-
296295

297296
[WARNING]
298297
====
@@ -380,7 +379,7 @@ Besides that, you can configure `metadataCopy` task through the command line as
380379

381380
Here is an example of a valid `metadataCopy` usage:
382381

383-
[source,bash,subs="verbatim,attributes"]
382+
[source,bash,subs="verbatim,attributes", role="multi-language-sample"]
384383
----
385384
./gradlew metadataCopy --task test --dir resources/META-INF/native-image/org.example
386385
----
@@ -414,9 +413,9 @@ First thing that you can configure is the agent mode.
414413
There are three possible agent modes:
415414

416415
* `standard` - only generates metadata without any special processing (this is the default mode). No additional options available.
417-
* `conditional` - entries of the generated metadata will be included in the Native Image only if the condition in the entry is satisfied. Consumes following additional options:
416+
* `conditional` - entries of the generated metadata will be included in the Native Image only if the condition in the entry is satisfied. Consumes following options:
418417
** `userCodeFilterPath` - specifies a filter file used to classify classes as user application classes. Generated conditions will only reference these classes See <<agent-filter-file, the following section>>
419-
** `extraFilterPath` - extra filter used to further filter the collected metadata. See <<agent-filter-file, the following section>>
418+
** `extraFilterPath` - (optional) extra filter used to further filter the collected metadata. See <<agent-filter-file, the following section>>
420419
* `direct` - in this mode user configures the agent completely manually by adding all options with:
421420
** `options.add("<option>")`
422421

@@ -581,7 +580,6 @@ These filter files that agent consumes have the following structure:
581580
The process how you can pass the config files to the agent is described in the <<additional-agent-options,previous section>>.
582581

583582
We can see on the example how different filter files affect generated metadata:
584-
**Note that the following example was created with GraalVM 21 and that the format of the generated metadata can vary from version to version.**
585583

586584
We are starting with the simple filter file:
587585

@@ -594,104 +592,143 @@ We are starting with the simple filter file:
594592
}
595593
----
596594

597-
This filter file will instruct the agent to include everything and therefore, you will get a massive config files.
598-
For example this is how `resource-config.json` looks like:
595+
This filter file will instruct the agent to include everything and therefore, you will get a massive config file.
596+
For example this is how `reachability-metadata.json` looks like:
599597

600598
[source,json,subs="verbatim,attributes", role="multi-language-sample"]
601599
----
602-
{{
603-
"resources":{
604-
"includes":[{
605-
"condition":{"typeReachable":"jdk.internal.logger.BootstrapLogger$DetectBackend$1"},
606-
"pattern":"\\QMETA-INF/services/java.lang.System$LoggerFinder\\E"
607-
}, {
608-
"condition":{"typeReachable":"jdk.internal.logger.LoggerFinderLoader"},
609-
"pattern":"\\QMETA-INF/services/java.lang.System$LoggerFinder\\E"
610-
}, {
611-
"condition":{"typeReachable":"java.nio.channels.spi.SelectorProvider$Holder"},
612-
"pattern":"\\QMETA-INF/services/java.nio.channels.spi.SelectorProvider\\E"
613-
}, {
614-
"condition":{"typeReachable":"java.time.zone.ZoneRulesProvider"},
615-
"pattern":"\\QMETA-INF/services/java.time.zone.ZoneRulesProvider\\E"
616-
}, {
617-
"condition":{"typeReachable":"org.junit.platform.launcher.core.LauncherFactory"},
618-
"pattern":"\\QMETA-INF/services/org.junit.platform.engine.TestEngine\\E"
619-
}, {
620-
"condition":{"typeReachable":"org.junit.platform.launcher.core.LauncherFactory"},
621-
"pattern":"\\QMETA-INF/services/org.junit.platform.launcher.LauncherDiscoveryListener\\E"
622-
}, {
623-
"condition":{"typeReachable":"org.junit.platform.launcher.core.LauncherFactory"},
624-
"pattern":"\\QMETA-INF/services/org.junit.platform.launcher.LauncherSessionListener\\E"
625-
}, {
626-
"condition":{"typeReachable":"org.junit.platform.launcher.core.LauncherFactory"},
627-
"pattern":"\\QMETA-INF/services/org.junit.platform.launcher.PostDiscoveryFilter\\E"
628-
}, {
629-
"condition":{"typeReachable":"java.util.Iterator"},
630-
"pattern":"\\QMETA-INF/services/org.junit.platform.launcher.TestExecutionListener\\E"
631-
}, {
632-
"condition":{"typeReachable":"org.junit.platform.launcher.core.LauncherConfigurationParameters"},
633-
"pattern":"\\Qjunit-platform.properties\\E"
634-
}, {
635-
"condition":{"typeReachable":"org.slf4j.LoggerFactory"},
636-
"pattern":"\\Qorg/slf4j/impl/StaticLoggerBinder.class\\E"
637-
}, {
638-
"condition":{"typeReachable":"worker.org.gradle.internal.classloader.FilteringClassLoader"},
639-
"pattern":"\\Qorg/slf4j/impl/StaticLoggerBinder.class\\E"
640-
}, {
641-
"condition":{"typeReachable":"java.lang.ClassLoader"},
642-
"pattern":"\\Qresource.txt\\E"
643-
}]},
644-
"bundles":[]
645-
}
646-
----
647-
648-
As you can see, there are lots of resources that you may don't want.
600+
{
601+
{
602+
"reflection": [
603+
{
604+
"condition": {
605+
"typeReached": "java.io.ObjectInputStream"
606+
},
607+
"type": "[Ljava.lang.Object;"
608+
},
609+
{
610+
"condition": {
611+
"typeReached": "java.io.ObjectInputStream"
612+
},
613+
"type": "java.util.LinkedHashSet"
614+
},
615+
{
616+
"condition": {
617+
"typeReached": "org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor"
618+
},
619+
"type": "org.example.NativeTests"
620+
},
621+
{
622+
"condition": {
623+
"typeReached": "org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor"
624+
},
625+
"type": "org.example.NativeTests",
626+
"allDeclaredFields": true
627+
},
628+
{
629+
"condition": {
630+
"typeReached": "org.junit.jupiter.engine.descriptor.ExtensionUtils"
631+
},
632+
"type": "org.example.NativeTests"
633+
},
634+
...
635+
],
636+
"resources": [
637+
{
638+
"condition": {
639+
"typeReached": "org.junit.platform.launcher.core.LauncherFactory"
640+
},
641+
"glob": "META-INF/services/org.junit.platform.engine.TestEngine"
642+
},
643+
{
644+
"condition": {
645+
"typeReached": "java.lang.ClassLoader"
646+
},
647+
"glob": "TestResource.txt"
648+
},
649+
...
650+
],
651+
"bundles": [],
652+
"jni": [
653+
{
654+
"condition": {
655+
"typeReached": "java.net.InetAddress"
656+
},
657+
"type": "java.lang.Boolean",
658+
"methods": [
659+
{
660+
"name": "getBoolean",
661+
"parameterTypes": [
662+
"java.lang.String"
663+
]
664+
}
665+
]
666+
}
667+
]
668+
}
669+
----
670+
671+
As you can see, there are lots of entries that you may don't want.
649672
To reduce the amount of generated metadata, we will use the following `user-code-filter.json`:
650673

651674
[source,json,subs="verbatim,attributes", role="multi-language-sample"]
652675
----
653676
{
654677
"rules": [
655678
{"includeClasses": "**"},
656-
{"excludeClasses": "java.time.zone.**"},
657-
{"excludeClasses": "org.junit.platform..**"}
679+
{"excludeClasses": "org.junit.**"},
680+
{"excludeClasses": "org.gradle.**"},
681+
{"excludeClasses": "worker.org.gradle.**"},
682+
{"excludeClasses": "org.slf4j.**"},
683+
{"excludeClasses": "java.**"}
658684
]
659685
}
660686
----
661687

662-
After we regenerate the metadata with the new filter, `resource-config.json` generated on the same example as above will look like this:
688+
[WARNING]
689+
====
690+
Be careful to remove only redundant metadata and keep metadata necessary for your project.
691+
====
692+
693+
After we regenerate the metadata with the new filter, `reachability-metadata.json` generated on the same example as above will look like this:
663694

664695
[source,json,subs="verbatim,attributes", role="multi-language-sample"]
665696
----
666697
{
667-
"resources":{
668-
"includes":[{
669-
"condition":{"typeReachable":"jdk.internal.logger.BootstrapLogger$DetectBackend$1"},
670-
"pattern":"\\QMETA-INF/services/java.lang.System$LoggerFinder\\E"
671-
}, {
672-
"condition":{"typeReachable":"jdk.internal.logger.LoggerFinderLoader"},
673-
"pattern":"\\QMETA-INF/services/java.lang.System$LoggerFinder\\E"
674-
}, {
675-
"condition":{"typeReachable":"java.nio.channels.spi.SelectorProvider$Holder"},
676-
"pattern":"\\QMETA-INF/services/java.nio.channels.spi.SelectorProvider\\E"
677-
}, {
678-
"condition":{"typeReachable":"java.util.Iterator"},
679-
"pattern":"\\QMETA-INF/services/org.junit.platform.launcher.TestExecutionListener\\E"
680-
}, {
681-
"condition":{"typeReachable":"org.slf4j.LoggerFactory"},
682-
"pattern":"\\Qorg/slf4j/impl/StaticLoggerBinder.class\\E"
683-
}, {
684-
"condition":{"typeReachable":"worker.org.gradle.internal.classloader.FilteringClassLoader"},
685-
"pattern":"\\Qorg/slf4j/impl/StaticLoggerBinder.class\\E"
686-
}, {
687-
"condition":{"typeReachable":"java.lang.ClassLoader"},
688-
"pattern":"\\Qresource.txt\\E"
689-
}]},
690-
"bundles":[]
698+
"reflection": [
699+
{
700+
"condition": {
701+
"typeReached": "org.example.NativeTests"
702+
},
703+
"type": "org.example.NativeTests$Person",
704+
"allDeclaredFields": true
705+
},
706+
{
707+
"condition": {
708+
"typeReached": "sun.security.jca.GetInstance"
709+
},
710+
"type": "sun.security.provider.SHA",
711+
"methods": [
712+
{
713+
"name": "<init>",
714+
"parameterTypes": []
715+
}
716+
]
717+
}
718+
],
719+
"resources": [
720+
{
721+
"condition": {
722+
"typeReached": "org.example.NativeTests"
723+
},
724+
"glob": "TestResource.txt"
725+
}
726+
],
727+
"bundles": []
691728
}
692729
----
693730

694-
As you can see there are no more entries that contain classes from `org.junit.platform.launcher` (as their condition) for example.
731+
As you can see there are no more entries that contain classes from `org.junit` (as their condition) for example.
695732

696733

697734
[[maintain-generated-metadata]]
@@ -732,12 +769,14 @@ In some cases, when you want to maintain multiple projects that share common met
732769
https://github.com/oracle/graalvm-reachability-metadata/blob/master/CONTRIBUTING.md[Contributing to the repository] should be simple:
733770

734771
- Clone repository locally:
772+
735773
[source,bash,subs="verbatim,attributes", role="multi-language-sample"]
736774
----
737775
git clone [email protected]:oracle/graalvm-reachability-metadata.git
738776
----
739777

740778
- generate metadata and test stubs (replace with the GAV coordinates of library you are providing metadata for):
779+
741780
[source,bash,subs="verbatim,attributes", role="multi-language-sample"]
742781
----
743782
./gradlew scaffold --coordinates com.example:my-library:1.0.0
@@ -769,11 +808,64 @@ When the Native Image build is completed, you will find a path to the generated
769808
```
770809
------------------------------------------------------------------------------------
771810
Build artifacts:
772-
/build/native/nativeCompile/main (executable)
773-
/build/native/nativeCompile/main-build-report.html (build_info)
811+
/tmp/build/native/nativeCompile/main (executable)
812+
/tmp/build/native/nativeCompile/main-build-report.html (build_info)
774813
====================================================================================
775814
```
776815
You can read more about build report features https://www.graalvm.org/latest/reference-manual/native-image/overview/build-report/[here].
777816
778817
[NOTE]
779818
Note that Build Report features vary depending on a GraalVM version you use.
819+
820+
[[example]]
821+
== Example
822+
823+
Whole guide has been tested on the following test class:
824+
825+
[source,java,subs="verbatim,attributes", role="multi-language-sample"]
826+
----
827+
import org.junit.jupiter.api.Test;
828+
829+
import java.io.BufferedReader;
830+
import java.io.IOException;
831+
import java.io.InputStream;
832+
import java.io.InputStreamReader;
833+
import java.lang.reflect.Field;
834+
import java.util.Arrays;
835+
import java.util.List;
836+
837+
import static org.junit.jupiter.api.Assertions.assertTrue;
838+
839+
public class NativeTests {
840+
841+
private static final List<String> resources = List.of("/TestResource.txt");
842+
843+
@Test
844+
public void resourceTest() {
845+
try (InputStream is = NativeTests.class.getResourceAsStream(resources.get(0))) {
846+
if (is != null) {
847+
var reader = new BufferedReader(new InputStreamReader(is));
848+
reader.lines().forEach(System.out::println);
849+
} else {
850+
throw new IOException("Cannot read content of: " + resources.get(0));
851+
}
852+
} catch (IOException e) {
853+
throw new RuntimeException(e);
854+
}
855+
}
856+
857+
private static class Person {
858+
private String name;
859+
private String surname;
860+
}
861+
862+
@Test
863+
public void reflectionTest() {
864+
Object person = new Person();
865+
Field[] fields = person.getClass().getDeclaredFields();
866+
List<String> actualFieldNames = Arrays.stream(fields).map(Field::getName).toList();
867+
868+
assertTrue(actualFieldNames.containsAll(Arrays.asList("name", "surname")));
869+
}
870+
}
871+
----

0 commit comments

Comments
 (0)