CMake linking with dll on windows - undefined reference - c++

I am trying to link with a DLL executable using CMake. This works fine on Linux (of course with .so), but on Windows, it seems to be tricky. I thought it might be some other dependencies, so I tried creating a simple test program, but I couldn't get that to work either.
My folder structure is simple:
main.cpp
CmakeLists.txt
lib/libtest.dll
lib/CMakeLists.txt
I based this structure on the answer to another question that was asked.
Unfortunately, when I build, it it fails.
The content of the lib/CMakeLists.txt are:
message("-- Linking Test")
add_library(TEST libtest.dll)
set_target_properties(TEST PROPERTIES LINKER_LANGUAGE C)
And the content of the main CMakeLists.txt are:
cmake_minimum_required(VERSION 3.15)
project(Test_program)
set(CMAKE_CXX_STANDARD 14)
set(GCC_COVERAGE_COMPILE_FLAGS "-DDebug")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_COVERAGE_COMPILE_FLAGS}" )
# Contain the header files for testlib
include_directories("C:/test/utils/")
add_subdirectory(lib)
add_executable(Test_program main.cpp)
target_link_libraries(Test_program TEST)
This results in the following output:
-- Linking Test
-- Configuring done
-- Generating done
-- Build files have been written to: C:/Users/akda/Test_program/cmake-build-debug
[ 33%] Linking C static library libTEST.a
[ 33%] Built target TEST
[ 66%] Building CXX object CMakeFiles/Test_program.dir/main.cpp.obj
[100%] Linking CXX executable Test_program.exe
CMakeFiles\Test_program.dir/objects.a(main.cpp.obj): In function `powerOn()':
C:/Users/akda/Test_program/main.cpp:8: undefined reference to `My_Library_Function'
collect2.exe: error: ld returned 1 exit status
mingw32-make.exe[2]: *** [CMakeFiles\Test_program.dir\build.make:87: Test_program.exe] Error 1
mingw32-make.exe[1]: *** [CMakeFiles\Makefile2:77: CMakeFiles/Test_program.dir/all] Error 2
mingw32-make.exe: *** [Makefile:83: all] Error 2
Using tools as DLLExportViewer, I can confirm that the DLL, in fact, does contain the function called.
What am I doing wrong? I'll never claim to be an expert in CMake, but I really can't see what's wrong.
Update with link to .a file
As pointed out, I could try linking with the .a file.
I've added this to the library folder, and changed the main CMakeLists.txt to the following:
cmake_minimum_required(VERSION 3.15)
project(Test_program)
set(CMAKE_CXX_STANDARD 14)
set(GCC_COVERAGE_COMPILE_FLAGS "-DDebug")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_COVERAGE_COMPILE_FLAGS}" )
include_directories("C:/test/utils/")
find_library(TEST ${CMAKE_SOURCE_DIR}/lib/libtest.a)
add_executable(Test_program main.cpp)
target_link_libraries(Test_program ${TEST})
And this ends up in the same way, with the same errors.

Related

Boost.Test linker error by use with precompiled headers (PCH)

I have a linker error when using Boost.Test with precompiled header (PCH) that does not occur without PCH. I use the dynamically linked library as described in Usage variants.
How can I fix the error to use Boost.Test also with PCH?
The problem occurs at least with Fedora and boost 1.73 (has only dynamic libraries) and g++ 10/clang 11.
$ cmake ../ && make
-- Configuring done
-- Generating done
-- Build files have been written to: /home/.../boost_test_pch/build
[ 33%] Building CXX object CMakeFiles/boost_utf_pch.dir/test_driver.cpp.o
[ 66%] Building CXX object CMakeFiles/boost_utf_pch.dir/test.cpp.o
[100%] Linking CXX executable boost_utf_pch
[100%] Built target boost_utf_pch
vs.
$ cmake -DEDA_ENABLE_PCH=TRUE ../ && make
-- Configuring done
-- Generating done
-- Build files have been written to: /home/.../boost_test_pch/build
[ 25%] Building CXX object CMakeFiles/boost_utf_pch.dir/cmake_pch.hxx.gch
[ 50%] Building CXX object CMakeFiles/boost_utf_pch.dir/test_driver.cpp.o
cc1plus: warning: /home/.../boost_test_pch/build/CMakeFiles/boost_utf_pch.dir/cmake_pch.hxx.gch: not used because `BOOST_TEST_DYN_LINK' is defined [-Winvalid-pch]
[ 75%] Building CXX object CMakeFiles/boost_utf_pch.dir/test.cpp.o
[100%] Linking CXX executable boost_utf_pch
/usr/bin/ld: /usr/lib/gcc/x86_64-redhat-linux/10/../../../../lib64/crt1.o: in function `_start':
(.text+0x24): undefined reference to `main'
collect2: error: ld returned 1 exit status
make[2]: *** [CMakeFiles/boost_utf_pch.dir/build.make:138: boost_utf_pch] Error 1
make[1]: *** [CMakeFiles/Makefile2:95: CMakeFiles/boost_utf_pch.dir/all] Error 2
make: *** [Makefile:103: all] Error 2
I can not do anything with the warning message before ...
Here the playground files:
CMakeLists.txt:
project(boost_utf_pch LANGUAGES CXX)
cmake_minimum_required(VERSION 3.18)
add_executable(${PROJECT_NAME} "")
find_package(Boost 1.73.0 REQUIRED COMPONENTS
unit_test_framework)
target_sources(${PROJECT_NAME} PRIVATE
test_driver.cpp test.cpp)
target_link_libraries(${PROJECT_NAME} PRIVATE
Boost::unit_test_framework)
set_source_files_properties(test_driver.cpp
APPEND PROPERTIES COMPILE_DEFINITIONS "BOOST_TEST_DYN_LINK")
option(EDA_ENABLE_PCH "Enable PCH" OFF)
if (EDA_ENABLE_PCH)
target_precompile_headers(${PROJECT_NAME} PRIVATE pch.hpp)
endif()
pch.hpp
#pragma once
#include <boost/test/unit_test.hpp>
test.cpp
#include <boost/test/unit_test.hpp>
BOOST_AUTO_TEST_SUITE( my_test )
BOOST_AUTO_TEST_CASE( test_case1 )
{
BOOST_TEST_WARN( sizeof(int) < 4U );
}
BOOST_AUTO_TEST_SUITE_END()
test_driver.cpp
#define BOOST_TEST_MODULE "Boost.UTF PCH Test Suite"
#include <boost/test/unit_test.hpp>
Alan Birtles got the hint into the right direction. I was not aware of the influence of the compiler switch BOOST_TEST_DYN_LINK for the single file in the context of PCH here. A definition for all files of the project in the style of:
target_compile_definitions(${PROJECT_NAME} PRIVATE
"BOOST_TEST_DYN_LINK")
incomprehensibly does not solve the problem. Only after setting the property SKIP_PRECOMPILE_HEADERS for driver 'main' it compiles and links as expected:
project(boost_utf_pch LANGUAGES CXX)
cmake_minimum_required(VERSION 3.18)
add_executable(${PROJECT_NAME} "")
find_package(Boost 1.73.0 REQUIRED COMPONENTS
unit_test_framework)
target_sources(${PROJECT_NAME} PRIVATE
test_driver.cpp test.cpp)
target_link_libraries(${PROJECT_NAME} PRIVATE
Boost::unit_test_framework)
set_source_files_properties(test_driver.cpp
APPEND PROPERTIES COMPILE_DEFINITIONS "BOOST_TEST_DYN_LINK")
set_source_files_properties(test_driver.cpp
PROPERTIES SKIP_PRECOMPILE_HEADERS ON)
option(EDA_ENABLE_PCH "Enable PCH" ON)
if (EDA_ENABLE_PCH)
target_precompile_headers(${PROJECT_NAME} PRIVATE pch.hpp)
endif()

CMake error: linker command failed with exit code 1 and cpp.o files

I am using CMake to compile a small project.
Here is what I have written in CMakeLists.txt:
cmake_minimum_required(VERSION 3.2)
set (CMAKE_CXX_STANDARD 11)
project(DAF)
find_package(OpenCV REQUIRED)
include_directories(include)
include_directories(${OpenCV_INCLUDE_DIRS} )
file(GLOB Src_Sources "src/*.cpp")
file(GLOB Test_Sources "test/*.cpp")
add_executable(executable ${Src_Sources} ${Test_Sources})
target_link_libraries( executable include ${OpenCV_LIBS} )
I have two directories src and test. The src directory contains only the files that hold the functions, whereas the test directory contains the main file.
Once I use the cmake command and then the make command I get this error:
Scanning dependencies of target executation
[ 25%] Building CXX object CMakeFiles/executation.dir/src/image.cpp.o
[ 50%] Building CXX object CMakeFiles/executation.dir/test/starter_1.cpp.o
[ 75%] Linking CXX executable executation
ld: library not found for -linclude
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make[2]: *** [executation] Error 1
make[1]: *** [CMakeFiles/executation.dir/all] Error 2
make: *** [all] Error 2
So what I find strange is that it creates .cpp.o files. Is that normal?
And then how do I fix the error library not found for -linclude?
Yes, it is normal for CMake to take the name of the source file, and append a .o when compiling the respective object files. For example, the source file image.cpp will be compiled into image.cpp.o. The Makefiles generated by CMake will contain a unique target <someSourceFileName>.o for each source file, where <someSourceFileName> can be a .cc, .cpp, .c, etc.
The error:
ld: library not found for -linclude
indicates that you have tried to link a library called include to the executable. This is likely not what you intend. It appears include is actually your include directory, and you have already specified the include directories in your CMake with this line:
include_directories(include)
To remove the error, just take out the include from the target_link_libraries() command, like so:
target_link_libraries( executable ${OpenCV_LIBS} )

Create a CMakeFiles.txt file for newbies

I have a project with the following structure
/cmake_modules/
FindSFML.cmake
/includes/
car.hpp
motor.hpp
tires.hpp
/sources/
car.cpp
motor.cpp
tires.cpp
/main.cpp
/main.hpp
I have the following CMakeFiles.txt file:
cmake_minimum_required(VERSION 2.8)
project (MYGAME)
set (MYGAME_VERSION_MAJOR 1)
set (MYGAME_VERSION_MINOR 0)
set (EXECUTABLE_NAME "mygame")
include_directories ("${MYGAME_BINARY_DIR}")
include_directories ("${MYGAME_BINARY_DIR}/includes")
link_directories ("${MYGAME_BINARY_DIR}/sources")
add_executable(${EXECUTABLE_NAME} main.cpp)
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake_modules" ${CMAKE_MODULE_PATH})
find_package(SFML 2.0 REQUIRED system window graphics network audio)
target_link_libraries(${EXECUTABLE_NAME} ${SFML_LIBRARIES})
When I try to execute make I get this:
[100%] Building CXX object CMakeFiles/mygame.dir/main.cpp.o
Linking CXX executable mygame
CMakeFiles/mygame.dir/main.cpp.o: In function `main':
main.cpp:(.text+0x11): undefined reference to `mynamespace::Car::Instance()'
main.cpp:(.text+0x21): undefined reference to `mynamespace::Car::start()'
collect2: error: ld returned 1 exit status
make[2]: *** [mygame] Error 1
make[1]: *** [CMakeFiles/mygame.dir/all] Error 2
make: *** [all] Error 2
How do I fix it?
You need to include the rest of your sources (car.cpp, motor.cpp and tires.cpp) in the build in some way.
You can either add them along with main.cpp in the executable directly:
set(MySources sources/car.cpp sources/motor.cpp sources/tires.cpp main.cpp)
add_executable(${EXECUTABLE_NAME} ${MySources})
or you can make these into a library and link that:
set(MyLibSources sources/car.cpp sources/motor.cpp sources/tires.cpp)
add_library(MyLib ${MyLibSources})
add_executable(${EXECUTABLE_NAME} main.cpp)
...
target_link_libraries(${EXECUTABLE_NAME} MyLib ${SFML_LIBRARIES})
A couple of other points to note:
You should avoid the use of link_directories if possible (its own documentation discourages its use), and it's often helpful to include the headers in the list of files added via add_executable or add_library since these then show up in IDEs like MS Visual Studio.

Cmake target_link_libraries not linking my library

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.

Cmake compile with Frameworks on Mac OSX and treat .cpp files like .m/.mm

I'm looking for a tip to get the following to work, here is my CMakeLists.txt
# cmake_minimum_required(2.8.2)
project(boilerplate)
# base files
set(src_files
src/greet.h
src/main.cpp
)
# if on OSX, these files are needed
if(APPLE)
SET(CMAKE_EXE_LINKER_FLAGS "-framework Foundation -w")
set(src_files
${src_files}
src/mac/greet.mm
src/mac/greeting.h
src/mac/greeting.m
)
endif()
# if on windows, these files are needed
if(WIN32)
set(src_files
${src_files}
src/win/greet.cpp
)
endif()
add_executable(greeting
${src_files}
)
I require that on OSX the .cpp files are treated like .mm files (but on Windows, not) and that I can load the core foundation, etc frameworks... I'm a complete cmake newbie, so I can't even begin to know where to start, but I hope I'm somehow in the right direction, current output is:
$ cmake CMakeLists.txt && make
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/leehambley/Projects/watched.it-client
Scanning dependencies of target greeting
[ 33%] Building CXX object CMakeFiles/greeting.dir/src/mac/greet.o
Linking CXX executable greeting
Undefined symbols:
"greet()", referenced from:
_main in main.o
ld: symbol(s) not found
collect2: ld returned 1 exit status
make[2]: *** [greeting] Error 1
make[1]: *** [CMakeFiles/greeting.dir/all] Error 2
make: *** [all] Error 2
1
This turned out to be rather easy once I understood what was supposed to be happening under the hood:
set(CMAKE_CXX_FLAGS "-x objective-c++")
Which tells gcc that you want to set the language property (-x language, in man gcc) to Objective-C++.
You can also do this for individual files with:
set_source_files_properties(${SOURCE_FILES} PROPERTIES
COMPILE_FLAGS "-x objective-c++")
I've had mixed success with both, probably highlighting some of the things I don't fully understand about CMake.