Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

7903933: Move sharable items from different generations to a common file #278

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion doc/GUIDE.md
Original file line number Diff line number Diff line change
@@ -986,9 +986,10 @@ A complete list of all the supported command line options is given below:
| `--output <path>` | specify where to place generated files |
| `--dump-includes <String>` | dump included symbols into specified file (see below) |
| `--include-[function,constant,struct,union,typedef,var]<String>` | Include a symbol of the given name and kind in the generated bindings. When one of these options is specified, any symbol that is not matched by any specified filters is omitted from the generated bindings. |
| `--sharable-utils <name>` | generate shared utility class for common layouts and methods |
| `--version` | print version information and exit |
| `-F <dir>` (macOs only) | specify the framework directory include files. Defaults to the current Mac OS X SDK dir.|
| `-framework <framework>` (macOs only) | specify the name of the library, path will be expanded to that of the framework folder.|
| `--framework <framework>` (macOs only) | specify the name of the library, path will be expanded to that of the framework folder.|


Jextract accepts one or more header files. When multiple header files are specified,
4 changes: 2 additions & 2 deletions samples/opengl/compilesource.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
jextract -t opengl -framework GLUT \
-framework OpenGL \
jextract -t opengl --framework GLUT \
--framework OpenGL \
"<GLUT/glut.h>"

javac --source=22 -d . opengl/*.java
27 changes: 18 additions & 9 deletions src/main/java/org/openjdk/jextract/JextractTool.java
Original file line number Diff line number Diff line change
@@ -137,7 +137,8 @@ private static List<JavaSourceFile> generateInternal(Declaration.Scoped decl, St
.findFirst().get();
return logger.hasErrors() ?
List.of() :
List.of(OutputFactory.generateWrapped(transformedDecl, targetPkg, libs, useSystemLoadLibrary));
List.of(OutputFactory.generateWrapped(transformedDecl, targetPkg,
libs, useSystemLoadLibrary, includeHelper.getSharableItems()));
}

/**
@@ -334,7 +335,7 @@ OptionSet parse(String[] args) {
// so that option lookup, value lookup will work regardless
// which alias was used to check.
options.put(spec.name(), values);
for (String alias : spec.aliases()) {
for (String _ : spec.aliases()) {
options.put(spec.name(), values);
}
} else { // !isOption(arg)
@@ -356,6 +357,7 @@ private int run(String[] args) {
OptionParser parser = new OptionParser();
parser.accepts("-D", List.of("--define-macro"), "help.D", true);
parser.accepts("--dump-includes", "help.dump-includes", true);
parser.accepts("--sharable-items", "help.sharable.items", true);
for (IncludeHelper.IncludeKind includeKind : IncludeHelper.IncludeKind.values()) {
parser.accepts("--" + includeKind.optionName(), "help." + includeKind.optionName(), true);
}
@@ -370,7 +372,7 @@ private int run(String[] args) {

if (isMacOSX) {
parser.accepts("-F", "help.mac.framework", true);
parser.accepts("-framework", "help.framework.library.path", true);
parser.accepts("--framework", "help.framework.library.path", true);
}

OptionSet optionSet;
@@ -399,7 +401,7 @@ private int run(String[] args) {
Path compileFlagsTxt = Paths.get(".", "compile_flags.txt");
if (Files.exists(compileFlagsTxt)) {
try {
Files.lines(compileFlagsTxt).forEach(opt -> builder.addClangArg(opt));
Files.lines(compileFlagsTxt).forEach(builder::addClangArg);
} catch (IOException ioExp) {
logger.fatal(ioExp, "jextract.bad.compile.flags", ioExp.getMessage());
return OPTION_ERROR;
@@ -439,6 +441,10 @@ private int run(String[] args) {
builder.setDumpIncludeFile(optionSet.valueOf("--dump-includes"));
}

if (optionSet.has("--sharable-items")) {
builder.useSharableItems(optionSet.valueOf("--sharable-items"));
}

if (optionSet.has("--output")) {
builder.setOutputDir(optionSet.valueOf("--output"));
}
@@ -471,7 +477,7 @@ private int run(String[] args) {

builder.addClangArg("-I" + System.getProperty("user.dir"));

if (optionSet.nonOptionArguments().size() == 0) {
if (optionSet.nonOptionArguments().isEmpty()) {
printOptionError(logger.format("expected.atleast.one.header"));
return OPTION_ERROR;
}
@@ -514,7 +520,7 @@ private int run(String[] args) {
}

try {
if (options.includeHelper.dumpIncludesFile != null) {
if (options.includeHelper.getDumpIncludesFile() != null) {
options.includeHelper.dumpIncludes();
} else {
Path output = Path.of(options.outputDir);
@@ -536,10 +542,13 @@ private int run(String[] args) {
}

private int parseLibraries(String optionString, OptionSet optionSet, boolean useSystemLoadLibrary, Options.Builder builder) {
if (optionSet.has("-" + optionString)) {
for (String lib : optionSet.valuesOf("-" + optionString)) {
String cmdOption = optionString.length() < 3 ?
"-" + optionString :
"--" + optionString;
if (optionSet.has(cmdOption)) {
for (String lib : optionSet.valuesOf(cmdOption)) {
try {
String spec = optionString.equals("framework") ?
String spec = cmdOption.equals("--framework") ?
resolveFrameworkPath(lib) :
lib;

20 changes: 20 additions & 0 deletions src/main/java/org/openjdk/jextract/impl/HeaderFileBuilder.java
Original file line number Diff line number Diff line change
@@ -392,6 +392,26 @@ static MemoryLayout align(MemoryLayout layout, long align) {
""");
}

void emitBasicPrimitiveTypes(){
appendIndentedLines("""

public static final ValueLayout.OfBoolean C_BOOL = (ValueLayout.OfBoolean) Linker.nativeLinker().canonicalLayouts().get("bool");
public static final ValueLayout.OfByte C_CHAR =(ValueLayout.OfByte)Linker.nativeLinker().canonicalLayouts().get("char");
public static final ValueLayout.OfShort C_SHORT = (ValueLayout.OfShort) Linker.nativeLinker().canonicalLayouts().get("short");
public static final ValueLayout.OfInt C_INT = (ValueLayout.OfInt) Linker.nativeLinker().canonicalLayouts().get("int");
public static final ValueLayout.OfLong C_LONG_LONG = (ValueLayout.OfLong) Linker.nativeLinker().canonicalLayouts().get("long long");
public static final ValueLayout.OfFloat C_FLOAT = (ValueLayout.OfFloat) Linker.nativeLinker().canonicalLayouts().get("float");
public static final ValueLayout.OfDouble C_DOUBLE = (ValueLayout.OfDouble) Linker.nativeLinker().canonicalLayouts().get("double");
public static final AddressLayout C_POINTER = ((AddressLayout) Linker.nativeLinker().canonicalLayouts().get("void*"))
.withTargetLayout(MemoryLayout.sequenceLayout(java.lang.Long.MAX_VALUE, C_CHAR));
""");
if (TypeImpl.IS_WINDOWS) {
appendIndentedLines("public static final ValueLayout.OfInt C_LONG = (ValueLayout.OfInt) Linker.nativeLinker().canonicalLayouts().get(\"long\");");
appendIndentedLines("public static final ValueLayout.OfDouble C_LONG_DOUBLE = (ValueLayout.OfDouble) Linker.nativeLinker().canonicalLayouts().get(\"double\");");
} else {
appendIndentedLines("public static final ValueLayout.OfLong C_LONG = (ValueLayout.OfLong) Linker.nativeLinker().canonicalLayouts().get(\"long\");");
}
}
private void emitGlobalGetter(String holderClass, String javaName,
Declaration.Variable decl, String docHeader) {
appendBlankLine();
25 changes: 21 additions & 4 deletions src/main/java/org/openjdk/jextract/impl/IncludeHelper.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -83,10 +83,27 @@ static IncludeKind fromScoped(Declaration.Scoped scoped) {

private final EnumMap<IncludeKind, Set<String>> includesSymbolNamesByKind = new EnumMap<>(IncludeKind.class);
private final Set<Declaration> usedDeclarations = new HashSet<>();
public String dumpIncludesFile;

private String dumpIncludesFile;
private String sharableItems;

public String getDumpIncludesFile() {
return dumpIncludesFile;
}

public void setDumpIncludesFile(String dumpIncludesFile) {
this.dumpIncludesFile = dumpIncludesFile;
}

public void setSharableItems(String sharableItems) {
this.sharableItems = sharableItems;
}
public String getSharableItems() {
return sharableItems;
}

public void addSymbol(IncludeKind kind, String symbolName) {
Set<String> names = includesSymbolNamesByKind.computeIfAbsent(kind, (_unused) -> new HashSet<>());
Set<String> names = includesSymbolNamesByKind.computeIfAbsent(kind, _unused -> new HashSet<>());
names.add(symbolName);
}

@@ -122,7 +139,7 @@ private boolean isIncludedInternal(IncludeKind kind, Declaration declaration) {
if (!isEnabled()) {
return true;
} else {
Set<String> names = includesSymbolNamesByKind.computeIfAbsent(kind, (_unused) -> new HashSet<>());
Set<String> names = includesSymbolNamesByKind.computeIfAbsent(kind, _unused -> new HashSet<>());
return names.contains(declaration.name());
}
}
8 changes: 6 additions & 2 deletions src/main/java/org/openjdk/jextract/impl/Options.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -99,7 +99,11 @@ public void setTargetPackage(String pkg) {
}

public void setDumpIncludeFile(String dumpIncludesFile) {
includeHelper.dumpIncludesFile = dumpIncludesFile;
includeHelper.setDumpIncludesFile(dumpIncludesFile);
}

public void useSharableItems(String name) {
includeHelper.setSharableItems(name);
}

public void addIncludeSymbol(IncludeHelper.IncludeKind kind, String symbolName) {
14 changes: 11 additions & 3 deletions src/main/java/org/openjdk/jextract/impl/OutputFactory.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, 2022 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2025 Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -45,9 +45,17 @@ public class OutputFactory implements Declaration.Visitor<Void, Declaration> {

public static JavaSourceFile[] generateWrapped(Declaration.Scoped decl,
String pkgName,
List<Options.Library> libs, boolean useSystemLoadLibrary) {
List<Options.Library> libs,
boolean useSystemLoadLibrary,
String generateShareableItems) {
String clsName = JavaName.getOrThrow(decl);
ToplevelBuilder toplevelBuilder = new ToplevelBuilder(pkgName, clsName, libs, useSystemLoadLibrary);
ToplevelBuilder toplevelBuilder = new ToplevelBuilder(
pkgName,
clsName,
libs,
useSystemLoadLibrary,
generateShareableItems
);
return new OutputFactory(toplevelBuilder).generate(decl);
}

70 changes: 46 additions & 24 deletions src/main/java/org/openjdk/jextract/impl/ToplevelBuilder.java
Original file line number Diff line number Diff line change
@@ -43,55 +43,77 @@ class ToplevelBuilder implements OutputFactory.Builder {
private static final int DECLS_PER_HEADER_CLASS = Integer.getInteger("jextract.decls.per.header", 1000);
public static final String PREV_SUFFIX = "#{PREV_SUFFIX}";
private static final String SUFFIX = "#{SUFFIX}";
private final String jextractUTILS;

private int declCount;
private final List<SourceFileBuilder> headerBuilders = new ArrayList<>();
private final List<SourceFileBuilder> otherBuilders = new ArrayList<>();
private HeaderFileBuilder lastHeader;
private final ClassDesc headerDesc;
private SourceFileBuilder jextractUtilsBuilder;

ToplevelBuilder(String packageName, String headerClassName,
List<Options.Library> libs, boolean useSystemLoadLibrary) {
List<Options.Library> libs,
boolean useSystemLoadLibrary,
String generateShareableItems) {
jextractUTILS = generateShareableItems;
this.headerDesc = ClassDesc.of(packageName, headerClassName);

if (jextractUTILS != null) {
jextractUtilsBuilder = createJextractUtilsBuilder(packageName,libs,useSystemLoadLibrary);
}

SourceFileBuilder sfb = SourceFileBuilder.newSourceFile(packageName, headerClassName);
headerBuilders.add(sfb);
lastHeader = createFirstHeader(sfb, libs, useSystemLoadLibrary);
lastHeader = createFirstHeader(sfb, libs, useSystemLoadLibrary, jextractUTILS);
}

private static HeaderFileBuilder createFirstHeader(SourceFileBuilder sfb, List<Options.Library> libs, boolean useSystemLoadLibrary) {
HeaderFileBuilder first = new HeaderFileBuilder(sfb, String.format("%1$s#{SUFFIX}",sfb.className()), null, sfb.className());
private static HeaderFileBuilder createFirstHeader(SourceFileBuilder sfb,
List<Options.Library> libs,
boolean useSystemLoadLibrary,
String generateShareableItems) {
HeaderFileBuilder first = new HeaderFileBuilder(sfb,
String.format("%1$s#{SUFFIX}", sfb.className()),
generateShareableItems,
sfb.className());
first.appendBlankLine();
first.classBegin();
first.emitDefaultConstructor();
first.emitRuntimeHelperMethods();
first.emitFirstHeaderPreamble(libs, useSystemLoadLibrary);
// emit basic primitive types
first.appendIndentedLines("""

public static final ValueLayout.OfBoolean C_BOOL = (ValueLayout.OfBoolean) Linker.nativeLinker().canonicalLayouts().get("bool");
public static final ValueLayout.OfByte C_CHAR =(ValueLayout.OfByte)Linker.nativeLinker().canonicalLayouts().get("char");
public static final ValueLayout.OfShort C_SHORT = (ValueLayout.OfShort) Linker.nativeLinker().canonicalLayouts().get("short");
public static final ValueLayout.OfInt C_INT = (ValueLayout.OfInt) Linker.nativeLinker().canonicalLayouts().get("int");
public static final ValueLayout.OfLong C_LONG_LONG = (ValueLayout.OfLong) Linker.nativeLinker().canonicalLayouts().get("long long");
public static final ValueLayout.OfFloat C_FLOAT = (ValueLayout.OfFloat) Linker.nativeLinker().canonicalLayouts().get("float");
public static final ValueLayout.OfDouble C_DOUBLE = (ValueLayout.OfDouble) Linker.nativeLinker().canonicalLayouts().get("double");
public static final AddressLayout C_POINTER = ((AddressLayout) Linker.nativeLinker().canonicalLayouts().get("void*"))
.withTargetLayout(MemoryLayout.sequenceLayout(java.lang.Long.MAX_VALUE, C_CHAR));
""");
if (TypeImpl.IS_WINDOWS) {
first.appendIndentedLines("public static final ValueLayout.OfInt C_LONG = (ValueLayout.OfInt) Linker.nativeLinker().canonicalLayouts().get(\"long\");");
first.appendIndentedLines("public static final ValueLayout.OfDouble C_LONG_DOUBLE = (ValueLayout.OfDouble) Linker.nativeLinker().canonicalLayouts().get(\"double\");");
} else {
first.appendIndentedLines("public static final ValueLayout.OfLong C_LONG = (ValueLayout.OfLong) Linker.nativeLinker().canonicalLayouts().get(\"long\");");

if (generateShareableItems == null) {
first.emitRuntimeHelperMethods();
first.emitFirstHeaderPreamble(libs, useSystemLoadLibrary);
first.emitBasicPrimitiveTypes();
}

return first;
}

private SourceFileBuilder createJextractUtilsBuilder(String packageName, List<Options.Library> libs, boolean useSystemLoadLibrary) {
SourceFileBuilder utilsBuilder = SourceFileBuilder.newSourceFile(packageName, jextractUTILS);
HeaderFileBuilder utilsHeader = new HeaderFileBuilder(utilsBuilder, jextractUTILS, null, jextractUTILS);

utilsHeader.appendBlankLine();
utilsHeader.classBegin();
utilsHeader.emitDefaultConstructor();

utilsHeader.emitRuntimeHelperMethods();
utilsHeader.emitFirstHeaderPreamble(libs, useSystemLoadLibrary);
utilsHeader.emitBasicPrimitiveTypes();
utilsHeader.classEnd();

return utilsBuilder;
}

public List<JavaSourceFile> toFiles() {
lastHeader.classEnd();

List<JavaSourceFile> files = new ArrayList<>();

if (jextractUTILS != null && jextractUtilsBuilder != null) {
files.add(jextractUtilsBuilder.toFile());
}

if (headerBuilders.size() == 1) {
files.add(headerBuilders.getFirst().toFile(s -> s.replace(SUFFIX, "")));
} else {
Original file line number Diff line number Diff line change
@@ -40,6 +40,7 @@ help.include-struct=name of struct definition to include
help.include-union=name of union definition to include
help.D=define a C preprocessor macro
help.dump-includes=dump included symbols into specified file
help.sharable.items=generate shared utility class for common layouts and methods
help.h=print help
help.header-class-name=name of the header class
help.l=specify a library
@@ -84,11 +85,12 @@ Option Description
\ option is not specified, then current directory is used. \n\
-t, --target-package <package> target package name for the generated classes. If this option\n\
\ is not specified, then unnamed package is used. \n\
--sharable-items <name> generate shared utility class for common layouts and methods \n\
--version print version information and exit \n\
\ \n\
macOS platform options for running jextract (available only when running on macOS): \n\
-F <dir> specify the framework directory \n\
-framework <framework> specify framework library. -framework libGL is equivalent to \n\
--framework <framework> specify framework library. --framework libGL is equivalent to \n\
\ -l :/System/Library/Frameworks/libGL.framework/libGL

jextract.version=\
55 changes: 55 additions & 0 deletions test/jtreg/generator/sharableItems/TestSharableItems.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

import org.testng.annotations.Test;
import test.jextract.sharableItems.*;

import java.lang.foreign.Arena;
import java.lang.foreign.GroupLayout;
import java.lang.foreign.MemorySegment;
import java.util.concurrent.atomic.AtomicInteger;

import static org.testng.Assert.assertEquals;
import static test.jextract.sharableItems.sharableItems_h.*;

/*
* @test
* @bug 7903933
* @summary This is verbatim copy of TestNestedInsideAnon except for the `@run` command, used to test the `--sharable-items` option
* @library /lib
* @build testlib.TestUtils
* @run main/othervm JtregJextract --sharable-items FFM_UTILS -t test.jextract.sharableItems sharableItems.h
* @build TestSharableItems
* @run testng/othervm --enable-native-access=ALL-UNNAMED TestSharableItems
*/
public class TestSharableItems {
@Test
public void testAnonField() {
checkLayout(S.layout());
}

void checkLayout(GroupLayout layout) {
assertEquals(((GroupLayout)layout.memberLayouts().get(0)).memberLayouts().get(0).withoutName(),
S.Flags.layout().withoutName());
}
}
30 changes: 30 additions & 0 deletions test/jtreg/generator/sharableItems/sharableItems.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

typedef struct {
struct {
struct {
char y;
} Flags;
};
} S;