diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index d236903..e83fb3e 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -42,9 +42,9 @@ jobs: cd annotation-processor-test/src/test/resources/libvlc && git clone --depth 1 https://github.com/videolan/vlc.git - name: Getting linux header version for tests run: | - echo "headerVersion=$(uname -r)" >> "$GITHUB_ENV" + echo "linuxVersion=$(uname -r)" >> "$GITHUB_ENV" && echo "gccVersion=12" >> "$GITHUB_ENV" - name: Change wrapper permissions run: chmod +x ./gradlew - name: Build Gradle run: | - ./gradlew build -Dversion=$headerVersion + ./gradlew build -Dlinux-version=$linuxVersion -Dgcc-version=$gccVersion diff --git a/annotation-processor-test/gradle.properties b/annotation-processor-test/gradle.properties index d04e9b2..d7fdd8a 100644 --- a/annotation-processor-test/gradle.properties +++ b/annotation-processor-test/gradle.properties @@ -1 +1,2 @@ -systemProp.version=6.2.0-39 \ No newline at end of file +systemProp.linux-version=6.8.0-52-generic +systemProp.gcc-version=11 \ No newline at end of file diff --git a/annotation-processor-test/src/test/java/io/github/digitalsmile/gpio/functions/LibcFunctionTests.java b/annotation-processor-test/src/test/java/io/github/digitalsmile/gpio/functions/LibcFunctionTests.java new file mode 100644 index 0000000..ea4a688 --- /dev/null +++ b/annotation-processor-test/src/test/java/io/github/digitalsmile/gpio/functions/LibcFunctionTests.java @@ -0,0 +1,16 @@ +package io.github.digitalsmile.gpio.functions; + +import io.github.digitalsmile.annotation.NativeMemoryException; +import org.junit.jupiter.api.Test; + +public class LibcFunctionTests { + + @Test + public void testOpenRead() throws NativeMemoryException { + var file = new LibcFunctionsNative(); + var osRelease = file.open("/etc/os-release", 0); + var buffer = file.read(osRelease, new byte[1024], 1024); + System.out.println(new String(buffer)); + file.close(); + } +} diff --git a/annotation-processor-test/src/test/java/io/github/digitalsmile/gpio/functions/FunctionTest.java b/annotation-processor-test/src/test/java/io/github/digitalsmile/gpio/functions/LibcFunctions.java similarity index 81% rename from annotation-processor-test/src/test/java/io/github/digitalsmile/gpio/functions/FunctionTest.java rename to annotation-processor-test/src/test/java/io/github/digitalsmile/gpio/functions/LibcFunctions.java index b6d7c75..9e20949 100644 --- a/annotation-processor-test/src/test/java/io/github/digitalsmile/gpio/functions/FunctionTest.java +++ b/annotation-processor-test/src/test/java/io/github/digitalsmile/gpio/functions/LibcFunctions.java @@ -1,15 +1,10 @@ package io.github.digitalsmile.gpio.functions; -import io.github.digitalsmile.annotation.NativeMemory; import io.github.digitalsmile.annotation.function.*; import io.github.digitalsmile.annotation.NativeMemoryException; -import io.github.digitalsmile.annotation.library.NativeFunction; -import io.github.digitalsmile.annotation.library.NativeMemoryLibrary; import io.github.digitalsmile.annotation.types.interfaces.NativeMemoryLayout; -import java.util.List; - -public interface FunctionTest { +public interface LibcFunctions { @NativeManualFunction(name = "ioctl", useErrno = true) int callByValue(int fd, long command, long data) throws NativeMemoryException; @@ -28,7 +23,7 @@ public interface FunctionTest { @NativeManualFunction(name = "close") void close(int fd) throws NativeMemoryException; - @NativeManualFunction(name = "read", useErrno = true) + @NativeManualFunction(name = "read", useErrno = true, nativeReturnType = int.class) byte[] read(int fd, @Returns @ByAddress byte[] buffer, int size) throws NativeMemoryException; @NativeManualFunction(name = "write", useErrno = true) diff --git a/annotation-processor-test/src/test/java/io/github/digitalsmile/gpio/libcurl/Libcurl.java b/annotation-processor-test/src/test/java/io/github/digitalsmile/gpio/libcurl/Libcurl.java index d4cc901..e253938 100644 --- a/annotation-processor-test/src/test/java/io/github/digitalsmile/gpio/libcurl/Libcurl.java +++ b/annotation-processor-test/src/test/java/io/github/digitalsmile/gpio/libcurl/Libcurl.java @@ -17,7 +17,7 @@ @NativeMemory(headers = "libcurl/curl/include/curl/curl.h") @NativeMemoryOptions(systemIncludes = { - "/usr/lib/gcc/x86_64-linux-gnu/12/include/" + "/usr/lib/gcc/x86_64-linux-gnu/${gcc-version}/include/" }, debugMode = true, processRootConstants = true) @Structs({ @Struct(name = "CURL", javaName = "CurlInstance") diff --git a/annotation-processor-test/src/test/java/io/github/digitalsmile/gpio/libvlc/LibVLC.java b/annotation-processor-test/src/test/java/io/github/digitalsmile/gpio/libvlc/LibVLC.java index 14f898c..acb614e 100644 --- a/annotation-processor-test/src/test/java/io/github/digitalsmile/gpio/libvlc/LibVLC.java +++ b/annotation-processor-test/src/test/java/io/github/digitalsmile/gpio/libvlc/LibVLC.java @@ -16,7 +16,7 @@ @NativeMemory(headers = "libvlc/vlc/include/vlc/vlc.h") @NativeMemoryOptions( includes = "libvlc/vlc/include", - systemIncludes = "/usr/lib/gcc/x86_64-linux-gnu/12/include/", + systemIncludes = "/usr/lib/gcc/x86_64-linux-gnu/${gcc-version}/include/", debugMode = true ) @Structs diff --git a/annotation-processor-test/src/test/java/io/github/digitalsmile/gpio/types/all/GPIOTypesAll.java b/annotation-processor-test/src/test/java/io/github/digitalsmile/gpio/types/all/GPIOTypesAll.java index 7ac82f3..287acf0 100644 --- a/annotation-processor-test/src/test/java/io/github/digitalsmile/gpio/types/all/GPIOTypesAll.java +++ b/annotation-processor-test/src/test/java/io/github/digitalsmile/gpio/types/all/GPIOTypesAll.java @@ -6,7 +6,7 @@ import io.github.digitalsmile.annotation.structure.Structs; import io.github.digitalsmile.annotation.structure.Unions; -@NativeMemory(headers = "/usr/src/linux-headers-${version}/include/uapi/linux/gpio.h") +@NativeMemory(headers = "/usr/src/linux-headers-${linux-version}/include/uapi/linux/gpio.h") @NativeMemoryOptions( processRootConstants = true ) diff --git a/annotation-processor-test/src/test/java/io/github/digitalsmile/gpio/types/custom/GPIOTest.java b/annotation-processor-test/src/test/java/io/github/digitalsmile/gpio/types/custom/GPIOTest.java index dd3afc8..4523b79 100644 --- a/annotation-processor-test/src/test/java/io/github/digitalsmile/gpio/types/custom/GPIOTest.java +++ b/annotation-processor-test/src/test/java/io/github/digitalsmile/gpio/types/custom/GPIOTest.java @@ -7,7 +7,7 @@ import io.github.digitalsmile.annotation.structure.Structs; import io.github.digitalsmile.annotation.structure.Unions; -@NativeMemory(headers = "/usr/src/linux-headers-${version}/include/uapi/linux/gpio.h") +@NativeMemory(headers = "/usr/src/linux-headers-${linux-version}/include/uapi/linux/gpio.h") @NativeMemoryOptions( processRootConstants = true ) diff --git a/annotation-processor/src/main/java/io/github/digitalsmile/NativeProcessor.java b/annotation-processor/src/main/java/io/github/digitalsmile/NativeProcessor.java index 153bdec..6f2ae38 100644 --- a/annotation-processor/src/main/java/io/github/digitalsmile/NativeProcessor.java +++ b/annotation-processor/src/main/java/io/github/digitalsmile/NativeProcessor.java @@ -298,7 +298,7 @@ private void processFunctions(Element rootElement, List functionElement var parameterNode = new ParameterNode(variableName, node, returns != null, byAddress); parameters.add(parameterNode); } - var functionOptions = new FunctionOptions(instance.name(), instance.isAlreadyLoaded(), instance.useErrno()); + var functionOptions = new FunctionOptions(instance.name(), instance.isAlreadyLoaded(), instance.useErrno(), OriginalType.of(instance.nativeReturnType())); var functionNode = new FunctionNode(functionElement.getSimpleName().toString(), functionOptions, returnNode, parameters, functionElement.getTypeParameters()); nodes.add(functionNode); var libraryFileName = instance.library(); @@ -398,7 +398,7 @@ private List getHeaderPaths(String... headerFiles) throws ValidationExcept } private File calculatePath(File directory) { - if (directory.isDirectory() && directory.getName().equals("build")) { + if (directory.isDirectory() && (directory.getName().equals("build") || directory.getName().equals("target"))) { return directory.getParentFile(); } return calculatePath(directory.getParentFile()); diff --git a/annotation-processor/src/main/java/io/github/digitalsmile/composers/ContextComposer.java b/annotation-processor/src/main/java/io/github/digitalsmile/composers/ContextComposer.java index 9d98b72..e1ed749 100644 --- a/annotation-processor/src/main/java/io/github/digitalsmile/composers/ContextComposer.java +++ b/annotation-processor/src/main/java/io/github/digitalsmile/composers/ContextComposer.java @@ -68,8 +68,8 @@ public String compose(String packageName, String javaName, Map parameters.add(CodeBlock.builder().add("$T.$L", ValueLayout.class, primitiveTypeMapping.valueLayoutName()).build()); case ObjectOriginalType _, ArrayOriginalType _ -> { @@ -81,6 +81,21 @@ public String compose(String packageName, String javaName, Map throw new IllegalStateException("Unexpected value: " + returnType); } + } else { + if (!returnType.carrierClass().equals(void.class)) { + switch (returnType) { + case PrimitiveOriginalType primitiveTypeMapping -> + parameters.add(CodeBlock.builder().add("$T.$L", ValueLayout.class, primitiveTypeMapping.valueLayoutName()).build()); + case ObjectOriginalType _, ArrayOriginalType _ -> { + if (returnNode.getNodeType().isEnum()) { + parameters.add(CodeBlock.builder().add("$T.JAVA_INT", ValueLayout.class).build()); + } else { + parameters.add(CodeBlock.builder().add("$T.ADDRESS", ValueLayout.class).build()); + } + } + default -> throw new IllegalStateException("Unexpected value: " + returnType); + } + } } for (ParameterNode parameterNode : functionNode.functionParameters()) { var node = parameterNode.nativeMemoryNode(); diff --git a/annotation-processor/src/main/java/io/github/digitalsmile/composers/FunctionComposer.java b/annotation-processor/src/main/java/io/github/digitalsmile/composers/FunctionComposer.java index c5f9cb3..150f901 100644 --- a/annotation-processor/src/main/java/io/github/digitalsmile/composers/FunctionComposer.java +++ b/annotation-processor/src/main/java/io/github/digitalsmile/composers/FunctionComposer.java @@ -116,26 +116,50 @@ public String compose(String packageName, String originalName, List + methodBody.addStatement("var callResult = ($T) $T.$L.invoke($L)", + functionNode.returnNode().getNodeType().isEnum() ? int.class : MemorySegment.class, + context, nativeFunctionNames.get(functionNode), CodeBlock.join(arguments, ", ")); + default -> methodBody.addStatement("var callResult = ($T) $T.$L.invoke($L)", + returnType.carrierClass(), + context, nativeFunctionNames.get(functionNode), CodeBlock.join(arguments, ", ")); + } + } + } } else { - if (returnType.carrierClass().equals(void.class)) { - methodBody.addStatement("$T.$L.invoke($L)", context, nativeFunctionNames.get(functionNode), CodeBlock.join(arguments, ", ")); + if (options.useErrno()) { + methodBody.addStatement("var capturedState = context.allocate(CAPTURED_STATE_LAYOUT)"); + methodBody.addStatement("var callResult = ($T) $T.$L.invoke(capturedState, $L)", options.nativeReturnType().carrierClass(), context, nativeFunctionNames.get(functionNode), CodeBlock.join(arguments, ", ")); + methodBody.addStatement("processError(callResult, capturedState, $S, $L)", functionNode.functionName(), CodeBlock.join(arguments, ", ")); } else { - switch (returnType) { - case ArrayOriginalType _, ObjectOriginalType _ -> - methodBody.addStatement("var callResult = ($T) $T.$L.invoke($L)", - functionNode.returnNode().getNodeType().isEnum() ? int.class : MemorySegment.class, - context, nativeFunctionNames.get(functionNode), CodeBlock.join(arguments, ", ")); - default -> methodBody.addStatement("var callResult = ($T) $T.$L.invoke($L)", - returnType.carrierClass(), - context, nativeFunctionNames.get(functionNode), CodeBlock.join(arguments, ", ")); + if (returnType.carrierClass().equals(void.class)) { + methodBody.addStatement("$T.$L.invoke($L)", context, nativeFunctionNames.get(functionNode), CodeBlock.join(arguments, ", ")); + } else { + switch (returnType) { + case ArrayOriginalType _, ObjectOriginalType _ -> + methodBody.addStatement("var callResult = ($T) $T.$L.invoke($L)", + functionNode.returnNode().getNodeType().isEnum() ? int.class : MemorySegment.class, + context, nativeFunctionNames.get(functionNode), CodeBlock.join(arguments, ", ")); + default -> methodBody.addStatement("var callResult = ($T) $T.$L.invoke($L)", + returnType.carrierClass(), + context, nativeFunctionNames.get(functionNode), CodeBlock.join(arguments, ", ")); + } } } } + + if (!returnType.carrierClass().equals(void.class) && functionNode.functionParameters().stream().noneMatch(ParameterNode::returns)) { if (returnType instanceof ObjectOriginalType) { if (returnType.carrierClass().equals(String.class)) { diff --git a/annotation-processor/src/main/java/io/github/digitalsmile/functions/FunctionOptions.java b/annotation-processor/src/main/java/io/github/digitalsmile/functions/FunctionOptions.java index 011c532..e1b8086 100644 --- a/annotation-processor/src/main/java/io/github/digitalsmile/functions/FunctionOptions.java +++ b/annotation-processor/src/main/java/io/github/digitalsmile/functions/FunctionOptions.java @@ -1,4 +1,6 @@ package io.github.digitalsmile.functions; -public record FunctionOptions(String nativeFunctionName, boolean isAlreadyLoaded, boolean useErrno) { +import io.github.digitalsmile.headers.mapping.OriginalType; + +public record FunctionOptions(String nativeFunctionName, boolean isAlreadyLoaded, boolean useErrno, OriginalType nativeReturnType) { } diff --git a/annotation/src/main/java/io/github/digitalsmile/annotation/function/NativeManualFunction.java b/annotation/src/main/java/io/github/digitalsmile/annotation/function/NativeManualFunction.java index 284e8d9..b80f623 100644 --- a/annotation/src/main/java/io/github/digitalsmile/annotation/function/NativeManualFunction.java +++ b/annotation/src/main/java/io/github/digitalsmile/annotation/function/NativeManualFunction.java @@ -89,4 +89,11 @@ * @return true if use errno/strerr */ boolean useErrno() default false; + + /** + * Native return type, as described in function docs. + * + * @return native function return type + */ + Class nativeReturnType() default Void.class; }