|
30 | 30 | import java.util.List;
|
31 | 31 | import java.util.Map;
|
32 | 32 | import java.util.Set;
|
| 33 | +import java.util.function.Function; |
33 | 34 | import java.util.stream.Stream;
|
34 | 35 |
|
35 | 36 | import com.oracle.svm.core.option.LayerVerification;
|
36 | 37 | import com.oracle.svm.core.option.LayerVerification.Kind;
|
37 | 38 | import com.oracle.svm.core.option.OptionOrigin;
|
| 39 | +import com.oracle.svm.core.option.SubstrateOptionsParser; |
38 | 40 | import com.oracle.svm.core.util.ArchiveSupport;
|
39 | 41 | import com.oracle.svm.core.util.UserError;
|
| 42 | +import com.oracle.svm.core.util.VMError; |
40 | 43 | import com.oracle.svm.hosted.NativeImageClassLoaderSupport;
|
| 44 | +import com.oracle.svm.hosted.imagelayer.HostedImageLayerBuildingSupport.OptionLayerVerificationRequests; |
| 45 | +import com.oracle.svm.hosted.imagelayer.LoadLayerArchiveSupport.ArgumentOrigin.NameValue; |
41 | 46 | import com.oracle.svm.hosted.util.DiffTool;
|
42 | 47 | import com.oracle.svm.util.LogUtils;
|
43 | 48 |
|
@@ -66,63 +71,74 @@ protected void validateLayerFile(Path layerFile) {
|
66 | 71 | }
|
67 | 72 | }
|
68 | 73 |
|
69 |
| - public void verifyCompatibility(NativeImageClassLoaderSupport classLoaderSupport, Map<String, Map<Kind, LayerVerification>> verifications) { |
70 |
| - |
71 |
| - // var errorMessagePrefix = "Layer Compatibility Error: "; |
72 |
| - var strippedBuilderArguments = builderArguments.stream() |
73 |
| - .map(argument -> splitArgumentOrigin(argument).argument) |
74 |
| - .toList(); |
75 |
| - List<String> currentBuilderArguments = classLoaderSupport.getHostedOptionParser().getArguments(); |
76 |
| - var strippedCurrentBuilderArguments = currentBuilderArguments.stream() |
77 |
| - .map(argument -> splitArgumentOrigin(argument).argument) |
78 |
| - .toList(); |
| 74 | + public void verifyCompatibility(NativeImageClassLoaderSupport classLoaderSupport, Map<String, OptionLayerVerificationRequests> allRequests, boolean strict) { |
| 75 | + Function<String, String> filterFunction = argument -> splitArgumentOrigin(argument).argument; |
| 76 | + verifyCompatibility(builderArguments, classLoaderSupport.getHostedOptionParser().getArguments(), filterFunction, allRequests, strict, true); |
| 77 | + verifyCompatibility(builderArguments, classLoaderSupport.getHostedOptionParser().getArguments(), filterFunction, allRequests, strict, false); |
| 78 | + } |
79 | 79 |
|
80 |
| - var diffResults = DiffTool.diffResults(strippedBuilderArguments, strippedCurrentBuilderArguments); |
| 80 | + private static void verifyCompatibility(List<String> parentArgs, List<String> currentArgs, Function<String, String> filterFunction, |
| 81 | + Map<String, OptionLayerVerificationRequests> allRequests, boolean strict, boolean positional) { |
81 | 82 |
|
82 |
| - if (Boolean.getBoolean("org.graalvm.nativeimage.layers.verification.verbose") && !diffResults.isEmpty()) { |
83 |
| - System.out.println("{".repeat(40)); |
84 |
| - for (var diffResult : diffResults) { |
85 |
| - System.out.println(diffResult.toString(builderArguments, currentBuilderArguments)); |
86 |
| - } |
87 |
| - System.out.println("}".repeat(40)); |
| 83 | + List<String> left; |
| 84 | + List<String> right; |
| 85 | + if (positional) { |
| 86 | + left = parentArgs; |
| 87 | + right = currentArgs; |
| 88 | + } else { |
| 89 | + // Use sorted lists for position-independent diff results |
| 90 | + left = parentArgs.stream().sorted().toList(); |
| 91 | + right = currentArgs.stream().sorted().toList(); |
88 | 92 | }
|
89 | 93 |
|
| 94 | + List<String> filteredLeft = left.stream().map(filterFunction).toList(); |
| 95 | + List<String> filteredRight = right.stream().map(filterFunction).toList(); |
| 96 | + var diffResults = DiffTool.diffResults(filteredLeft, filteredRight); |
90 | 97 | for (var diffResult : diffResults) {
|
91 | 98 | Set<Kind> verificationKinds = switch (diffResult.kind()) {
|
92 | 99 | case Removed -> Set.of(Kind.Removed, Kind.Changed);
|
93 | 100 | case Added -> Set.of(Kind.Added, Kind.Changed);
|
94 | 101 | default -> Set.of();
|
95 | 102 | };
|
96 | 103 | for (Kind verificationKind : verificationKinds) {
|
97 |
| - ArgumentOrigin argumentOrigin = splitArgumentOrigin(diffResult.getEntry(builderArguments, currentBuilderArguments)); |
98 |
| - Map<Kind, LayerVerification> perOptionVerifications = verifications.get(argumentOrigin.nameValue().name); |
| 104 | + ArgumentOrigin argumentOrigin = splitArgumentOrigin(diffResult.getEntry(left, right)); |
| 105 | + NameValue argumentNameAndValue = argumentOrigin.nameValue(); |
| 106 | + var perOptionVerifications = allRequests.get(argumentNameAndValue.name); |
99 | 107 | if (perOptionVerifications == null) {
|
100 | 108 | continue;
|
101 | 109 | }
|
102 |
| - LayerVerification verification = perOptionVerifications.get(verificationKind); |
103 |
| - if (verification != null) { |
104 |
| - OptionOrigin origin = OptionOrigin.from(argumentOrigin.origin); |
105 |
| - String message = "Parent layer '" + layerProperties.layerName() + "' specified via layer-file '" + layerFile.getFileName() + "'" + |
106 |
| - " was built with option argument '" + argumentOrigin.argument + "' from " + origin + "."; |
107 |
| - String suffix; |
108 |
| - if (!verification.Message().isEmpty()) { |
109 |
| - suffix = verification.Message(); |
110 |
| - } else { |
111 |
| - suffix = switch (verificationKind) { |
112 |
| - case Removed, Changed -> "This is also required to be specified for the current layered image build."; |
113 |
| - case Added -> "This is also required to be specified for the parent layer build."; |
114 |
| - }; |
115 |
| - } |
116 |
| - message += " " + suffix; |
117 |
| - switch (verification.Severity()) { |
118 |
| - case Info -> LogUtils.info(message); |
119 |
| - case Warn -> LogUtils.warning(message); |
120 |
| - case Error -> { |
121 |
| - if (!Boolean.getBoolean("org.graalvm.nativeimage.layers.verification.strict")) { |
122 |
| - LogUtils.warning(message); |
123 |
| - } else { |
124 |
| - UserError.abort(message); |
125 |
| - } |
| 110 | + LayerVerification request = perOptionVerifications.requests().get(verificationKind); |
| 111 | + if (request == null || request.Positional() != positional) { |
| 112 | + continue; |
| 113 | + } |
| 114 | + |
| 115 | + OptionOrigin origin = OptionOrigin.from(argumentOrigin.origin); |
| 116 | + String argument = SubstrateOptionsParser.commandArgument(perOptionVerifications.option().getOptionKey(), argumentNameAndValue.value); |
| 117 | + String message = switch (diffResult.kind()) { |
| 118 | + case Removed -> "Parent layer was"; |
| 119 | + case Added -> "Current layer gets"; |
| 120 | + case Equal -> throw VMError.shouldNotReachHere("diff for equal"); |
| 121 | + } + " built with option argument '" + argument + "' from " + origin + "."; |
| 122 | + String suffix; |
| 123 | + if (!request.Message().isEmpty()) { |
| 124 | + suffix = request.Message(); |
| 125 | + } else { |
| 126 | + /* fallback to generic verification message */ |
| 127 | + suffix = "This is also required to be specified for the " + switch (diffResult.kind()) { |
| 128 | + case Removed -> "current layered image build"; |
| 129 | + case Added -> "parent layer build"; |
| 130 | + case Equal -> throw VMError.shouldNotReachHere("diff for equal"); |
| 131 | + }; |
| 132 | + } |
| 133 | + message += " " + suffix + (positional ? " at the same position." : "."); |
| 134 | + switch (request.Severity()) { |
| 135 | + case Info -> LogUtils.info(message); |
| 136 | + case Warn -> LogUtils.warning(message); |
| 137 | + case Error -> { |
| 138 | + if (strict) { |
| 139 | + UserError.abort(message); |
| 140 | + } else { |
| 141 | + LogUtils.warning(message); |
126 | 142 | }
|
127 | 143 | }
|
128 | 144 | }
|
|
0 commit comments