I've written a binding C++ class that calls java from C++. I am trying to compile the binding class using CMake (because the tool that will use the binding class uses CMake).
However I receive the following error:
CMakeFiles/JNIWrapper.dir/JNIWrapper.cpp.o: In function `createVM(JavaVM_**)':
JNIWrapper.cpp:(.text+0x52): undefined reference to `JNI_CreateJavaVM'
collect2: ld returned 1 exit status
make[2]: *** [JNIWrapper] Error 1
make[1]: *** [CMakeFiles/JNIWrapper.dir/all] Error 2
make: *** [all] Error 2
Here is my CMakeLists.txt:
cmake_minimum_required(VERSION 2.8.6)
project(AProject)
FIND_PACKAGE(JNI REQUIRED)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -I/usr/lib/jvm/java-6-openjdk-amd64/include -I/usr/lib/jvm/java-6-openjdk-amd64/include/linux -L/usr/lib/jvm/java-6-openjdk-amd64/jre/lib/amd64/server")
SET(CMAKE_EXE_LINKER_FLAGS "-ljvm")
# add the binary tree directory to the search path for include files
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
include_directories(${JNI_INCLUDE_DIRS})
# add the executable
add_executable (JNIWrapper JNIWrapper.cpp)
Any suggestion is appreciated.
PS: I have tried to compile it using the traditional way and by writing a makefile. I followed that example and wrote the CMakeLists.txt script above.
The following CMakeLists.txt sketches the steps required to build a sample JNI project with CMake:
cmake_minimum_required (VERSION 3.0)
find_package(Java REQUIRED)
find_package(JNI REQUIRED)
include(UseJava)
enable_testing()
project (JNIFoo)
# compile JNIFoo.java to class file
set(CMAKE_JAVA_COMPILE_FLAGS "-source" "1.6" "-target" "1.6")
add_jar(JNIFoo JNIFoo.java)
get_target_property(_jarFile JNIFoo JAR_FILE)
get_target_property(_classDir JNIFoo CLASSDIR)
# generate JNIFoo.h stub
set (_stubDir "${CMAKE_CURRENT_BINARY_DIR}")
add_custom_command(
OUTPUT JNIFoo.h
COMMAND ${Java_JAVAH_EXECUTABLE} -verbose
-classpath ${_classDir}
-d ${_stubDir}
-jni JNIFoo
DEPENDS JNIFoo
)
# generate libfoo.jnilib
include_directories(${JNI_INCLUDE_DIRS} ${_classDir} ${_stubDir})
add_library(foo MODULE foo.c JNIFoo.h)
set_target_properties(foo PROPERTIES SUFFIX ".jnilib")
target_link_libraries(foo ${JNI_LIBRARIES})
# add test to run JNIFoo
add_test(NAME TestJNIFoo
COMMAND ${Java_JAVA_EXECUTABLE}
-Djava.library.path=${CMAKE_CURRENT_BINARY_DIR}
-cp ${_jarFile} JNIFoo)
The file JNIFoo.java contains a Java class that declares a function in foo.c as a native method nativeFoo. foo.c contains the C implementation of the method nativeFoo.
The CMake function add_jar compiles the Java class to a jar file and, as a side effect, creates a class file which needs to be passed as an input to the javah C stub file generator executable. A custom command is used to invoke javah to generate the stub header JNIFoo.h as an output file.
Because Java uses System.loadLibrary to load JNI libraries at runtime, the JNI library must be generated as a MODULE library using the CMake command add_library. Adding JNIFoo.h as a source file ensures that JNIFoo.h will be created before the library is compiled. The compiled JNI library needs to be linked with the JDK JNI libraries contained in the variable JNI_LIBRARIES. JNI_INCLUDE_DIRS contains the the JNI include dirs to use.
Finally, a test is added to run the class JNIFoo with the JVM. The system property java.library.path must be set to the directory containing the generated JNI library libfoo.jnilib.
Related
Edit 2 - Fixed!
The issue was fixed by correctly using absolute paths rather than relative paths, and by adding add_subdirectory to example/CMakelists.txt.
I have updated the provided code (and will leave the repository in case somebody wants to use it as a starting point.
Edit:
Adjusted CMakelists.txt files to use absolute paths only (as per recommendation from #Tsyvarev
Added exact compilation error message
Original post:
I am trying to write a library, that contains example project, and can be added as a Git submodule. The desired structure would be this:
- source
- MyLib
lib.cpp
- CMakelists.txt
- include
- MyLib
lib.h
- example
main.cpp
CMakelists.txt
CMakelists.txt (main CMake for library)
What am I trying to achieve:
The structure should probably more or less stay, so one can install the library just by adding a gitmodule
The example project should be capable of running on its own by loading the CMakelists.txt in the directory, and should be able to use the library
What is my problem:
My biggest problem is linking the example to the library which lives in a sibling folder, and make sure it compiles. I managed to write a CMakelists.txt in a way that my IDE understands #include statements correctly, but during compilation the function definitions are not found.
Could anyone provide some pointers, please?
Exact compilation error message:
C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/10.3.0/../../../../x86_64-w64-mingw32/bin/ld.exe: cannot find -lMY_LIB
collect2.exe: error: ld returned 1 exit status
mingw32-make[3]: *** [CMakeFiles\MY_LIB_EXAMPLE.dir\build.make:95: MY_LIB_EXAMPLE.exe] Error 1
mingw32-make[2]: *** [CMakeFiles\Makefile2:82: CMakeFiles/MY_LIB_EXAMPLE.dir/all] Error 2
mingw32-make[1]: *** [CMakeFiles\Makefile2:89: CMakeFiles/MY_LIB_EXAMPLE.dir/rule] Error 2
mingw32-make: *** [Makefile:123: MY_LIB_EXAMPLE] Error 2
CMakelists.txt files
CMakelists.txt:
cmake_minimum_required(VERSION 3.20)
set(CMAKE_CXX_STANDARD 20)
project(MY_LIB)
add_subdirectory(source)
source/CMakelists.txt
add_library(MY_LIB MyLib/library.cpp ../include/MyLib/library.h)
target_include_directories(MY_LIB PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include/MyLib)
target_include_directories(MY_LIB PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/source/MyLib)
example/CMakelists.txt
cmake_minimum_required(VERSION 3.20)
set(CMAKE_CXX_STANDARD 20)
#--------------------------------------------------------------------
# Get solution root
#--------------------------------------------------------------------
cmake_path(GET CMAKE_CURRENT_SOURCE_DIR PARENT_PATH LIB_PATH)
cmake_path(SET LIB_INCLUDE_PATH "${LIB_PATH}/include")
message(${LIB_PATH})
message(${LIB_INCLUDE_PATH})
#--------------------------------------------------------------------
# Set project name
#--------------------------------------------------------------------
project(MY_LIB_EXAMPLE)
#--------------------------------------------------------------------
# Add source
#--------------------------------------------------------------------
add_executable(MY_LIB_EXAMPLE main.cpp)
add_subdirectory(${LIB_PATH} library-build)
#--------------------------------------------------------------------
# Link libraries
#--------------------------------------------------------------------
target_link_libraries(MY_LIB_EXAMPLE MY_LIB)
#--------------------------------------------------------------------
# Link include directories
#--------------------------------------------------------------------
target_include_directories(MY_LIB_EXAMPLE PUBLIC ${LIB_INCLUDE_PATH})
I created a sample repository with my setup: https://github.com/jiriKralovec/cmake-library
he example project should be capable of running on its own by loading the CMakelists.txt in the directory, and should be able to use the library
Just add:
add_subdirectory(./../ some_unique_name_here)
I think I would remove source/CMakelists.txt and write it all in root CMakelists.txt. It's odd to use ../ to refer to include directories.
I suggest doing options and/or unit tests:
root CMakeLists.txt:
include(CTest)
add_library(MY_LIB ....)
# one design
if (BUILD_TESTING)
add_subdirectory(example)
endif()
# another design
add_subdirectory(utilities)
example/CMakeLists.txt:
# if it is something simple, add unit test:
add_executable(MY_LIB_EXAMPLE1 <maybe EXCLUDE_FROM_ALL?> ...)
add_test(NAME MY_LIB_EXAMPLE1 COMMAND MY_LIB_EXAMPLE1)
utilities/CMakeLists.txt:
# if it is a utility, optionally build it
add_executable(MY_LIB_UTILITY_TO_DO_SMTH ...)
option(... BUILD_MY_LIB_UTILITY_TO_DO_SMTH OFF)
if(NOT BUILD_MY_LIB_UTILITY_TO_DO_SMTH)
set_target_properties(
MY_LIB_UTILITY_TO_DO_SMTH
PROPERTIES EXCLUDE_FROM_ALL ON
)
endif()
Either way, do one CMake build. Then if user wants to build the utility, he will do cmake --build <builddir> --target MY_LIB_UTILITY_TO_DO_SMTH (or make MY_LIB_UTILITY_TO_DO_SMTH). If you would want to build unit tests, you would do cmake ... -D BUILD_TESTING=1.
Some people add all unit tests executables as EXCLUDE_FROM_ALL and make special add_custom_target(build_tests) add_target_dependencies(build_tests MY_LIB_EXAMPLE1 etc. etc.) and build that custom target before testing.
I am trying to link the following library : nngpp
using the following commands
mkdir build
cd build
cmake ..
make
make install
However when testing the demos or using the library in a project with the following in CMakeLists.txt :
...
add_executable(target main.cpp)
target_link_libraries(target nngpp)
I get the following error:
fatal error: 'nngpp/nngpp.h' file not found
#include <nngpp/nngpp.h>
^~~~~~~~~~~~~~~
1 error generated.
make[2]: *** [CMakeFiles/rest.dir/rest/server.o] Error 1
make[1]: *** [CMakeFiles/rest.dir/all] Error 2
make: *** [all] Error 2
note : The library is header-only. but I don't want to copy it in my project.
With
find_package(nngpp)
using the library is straightforward:
add_executable(target main.cpp)
target_link_libraries(target nng::nngpp)
Here nng::nngpp is IMPORTED library, so it cares about include directories for you.
It's hard to know exactly what's wrong without more information. I would suggest running make VERBOSE=1. This will show you the command line that is being executed when trying to compile the file that gives you the error.
Look in the command line for include flags (e.g. -I flags if you're using gcc). Find the directory for nngpp and double-check if your header files are in there, and what the correct path relative to that directory is to reference the header file. Maybe you need to only #include <nngpp.h> instead?
The following worked :
cmake_minimum_required(VERSION 3.0 FATAL_ERROR)
project(comm)
set(CMAKE_CXX_STANDARD 11)
find_package(nng REQUIRED)
find_package(Threads)
find_package(nngpp REQUIRED)
add_executable(server src/tfo-server.cpp)
target_link_libraries(server nng::nng nng::nngpp)
This also worked:
cmake_minimum_required(VERSION 3.0 FATAL_ERROR)
project(comm)
set(CMAKE_CXX_STANDARD 11)
add_executable(server src/tfo-server.cpp)
include_directories(server /usr/local/include)
I'm trying to link a C++ project to the RCpp library; the file is called Rcpp.so, not the linux-default libRcpp.so. Furthermore, the library resides at the non-standard location /usr/lib/R/site-library/Rcpp/libs.
So I tried using a combination of find_library and target_link_libraries:
cmake_minimum_required(VERSION 3.8)
project("R-Tests")
find_library(RCPP
NAMES Rcpp.so
HINTS /usr/lib/R/site-library/Rcpp/libs
)
if (NOT RCPP)
message(FATAL_ERROR "Could not find Rcpp - exiting.")
else()
message("Found Rcpp: " ${RCPP})
endif()
# test target
add_executable(rcpptest main.cpp)
target_link_libraries(rcpptest ${RCPP})
Configuring works fine, CMake outputs:
Found Rcpp: /usr/lib/R/site-library/Rcpp/libs/Rcpp.so
However, during build, CMake passes -lRcpp to the compiler, which causes the compilation to fail, since the library file is not named libRcpp.so but instead Rcpp.so:
[100%] Linking CXX executable rcpptest
/usr/bin/cmake -E cmake_link_script CMakeFiles/rcpptest.dir/link.txt --verbose=1
c++ CMakeFiles/rcpptest.dir/main.cpp.o -o rcpptest -L/usr/lib/R/site-library/Rcpp/libs -Wl,-rpath,/usr/lib/R/site-library/Rcpp/libs -lRcpp
/usr/bin/ld: cannot find -lRcpp
collect2: error: ld returned 1 exit status
Since the message line prints the full path to the Rcpp.so file just fine, is there any way to let target_link_libraries just add this path to the compiler instead of a combination of -L and -l?
According to this question, this should be disabled by adding cmake_policy(SET CMP0060 NEW); however, I can't see any change in the behavior of CMake if I set this to NEW or OLD.
You may have been bitten by the OLD (default) behavior of CMP0060, which converts absolute paths back to -lfoo.
Alternatively, define and use an IMPORTED target:
add_library(Rcpp SHARED IMPORTED)
set_property(TARGET Rcpp PROPERTY IMPORTED_LOCATION /usr/lib/R/site-library/Rcpp/libs/Rcpp.so)
target_link_libraries(rcpptest Rcpp)
I am working to switch an existing project from manual Makefiles to CMake (because the project is growing quite large), but there is an issue when I run an executable which is linked to a shared library which I create.
The my directory structure is as follows:
--CMakeLists.txt
--include
|
--include_files.h
--src
|
--src_files.cpp
--test
|
--CMakeLists.txt
--test1.cpp
The CMakeLists.txt in the root directory is as follows:
project(Project)
include_directories(${PROJECT_SOURCE_DIR}/include/)
file(GLOB SOURCES "${PROJECT_SOURCE_DIR}/src/*.cpp")
add_library(Project SHARED ${SOURCES})
include_directories($ENV{EXTERN_INCLUDE}/lnInclude)
target_link_libraries(Project PRIVATE "$ENV{EXTERN_LIBBIN}/libextern.so")
add_subdirectory(test)
The CMakeLists.txt in the test directory is as follows:
cmake_minimum_required(VERSION 2.8.9)
include_directories(${PROJECT_SOURCE_DIR}/include/)
add_executable(test1 test1.cpp)
include_directories($ENV{EXTERN_INCLUDE}/lnInclude)
target_link_libraries(test1 PRIVATE "$ENV{EXTERN_LIBBIN}/libextern.so")
add_dependencies(test1 Project)
target_link_libraries(test1 PUBLIC ${CMAKE_BINARY_DIR}/libProject.so)
When I run cmake, then make, the shared library libProject.so is built (no errors), and the executable test1 is created, but when test1 is run, there are errors during runtime.
If I add these lines to the CMakeLists.txt in the test directory, the executable and shared library are built, and work perfectly:
file(GLOB SOURCES "${PROJECT_SOURCE_DIR}/src/*.cpp")
add_executable(test1 test1.cpp ${SOURCES})
This is not ideal though, because it means that I am compiling the src twice.
I have also tried to manually construct the Makefile for the test, but not the shared library, and this works fine without having to recompile the src. This makes me think that the issue is due to my use of cmake.
My question is: How do I correctly link the test executable with the shared library using CMake?
An example of the runtime error is as follows:
A was returned outside range, A=-nan
Inside the test function, I construct a class and pass it some info (node is defined in include and compiled into libProject.so):
Node& station1(*new Node(station1));
station1.X(X);
station1.Y(Y);
station1.Z(Z);
Then I call the functions of the class:
station1.A();
I expect this function to return a number in a predefined range, but it does not.
This is the output from gdb which makes me suspect that the shared library is not being loaded:
Function "Node::A()" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 3 (Node::A()) pending.
(gdb) continue
Continuing.
terminate called after throwing an instance of 'std::range_error'
what(): Error: A lower than 0
For the test1 target, don't manually add the dependency to Project or link with the libProject.so file.
Instead let CMake handle everything, simply by doing e.g.
include_directories(${PROJECT_SOURCE_DIR}/include/)
add_executable(test1 test1.cpp)
target_link_libraries(test1 Project)
Note that I also removed the extern dependency. If the test program doesn't actually use that library it's not needed..
I'll begin stating that I'm almost complete dumb in Cmake matter.
I have the following CMakeLists.txt for a Kdevelop 4.1 project:
project(uart)
find_package(KDE4 REQUIRED)
include (KDE4Defaults)
include_directories( ${KDE4_INCLUDES} ${QT_INCLUDES} src/include src/include/QSerialDevce )
add_subdirectory(doc)
add_subdirectory(src)
add_subdirectory(icons)
link_directories(/usr/lib)
find_library(SERIALDEVICE_LIB qserialdeviced)
add_executable(uart ${uart_SRCS})
target_link_libraries(uart ${SERIALDEVICE_LIB})
When I try to build my project I see:
uart/build> make -j2
-- Found Qt-Version 4.6.3 (using /usr/bin/qmake-qt4)
-- Found X11: /usr/lib64/libX11.so
-- Found KDE 4.5 include dir: /usr/include/kde4
-- Found KDE 4.5 library dir: /usr/lib64/kde4/devel
-- Found the KDE4 kconfig_compiler4 preprocessor: /usr/bin/kconfig_compiler4
-- Found automoc4: /usr/bin/automoc4
CMake Error at CMakeLists.txt:16 (add_executable):
add_executable called with incorrect number of arguments
CMake Error: Attempt to add link library "/usr/lib/libqserialdeviced.so" to target "uart" which is not built by this project.
-- Configuring incomplete, errors occurred!
make: *** [cmake_check_build_system] Error 1
*** Failed ***
Everything I read says that add_executable and target_link_libraries should look like the last two lines of my file:
add_executable(uart ${uart_SRCS})
target_link_libraries(uart ${SERIALDEVICE_LIB})
If I change those two lines of CMakeLists.txt leaving it as:
project(uart)
find_package(KDE4 REQUIRED)
include (KDE4Defaults)
include_directories( ${KDE4_INCLUDES} ${QT_INCLUDES} src/include src/include/QSerialDevce )
add_subdirectory(doc)
add_subdirectory(src)
add_subdirectory(icons)
link_directories(/usr/lib)
find_library(SERIALDEVICE_LIB qserialdeviced)
target_link_libraries(${SERIALDEVICE_LIB})
I see:
uart/build> make -j2
-- Found Qt-Version 4.6.3 (using /usr/bin/qmake-qt4)
-- Found X11: /usr/lib64/libX11.so
-- Found KDE 4.5 include dir: /usr/include/kde4
-- Found KDE 4.5 library dir: /usr/lib64/kde4/devel
-- Found the KDE4 kconfig_compiler4 preprocessor: /usr/bin/kconfig_compiler4
-- Found automoc4: /usr/bin/automoc4
-- Configuring done
-- Generating done
-- Build files have been written to: uart/build
[ 11%] Built target doc-handbook
[ 11%] Built target uart_automoc
Linking CXX executable uart
CMakeFiles/uart.dir/uart.o: In function `uart::setupSerial()':
uart/src/uart.cpp:126: undefined reference to `AbstractSerial::AbstractSerial(QObject*)'
CMakeFiles/uart.dir/uart.o: In function `uart::setupEnumerator()':
uart/src/uart.cpp:108: undefined reference to `SerialDeviceEnumerator::SerialDeviceEnumerator(QObject*)'
CMakeFiles/uart.dir/uart.o: In function `uart::setupSerial()':
uart_/uart/src/uart.cpp:136: undefined reference to `AbstractSerial::enableEmitStatus(bool)'
CMakeFiles/uart.dir/uart.o: In function `uart::setupEnumerator()':
uart_/uart/src/uart.cpp:112: undefined reference to `SerialDeviceEnumerator::setEnabled(bool)'
collect2: ld returned 1 exit status
make[2]: *** [src/uart] Error 1
make[1]: *** [src/CMakeFiles/uart.dir/all] Error 2
make: *** [all] Error 2
*** Failed ***
That clearly shows that target_link_libraries is not linking my qserialdeviced.
qserialdeviced is at /usr/lib/libqserialdeviced.so.1.0.0, correctly simlinked to /usr/lib/libqserialdeviced.so and easily found if I manually add it in the Makefile.
I obviously tried:
target_link_libraries(-lqserialdeviced)
with no change.
I also tried:
if ("${SERIALDEVICE_LIB}" STREQUAL "SERIALDEVICE_LIB-NOTFOUND")
message(FATAL_ERROR "'qserialdeviced' wasn't found!")
else()
message("'qserialdeviced' found: " ${SERIALDEVICE_LIB})
endif ()
But this test succeeds. The library is found:
'qserialdeviced' found: /usr/lib/libqserialdeviced.so
Can anybody please help me to understand what happens here?
I am using Linux Fedora 13, cmake version 2.8.0, gcc (GCC) 4.4.5 20101112 (Red Hat 4.4.5-2) and kdevelop-4.1.0-1.fc13.x86_64.
Thanks i advance.
EDIT:
As suggested by #DatChu, I split my CMakeLists.txt across my subdirectories and everything makes sense to me now.
Thanks everbody!
For the original CMakeLists.txt file, the problem is not with target_link_libraries but with add_executable
add_executable(uart ${uart_SRCS})
where did you set your uart_SRCS variable? Do you have
set(uart_SRCS src/blahblah.cpp src/somethingblahblah.cpp)
I think you might misunderstand what add_subdirectory does. It does not add the source files inside. It tells CMake to descend into that folder and look for another CMakeLists.txt. You typically use it when you have a sub-project inside of your project folder.
If you have many source files which you don't want to manually set, you can also do
file(GLOB uart_SRCS src/*.cpp src/*.c)
The downside is you need to manually re-run CMake in order for it to detect new files. See Jack's comment on why this might not be what you want to use.
Your CMakeLists.txt will most likely be
project(uart)
find_package(Qt4 REQUIRED)
include (${QT_USE_FILE})
find_package(KDE4 REQUIRED)
include (KDE4Defaults)
include_directories( ${KDE4_INCLUDES} ${QT_INCLUDES} src/include src/include/QSerialDevice )
link_directories(/usr/lib)
file(GLOB uart_SRCS src/*.cpp src/*.h)
file(GLOB uart_HDRS include/*.h include/QSerialDevice/*.h)
find_library(SERIALDEVICE_LIB qserialdeviced)
add_executable(uart ${uart_SRCS} ${uart_HDRS})
target_link_libraries(uart ${SERIALDEVICE_LIB} ${QT_LIBRARIES})
This isn't really a direct solution, but I was having such difficulty with "undefined reference" errors (solved previously by linking the appropriate libraries, but not in this case), until I just discovered something - an incompatibility with c vs cpp somehow. The files that defined these reference functions were in .c files (which would default cmake to compile with a C compiler.) and my file referencing these functions is a .cpp file (using g++ compiler or whatever your environment c++ compiler is). Once I changed the .c file to .cpp the "undefined reference" errors disappeared. Above it looks like your uart file is .cpp, but maybe check what the other files are and try this method. It's probably not the appropriate solution or even one at all, but this might get you through the day and moving forward.