Search code examples
c++cmakebuildlinkerconfiguration-files

How do I set up a CMake project with subfolders?


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}
  )

Solution

  • 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).