Understanding writing a CMakeLists.txt [duplicate] - c++

This question already has answers here:
CMake link to external library
(6 answers)
Closed 2 years ago.
I have the following code that works perfectly with gcc by running the command:
gcc -L ~/Installed/C_LIBS/cmocka/lib -I ~/Installed/C_LIBS/cmocka/include hello.c -lcmocka -o hello
When I try to convert that to a CMakeLists.txt it breaks after running cd build && cmake .. && make with the following error codes:
Scanning dependencies of target hello
[ 50%] Building C object CMakeFiles/hello.dir/main.c.o
[100%] Linking C executable hello
ld: library not found for -lcmocka
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make[2]: *** [hello] Error 1
make[1]: *** [CMakeFiles/hello.dir/all] Error 2
make: *** [all] Error 2
I have the code setup like this:
my-proj/
- CMakeLists.txt
- main.c
- build/
Here are my files:
main.c
#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include <cmocka.h>
/* A test case that does nothing and succeeds. */
static void null_test_success(void **state) {
(void) state; /* unused */
}
int main(void) {
const struct CMUnitTest tests[] = {
cmocka_unit_test(null_test_success),
};
return cmocka_run_group_tests(tests, NULL, NULL);
}
CMakeLists.txt
cmake_minimum_required(VERSION 2.8.9)
project (hello)
include_directories(
SYSTEM ~/Installed/C_LIBS/cmocka/lib
SYSTEM ~/Installed/C_LIBS/cmocka/include
)
add_executable(hello main.c)
target_link_libraries(hello cmocka)
Can someone please tell me what I am doing wrong here? Also maybe point me in the direction of where to learn CMake better?

The include_directories() command only affects header search paths (stuff that is used through #include). It has no effect on library search paths.
Since you know the full path of the library, you should just be linking directly against said full path:
target_link_libraries(hello ~/Installed/C_LIBS/cmocka/lib/libcmocka.a)
However, this is just patching your CMakeLists.txt. What you should be doing is use CMake's library functions, which will be a lot more flexible:
# Include dir
find_path(MOCKA_INCLUDE_DIR
NAMES cmocka.h
PATHS ~/Installed/C_LIBS/cmocka/include
)
#library itself
find_library(MOCKA_LIBRARY
NAMES cmocka
PATHS ~/Installed/C_LIBS/cmocka/lib
)
target_include_directories(hello PRIVATE ${MOCKA_INCLUDE_DIR})
target_link_libraries(hello ${MOCKA_LIBRARY})

Related

CMake does not link Boost correctly, but compiling from a terminal does

I am running Ubuntu 21.04 on a Raspberry Pi 4b (8gb), and am using QtCreator for an IDE, with default settings, which landed on Clang as a compiler.
Most code (excepting libconfig) ran perfectly fine, but for reasons unknown to me, linking Boost::filesystem and Boost::system is proving impossible. See the minimal code below for the snippet I have been testing with:
#include <boost/filesystem.hpp>
#include <iostream>
int main()
{
boost::filesystem::path full_path(boost::filesystem::current_path());
std::cout << "Current path is : " << full_path << std::endl;
return 0;
}
If I open a terminal on this source file, and run
clang++ main.cpp -lboost_system -lboost_filesystem
I get an output that behaves exactly as one'd expect, reporting '/home/username/repo/boostsandbox/'.
Now, if I attempt to compile with CMake, using the following CMakeLists.txt:
cmake_minimum_required(VERSION 3.5)
project(boostsandbox LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Compiler Options --------------------
add_compile_options(--verbose)
# Package Management ------------------
find_package( Boost COMPONENTS filesystem system REQUIRED)
include_directories(... ${Boost_INCLUDE_DIRS})
link_directories(... ${Boost_LIBRARY_DIRS})
message("Found Boost, {${Boost_LIBRARIES}}, at ${Boost_INCLUDE_DIR}")
add_executable(boostsandbox main.cpp)
link_libraries(boostsandbox Boost::filesystem Boost::system)
I receive the CMake parsing message 'Found Boost, {Boost::filesystem;Boost::system}, at /usr/include', and building halts at compile time, reporting the following:
/usr/bin/ld: CMakeFiles/boostsandbox.dir/main.cpp.o: in function `boost::filesystem::current_path()':
/usr/include/boost/filesystem/operations.hpp:244: undefined reference to `boost::filesystem::detail::current_path(boost::system::error_code*)'
clang: error: linker command failed with exit code 1 (use -v to see invocation)
gmake[2]: *** [CMakeFiles/boostsandbox.dir/build.make:133: boostsandbox] Error 1
gmake[1]: *** [CMakeFiles/Makefile2:95: CMakeFiles/boostsandbox.dir/all] Error 2
gmake: *** [Makefile:103: all] Error 2
10:13:31: The process "/usr/bin/cmake" exited with code 2.
I am starting to feel like I've used every trick possible by now (including the C++11 enum define that gets Boost to work for some people), but I'm completely stumped. Please help!
TLDR:
Compiling with g++ or clang++, linking lboost_filesystem + lboost_system, building and executing functions perfectly. Building via the QtCreator IDE using CMake, compilation halts on a linking error, while Boost is found and linked.

CMake Beginner's Question: project with 3rd party library gets not compiled

So I am struggeling with cmake for some time now. I want to use the xmlrpc-c library from here. So I started a new project with main.cpp and CMakeLists.txt and copied the xmlrpc-c as a subdirectory into my project (since xmlrpc-c is unfortunately not a cmake library):
My code is exactly a example from here and looks like this:
#include <iostream>
#include <string>
#include "xmlrpc-c/include/xmlrpc-c/base.hpp"
#include "xmlrpc-c/include/xmlrpc-c/registry.hpp"
#include "xmlrpc-c/include/xmlrpc-c/server_abyss.hpp"
using namespace std;
class hello : public xmlrpc_c::method
{
public:
void execute(const xmlrpc_c::paramList& params, xmlrpc_c::value* retval)
{
string msg(params.getString(0));
params.verifyEnd(1);
cout << msg << endl;
*retval = xmlrpc_c::value_string("XMLRPC server says hello!");
}
};
int main(int argc, char** argv)
{
xmlrpc_c::registry registry;
registry.addMethod("hello", new hello);
xmlrpc_c::serverAbyss server(xmlrpc_c::serverAbyss::constrOpt().registryP(&registry).portNumber(8080));
server.run();
return 1;
}
CMakeLists.txt looks like this
cmake_minimum_required(VERSION 3.16)
project(xmlrpc_c_server C CXX)
add_executable(xmlrpc_c_server main.cpp)
target_link_libraries(xmlrpc_c_server -lxmlrpc++ -lxmlrpc_server++ -lxmlrpc_server_abyss++ -lxmlrpc_util++)
The problem I have is that my build-process fails with a linker-error: as far as I understand is the header file registry.hpp not included correctly. If I comment out the code line registry.addMethod("hello", new hello);, I can compile the program without any errors.
====================[ Build | xmlrpc_c_server | Debug ]=========================
/usr/bin/cmake --build /mnt/c/Users/valentin.ackva/CLionProjects/xmlrp-c-server/cmake-build-debug --target xmlrpc_c_server -- -j 9
Scanning dependencies of target xmlrpc_c_server
[ 50%] Building CXX object CMakeFiles/xmlrpc_c_server.dir/main.cpp.o
[100%] Linking CXX executable xmlrpc_c_server
/usr/bin/ld: CMakeFiles/xmlrpc_c_server.dir/main.cpp.o: in function `main':
/mnt/c/Users/struppel/CLionProjects/xmlrp-c-server/main.cpp:31: undefined reference to `xmlrpc_c::registry::addMethod(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, xmlrpc_c::method*)'
collect2: error: ld returned 1 exit status
make[3]: *** [CMakeFiles/xmlrpc_c_server.dir/build.make:84: xmlrpc_c_server] Error 1
make[2]: *** [CMakeFiles/Makefile2:76: CMakeFiles/xmlrpc_c_server.dir/all] Error 2
make[1]: *** [CMakeFiles/Makefile2:83: CMakeFiles/xmlrpc_c_server.dir/rule] Error 2
make: *** [Makefile:118: xmlrpc_c_server] Error 2
What is missing?
Since the library you want to use doesn't have a cmake project you need to handle this on your own. I would suggest using add_subdirectory and creating a sub-project, where you build the library (either as shared or static depending on your needs).
In addition you need to point cmake to the location of the headers. Try manually adding what's missing to the add_executable or use include_directories and adjust your #includes accordingly.
You can also use some unofficial cmake version of it like this one. If you are using git you can add the repo as a submodule and integrate the code from that repo into your main project.
Since you are new to CMake maybe it's worth looking at a tutorial instead?
https://cliutils.gitlab.io/modern-cmake/
I think it would help your confusion a lot.

How to link libavformat in Cmake on mac?

I am trying to use the libavformat from ffmpeg in a C++ project. I have ffmpeg installed using homebrew.
My CMakeLists.txt :
cmake_minimum_required(VERSION 3.14)
project(av_test)
set(CMAKE_CXX_STANDARD 11)
INCLUDE_DIRECTORIES(/usr/local/Cellar/ffmpeg/4.2.1_2/include)
LINK_DIRECTORIES(/usr/local/Cellar/ffmpeg/4.2.1_2/lib)
add_executable(av_test main.cpp)
TARGET_LINK_LIBRARIES(av_test libavformat)
When running cmake I get this error:
ld: library not found for -llibavformat
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make[3]: *** [av_test] Error 1
make[2]: *** [CMakeFiles/av_test.dir/all] Error 2
make[1]: *** [CMakeFiles/av_test.dir/rule] Error 2
make: *** [av_test] Error 2
A quick find libavformat* into /usr/local/Cellar/ffmpeg/4.2.1_2/lib returns:
libavformat.58.29.100.dylib
libavformat.58.dylib
libavformat.a
libavformat.dylib
Also in /usr/local/Cellar/ffmpeg/4.2.1_2/include/libavformat there is avformat.h
My main.cpp:
#include <libavformat/avformat.h>
int main() {
AVFormatContext *pFormatContext;
return 0;
}
I am running on mac os 10.14 with cmake version 3.15.5 , ffmpeg version 4.2.1
The problem here is noted from the error:
ld: library not found for -llibavformat
As you can see, it has both '-l' and 'lib' as prefix even though '-l' should replace 'lib' before the library name. It seems the TARGET_LINK_LIBRARIES function parses library names with a prefix -l (or lib) automatically attached. So, you need to write either of the following:
TARGET_LINK_LIBRARIES(av_test avformat)
TARGET_LINK_LIBRARIES(av_test -lavformat)
Adding -l at start doesn't make a difference, but is still accepted by cmake.

Minimal example with GLFW fails because of undefined symbols for architecture x86_64

I am trying to compile the following minimal example for GLFW:
#include <GLFW/glfw3.h>
#include <thread>
int main() {
glfwInit();
std::this_thread::sleep_for(std::chrono::seconds(1));
glfwTerminate();
return 0;
}
My CMakeLists.txt file is:
cmake_minimum_required(VERSION 3.9)
project(viewer)
set(CMAKE_CXX_STANDARD 11)
find_package(glfw3 3.2 REQUIRED)
include_directories(${GLFW_INCLUDE_DIRS})
add_executable(viewer main.cpp)
target_link_libraries(viewer ${GLFW_LIBRARIES})
If I try to compile the code, it fails with the following error message:
[ 50%] Linking CXX executable visualiser
Undefined symbols for architecture x86_64:
"_glfwInit", referenced from:
_main in main.cpp.o
"_glfwTerminate", referenced from:
_main in main.cpp.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make[3]: *** [visualiser] Error 1
make[2]: *** [CMakeFiles/visualiser.dir/all] Error 2
make[1]: *** [CMakeFiles/visualiser.dir/rule] Error 2
make: *** [visualiser] Error 2
I see the similar questions, e.g.:
Trouble compiling GLFW, undefined symbols
http://dudandan.com/2017/02/15/Setup-glfw-and-glew/
But their solutions did not really help.
UPD: Following the comment of #thomas_f, I modified my CMakeLists.txt file as follows:
cmake_minimum_required(VERSION 3.9)
project(viewer)
set(CMAKE_CXX_STANDARD 11)
find_package(glfw3 3.2 REQUIRED)
include_directories(${GLFW3_INCLUDE_DIR})
add_executable(viewer main.cpp)
target_link_libraries(viewer ${GLFW3_LIBRARY})
message(GLFW LIB: ${GLFW3_LIBRARY})
I also made sure that there is no CMakeCache.txt in my build directory:
$ ls -a
. .. .idea CMakeLists.txt cmake-build-debug main.cpp
However, I still get the same error message. It seems, that
/Applications/CLion.app/Contents/bin/cmake/bin/cmake -DCMAKE_BUILD_TYPE=Debug -G "CodeBlocks - Unix Makefiles" ........./viewer
GLFWLIB:
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/denis/Documents/projects/theia/code/visualiser/cmake-build-debug
[Finished]
So it seems that ${GLFW3_LIBRARY} is not defined.
From glfw3Config.cmake:
# - Config file for the glfw3 package
# It defines the following variables
# GLFW3_INCLUDE_DIR, the path where GLFW headers are located
# GLFW3_LIBRARY_DIR, folder in which the GLFW library is located
# GLFW3_LIBRARY, library to link against to use GLFW
It seems that you are referencing the wrong variables. Also, there is no need to invoke include_directories() in this case, since GLFW is obviously installed in a standard location, i.e. your compiler already knows where to find it.
Edit: I was able to reproduce the linker error and changing the variable name to GLFW3_LIBRARY fixed it.
Clarification: Change your link command to: target_link_libraries(viewer ${GLFW3_LIBRARY})
Update if you're on Mac OS: If you're experiencing the same issues as OP, it may be because glfw3Config.cmake does not export the variables mentioned in my answer. Instead it creates an imported library, glfw. In this case, the correct way to link to glfw would be to simply do this: target_link_libraries(<target> glfw).
If glfw was installed using brew, you should find the .cmake-files under: /usr/local/Cellar/glfw/<version>/lib/cmake/glfw3.

CMake Eclipse C++ Hello World easy tutorial - trouble with two projects part

I am working on a tutorial and was making great progress until I got to the hardest part. The tutorial is Using CMake: Hello World Example. The last and hardest part is the section for "Building a library and program separately". I have created the two projects in Eclipse and followed the instructions all the way to the end. The writer gives a hint that there is still more work to be done to make the CMake build successful. Meanwhile, I would like to get both projects working with each other in Eclipse.
When I try to build all I receive this error:
12:25:45 **** Build of configuration Debug for project Hello ****
make all
Building file: ../src/hello/Hello.cpp
Invoking: GCC C++ Compiler
g++ -I"/Users/pdl/Development/HelloWorld/Namer/src/namer" -include"/Users/pdl/Development/HelloWorld/Namer/src/namer/World.h" -O0 -g3 -Wall -c -fmessage-length=0 -MMD -MP -MF"src/hello/Hello.d" -MT"src/hello/Hello.d" -o "src/hello/Hello.o" "../src/hello/Hello.cpp"
Finished building: ../src/hello/Hello.cpp
Building target: Hello
Invoking: MacOS X C++ Linker
g++ -L"/Users/pdl/Development/HelloWorld/Namer" -o "Hello" ./src/hello/Hello.o -l"/Users/pdl/Development/HelloWorld/Namer/src/namer/World.h"
ld: library not found for -l/Users/pdl/Development/HelloWorld/Namer/src/namer/World.h
collect2: ld returned 1 exit status
make: *** [Hello] Error 1
12:25:45 Build Finished (took 161ms)
and the Problems are:
4 duplicate symbols for architecture x86_64
library not found for -l/Users/pdl/Development/HelloWorld/Namer/src/namer/World.h
make: ***[Hello] Error 1
make: ***[Namer] Error 1
When I try to build just the library project (Namer), I receive these errors:
12:36:11 **** Incremental Build of configuration Debug for project Namer ****
make all
Building target: Namer
Invoking: MacOS X C++ Linker
g++ -o "Namer" ./src/namer/CMakeFiles/2.8.11.2/CompilerIdCXX/CMakeCXXCompilerId.o ./src/namer/CMakeFiles/2.8.11.2/CompilerIdC/CMakeCCompilerId.o ./src/namer/World.o
duplicate symbol _main in:
./src/namer/CMakeFiles/2.8.11.2/CompilerIdCXX/CMakeCXXCompilerId.o
./src/namer/CMakeFiles/2.8.11.2/CompilerIdC/CMakeCCompilerId.o
duplicate symbol _info_compiler in:
./src/namer/CMakeFiles/2.8.11.2/CompilerIdCXX/CMakeCXXCompilerId.o
./src/namer/CMakeFiles/2.8.11.2/CompilerIdC/CMakeCCompilerId.o
duplicate symbol _info_platform in:
./src/namer/CMakeFiles/2.8.11.2/CompilerIdCXX/CMakeCXXCompilerId.o
./src/namer/CMakeFiles/2.8.11.2/CompilerIdC/CMakeCCompilerId.o
duplicate symbol _info_arch in:
./src/namer/CMakeFiles/2.8.11.2/CompilerIdCXX/CMakeCXXCompilerId.o
./src/namer/CMakeFiles/2.8.11.2/CompilerIdC/CMakeCCompilerId.o
ld: 4 duplicate symbols for architecture x86_64
collect2: ld returned 1 exit status
make: *** [Namer] Error 1
12:36:11 Build Finished (took 61ms)
and the Problems are:
4 duplicate symbols for architecture x86_64
make: ***[Namer] Error 1
Is anyone out there up to the task of helping me? This is supposed to be an easy HelloWorld example where the Hello part comes from the main project and the name comes from the Library project. It probably is easy for someone who understands configuring multiple C++ projects in Eclipse. Maybe the CMake is fighting Eclipse, I don't know.
Thank you in advance for your help.
-------------------------------------------- Source Code ----------------------------------
OK. For the Hello project, there is a CMakeLists.txt file and a FindNamer.cmake file.
CMakeLists.txt
cmake_minimum_required(VERSION 2.8.6)
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}")
find_package(Namer REQUIRED)
include_directories("${Namer_INCLUDE-DIRS}")
add_executable(hello main.cpp)
target_link_libraries(hello ${Namer_LIBRARIES})
FindNamer.cmake
find_path(Namer_INCLUDE_DIRS World.h /usr/include "$ENV{NAMER_ROOT}")
find_library(Namer_LIBRARIES namer /usr/lib "$ENV{NAMER_ROOT}")
set(Namer_FOUND TRUE)
if (NOT Namer_INCLUDE_DIRS)
set(Namer_FOUND FALSE)
endif (NOT Namer_INCLUDE_DIRS)
if (NOT Namer_LIBRARIES)
set(Namer_FOUND FALSE)
endif (NOT Namer_LIBRARIES)
And the Hello.cpp file:
#include <stdio.h>
#include "World.h"
int main() {
printf("Hello %s\n", getWorld().c_str());
return 0;
}
The Namer project has a CMakeLists.txt file:
cmake_minimum_required(VERSION 2.8.6)
project(Namer)
include_directories("${CMAKE_SOURCE_DIR}")
add_library(namer World.cpp World.h)
Here is the World.cpp:
#include "World.h"
std::string getWorld() { return "Earth"; }
Here is the World.h:
#include <string>
std::string getWorld();
Please let me know if there is anything else you need. Thank you again.