Skip to content

Commit 9f31481

Browse files
committed
Linux: use latest jextract
1 parent 5fbfa68 commit 9f31481

File tree

11 files changed

+136
-20
lines changed

11 files changed

+136
-20
lines changed

java-does-usb/jextract/README.md

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,11 @@ The resulting code is then committed to the source code repository. Before the c
1313

1414
## General limitations
1515

16-
- The binaries for *jextract* on https://jdk.java.net/jextract/ have not been updated for JDK 21. So it must be built from source. Instructions can be found at [Building & Testing](https://github.com/openjdk/jextract#building--testing).
17-
1816
- According to the jextract mailing list, it would be required to create separate code for Intel x64 and ARM64 architecture. And jextract would need to be run on each architecture separately (no cross-compilation). Fortunately, this doesn't seem to be the case. Linux code generated on Intel x64 also runs on ARM64 without change. The same holds for macOS. However, jextract needs to be run on each operating system separately.
1917

2018
- JDK 20 introduced a new feature for saving the thread-specific error values (`GetLastError()` on Windows, `errno` on Linux). To use it, an additional parameter must be added to function calls. Unfortunately, this is not yet supported by jextract. So a good number of function bindings have to be written manually.
2119

22-
- `typedef` and `struct`:
23-
24-
1. If only the `typedef` is included (`--include-typedef`), an empty Java class is generated.
25-
2. If both *typedef* and the `struct` it refers to are included, the `typedef` class inherits from the `struct` class, which contains all the `struct` members.
26-
3. If the `typedef` refers to an unnamed `struct`, the generated class contains all the `struct` members.
27-
28-
Case 1 looks like a bug.
20+
- Dependencies: to be updated...
2921

3022
- *jextract* is not really transparent about what it does. It often skips elements without providing any information. In particular, it will silently skip a requested element in these cases:
3123

@@ -37,7 +29,6 @@ The resulting code is then committed to the source code repository. Before the c
3729
- `--include-typedef mytypedef` if `mytypedef` is a `typedef` for a primitive type.
3830

3931

40-
4132
## Linux
4233

4334
To run the script, the header files for *libudev* must be present. In most cases, they aren't install by default (in contrast to the library itself):
@@ -48,21 +39,20 @@ sudo apt-get install libudev-dev
4839

4940
On Linux, the limitations are:
5041

51-
- `usbdevice_fs.h`: The macro `USBDEVFS_CONTROL` and all similar ones are not generated. They are probably considered function-like macros. *jextract* does not generate code for function-like macros. But `USBDEVFS_CONTROL` evaluates to a constant.
42+
- `usbdevice_fs.h`: The macro `USBDEVFS_CONTROL` and all similar ones are not generated. They are probably considered function-like macros. *jextract* does not generate code for function-like macros. `USBDEVFS_CONTROL` would evaluate to a constant.
5243

5344
- `sd-device.h` (header file for *libsystemd*): *jextract* fails with *"Error: /usr/include/inttypes.h:290:8: error: unknown type name 'intmax_t'"*. The reason is yet unknown. This code is currently not needed as *libudev* is used instead of *libsystemd*. They are related, *libsystemd* is the future solution, but it is missing support for monitoring devices.
5445

55-
- `libudev.h`: After code generation, the class `RuntimeHelper.java` in `.../linux/gen/udev` must be manually modified as the code to access the library does not work for the directory the library is located in. So replace:
46+
- `libudev.h`: After code generation, the class `udev` in `.../linux/gen/udev` must be manually modified. In most Linux installations, there is no `libudev.so` alias to an actual version like `libudev.so.1.7.2`. The only alias is `libudev.so.1`. This is probably a deliberate decision by the authors as they do not plan to provide backward compatibility across major versions. But there does not seem to be a way to make *jextract* generate valid code for this setup. So manually replace:
5647

5748
```
58-
System.loadLibrary("udev");
59-
SymbolLookup loaderLookup = SymbolLookup.loaderLookup();
49+
static final SymbolLookup SYMBOL_LOOKUP = SymbolLookup.libraryLookup(System.mapLibraryName("udev"), LIBRARY_ARENA)
6050
```
6151

6252
with:
6353

6454
```
65-
SymbolLookup loaderLookup = SymbolLookup.libraryLookup("libudev.so", MemorySession.openImplicit());
55+
static final SymbolLookup SYMBOL_LOOKUP = SymbolLookup.libraryLookup("libudev.so.1", LIBRARY_ARENA)
6656
```
6757

6858

java-does-usb/jextract/linux/gen_linux.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/bin/sh
22

3-
JEXTRACT=../../../../jextract-22/bin/jextract
3+
JEXTRACT=../../../../jextract/build/jextract/bin/jextract
44

55
# errno.h
66
$JEXTRACT --output ../../src/main/java \

java-does-usb/src/main/java/net/codecrete/usb/linux/gen/epoll/epoll.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,13 @@
55
import java.lang.foreign.AddressLayout;
66
import java.lang.foreign.Arena;
77
import java.lang.foreign.FunctionDescriptor;
8+
import java.lang.foreign.GroupLayout;
89
import java.lang.foreign.Linker;
910
import java.lang.foreign.MemoryLayout;
1011
import java.lang.foreign.MemorySegment;
12+
import java.lang.foreign.PaddingLayout;
13+
import java.lang.foreign.SequenceLayout;
14+
import java.lang.foreign.StructLayout;
1115
import java.lang.foreign.SymbolLookup;
1216
import java.lang.foreign.ValueLayout;
1317
import java.lang.invoke.MethodHandle;
@@ -46,6 +50,20 @@ static MethodHandle upcallHandle(Class<?> fi, String name, FunctionDescriptor fd
4650
}
4751
}
4852

53+
static MemoryLayout align(MemoryLayout layout, long align) {
54+
return switch (layout) {
55+
case PaddingLayout p -> p;
56+
case ValueLayout v -> v.withByteAlignment(align);
57+
case GroupLayout g -> {
58+
MemoryLayout[] alignedMembers = g.memberLayouts().stream()
59+
.map(m -> align(m, align)).toArray(MemoryLayout[]::new);
60+
yield g instanceof StructLayout ?
61+
MemoryLayout.structLayout(alignedMembers) : MemoryLayout.unionLayout(alignedMembers);
62+
}
63+
case SequenceLayout s -> MemoryLayout.sequenceLayout(s.elementCount(), align(s.elementLayout(), align));
64+
};
65+
}
66+
4967
static final SymbolLookup SYMBOL_LOOKUP = SymbolLookup.loaderLookup()
5068
.or(Linker.nativeLinker().defaultLookup());
5169

java-does-usb/src/main/java/net/codecrete/usb/linux/gen/epoll/epoll_data.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ public class epoll_data {
3131
}
3232

3333
private static final GroupLayout $LAYOUT = MemoryLayout.unionLayout(
34-
epoll.C_POINTER.withName("ptr").withByteAlignment(4),
34+
epoll.C_POINTER.withName("ptr"),
3535
epoll.C_INT.withName("fd"),
3636
epoll.C_INT.withName("u32"),
37-
epoll.C_LONG.withName("u64").withByteAlignment(4)
37+
epoll.C_LONG.withName("u64")
3838
).withName("epoll_data");
3939

4040
/**

java-does-usb/src/main/java/net/codecrete/usb/linux/gen/epoll/epoll_event.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ public class epoll_event {
2727
}
2828

2929
private static final GroupLayout $LAYOUT = MemoryLayout.structLayout(
30-
epoll.C_INT.withByteAlignment(1).withName("events"),
31-
epoll_data.layout().withName("data")
30+
epoll.align(epoll.C_INT, 1).withName("events"),
31+
epoll.align(epoll_data.layout(), 1).withName("data")
3232
).withName("epoll_event");
3333

3434
/**

java-does-usb/src/main/java/net/codecrete/usb/linux/gen/errno/errno.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,13 @@
55
import java.lang.foreign.AddressLayout;
66
import java.lang.foreign.Arena;
77
import java.lang.foreign.FunctionDescriptor;
8+
import java.lang.foreign.GroupLayout;
89
import java.lang.foreign.Linker;
910
import java.lang.foreign.MemoryLayout;
1011
import java.lang.foreign.MemorySegment;
12+
import java.lang.foreign.PaddingLayout;
13+
import java.lang.foreign.SequenceLayout;
14+
import java.lang.foreign.StructLayout;
1115
import java.lang.foreign.SymbolLookup;
1216
import java.lang.foreign.ValueLayout;
1317
import java.lang.invoke.MethodHandle;
@@ -46,6 +50,20 @@ static MethodHandle upcallHandle(Class<?> fi, String name, FunctionDescriptor fd
4650
}
4751
}
4852

53+
static MemoryLayout align(MemoryLayout layout, long align) {
54+
return switch (layout) {
55+
case PaddingLayout p -> p;
56+
case ValueLayout v -> v.withByteAlignment(align);
57+
case GroupLayout g -> {
58+
MemoryLayout[] alignedMembers = g.memberLayouts().stream()
59+
.map(m -> align(m, align)).toArray(MemoryLayout[]::new);
60+
yield g instanceof StructLayout ?
61+
MemoryLayout.structLayout(alignedMembers) : MemoryLayout.unionLayout(alignedMembers);
62+
}
63+
case SequenceLayout s -> MemoryLayout.sequenceLayout(s.elementCount(), align(s.elementLayout(), align));
64+
};
65+
}
66+
4967
static final SymbolLookup SYMBOL_LOOKUP = SymbolLookup.loaderLookup()
5068
.or(Linker.nativeLinker().defaultLookup());
5169

java-does-usb/src/main/java/net/codecrete/usb/linux/gen/fcntl/fcntl.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,13 @@
55
import java.lang.foreign.AddressLayout;
66
import java.lang.foreign.Arena;
77
import java.lang.foreign.FunctionDescriptor;
8+
import java.lang.foreign.GroupLayout;
89
import java.lang.foreign.Linker;
910
import java.lang.foreign.MemoryLayout;
1011
import java.lang.foreign.MemorySegment;
12+
import java.lang.foreign.PaddingLayout;
13+
import java.lang.foreign.SequenceLayout;
14+
import java.lang.foreign.StructLayout;
1115
import java.lang.foreign.SymbolLookup;
1216
import java.lang.foreign.ValueLayout;
1317
import java.lang.invoke.MethodHandle;
@@ -46,6 +50,20 @@ static MethodHandle upcallHandle(Class<?> fi, String name, FunctionDescriptor fd
4650
}
4751
}
4852

53+
static MemoryLayout align(MemoryLayout layout, long align) {
54+
return switch (layout) {
55+
case PaddingLayout p -> p;
56+
case ValueLayout v -> v.withByteAlignment(align);
57+
case GroupLayout g -> {
58+
MemoryLayout[] alignedMembers = g.memberLayouts().stream()
59+
.map(m -> align(m, align)).toArray(MemoryLayout[]::new);
60+
yield g instanceof StructLayout ?
61+
MemoryLayout.structLayout(alignedMembers) : MemoryLayout.unionLayout(alignedMembers);
62+
}
63+
case SequenceLayout s -> MemoryLayout.sequenceLayout(s.elementCount(), align(s.elementLayout(), align));
64+
};
65+
}
66+
4967
static final SymbolLookup SYMBOL_LOOKUP = SymbolLookup.loaderLookup()
5068
.or(Linker.nativeLinker().defaultLookup());
5169

java-does-usb/src/main/java/net/codecrete/usb/linux/gen/string/string.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,13 @@
55
import java.lang.foreign.AddressLayout;
66
import java.lang.foreign.Arena;
77
import java.lang.foreign.FunctionDescriptor;
8+
import java.lang.foreign.GroupLayout;
89
import java.lang.foreign.Linker;
910
import java.lang.foreign.MemoryLayout;
1011
import java.lang.foreign.MemorySegment;
12+
import java.lang.foreign.PaddingLayout;
13+
import java.lang.foreign.SequenceLayout;
14+
import java.lang.foreign.StructLayout;
1115
import java.lang.foreign.SymbolLookup;
1216
import java.lang.foreign.ValueLayout;
1317
import java.lang.invoke.MethodHandle;
@@ -46,6 +50,20 @@ static MethodHandle upcallHandle(Class<?> fi, String name, FunctionDescriptor fd
4650
}
4751
}
4852

53+
static MemoryLayout align(MemoryLayout layout, long align) {
54+
return switch (layout) {
55+
case PaddingLayout p -> p;
56+
case ValueLayout v -> v.withByteAlignment(align);
57+
case GroupLayout g -> {
58+
MemoryLayout[] alignedMembers = g.memberLayouts().stream()
59+
.map(m -> align(m, align)).toArray(MemoryLayout[]::new);
60+
yield g instanceof StructLayout ?
61+
MemoryLayout.structLayout(alignedMembers) : MemoryLayout.unionLayout(alignedMembers);
62+
}
63+
case SequenceLayout s -> MemoryLayout.sequenceLayout(s.elementCount(), align(s.elementLayout(), align));
64+
};
65+
}
66+
4967
static final SymbolLookup SYMBOL_LOOKUP = SymbolLookup.loaderLookup()
5068
.or(Linker.nativeLinker().defaultLookup());
5169

java-does-usb/src/main/java/net/codecrete/usb/linux/gen/udev/udev.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,13 @@
55
import java.lang.foreign.AddressLayout;
66
import java.lang.foreign.Arena;
77
import java.lang.foreign.FunctionDescriptor;
8+
import java.lang.foreign.GroupLayout;
89
import java.lang.foreign.Linker;
910
import java.lang.foreign.MemoryLayout;
1011
import java.lang.foreign.MemorySegment;
12+
import java.lang.foreign.PaddingLayout;
13+
import java.lang.foreign.SequenceLayout;
14+
import java.lang.foreign.StructLayout;
1115
import java.lang.foreign.SymbolLookup;
1216
import java.lang.foreign.ValueLayout;
1317
import java.lang.invoke.MethodHandle;
@@ -46,6 +50,20 @@ static MethodHandle upcallHandle(Class<?> fi, String name, FunctionDescriptor fd
4650
}
4751
}
4852

53+
static MemoryLayout align(MemoryLayout layout, long align) {
54+
return switch (layout) {
55+
case PaddingLayout p -> p;
56+
case ValueLayout v -> v.withByteAlignment(align);
57+
case GroupLayout g -> {
58+
MemoryLayout[] alignedMembers = g.memberLayouts().stream()
59+
.map(m -> align(m, align)).toArray(MemoryLayout[]::new);
60+
yield g instanceof StructLayout ?
61+
MemoryLayout.structLayout(alignedMembers) : MemoryLayout.unionLayout(alignedMembers);
62+
}
63+
case SequenceLayout s -> MemoryLayout.sequenceLayout(s.elementCount(), align(s.elementLayout(), align));
64+
};
65+
}
66+
4967
// Manually fix. Otherwise, libudev will not be found if "libudev-dev" is not installed
5068
// static final SymbolLookup SYMBOL_LOOKUP = SymbolLookup.libraryLookup(System.mapLibraryName("udev"), LIBRARY_ARENA)
5169
static final SymbolLookup SYMBOL_LOOKUP = SymbolLookup.libraryLookup("libudev.so.1", LIBRARY_ARENA)

java-does-usb/src/main/java/net/codecrete/usb/linux/gen/unistd/unistd.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,13 @@
55
import java.lang.foreign.AddressLayout;
66
import java.lang.foreign.Arena;
77
import java.lang.foreign.FunctionDescriptor;
8+
import java.lang.foreign.GroupLayout;
89
import java.lang.foreign.Linker;
910
import java.lang.foreign.MemoryLayout;
1011
import java.lang.foreign.MemorySegment;
12+
import java.lang.foreign.PaddingLayout;
13+
import java.lang.foreign.SequenceLayout;
14+
import java.lang.foreign.StructLayout;
1115
import java.lang.foreign.SymbolLookup;
1216
import java.lang.foreign.ValueLayout;
1317
import java.lang.invoke.MethodHandle;
@@ -46,6 +50,20 @@ static MethodHandle upcallHandle(Class<?> fi, String name, FunctionDescriptor fd
4650
}
4751
}
4852

53+
static MemoryLayout align(MemoryLayout layout, long align) {
54+
return switch (layout) {
55+
case PaddingLayout p -> p;
56+
case ValueLayout v -> v.withByteAlignment(align);
57+
case GroupLayout g -> {
58+
MemoryLayout[] alignedMembers = g.memberLayouts().stream()
59+
.map(m -> align(m, align)).toArray(MemoryLayout[]::new);
60+
yield g instanceof StructLayout ?
61+
MemoryLayout.structLayout(alignedMembers) : MemoryLayout.unionLayout(alignedMembers);
62+
}
63+
case SequenceLayout s -> MemoryLayout.sequenceLayout(s.elementCount(), align(s.elementLayout(), align));
64+
};
65+
}
66+
4967
static final SymbolLookup SYMBOL_LOOKUP = SymbolLookup.loaderLookup()
5068
.or(Linker.nativeLinker().defaultLookup());
5169

java-does-usb/src/main/java/net/codecrete/usb/linux/gen/usbdevice_fs/usbdevice_fs.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,13 @@
55
import java.lang.foreign.AddressLayout;
66
import java.lang.foreign.Arena;
77
import java.lang.foreign.FunctionDescriptor;
8+
import java.lang.foreign.GroupLayout;
89
import java.lang.foreign.Linker;
910
import java.lang.foreign.MemoryLayout;
1011
import java.lang.foreign.MemorySegment;
12+
import java.lang.foreign.PaddingLayout;
13+
import java.lang.foreign.SequenceLayout;
14+
import java.lang.foreign.StructLayout;
1115
import java.lang.foreign.SymbolLookup;
1216
import java.lang.foreign.ValueLayout;
1317
import java.lang.invoke.MethodHandle;
@@ -46,6 +50,20 @@ static MethodHandle upcallHandle(Class<?> fi, String name, FunctionDescriptor fd
4650
}
4751
}
4852

53+
static MemoryLayout align(MemoryLayout layout, long align) {
54+
return switch (layout) {
55+
case PaddingLayout p -> p;
56+
case ValueLayout v -> v.withByteAlignment(align);
57+
case GroupLayout g -> {
58+
MemoryLayout[] alignedMembers = g.memberLayouts().stream()
59+
.map(m -> align(m, align)).toArray(MemoryLayout[]::new);
60+
yield g instanceof StructLayout ?
61+
MemoryLayout.structLayout(alignedMembers) : MemoryLayout.unionLayout(alignedMembers);
62+
}
63+
case SequenceLayout s -> MemoryLayout.sequenceLayout(s.elementCount(), align(s.elementLayout(), align));
64+
};
65+
}
66+
4967
static final SymbolLookup SYMBOL_LOOKUP = SymbolLookup.loaderLookup()
5068
.or(Linker.nativeLinker().defaultLookup());
5169

0 commit comments

Comments
 (0)