I'm new to CMake so I apologize if my question turns out to be a noob one.
I'm trying to set up a project in C++ with a directory structure similar to what Maven would create if I was coding in Java (src directory and build directory):
root
├── build (where to build)
├── CMakeLists.txt (main one)
├── compile_commands.json -> ./build/compile_commands.json
├── doc
├── Doxyfile
└── src
├── CMakeLists.txt
├── common
│ └── Terminal.hpp
├── fsa
│ ├── CMakeLists.txt
│ ├── Machine.cpp
│ ├── Machine.hpp
│ └── MachineState.hpp
└── main.cpp
I don't know how to properly set CMake so to recognize, compile and link all the files. In particular, I think I should use (a mix of) add_subdirectory(), add_executable(), link_directories(), target_link_libraries(), add_library() and target_include_directories(), but I'm not sure I got how.
I provide later my CMakeLists.txt files, but when I configure and compile, I get:
/usr/bin/ld: fsa/libfsalib.a(Machine.cpp.o): in function `Machine::addState(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, bool)':
Machine.cpp:(.text+0xd1): undefined reference to `MachineState::MachineState(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool)'
collect2: error: ld returned 1 exit status
make[2]: *** [src/CMakeFiles/elr1.dir/build.make:98: src/elr1] Error 1
make[1]: *** [CMakeFiles/Makefile2:115: src/CMakeFiles/elr1.dir/all] Error 2
make: *** [Makefile:91: all] Error 2
What do I do wrong?
EDIT: turned out it was a very stupid mistake of mine, I forgot to add an implementation. However, some questions remain:
Can you please tip me if this project/cmake structure is best practice or not?
I dind't get the where i should use link_directories() and target_include_directories().
More in general, how can I keep my codebase tidy and compile my project?
Thanks in advance
My commands are
to configure: "cmake -S /path_to_root_project -B /path_to_root_project/build -D CMAKE_EXPORT_COMPILE_COMMANDS=ON"
to compile: "cmake --build /path_to_root_project/build"
root_project/CMakeLists.txt:
cmake_minimum_required(VERSION 3.10)
# set the project name
project(elr1 VERSION 0.1)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
add_subdirectory(src)
root_project/src/CMakeLists.txt:
add_subdirectory(fsa)
# add the executable
add_executable(elr1 main.cpp)
link_directories(fsa)
target_link_libraries(elr1 #target in which link
fsalib #library name
)
root_project/src/fsa/CMakeLists.txt:
add_library(fsalib #name
Machine.cpp #files
MachineState.hpp
)
target_include_directories(fsalib PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
)
Can you please tip me if this project/cmake structure is best practice or not?
There are none, or endless, best practices, and every day someone invents a new one. Especially as to how to structure your project, which is unrelated to CMake. Structure it in a way that you want, and you judge is the best. Your structure seems completely fine.
Look a look at endless google results. What's a good directory structure for larger C++ projects using Makefile? and http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1204r0.html project.
As for CMake you can take a look at the ultimate https://github.com/friendlyanon/cmake-init , https://github.com/cmake-lint/cmake-lint , https://gist.github.com/mbinna/c61dbb39bca0e4fb7d1f73b0d66a4fd1 .
where i should use link_directories() and target_include_directories().
Generally you should prefer target stuff, i.e. target_link_directories over non-target.
Use target_include_directories to add a path to #include <thishere> search path.
Use target_link_directories to add a library path to the search path target_link_libraries(... this_library_here). Usually you want to use add_library(... IMPORTED), then find_library, instead of target_link_directories. See man ld.
In your project there are no external shared .so nor static .a libraries. I see no reason to use link_directories at all.
how can I keep my codebase tidy and compile my project?
Well, you can work hard and cleanup your project often. Remember about regular exercise, sleep and to eat healthy.
Instead of set(CMAKE_CXX_STANDARD 11) prefer target_set_properties(. .. CXX_STANDARD 11).
Related
I have a CMake project that uses mosquittopp. It is properly running on Linux without needing any additional configurations (i.e. #include <mosquittopp.h> works without target_include_directories; and libraries are linked without find_library)
However, when I try the same on Windows, I am constantly having undefined reference problems. Here is the minimal reproducible example:
Install mosquitto to the system; Here are some files from C:/Program Files/mosquitto:
├── libcrypto.dll
├── libssl.dll
├── mosquitto.dll
├── mosquitto.exe
├── mosquittopp.dll
├── devel
│ ├── mosquitto.h
│ ├── mosquitto.lib
│ ├── mosquittopp.h
│ └── mosquittopp.lib
Write and save the following to main.cpp:
#include <iostream>
#include <mosquittopp.h>
int main() {
std::cout << "hello world";
mosqpp::lib_init();
return 0;
}
Create the following CMakeLists.txt file:
111
cmake_minimum_required(VERSION 3.15)
project(sandbox LANGUAGES CXX)
add_executable(MainFile main.cpp)
target_include_directories(MainFile PUBLIC "C:/Program Files/mosquitto/devel")
target_link_libraries(MainFile "C:/Program Files/mosquitto/devel/mosquittopp.lib")
set_property(TARGET MainFile PROPERTY CXX_STANDARD 17)
set_property(TARGET MainFile PROPERTY CXX_STANDARD_REQUIRED On)
set_property(TARGET MainFile PROPERTY CXX_EXTENSIONS Off)
I tried to use different combinations of find_library(), absolute paths, adding and removing dll files, adding all lib files but to no avail.
EDIT: The error message in this example is:
[build] C:/Users/myuser/OneDrive/Desktop/test/main.cpp:6: undefined reference to `__imp__ZN6mosqpp8lib_initEv'
[build] collect2.exe: error: ld returned 1 exit status
[build] mingw32-make[2]: *** [CMakeFiles\MainFile.dir\build.make:101: MainFile.exe] Error 1
[build] mingw32-make[1]: *** [CMakeFiles\Makefile2:82: CMakeFiles/MainFile.dir/all] Error 2
[build] mingw32-make: *** [Makefile:90: all] Error 2
this is a minimal reproducible example, but the similar errors arise in the full project as well.
I think I know where the issue is now after looking at the error message and checking out mosquitopp.
My current guess is that you've downloaded pre-compiled binaries from their download page.
These binaries were built using MSVC (2019) however you are using mingw32 to build your project. This is apparent from the line here:
[build] mingw32-make[2]: *** [CMakeFiles\MainFile.dir\build.make:101: MainFile.exe] Error 1
You cannot use different compilers, the binaries will always most likely be different. So you have two options now to solve this:
Build your own mosquittopp library using mingw32
Build your application using MSVC 2019
I am working on a C project as of recently and want to learn how to use CMake properly.
The project consists of the following directory structure (as of now):
.
└── system
├── collections
│ ├── bin
│ ├── build
│ ├── includes
│ └── src
└── private
└── corelib
├── bin
├── build
├── includes
└── src
Every directory including 'bin' sub-directories is a separate library. They contain a CMakeLists.txt each.
The plan is to link the libraries in such a way that, during development, no manual recompilation of 'corelib' should be required to receive updated code from it, while also ensuring that dependencies would be resolved once all libraries get compiled as SHARED libraries and put in a place such as 'usr/local/lib' or similar.
I have a dependency of library 'corelib' in library 'collections'.
Trying to resolve said dependency, I have come up with the following CMakeLists.txt in 'collections':
cmake_minimum_required(VERSION 3.0.0)
project(collections VERSION 0.1.0 LANGUAGES C)
set(LIBRARY_OUTPUT_PATH ../bin)
add_subdirectory(../private/corelib ${LIBRARY_OUTPUT_PATH})
include_directories(./includes)
aux_source_directory(./src SOURCES)
add_library(collections SHARED ${SOURCES} main.c)
However, this does not produce the result I am looking for, as I get the following output on build:
[main] Building folder: collections
[build] Starting build
[proc] Executing command: /usr/bin/cmake --build /home/codeuntu/Repositories/netcore-c/src/system/collections/build --config Debug --target all -j 6 --
[build] gmake[1]: *** No rule to make target '../bin/all', needed by 'all'. Stop.
[build] gmake[1]: *** Waiting for unfinished jobs....
[build] Consolidate compiler generated dependencies of target collections
[build] [ 50%] Built target collections
[build] gmake: *** [Makefile:91: all] Error 2
[build] Build finished with exit code 2
It seems this is the wrong way to go about it. Any help is greatly appreciated.
This is the CMakeLists.txt for 'corelib':
cmake_minimum_required(VERSION 3.0.0)
project(corelib VERSION 0.1.0 LANGUAGES C)
include_directories(./includes)
aux_source_directory(./src SOURCES)
set(LIBRARY_OUTPUT_PATH ../bin)
add_library(corelib SHARED ${SOURCES} main.c)
Binary directory has to be a subdirectory of current dir, it can't be above ../bin. Use:
add_subdirectory(../private/corelib some_unique_name)
Overall, let's fix some issues. A more advanced CMake might look like this:
# system/CmakeLists.txt
add_subdirectory(private EXCLUDE_FROM_ALL)
add_subdirectory(collections)
# system/collections/CMakeLists.txt
cmake_minimum_required(VERSION 3.11)
project(collections VERSION 0.1.0 LANGUAGES C)
file(GLOB_RECURSE srcs *.c *.h)
add_library(collections ${srcs})
# Use only target_* intefaces
target_include_directories(collections PUBLIC
./includes
)
target_link_libraries(collections PRIVATE
corelib
)
# system/private/CMakeLists.txt
add_subdirectory(corelib)
# system/private/corelib/CMakeLists.txt
cmake_minimum_required(VERSION 3.11)
project(corelib VERSION 0.1.0 LANGUAGES C)
file(GLOB_RECURSE srcs *.c *.h)
add_library(corelib ${srcs})
target_include_directorieS(corelib PUBLIC ./includes)
# system/CMakePresets.json
{
... see documentation ...
"configurePresets": [
{
...
"cacheVariables": {
"BUILD_SHARED_LIBS": "1",
"ARCHIVE_OUTPUT_DIRECTORY": "${binaryDir}/bin",
"LIBRARY_OUTPUT_DIRECTORY": "${binaryDir}/bin",
"RUNTIME_OUTPUT_DIRECTORY": "${binaryDir}/bin"
}
}
I.e. overall, I do not think every project inside system wants to compile his own separate instance of corelib, rather one corelib should be shared. Just add corelib once, from anywhere. Note that it doesn't have to be in order - you can target_link_libraries on targets before they are defined.
Based on this stackoverflow answer to a similar question (Cmake include header only target from header only target), I am creating a header only library and trying to use it in an executable.
My folder structure is below:
├── CMakeLists.txt // root folder CMake file
├── srcs // this is the hdr only library
│ ├── src1.hpp
│ ├── CMakeLists.txt
│ ├── src2.hpp
│ └── src3.hpp
│ └── ...
└── test // this is the executable project
├── CMakeLists.txt
└── main.cpp
Root level CMakeLists.txt
cmake_minimum_required(VERSION 3.13)
project (MyProj CXX)
add_subdirectory(srcs)
add_subdirectory(test)
src level CMakeLists.txt (for header only library)
add_library(MyLib INTERFACE)
target_sources(MyLib INTERFACE
"${CMAKE_CURRENT_SOURCE_DIR}/src1.hpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src2.hpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src3.hpp"
)
target_include_directories(MyLib
INTERFACE ${CMAKE_CURRENT_BINARY_DIR})
add_subdirectory(plugins)
CMake file for executable test project
add_executable(MyTest main.cpp)
target_sources(MyTest
PRIVATE main.cpp
)
target_include_directories(MyTest PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
target_link_libraries(MyTest PUBLIC MyLib)
Though this configures cmake without warnings/error, running the make fails, indicating that the executable project is unable to find the header file(s) from library.
/.../nettu2/test/main.cpp:6:10: fatal error: src1.hpp: No such
file or directory #include "src1.hpp"
^~~~~~~~~~~~~~~~ compilation terminated. test/CMakeFiles/MyTest.dir/build.make:62: recipe for target
'test/CMakeFiles/MyTest.dir/main.cpp.o' failed make[2]: *
[test/CMakeFiles/MyTest.dir/main.cpp.o] Error 1
CMakeFiles/Makefile2:126: recipe for target
'test/CMakeFiles/MyTest.dir/all' failed make[1]: *
[test/CMakeFiles/MyTest.dir/all] Error 2 Makefile:129: recipe for
target 'all' failed make: *** [all] Error 2
I am sure that I am missing some crucial but trivial thing here, but yet unable to figure out, what is going wrong here. How can I get this build working?
You have a minor mistake in this line:
target_include_directories(MyLib
INTERFACE ${CMAKE_CURRENT_BINARY_DIR})
The include directory you specify for MyLib expands to the build directory corresponding to srcs, i.e. it results in an invocation like
clang++ /path/to/build/test/test.cpp -I /path/to/build/srcs ...
but you want to pass the actual source directory to the preprocessor. To fix this, try
target_include_directories(MyLib
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
I use CMake 3.5.2. When trying to build my C++ code I get the following error:
[100%] Linking CXX executable SomeExecutable
CMakeFiles/SomeExecutable.dir/Common/src/FunctionOne.cpp.o: In function `FunctionOne::FunctionOne(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, double, double, unsigned int, bool)':
FunctionOne.cpp:(.text+0x490): undefined reference to `Helper::initLevel(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, bool)'
The program doesn't find Helper::initLevel() in myLib.so. How do I fix it?
CMakeLists.txt: (shortened for brevity)
cmake_minimum_required(VERSION 3.0)
project(ProjectName)
add_definitions(-std=c++11)
add_definitions(-Wall)
add_definitions(-O2)
link_directories(/usr/local/lib)
add_executable(SomeExecutable SomeExecutable.cpp ${FunctionOne} ${FunctionTwo})
target_link_libraries(SomeExecutable -pthread -lboost_thread ${Boost_LIBRARIES} myLib armadillo)
File structure (shortened for brevity):
├── bin
├── CMakeCache.txt
├── cmake_install.cmake
├── CMakeLists.txt
├── Common
│ ├── include
│ │ ├── dataContainer.h
│ │ ├── FunctionOne.h
│ │ ├── FunctionTwo.h
│ └── src
│ ├── FunctionOne.cpp
│ └── FunctionTwo.cpp
├── SomeExecutable.cpp
├── myLib
│ ├── example.cpp
│ ├── include
│ │ ├── Config.h
│ │ ├── Helper.h
│ ├── libmyLib.so
UPDATE:
myLib/include/Config.h:
Namespace Helper {
[...]
Level* initLevel(const std::string& jsonLvlPath, bool twiceSpatPts = false);
[..]
}
UPDATE 2:
kepj#laptop:~/ProjectName$ make VERBOSE=1
/usr/bin/cmake -E cmake_link_script CMakeFiles/SomeExecutable.dir/link.txt --verbose=1
/usr/bin/c++ CMakeFiles/SomeExecutable.dir/SomeExecutable.cpp.o CMakeFiles/SomeExecutable.dir/Common/src/FunctionOne.cpp.o CMakeFiles/SomeExecutable.dir/Common/src/FunctionTwo.cpp.o SomeExecutable -L/usr/local/lib -rdynamic -pthread -lboost_thread -lboost_system -lboost_filesystem -lmyLib -lsymbolicc++ -lmatio -larmadillo -Wl,-rpath,/usr/local/lib
CMakeFiles/SomeExecutable.dir/Common/src/FunctionTwo.cpp.o: In function `FunctionTwo::FunctionTwo(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, double, double, unsigned int, bool)':
FunctionTwo.cpp:(.text+0x490): undefined reference to `Helper::initLevel(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, bool)'
collect2: error: ld returned 1 exit status
CMakeFiles/SomeExecutable.dir/build.make:174: recipe for target 'SomeExecutable' failed
make[2]: *** [SomeExecutable] Error 1
make[2]: Leaving directory '/home/kepj/ProjectName'
CMakeFiles/Makefile2:67: recipe for target 'CMakeFiles/SomeExecutable.dir/all' failed
make[1]: *** [CMakeFiles/SomeExecutable.dir/all] Error 2
make[1]: Leaving directory '/home/kepj/ProjectName'
Makefile:83: recipe for target 'all' failed
make: *** [all] Error 2
So there is technically still not enough information about this issue to answer this question with certainty, but I am going to share how I would debug this isssue, only knowing what is in the question, explaining each step along the way.
Make sure you are linking to the correct myLib.so
This one is pretty explanatory, and would be my best guess as to the root of this problem.
Relevant compiler flags: -L/usr/local/lib -lmyLib
When trying to find the library you specified with the -l argument to your compile command, there is a series of steps that the linker takes:
1. It sees if the argument passed to -l was an absolute path (entire path to the library, ie starting with / on *nix computers or a drive letter on Windows) or just a name. If it is a absolute path, then it knows where it is. In this case, it is just a name, so continue on to step 2.
2. The next place to look is in the paths specified in -L arguments. In this case the only -L argument is to /usr/local/lib so it looks there. This may or may not be where it is found for this specific case. Look in your filesystem to see if the file /usr/local/lib/libmyLib.so exists, and if so make sure that is the version that is most recenpt.
3. Now it searches in configured linker paths. Use the same steps as in step 2 for each of these paths.
This is my guess because there is no reference to your myLib folder in your linker arguments, yet you claim it is in that folder, so I think it is using an out of date myLib.so.
Make sure the .so or .a file actually has the symbol that is undefined
On linux, you can list the symbols defined in a .so file. Make sure the symbol that is resolving to undefined is in there.
If they aren't there, here are the possible causes:
You aren't using all the C++ source files (.cpp) to create your library.
The definition and the declaration don't match:
You forgot to enter namespace Helper before defining the function
You misspelled the function name
The arguments/return type don't match
For member functions, you forgot to include MyClass:: before the function name.
ABI mismatch
(thanks to n.m in the comments for pointing this out)
Because there are multiple implementations (and versions) of the C++ standard library, it is possible that you have an ABI Mismatch.
This means that the standard library that myLib was compiled with is not compatable with the one you are trying to compile your executable with. For example, when you compile myLib with libstdc++ on linux, then go to mac and try to compile your executable, where the standard library is libc++ you will get errors. Why? The signature for anything, for example std::string is not the same. They are different classes entirely, and will cause a signature mismatch while linking.
Would anyone be able to post a simple example of how to compile code which uses libfreenect2? After installing the library, the following structure is created in my home directory:
→ tree freenect2
freenect2
├── include
│ └── libfreenect2
│ ├── config.h
│ ├── export.h
│ ├── frame_listener.hpp
│ ├── frame_listener_impl.h
│ ├── libfreenect2.hpp
│ ├── logger.h
│ ├── packet_pipeline.h
│ └── registration.h
└── lib
├── cmake
│ └── freenect2
│ └── freenect2Config.cmake
├── libfreenect2.so -> libfreenect2.so.0.2
├── libfreenect2.so.0.2 -> libfreenect2.so.0.2.0
├── libfreenect2.so.0.2.0
└── pkgconfig
└── freenect2.pc
I attempted to compile with the .pc file using a line similar to this found on the pkg-config wikipedia page:
gcc -o test test.c $(pkg-config --libs --cflags libpng)
But came with up with this error:
./test: error while loading shared libraries: libfreenect2.so.0.2: cannot open shared object file: No such file or directory
Obviously, I messed up the compilation process somewhere, but I'm not sure where to look since this is error occurs on runtime and not at compile time. There's also a .cmake file created with the library install, which I'm sure would lead to a more robust and proper solution, but I'm not entirely sure how to use that and haven't been able to find a simple guide showing how to do so. Any links to beginner-friendly documentation are also appreciated. In the documentation for libfreenect2, it says to use this line when compiling cmake -Dfreenect2_DIR=$HOME/freenect2/lib/cmake/freenect2 -- is this something that I'd have to use when making the library or when making my application?
Another tangentially related question, would it be better to move the /include and /lib directories to /usr/local/include and /usr/local/lib respectively? I believe that would "install" the library system-wide, but I imagine there's some reason that libfreenect2 doesn't do it automatically and I'm not sure what that is.
Well, I just use cmake with a CMakeLists.txt file that I create. Do like this:
Create a CMakeLists.txt file:
cmake_minimum_required(VERSION 2.8 FATAL_ERROR)
project("My Project")
set(CMAKE_CXX_FLAGS "-std=c++11")
find_package(freenect2 REQUIRED)
include_directories("/usr/include/libusb-1.0/")
INCLUDE_DIRECTORIES(
${freenect2_INCLUDE_DIR}
)
add_executable(main ./main.cpp)
target_link_libraries(main ${freenect2_LIBRARIES})
In this file, I assume we want to compile the main.cpp file that uses libfreenect2. So, in your local directory create a build folder, using the terminal:
mkdir build && cd build
Then, run the command in the terminal:
cmake -Dfreenect2_DIR=$HOME/freenect2/lib/cmake/freenect2 .. && make
this should create main executable in the build folder. Please, note that this cmake command specifies the freenect2 directory. In this case I assume it was placed in the /home directory.
However, I understand that having to type that long cmake command or search for it on the terminal history may be boring for some people. So, it is possible to embed the command like this:
cmake_minimum_required(VERSION 2.8 FATAL_ERROR)
project("My Project")
set(CMAKE_CXX_FLAGS "-std=c++11")
# Set cmake prefix path to enable cmake to find freenect2
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} $ENV{HOME}/freenect2/lib/cmake/freenect2)
find_package(freenect2 REQUIRED)
include_directories("/usr/include/libusb-1.0/")
INCLUDE_DIRECTORIES(
${freenect2_INCLUDE_DIR}
)
add_executable(main ./main.cpp)
target_link_libraries(main ${freenect2_LIBRARIES})
After, just run this in the terminal:
mkdir build && cd build && cmake .. & make
This answer was my source for this second way of compiling the code.
Hope this helps!