bytedeco / javacpp

The missing bridge between Java and native C++
Other
4.49k stars 583 forks source link

Maven compilation does not include JavaCPP_init function #523

Closed BackstreetCultist closed 3 years ago

BackstreetCultist commented 3 years ago

Hi there, I've been attempting to use javacpp to create a callback function which is then accessed in Haskell - at the moment, I've been working with the example code I found in the following SO thread: https://stackoverflow.com/questions/10370177/communication-between-java-and-haskell The code works fine with the commands suggested in the thread, but I've been unable to build the code through Maven. Whenever I do, I receive the following error when calling GHC:

Linking Main ...
Main.o(.text+0x91): error: undefined reference to 'JavaCPP_init'
Main.o(.text+0x469): error: undefined reference to 'JavaCPP_init'
collect2: error: ld returned 1 exit status
`gcc' failed in phase `Linker'. (Exit code: 1)

It seems that the JavaCPP_init function is not being packaged into the .so file? For reference, the Maven configuration is as follows:

<plugin>
                <groupId>org.bytedeco</groupId>
                <artifactId>javacpp</artifactId>
                <version>1.5.6</version>
                <configuration>
                    <classPath>${project.build.outputDirectory}</classPath>
                    <includePaths>
                        <includePath>${project.build.sourceDirectory}</includePath>
                    </includePaths>
                </configuration>
                <executions>
                    <execution>
                        <id>process-classes</id>
                        <phase>process-classes</phase>
                        <goals>
                            <goal>build</goal>
                        </goals>
                        <configuration>
                            <classOrPackageNames>
                                <classOrPackageName>somepackage.*</classOrPackageName>
                            </classOrPackageNames>
                            <header>true</header>
                            <copyLibs>true</copyLibs>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

And the header file generated appears to be correct:

// Generated by JavaCPP version 1.5.6: DO NOT EDIT THIS FILE

#include <jni.h>

#ifdef __cplusplus
extern "C" {
#endif
JNIIMPORT int JavaCPP_init(int argc, const char *argv[]);
JNIIMPORT int JavaCPP_uninit();

JNIIMPORT int fibonacci(int arg0);
JNIIMPORT int fibonacci1(int arg0);
JNIIMPORT int fibonacci2(int arg0);
JNIIMPORT int fibonacci3(int arg0);
JNIIMPORT int fibonacci4(int arg0);
JNIIMPORT int fibonacci5(int arg0);
JNIIMPORT int fibonacci6(int arg0);
JNIIMPORT int fibonacci7(int arg0);
JNIIMPORT int fibonacci8(int arg0);
JNIIMPORT int fibonacci9(int arg0);
#ifdef __cplusplus
}
#endif

Any help you are able to offer would be really appreciated! Thank you for your time.

saudet commented 3 years ago

It's only going to get generated when we're also generating the header file, with this parameter in the case of Maven: http://bytedeco.org/javacpp/apidocs/org/bytedeco/javacpp/tools/BuildMojo.html#header

BackstreetCultist commented 3 years ago

A header file is being generated, have I passed the value in incorrectly? I've included <header>true</header> in the <configuration> section as above, should it be somewhere else?

saudet commented 3 years ago

Right, sorry, missed it. So, it should work. What does the .cpp file look like?

BackstreetCultist commented 3 years ago

jnijavacpp.txt I've attached the generated cpp file as a txt, this is the one that is built if I set <deleteJniFiles>false. I'm not much of a C++ guy unfortunately but at line 120 the following code looks odd:

static JavaVM* JavaCPP_vm = NULL;
static bool JavaCPP_haveAllocObject = false;
static bool JavaCPP_haveNonvirtual = false;
static const char* JavaCPP_classNames[21] = {
        "org/bytedeco/javacpp/Loader",
        "org/bytedeco/javacpp/Pointer",
        "org/bytedeco/javacpp/BytePointer",
        "org/bytedeco/javacpp/ShortPointer",
        "org/bytedeco/javacpp/IntPointer",
        "org/bytedeco/javacpp/LongPointer",
        "org/bytedeco/javacpp/FloatPointer",
        "org/bytedeco/javacpp/DoublePointer",
        "org/bytedeco/javacpp/CharPointer",
        "org/bytedeco/javacpp/BooleanPointer",
        "org/bytedeco/javacpp/PointerPointer",
        "org/bytedeco/javacpp/BoolPointer",
        "org/bytedeco/javacpp/CLongPointer",
        "org/bytedeco/javacpp/SizeTPointer",
        "java/lang/UnsatisfiedLinkError",
        "java/lang/String",
        "org/bytedeco/javacpp/Pointer$NativeDeallocator",
        "java/nio/Buffer",
        "java/lang/Object",
        "java/nio/charset/Charset",
        "java/lang/NullPointerException" };
static jclass JavaCPP_classes[21] = { NULL };
static jfieldID JavaCPP_addressFID = NULL;
static jfieldID JavaCPP_positionFID = NULL;
static jfieldID JavaCPP_limitFID = NULL;
static jfieldID JavaCPP_capacityFID = NULL;
static jfieldID JavaCPP_deallocatorFID = NULL;
static jfieldID JavaCPP_ownerAddressFID = NULL;
static jmethodID JavaCPP_initMID = NULL;
static jmethodID JavaCPP_arrayMID = NULL;
static jmethodID JavaCPP_arrayOffsetMID = NULL;
static jfieldID JavaCPP_bufferPositionFID = NULL;
static jfieldID JavaCPP_bufferLimitFID = NULL;
static jfieldID JavaCPP_bufferCapacityFID = NULL;
static jmethodID JavaCPP_stringMID = NULL;
static jmethodID JavaCPP_getBytesMID = NULL;
static jmethodID JavaCPP_toStringMID = NULL;
#ifdef STRING_BYTES_CHARSET
#ifdef MODIFIED_UTF8_STRING
#pragma message ("warning: STRING_BYTES_CHARSET and MODIFIED_UTF8_STRING are mutually exclusive.")
#endif
static jobject JavaCPP_stringBytesCharset = NULL;
static jmethodID JavaCPP_stringWithCharsetMID = NULL;
static jmethodID JavaCPP_getBytesWithCharsetMID = NULL;
#endif

Should those be getting set? I'm not sure they are elsewhere.

saudet commented 3 years ago

There's supposed to be another file generated like jnisomepackage.cpp. There's no "JavaCPPinit" or "JavaCPP_init(" substrings in that one either??

BackstreetCultist commented 3 years ago

I've set <compile>false and <deleteJniFiles>false but it's still only generating that one file. Is there anything else I need to set?

Edit: I can definitely see it generating and then deleting a second file, jniMultiplyDemo.cpp, but there's no references to the file being deleted in the console output which is weird. At the same time, I think it moves the jnijavacpp.cpp file outside of the package structure mid-build, from target/classes/somepackage to target/classes.

saudet commented 3 years ago

Yes, they're probably not going to be generated in the same directory. Make sure the .class files are actually getting compiled before JavaCPP runs. BTW, Maven makes all this really complicated. If you want to start something from scratch and the build doesn't matter, I'd recommend going with Gradle JavaCPP: https://github.com/bytedeco/gradle-javacpp

BackstreetCultist commented 3 years ago

Okay thanks, I'll take a look into the build order and give it a go with gradle.

saudet commented 3 years ago

BTW, you could also check it out by modifying one of the simple presets like the one for zlib here: https://github.com/bytedeco/javacpp-presets/tree/zlib/zlib Running mvn clean install -Djavacpp.deleteJniFiles=false on that I can definitely see it leave jnizlib.cpp undeleted.

Further, if I add <header>true</header>, I do get a JNIEXPORT int JavaCPP_init(int argc, const char *argv[]) { function block in jnizlib.cpp. What about you?

BackstreetCultist commented 3 years ago

I've just tried that and it generated correctly - I'm sure between that and maybe the gradle alternative I'll be able to sort something out. Thank you so much for all your help!