Skip to content

Commit 59ef5d4

Browse files
committed
[GR-31019] Implement --add-exports and --add-opens for native-image
1 parent 0b9e062 commit 59ef5d4

File tree

10 files changed

+224
-7
lines changed

10 files changed

+224
-7
lines changed

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/APIOption.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@
5656
*/
5757
String[] name();
5858

59+
/**
60+
* This option should only be shown with --help-extra.
61+
*/
62+
boolean extra() default false;
63+
5964
/**
6065
* Make a boolean option part of a group of boolean options.
6166
**/

substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/APIOptionHandler.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,12 +70,13 @@ static final class OptionInfo {
7070
final boolean hasPathArguments;
7171
final boolean defaultFinal;
7272
final String deprecationWarning;
73+
final boolean extra;
7374

7475
final List<Function<Object, Object>> valueTransformers;
7576
final APIOptionGroup group;
7677

7778
OptionInfo(String[] variants, char valueSeparator, String builderOption, String defaultValue, String helpText, boolean hasPathArguments, boolean defaultFinal, String deprecationWarning,
78-
List<Function<Object, Object>> valueTransformers, APIOptionGroup group) {
79+
List<Function<Object, Object>> valueTransformers, APIOptionGroup group, boolean extra) {
7980
this.variants = variants;
8081
this.valueSeparator = valueSeparator;
8182
this.builderOption = builderOption;
@@ -86,6 +87,7 @@ static final class OptionInfo {
8687
this.deprecationWarning = deprecationWarning;
8788
this.valueTransformers = valueTransformers;
8889
this.group = group;
90+
this.extra = extra;
8991
}
9092

9193
boolean isDeprecated() {
@@ -234,7 +236,7 @@ private static void extractOption(String optionPrefix, OptionDescriptor optionDe
234236
apiOptions.put(apiOptionName,
235237
new APIOptionHandler.OptionInfo(apiAnnotation.name(), apiAnnotation.valueSeparator(), builderOption, defaultValue, helpText,
236238
apiAnnotation.kind().equals(APIOptionKind.Paths),
237-
booleanOption || apiAnnotation.fixedValue().length > 0, apiAnnotation.deprecated(), valueTransformers, group));
239+
booleanOption || apiAnnotation.fixedValue().length > 0, apiAnnotation.deprecated(), valueTransformers, group, apiAnnotation.extra()));
238240
}
239241
} catch (NoSuchFieldException e) {
240242
/* Does not qualify as APIOption */
@@ -323,10 +325,10 @@ private String tryCanonicalize(String path) {
323325
}
324326
}
325327

326-
void printOptions(Consumer<String> println) {
328+
void printOptions(Consumer<String> println, boolean extra) {
327329
SortedMap<String, List<OptionInfo>> optionInfo = new TreeMap<>();
328330
apiOptions.forEach((optionName, option) -> {
329-
if (option.isDeprecated()) {
331+
if (option.isDeprecated() || option.extra != extra) {
330332
return;
331333
}
332334
String groupOrOptionName = option.group != null ? APIOption.Utils.groupName(option.group) : optionName;

substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/DefaultOptionHandler.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ public boolean consume(ArgumentQueue args) {
7373
singleArgumentCheck(args, headArg);
7474
nativeImage.showMessage(helpText);
7575
nativeImage.showNewline();
76-
nativeImage.apiOptionHandler.printOptions(nativeImage::showMessage);
76+
nativeImage.apiOptionHandler.printOptions(nativeImage::showMessage, false);
7777
nativeImage.showNewline();
7878
nativeImage.optionRegistry.showOptions(null, true, nativeImage::showMessage);
7979
nativeImage.showNewline();
@@ -96,6 +96,8 @@ public boolean consume(ArgumentQueue args) {
9696
args.poll();
9797
singleArgumentCheck(args, headArg);
9898
nativeImage.showMessage(helpExtraText);
99+
nativeImage.apiOptionHandler.printOptions(nativeImage::showMessage, true);
100+
nativeImage.showNewline();
99101
nativeImage.optionRegistry.showOptions(MacroOptionKind.Macro, true, nativeImage::showMessage);
100102
nativeImage.showNewline();
101103
System.exit(0);
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright (c) 2021, 2021, 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 org.graalvm.compiler.options.Option;
28+
29+
import com.oracle.svm.core.option.APIOption;
30+
import com.oracle.svm.core.option.HostedOptionKey;
31+
import com.oracle.svm.core.option.LocatableMultiOptionValue;
32+
33+
public class NativeImageClassLoaderOptions {
34+
static final String AddExportsAndOpensFormat = "<module>/<package>=<target-module>(,<target-module>)*";
35+
36+
@APIOption(name = "add-exports", extra = true)//
37+
@Option(help = "Value " + AddExportsAndOpensFormat + " updates <module> to export <package> to <target-module>, regardless of module declaration." +
38+
" <target-module> can be ALL-UNNAMED to export to all unnamed modules.")//
39+
public static final HostedOptionKey<LocatableMultiOptionValue.Strings> AddExports = new HostedOptionKey<>(new LocatableMultiOptionValue.Strings());
40+
41+
@APIOption(name = "add-opens", extra = true)//
42+
@Option(help = "Value " + AddExportsAndOpensFormat + " updates <module> to open <package> to <target-module>, regardless of module declaration.")//
43+
public static final HostedOptionKey<LocatableMultiOptionValue.Strings> AddOpens = new HostedOptionKey<>(new LocatableMultiOptionValue.Strings());
44+
}

substratevm/src/com.oracle.svm.hosted.jdk11/src/com/oracle/svm/hosted/NativeImageClassLoaderSupport.java

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,21 @@
3131
import java.nio.file.Path;
3232
import java.nio.file.Paths;
3333
import java.util.Arrays;
34+
import java.util.Collections;
3435
import java.util.HashSet;
3536
import java.util.List;
37+
import java.util.Objects;
3638
import java.util.Optional;
3739
import java.util.Set;
3840
import java.util.concurrent.ForkJoinPool;
3941
import java.util.stream.Collectors;
4042
import java.util.stream.Stream;
4143

44+
import org.graalvm.collections.Pair;
45+
import org.graalvm.compiler.options.OptionValues;
46+
47+
import com.oracle.svm.core.option.LocatableMultiOptionValue;
48+
import com.oracle.svm.core.option.SubstrateOptionsParser;
4249
import com.oracle.svm.core.util.UserError;
4350
import com.oracle.svm.core.util.VMError;
4451
import com.oracle.svm.util.ModuleSupport;
@@ -136,6 +143,88 @@ public Optional<Module> findModule(String moduleName) {
136143
return moduleLayerForImageBuild.findModule(moduleName);
137144
}
138145

146+
@Override
147+
void processAddExportsAndAddOpens(OptionValues parsedHostedOptions) {
148+
LocatableMultiOptionValue.Strings addExports = NativeImageClassLoaderOptions.AddExports.getValue(parsedHostedOptions);
149+
addExports.getValuesWithOrigins().map(this::asAddExportsAndOpensFormatValue).forEach(val -> {
150+
if (val.targetModules.isEmpty()) {
151+
Modules.addExportsToAllUnnamed(val.module, val.packageName);
152+
} else {
153+
for (Module targetModule : val.targetModules) {
154+
Modules.addExports(val.module, val.packageName, targetModule);
155+
}
156+
}
157+
});
158+
LocatableMultiOptionValue.Strings addOpens = NativeImageClassLoaderOptions.AddOpens.getValue(parsedHostedOptions);
159+
addOpens.getValuesWithOrigins().map(this::asAddExportsAndOpensFormatValue).forEach(val -> {
160+
if (val.targetModules.isEmpty()) {
161+
Modules.addOpensToAllUnnamed(val.module, val.packageName);
162+
} else {
163+
for (Module targetModule : val.targetModules) {
164+
Modules.addOpens(val.module, val.packageName, targetModule);
165+
}
166+
}
167+
});
168+
}
169+
170+
private static final class AddExportsAndOpensFormatValue {
171+
private final Module module;
172+
private final String packageName;
173+
private final List<Module> targetModules;
174+
175+
private AddExportsAndOpensFormatValue(Module module, String packageName, List<Module> targetModules) {
176+
this.module = module;
177+
this.packageName = packageName;
178+
this.targetModules = targetModules;
179+
}
180+
}
181+
182+
private final AddExportsAndOpensFormatValue asAddExportsAndOpensFormatValue(Pair<String, String> valueOrigin) {
183+
String optionOrigin = valueOrigin.getRight();
184+
String optionValue = valueOrigin.getLeft();
185+
186+
String syntaxErrorMessage = " Allowed value format: " + NativeImageClassLoaderOptions.AddExportsAndOpensFormat;
187+
188+
String[] modulePackageAndTargetModules = optionValue.split("=", 2);
189+
if (modulePackageAndTargetModules.length != 2) {
190+
throw userErrorAddExportsAndOpens(optionOrigin, optionValue, syntaxErrorMessage);
191+
}
192+
String modulePackage = modulePackageAndTargetModules[0];
193+
String targetModuleNames = modulePackageAndTargetModules[1];
194+
195+
String[] moduleAndPackage = modulePackage.split("/");
196+
if (moduleAndPackage.length != 2) {
197+
throw userErrorAddExportsAndOpens(optionOrigin, optionValue, syntaxErrorMessage);
198+
}
199+
String moduleName = moduleAndPackage[0];
200+
String packageName = moduleAndPackage[1];
201+
202+
List<String> targetModuleNamesList = Arrays.asList(targetModuleNames.split(","));
203+
if (targetModuleNamesList.isEmpty()) {
204+
throw userErrorAddExportsAndOpens(optionOrigin, optionValue, syntaxErrorMessage);
205+
}
206+
207+
Module module = findModule(moduleName).orElseThrow(() -> {
208+
return userErrorAddExportsAndOpens(optionOrigin, optionValue, " Specified module '" + moduleName + "' is unknown.");
209+
});
210+
List<Module> targetModules;
211+
if (targetModuleNamesList.contains("ALL-UNNAMED")) {
212+
targetModules = Collections.emptyList();
213+
} else {
214+
targetModules = targetModuleNamesList.stream().map(mn -> {
215+
return findModule(mn).orElseThrow(() -> {
216+
throw userErrorAddExportsAndOpens(optionOrigin, optionValue, " Specified target-module '" + mn + "' is unknown.");
217+
});
218+
}).collect(Collectors.toList());
219+
}
220+
return new AddExportsAndOpensFormatValue(module, packageName, targetModules);
221+
}
222+
223+
private static UserError.UserException userErrorAddExportsAndOpens(String origin, String value, String detailMessage) {
224+
Objects.requireNonNull(detailMessage, "missing detailMessage");
225+
return UserError.abort("Invalid option %s provided by %s." + detailMessage, SubstrateOptionsParser.commandArgument(NativeImageClassLoaderOptions.AddExports, value), origin);
226+
}
227+
139228
@Override
140229
Class<?> loadClassFromModule(Object module, String className) throws ClassNotFoundException {
141230
if (module == null) {

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@
5555
import java.util.stream.Collectors;
5656
import java.util.stream.Stream;
5757

58+
import org.graalvm.compiler.options.OptionValues;
59+
5860
import com.oracle.svm.core.SubstrateOptions;
5961
import com.oracle.svm.core.util.ClasspathUtils;
6062
import com.oracle.svm.core.util.InterruptImageBuilding;
@@ -111,6 +113,8 @@ ClassLoader getClassLoader() {
111113

112114
abstract Optional<? extends Object> findModule(String moduleName);
113115

116+
abstract void processAddExportsAndAddOpens(OptionValues parsedHostedOptions);
117+
114118
abstract void initAllClasses(ForkJoinPool executor, ImageClassLoader imageClassLoader);
115119

116120
protected static class Util {

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import java.util.stream.StreamSupport;
4545

4646
import org.graalvm.collections.EconomicSet;
47+
import org.graalvm.compiler.options.OptionValues;
4748
import org.graalvm.compiler.word.Word;
4849
import org.graalvm.nativeimage.Platform;
4950
import org.graalvm.nativeimage.Platforms;
@@ -420,6 +421,10 @@ public Class<?> loadClassFromModule(Object module, String className) throws Clas
420421
public Optional<Object> findModule(String moduleName) {
421422
return classLoaderSupport.findModule(moduleName);
422423
}
424+
425+
public void processAddExportsAndAddOpens(OptionValues parsedHostedOptions) {
426+
classLoaderSupport.processAddExportsAndAddOpens(parsedHostedOptions);
427+
}
423428
}
424429

425430
class ClassLoaderQueryImpl implements ClassLoaderQuery {
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* Copyright (c) 2021, 2021, 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.util.Iterator;
28+
import java.util.NoSuchElementException;
29+
30+
import org.graalvm.compiler.options.OptionDescriptor;
31+
import org.graalvm.compiler.options.OptionDescriptors;
32+
33+
/**
34+
* For jdk8 we have no NativeImageClassLoaderOptions. We provide a dummy so that the jdk11 version
35+
* is able to supply a different version of this class.
36+
*/
37+
public class NativeImageClassLoaderOptions_OptionDescriptors implements OptionDescriptors {
38+
@Override
39+
public OptionDescriptor get(String value) {
40+
return null;
41+
}
42+
43+
@Override
44+
public Iterator<OptionDescriptor> iterator() {
45+
return new Iterator<OptionDescriptor>() {
46+
int i = 0;
47+
48+
@Override
49+
public boolean hasNext() {
50+
return false;
51+
}
52+
53+
@Override
54+
public OptionDescriptor next() {
55+
throw new NoSuchElementException();
56+
}
57+
};
58+
}
59+
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
import java.util.Optional;
3131
import java.util.concurrent.ForkJoinPool;
3232

33+
import org.graalvm.compiler.options.OptionValues;
34+
3335
public class NativeImageClassLoaderSupport extends AbstractNativeImageClassLoaderSupport {
3436

3537
NativeImageClassLoaderSupport(ClassLoader defaultSystemClassLoader, String[] classpath, @SuppressWarnings("unused") String[] modulePath) {
@@ -51,6 +53,11 @@ public Optional<Object> findModule(String moduleName) {
5153
return Optional.empty();
5254
}
5355

56+
@Override
57+
void processAddExportsAndAddOpens(OptionValues parsedHostedOptions) {
58+
/* Nothing to do for Java 8 */
59+
}
60+
5461
@Override
5562
Class<?> loadClassFromModule(Object module, String className) throws ClassNotFoundException {
5663
if (module != null) {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@
4343

4444
import org.graalvm.collections.Pair;
4545
import org.graalvm.compiler.debug.DebugContext;
46-
import org.graalvm.compiler.debug.DebugContext.Builder;
4746
import org.graalvm.compiler.options.OptionValues;
4847
import org.graalvm.compiler.printer.GraalDebugHandlersFactory;
4948
import org.graalvm.compiler.serviceprovider.JavaVersionUtil;
@@ -268,7 +267,7 @@ private int buildImage(String[] arguments, ImageClassLoader classLoader) {
268267
* to pass the OptionValues explicitly when accessing options.
269268
*/
270269
parsedHostedOptions = new OptionValues(optionParser.getHostedValues());
271-
DebugContext debug = new Builder(parsedHostedOptions, new GraalDebugHandlersFactory(GraalAccess.getOriginalSnippetReflection())).build();
270+
DebugContext debug = new DebugContext.Builder(parsedHostedOptions, new GraalDebugHandlersFactory(GraalAccess.getOriginalSnippetReflection())).build();
272271

273272
imageName = SubstrateOptions.Name.getValue(parsedHostedOptions);
274273
if (imageName.length() == 0) {
@@ -307,6 +306,7 @@ private int buildImage(String[] arguments, ImageClassLoader classLoader) {
307306
}
308307

309308
if (!className.isEmpty() || !moduleName.isEmpty()) {
309+
classLoader.processAddExportsAndAddOpens(parsedHostedOptions);
310310
Method mainEntryPoint;
311311
Class<?> mainClass;
312312
try {

0 commit comments

Comments
 (0)