Skip to content

Commit 7a0b6a8

Browse files
committed
Introduce -H:BuildArtifactsJSON option.
and deprecate `.build_artifacts.txt` files.
1 parent 62a5cde commit 7a0b6a8

File tree

10 files changed

+238
-46
lines changed

10 files changed

+238
-46
lines changed
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
{
2+
"$id": "https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/assets/build-artifacts-schema-v0.9.0.json",
3+
"$schema": "https://json-schema.org/draft/2019-09/schema",
4+
"default": {},
5+
"examples": [
6+
{
7+
"build_info": [
8+
"file:///home/janedoe/helloworld/build-output.json"
9+
],
10+
"debug_info": [
11+
"file:///home/janedoe/helloworld/helloworld",
12+
"file:///home/janedoe/helloworld/sources"
13+
],
14+
"executable": [
15+
"file:///home/janedoe/helloworld/helloworld"
16+
]
17+
}
18+
],
19+
"properties": {
20+
"build_info": {
21+
"default": [],
22+
"items": {
23+
"format": "uri",
24+
"title": "File or directory",
25+
"type": "string"
26+
},
27+
"title": "Build information produced by Native Image",
28+
"type": "array"
29+
},
30+
"c_header": {
31+
"default": [],
32+
"items": {
33+
"format": "uri",
34+
"title": "File or directory",
35+
"type": "string"
36+
},
37+
"title": "C header files produced by Native Image",
38+
"type": "array"
39+
},
40+
"debug_info": {
41+
"default": [],
42+
"items": {
43+
"format": "uri",
44+
"title": "File or directory",
45+
"type": "string"
46+
},
47+
"title": "Debug information produced by Native Image",
48+
"type": "array"
49+
},
50+
"executable": {
51+
"default": [],
52+
"items": {
53+
"format": "uri",
54+
"title": "File or directory",
55+
"type": "string"
56+
},
57+
"title": "Executables produced by Native Image",
58+
"type": "array"
59+
},
60+
"import_library": {
61+
"default": [],
62+
"items": {
63+
"format": "uri",
64+
"title": "File or directory",
65+
"type": "string"
66+
},
67+
"title": "Import libraries produced by Native Image",
68+
"type": "array"
69+
},
70+
"jdk_library": {
71+
"default": [],
72+
"items": {
73+
"format": "uri",
74+
"title": "File or directory",
75+
"type": "string"
76+
},
77+
"title": "JDK libraries copied by Native Image",
78+
"type": "array"
79+
},
80+
"shared_library": {
81+
"default": [],
82+
"items": {
83+
"format": "uri",
84+
"title": "File or directory",
85+
"type": "string"
86+
},
87+
"title": "Shared libraries produced by Native Image",
88+
"type": "array"
89+
}
90+
},
91+
"required": [],
92+
"title": "JSON schema for the build artifacts of GraalVM Native Image",
93+
"type": "object"
94+
}

substratevm/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ This changelog summarizes major changes to GraalVM Native Image.
99
* (GR-41674) Class instanceOf and isAssignableFrom checks do need to make the checked type reachable.
1010
* (GR-41100) Add support for `-XX:HeapDumpPath` to control where heap dumps are created.
1111
* (GR-42148) Adjust build output to report types (primitives, classes, interfaces, and arrays) instead of classes and revise the output schema of `-H:BuildOutputJSONFile`.
12+
* (GR-42375) Add `-H:BuildArtifactsJSON` option, which generates a JSON file with a list of all artifacts produced by Native Image. `.build_artifacts.txt` files are now deprecated and will be removed in a future release.
1213

1314
## Version 22.3.0
1415
* (GR-35721) Remove old build output style and the `-H:±BuildOutputUseNewStyle` option.

substratevm/mx.substratevm/mx_substratevm.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -415,26 +415,29 @@ def help_stdout_check(output):
415415
if t:
416416
hellomodule(args.extra_image_builder_arguments)
417417

418-
with Task('Validate JSON build output', tasks, tags=[mx_gate.Tags.style]) as t:
418+
with Task('Validate JSON build info', tasks, tags=[mx_gate.Tags.style]) as t:
419419
if t:
420420
import json
421421
try:
422422
from jsonschema import validate as json_validate
423423
from jsonschema.exceptions import ValidationError, SchemaError
424424
except ImportError:
425425
mx.abort('Unable to import jsonschema')
426+
with open(join(suite.dir, '..', 'docs', 'reference-manual', 'native-image', 'assets', 'build-artifacts-schema-v0.9.0.json')) as f:
427+
build_artifacts_schema = json.load(f)
426428
with open(join(suite.dir, '..', 'docs', 'reference-manual', 'native-image', 'assets', 'build-output-schema-v0.9.1.json')) as f:
427-
json_schema = json.load(f)
429+
build_output_schema = json.load(f)
428430
with tempfile.NamedTemporaryFile(prefix='build_json') as json_file:
429-
helloworld(['--output-path', svmbuild_dir(), f'-H:BuildOutputJSONFile={json_file.name}'])
431+
helloworld(['--output-path', svmbuild_dir(), f'-H:BuildOutputJSONFile={json_file.name}', f'-H:BuildArtifactsJSON={svmbuild_dir()}'])
430432
try:
431-
with open(json_file.name) as f:
432-
json_output = json.load(f)
433-
json_validate(json_output, json_schema)
433+
for file_name, schema in [(json_file.name, build_output_schema), (join(svmbuild_dir(), 'build-artifacts.json'), build_artifacts_schema)]:
434+
with open(file_name) as f:
435+
json_output = json.load(f)
436+
json_validate(json_output, schema)
434437
except IOError as e:
435-
mx.abort(f'Unable to load JSON build output: {e}')
438+
mx.abort(f'Unable to load JSON build info: {e}')
436439
except ValidationError as e:
437-
mx.abort(f'Unable to validate JSON build output against the schema: {e}')
440+
mx.abort(f'Unable to validate JSON build info against the schema: {e}')
438441
except SchemaError as e:
439442
mx.abort(f'JSON schema not valid: {e}')
440443

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/BuildArtifacts.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,13 @@
3232
public interface BuildArtifacts {
3333
enum ArtifactType {
3434
EXECUTABLE,
35-
SHARED_LIB,
36-
JDK_LIB,
37-
JDK_LIB_SHIM,
38-
HEADER,
39-
IMPORT_LIB,
35+
SHARED_LIBRARY,
36+
JDK_LIBRARY,
37+
JDK_LIBRARY_SHIM,
38+
C_HEADER,
39+
IMPORT_LIBRARY,
4040
DEBUG_INFO,
41+
BUILD_INFO,
4142
LANGUAGE_HOME,
4243
}
4344

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,11 @@ public Boolean getValue(OptionValues values) {
415415
@Option(help = "Alignment of AOT and JIT compiled code in bytes.")//
416416
public static final HostedOptionKey<Integer> CodeAlignment = new HostedOptionKey<>(16);
417417

418+
@Option(help = "Create a build_artifacts.json file in the specified directory (empty argument defaults to the working directory). " +
419+
"The output conforms to the JSON schema located at: " +
420+
"docs/reference-manual/native-image/assets/build-artifacts-schema-v0.9.0.json", type = OptionType.User)//
421+
public static final HostedOptionKey<String> BuildArtifactsJSON = new HostedOptionKey<>("");
422+
418423
/*
419424
* Build output options.
420425
*/
@@ -441,7 +446,7 @@ public Boolean getValue(OptionValues values) {
441446
public static final HostedOptionKey<Boolean> BuildOutputGCWarnings = new HostedOptionKey<>(true);
442447

443448
@Option(help = "Print build output statistics as JSON to the specified file. " +
444-
"The output is according to the JSON schema located at: " +
449+
"The output conforms to the JSON schema located at: " +
445450
"docs/reference-manual/native-image/assets/build-output-schema-v0.9.1.json", type = OptionType.User)//
446451
public static final HostedOptionKey<String> BuildOutputJSONFile = new HostedOptionKey<>("");
447452

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*
2+
* Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package com.oracle.svm.hosted;
26+
27+
import java.io.PrintWriter;
28+
import java.nio.file.Files;
29+
import java.nio.file.Path;
30+
import java.nio.file.Paths;
31+
import java.util.List;
32+
import java.util.Map;
33+
import java.util.function.Consumer;
34+
import java.util.stream.Collectors;
35+
36+
import com.oracle.svm.core.BuildArtifacts.ArtifactType;
37+
import com.oracle.graal.pointsto.reports.ReportUtils;
38+
import com.oracle.svm.core.BuildArtifacts;
39+
import com.oracle.svm.core.SubstrateOptions;
40+
import com.oracle.svm.core.option.HostedOptionValues;
41+
42+
public class BuildArtifactsExporter {
43+
private static final String FILENAME = "build-artifacts.json";
44+
45+
public static void run(String imageName, Map<ArtifactType, List<Path>> buildArtifacts) {
46+
run(buildArtifacts);
47+
// Unconditionally create deprecated .build_artifacts.txt file
48+
BuildArtifacts.singleton().add(ArtifactType.BUILD_INFO, reportDeprecatedBuildArtifacts(imageName, buildArtifacts));
49+
}
50+
51+
private static void run(Map<ArtifactType, List<Path>> buildArtifacts) {
52+
if (buildArtifacts.isEmpty() || !SubstrateOptions.BuildArtifactsJSON.hasBeenSet()) {
53+
return; // nothing to do
54+
}
55+
String userPath = SubstrateOptions.BuildArtifactsJSON.getValue();
56+
Path targetPath;
57+
if (userPath.isEmpty()) {
58+
targetPath = NativeImageGenerator.generatedFiles(HostedOptionValues.singleton());
59+
} else {
60+
targetPath = Paths.get(userPath);
61+
}
62+
if (!Files.exists(targetPath)) {
63+
System.out.printf("Skipping the creation of a %s in '%s' because the path does not exist.%n", FILENAME, targetPath);
64+
return;
65+
}
66+
Consumer<PrintWriter> writerConsumer = writer -> {
67+
writer.print('{');
68+
var iterator = buildArtifacts.entrySet().iterator();
69+
while (iterator.hasNext()) {
70+
var entry = iterator.next();
71+
writer.printf("\"%s\":[", entry.getKey().toString().toLowerCase());
72+
writer.print(entry.getValue().stream().map(p -> "\"" + p.toAbsolutePath().toUri().toString() + "\"").collect(Collectors.joining(",")));
73+
writer.print(']');
74+
if (iterator.hasNext()) {
75+
writer.print(',');
76+
}
77+
}
78+
writer.print('}');
79+
};
80+
BuildArtifacts.singleton().add(ArtifactType.BUILD_INFO, ReportUtils.report(FILENAME, targetPath.resolve(FILENAME), writerConsumer, false));
81+
}
82+
83+
private static Path reportDeprecatedBuildArtifacts(String imageName, Map<ArtifactType, List<Path>> buildArtifacts) {
84+
Path buildDir = NativeImageGenerator.generatedFiles(HostedOptionValues.singleton());
85+
86+
Consumer<PrintWriter> writerConsumer = writer -> {
87+
writer.printf("# .build_artifacts.txt files are deprecated and will be removed in a future version of GraalVM Native Image. Please use the -H:%s option instead.%n",
88+
SubstrateOptions.BuildArtifactsJSON.getName());
89+
buildArtifacts.forEach((artifactType, paths) -> {
90+
writer.println("[" + artifactType + "]");
91+
if (artifactType == BuildArtifacts.ArtifactType.JDK_LIBRARY_SHIM) {
92+
writer.println("# Note that shim JDK libraries depend on this");
93+
writer.println("# particular native image (including its name)");
94+
writer.println("# and therefore cannot be used with others.");
95+
}
96+
paths.stream().map(Path::toAbsolutePath).map(buildDir::relativize).forEach(writer::println);
97+
writer.println();
98+
});
99+
};
100+
return ReportUtils.report("build artifacts", buildDir.resolve(imageName + ".build_artifacts.txt"), writerConsumer, false);
101+
}
102+
}

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporter.java

Lines changed: 13 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -619,10 +619,11 @@ public void printEpilog(String imageName, NativeImageGenerator generator, boolea
619619
double totalSeconds = Utils.millisToSeconds(getTimer(TimerCollection.Registry.TOTAL).getTotalTime());
620620
recordJsonMetric(ResourceUsageKey.TOTAL_SECS, totalSeconds);
621621

622+
createArtifacts(imageName, generator, parsedHostedOptions, wasSuccessfulBuild);
622623
Map<ArtifactType, List<Path>> artifacts = generator.getBuildArtifacts();
623624
if (!artifacts.isEmpty()) {
624625
l().printLineSeparator();
625-
printArtifacts(imageName, generator, parsedHostedOptions, artifacts, wasSuccessfulBuild);
626+
printArtifacts(artifacts);
626627
}
627628

628629
l().printHeadlineSeparator();
@@ -638,7 +639,17 @@ public void printEpilog(String imageName, NativeImageGenerator generator, boolea
638639
executor.shutdown();
639640
}
640641

641-
private void printArtifacts(String imageName, NativeImageGenerator generator, OptionValues parsedHostedOptions, Map<ArtifactType, List<Path>> artifacts, boolean wasSuccessfulBuild) {
642+
private void createArtifacts(String imageName, NativeImageGenerator generator, OptionValues parsedHostedOptions, boolean wasSuccessfulBuild) {
643+
if (jsonHelper != null && wasSuccessfulBuild) {
644+
BuildArtifacts.singleton().add(ArtifactType.BUILD_INFO, jsonHelper.printToFile());
645+
}
646+
if (generator.getBigbang() != null && ImageBuildStatistics.Options.CollectImageBuildStatistics.getValue(parsedHostedOptions)) {
647+
BuildArtifacts.singleton().add(ArtifactType.BUILD_INFO, reportImageBuildStatistics(imageName, generator.getBigbang()));
648+
}
649+
BuildArtifactsExporter.run(imageName, generator.getBuildArtifacts());
650+
}
651+
652+
private void printArtifacts(Map<ArtifactType, List<Path>> artifacts) {
642653
l().yellowBold().a("Produced artifacts:").reset().println();
643654
// Use TreeMap to sort paths alphabetically.
644655
Map<Path, List<String>> pathToTypes = new TreeMap<>();
@@ -647,15 +658,6 @@ private void printArtifacts(String imageName, NativeImageGenerator generator, Op
647658
pathToTypes.computeIfAbsent(path, p -> new ArrayList<>()).add(artifactType.name().toLowerCase());
648659
}
649660
});
650-
if (jsonHelper != null && wasSuccessfulBuild) {
651-
Path jsonMetric = jsonHelper.printToFile();
652-
pathToTypes.computeIfAbsent(jsonMetric, p -> new ArrayList<>()).add("json");
653-
}
654-
if (generator.getBigbang() != null && ImageBuildStatistics.Options.CollectImageBuildStatistics.getValue(parsedHostedOptions)) {
655-
Path buildStatisticsPath = reportImageBuildStatistics(imageName, generator.getBigbang());
656-
pathToTypes.computeIfAbsent(buildStatisticsPath, p -> new ArrayList<>()).add("raw");
657-
}
658-
pathToTypes.computeIfAbsent(reportBuildArtifacts(imageName, artifacts), p -> new ArrayList<>()).add("txt");
659661
pathToTypes.forEach((path, typeNames) -> {
660662
l().a(" ").link(path).dim().a(" (").a(String.join(", ", typeNames)).a(")").reset().println();
661663
});
@@ -674,22 +676,6 @@ private static Path reportImageBuildStatistics(String imageName, BigBang bb) {
674676
}
675677
}
676678

677-
private static Path reportBuildArtifacts(String imageName, Map<ArtifactType, List<Path>> buildArtifacts) {
678-
Path buildDir = NativeImageGenerator.generatedFiles(HostedOptionValues.singleton());
679-
680-
Consumer<PrintWriter> writerConsumer = writer -> buildArtifacts.forEach((artifactType, paths) -> {
681-
writer.println("[" + artifactType + "]");
682-
if (artifactType == BuildArtifacts.ArtifactType.JDK_LIB_SHIM) {
683-
writer.println("# Note that shim JDK libraries depend on this");
684-
writer.println("# particular native image (including its name)");
685-
writer.println("# and therefore cannot be used with others.");
686-
}
687-
paths.stream().map(Path::toAbsolutePath).map(buildDir::relativize).forEach(writer::println);
688-
writer.println();
689-
});
690-
return ReportUtils.report("build artifacts", buildDir.resolve(imageName + ".build_artifacts.txt"), writerConsumer, false);
691-
}
692-
693679
private void printResourceStatistics() {
694680
double totalProcessTimeSeconds = Utils.millisToSeconds(System.currentTimeMillis() - ManagementFactory.getRuntimeMXBean().getStartTime());
695681
GCStats gcStats = GCStats.getCurrent();

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImage.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ private void writeHeaderFile(Path outDir, Header header, List<HostedMethod> meth
245245
writer.appendln("#endif");
246246

247247
Path headerFile = writer.writeFile(header.name() + dynamicSuffix);
248-
BuildArtifacts.singleton().add(ArtifactType.HEADER, headerFile);
248+
BuildArtifacts.singleton().add(ArtifactType.C_HEADER, headerFile);
249249
}
250250

251251
/**

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageViaCC.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,14 +153,14 @@ private void runLinkerCommand(String imageName, LinkerInvocation inv, List<Strin
153153

154154
Path imagePath = inv.getOutputFile();
155155
imageFileSize = (int) imagePath.toFile().length();
156-
BuildArtifacts.singleton().add(imageKindIsExecutable ? ArtifactType.EXECUTABLE : ArtifactType.SHARED_LIB, imagePath);
156+
BuildArtifacts.singleton().add(imageKindIsExecutable ? ArtifactType.EXECUTABLE : ArtifactType.SHARED_LIBRARY, imagePath);
157157

158158
if (Platform.includedIn(Platform.WINDOWS.class) && !imageKindIsExecutable) {
159159
/* Provide an import library for the built shared library. */
160160
String importLib = imageName + ".lib";
161161
Path importLibPath = imagePath.resolveSibling(importLib);
162162
Files.move(inv.getTempDirectory().resolve(importLib), importLibPath, StandardCopyOption.REPLACE_EXISTING);
163-
BuildArtifacts.singleton().add(ArtifactType.IMPORT_LIB, importLibPath);
163+
BuildArtifacts.singleton().add(ArtifactType.IMPORT_LIBRARY, importLibPath);
164164
}
165165

166166
if (SubstrateOptions.GenerateDebugInfo.getValue() > 0) {

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/JNIRegistrationSupport.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ private void copyJDKLibraries(Path jdkLibDir) {
223223
try {
224224
Path libraryPath = accessImpl.getImagePath().resolveSibling(library);
225225
Files.copy(jdkLibDir.resolve(library), libraryPath, REPLACE_EXISTING);
226-
BuildArtifacts.singleton().add(ArtifactType.JDK_LIB, libraryPath);
226+
BuildArtifacts.singleton().add(ArtifactType.JDK_LIBRARY, libraryPath);
227227
debug.log("%s: OK", library);
228228
} catch (NoSuchFileException e) {
229229
/* Ignore libraries that are not present in the JDK. */
@@ -272,7 +272,7 @@ private void makeShimDLL(String shimName) {
272272
if (FileUtils.executeCommand(linkerCommand) != 0) {
273273
VMError.shouldNotReachHere();
274274
}
275-
BuildArtifacts.singleton().add(ArtifactType.JDK_LIB_SHIM, shimDLL);
275+
BuildArtifacts.singleton().add(ArtifactType.JDK_LIBRARY_SHIM, shimDLL);
276276
debug.log("%s.dll: OK", shimName);
277277
} catch (InterruptedException e) {
278278
throw new InterruptImageBuilding();

0 commit comments

Comments
 (0)