This may be a tad hard to explain in full. I have setup a directory structure for a series of C++ libraries I am writing. I intend to use CMake to handle building these libraries. For the most part these libraries are completely separate "subsystems" but in the odd places one library needs access to the header file contained within another. Here is my directory structure.
base
├─ cmake
├─ docs
├─ examples
└─ sources
├─ libA
│ ├─ include
│ │ └─ libA
│ │ ├─ core.h
│ │ └─ *.h
│ ├─ source
│ │ └─*.cpp
└─ libB
├─ include
│ └─ libB
│ ├─ message.h
│ └─ *.h
└─ source
└─ *.cpp
There are CMakeLists.txt files in a few places. The main one is within the root directory base which sets up a bunch of variables and options and lastly calls add_subdirectory(sources). Within sources the CMakeLists.txt file simply calls:
add_subdirectory(libA)
add_subdirectory(libB)
Lastly there are CMakeLists.txt files in both the libA and libB directories. These are both largely the same except for the target name. An example of one of these files is:
set(target libA)
# Sources
set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}")
set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source")
# Add include directories
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/include")
# Set the source files to compile
file(GLOB_RECURSE sources ${source_path}/**/*.cpp)
# Build library
add_library(${target} ${sources})
Now separately both of these libraries can be built without issue but I need to be able to include a header from libA in a source file within libB
// base/sources/libB/source/message.cpp
#include <ctime>
#include <iostream>
#include <libA/core.h>
#include <libB/message.h>
However when I build this I get the following error:
fatal error: 'libA/core.h' file not found
I have tried using target_link_libraries and target_include_directories without any luck. Clearly I am using these wrong or simply don't understand what each of these do.
Since your library libB needs a header from libA to compile, let's say libA is a dependency of libB. Fortunately, CMake is very clever at handling dependencies, so your job is pretty easy:
libA
Instead of
# Add include directories
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/include")
add to the end
target_include_directories(${target} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
This will tell CMake that it must also include the headers for libA to the other projects that need it.
libB
Just tell CMake that libB needs libA:
target_link_libraries(${target} libA)
Note that this doesn't relate directly to the linking step when you compile. For libraries it mainly tells that there is this dependency, and in this case it will automatically add the paths for the headers automatically.
You can add to your CMakeLists.txt for libB, another include directory:
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../libA/include")
Related
I'm a little new here, this is my first post. I have a c++ project in CLion that is structured like so:
project-root/
├─ cmake-build-debug/
├─ controller/
├─ model/
│ ├─ foo/
│ │ ├─ a.h *
│ │ ├─ a.cpp *
│ ├─ bar/
│ │ ├─ b.h *
│ │ ├─ b.cpp *
├─ view/
│ ├─ c.h *
│ ├─ c.cpp *
├─ new_file
├─CMakeLists.txt
├─main.cpp *
I've starred the actual cpp and h files just for visual purposes.
In a.h, suppose I'd like to include b.h. The way I'd like to do this in a.h is by doing:
#include "project-root/model/bar/b.h"
Similarly, I'd like to use this pattern for all includes. For example, if I wanted to include a.h in c.h, in c.h I'd do:
#include "project-root/model/foo/a.h"
And, just one more example, even if I was in main.cpp and I'd like to include c.h, I'd do:
#include "project-root/view/c.h"
I tried doing the following in my CMakeLists.txt file:
cmake_minimum_required(VERSION 3.22)
project(project-root)
set(CMAKE_CXX_STANDARD 17) // this ".." seems incorrect
include_directories(..)
add_executable(project-root main.cpp <the rest of all the .h and .cpp files>)
And actually, this works! However, I think it seems a little weird to have include_directories(..), since there's a relative path above the project root. That doesn't seem correct to me.
I originally thought include_directory(.) would work, but unfortunately, no dice. When I try to do #include project-root/model/bar/b.h in a.h, I get an error saying project-root/model/bar/b.h not found
I would also advise against .. since you are depending on name of a folder that you might not necessarily control. Well, it depends on how you plan to distribute the code, for example if you use git, then project-root folder itself should be inside the repository, not the other way around.
The include paths have little to do with CMake, it will just pass them (their absolute versions) to the compiler.
In C++, you have two "types" of includes - #include "foo.h" or #include <foo.h>. Both are implementation-specific, i.e. the compiler dictates how they are interpreted. In practice, <> searches on include paths, "" searches relative paths first, then behaves like <>. One convention is to use <> for public headers in the include path and "" for relative includes or private headers.
Libraries might want to distinguish between public and private headers. Public ones are meant for the user of the library, private ones are internal. But this is not universally adhered to, sometimes the are internal directories...
Personally, I like to use the following structure for my projects:
. // root of a git repository
├── include/
│ └── project-name/
│ ├── model/
│ │ └── foo/
│ │ └── a.h
│ └── view/
│ └── c.h
├── src/
│ ├── model/
│ │ ├── foo/
│ │ │ └── a.cpp
│ │ └── bar/
│ │ ├── b.h
│ │ └── b.cpp
│ ├── view/
│ │ └── c.cpp
│ └── main.cpp
└── CMakeLists.txt
with CMakeLists.txt containing something like:
//Global
cmake_minimum_required(VERSION 3.22)
project(project-name)
//Repository-wide options
set(CMAKE_CXX_STANDARD 17)
//Local
add_executable(project-root)
target_include_directories(project-name PUBLIC include)
target_include_directories(project-name PRIVATE src)
// Project-specific options
// Assumes gcc/clang toolchain as an example.
target_compile_options(project-name -Wall -Wextra -Werror -pedantic)
target_sources(project-name
PUBLIC
include/project-name/model/foo/a.h
include/project-name/view/c.h
PRIVATE
src/model/foo/b.h
src/model/foo/b.cpp
src/view/c.cpp
src/main.cpp
)
Modern way is to use target_ variants instead of global ones.
If the repository contained more than one project, have one //Global CMakeLists.txt with the three lines above - with global options - and bunch of add_subdirectory calls which each contain //Local CMakeLists.txt for the submodule - executable/library.
a.h,c.h are marked as public, one should always include them with #include <project-name/model/foo/a.h> from anywhere.
I marked b.h as private just for demonstration purposes, there are two ways how to include it. b.cpp could use #include "b.h" or #include "model/foo/b.h" (no <> although it would work). I try to use the latter always apart from b.cpp-b.h declaration-definition pairs. I never use ../ or subdir/b.h since it makes a headache to move anything. This way, each header is included exactly the same way from anywhere - easily searchable and replaceable, b.{cpp,h} move together so the relative path is OK there.
For an executable, I usually just go with all public headers.
If this were a library, it can easily be converted to a e.g. a debian package since one can cleanly install the whole include directory to /usr/include without fear of clashes or hassle to extract the headers from src since src contains only private ones.
You can use cmake builtin variables to construct absolute paths, e.g. PROJECT_SOURCE_DIR.
include_directories(${PROJECT_SOURCE_DIR})
More fine-grained control over includes is possible using target_include_directories.
I have a project with structure like this:
project/
├─ src/
│ ├─ main.cpp
│ ├─ CMakeLists.txt
├─ lib/
│ ├─ libapi.so
├─ CMakeLists.txt
├─ dep/
│ ├─ api/
│ │ ├─ api_types.h
In the main CMakeLists I have:
add_subdirectory(dep)
add_executable("my_${PROJECT_NAME}")
add_subdirectory(src)
And in CMakeLists inside src folder:
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
add_library(api SHARED IMPORTED)
set_property(TARGET api PROPERTY IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/lib/libapi.so")
target_sources("my_${PROJECT_NAME}" PRIVATE main.cpp)
In main.cpp I do #include <api_types.h> but I get this error during compile:
api_types.h: No such file or directory
How can I fix it?
First - add_subdirectory in fact just looks for CMakeLists.txt in
the specified directory to apply the sub-set of the rules to the
parent project. If the specified folder doesn't contain any
CMakeLists.txt this command does nothing (i.e. the command
add_subdirectory(dep)).
Second - target_include_directories expects a target object to link against (not the project name). Assuming ${PROJECT_NAME} is not a name of any of your targets (and the code given in the question reveals only two targets my_${PROJECT_NAME} and api) the command target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) does nothing sensible.
Last - you don't specify path to your actual headers anywhere. CMAKE_CURRENT_SOURCE_DIR refers to the current folder CMakeLists.txt is located in. You should specify path to the folder the headers are located in.
Putting all the said info together, the desired content for the top-level CMakeLists.txt should be something like that:
cmake_minimum_required(VERSION 3.20)
set(PROJECT_NAME Hello)
project(${PROJECT_NAME})
add_executable(my_${PROJECT_NAME})
add_subdirectory(src)
And for src's file it's apparently something like that:
target_include_directories(my_${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/dep/api)
target_sources(my_${PROJECT_NAME} PRIVATE main.cpp)
This solution, however, doesn't resolve another mistake in your code - the CMake files you provided don't link the library itself to the executable target. However this is not the question asked, and with respect to the members of the community, I suggest referring to answers to another related question here.
target_include_directories requires your executable as target, in this case my_${PROJECT_NAME}.
You can try
target_include_directories(my_${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
As alternative I would recommend following structure
project/
├─ src/
│ ├─ main.cpp
├─ lib/
│ ├─ libapi.so
├─ CMakeLists.txt
├─ include/
│ ├─ api/
│ │ ├─ api_types.h
with following CMakeList.txt
add_executable(my_${PROJECT_NAME} src/main.cpp)
target_include_directories(my_${PROJECT_NAME} PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
link_directories(${CMAKE_SOURCE_DIR}/lib)
target_link_libraries(my_${PROJECT_NAME} libapi)
I was learning CMake for a project and feel confused when linking libraries.
I find it easier to ask with an example, as using terms I am not familiar could be misleading.
The questions are (also commented in the example codes)
how can I know what are the library name I can link to my target? Can I tell that from the build file structure, or I need to look into CMakeLists.txt of the external libraries. (e.g. the gtest-main and libhermes in the example)
How can I use a "grandchild" library that is part of the external library? (e.g. the jsi library in the example)
Example
Let's say I am writing some tests with dependency on the Hermes Library. I then write a CMakeList.txt file like
cmake_minimum_required(VERSION 3.13)
project(mytests_cxx)
set(CMAKE_CXX_STANDARD 14)
include(FetchContent)
FetchContent_Declare(
hermes
URL https://github.com/facebook/hermes/archive/d63feeb46d26fe0ca7e789fc793f409e5158b27f.zip
)
FetchContent_MakeAvailable(hermes)
enable_testing()
add_executable(
hello_test
./test.cpp
)
find_library (
LIB_HERMES
hermes # <- how can I know what is the name?
)
find_library (
LIB_JSI
jsi # <- How could I use a "grandchild" dependency, it is defined here (https://github.com/facebook/hermes/tree/main/API/jsi/jsi)
)
target_link_libraries(
hello_test
gtest_main <- Hermes seems to use gtest, how could make sure gtest_main is available?
LIB_HERMES
LIB_JSI
)
include(GoogleTest)
gtest_discover_tests(hello_test)
Running cmake to build
cmake -S . -B build -DHERMES_BUILD_SHARED_JSI=ON // I see an option defined in heremes'CMakeLists.txt but not sure if I am using it right.
the directory in build
tree ./build -L 2
./build
├── CMakeCache.txt
├── CMakeFiles
│ ├── 3.23.1
│ ├── CMakeDirectoryInformation.cmake
│ ├── CMakeError.log
│ ├── CMakeOutput.log
│ ├── CMakeRuleHashes.txt
│ ├── CMakeTmp
│ ├── Makefile.cmake
│ ├── Makefile2
│ ├── Progress
│ ├── TargetDirectories.txt
│ ├── cmake.check_cache
│ ├── hello_test.dir
│ └── progress.marks
├── CTestTestfile.cmake
├── ImportHermesc.cmake
├── Makefile
├── _deps
│ ├── hermes-build
│ ├── hermes-src
│ └── hermes-subbuild
├── bin
│ └── hermes-lit
├── cmake_install.cmake
├── compile_commands.json
└── hello_test[1]_include.cmake
then I run cmake --build build and see this error
/.../test.cpp:8:10: fatal error: 'jsi/jsi.h' file not found
#include <jsi/jsi.h>
Your question is too broad. Either ask a question about the list of libraries in cmake, or ask a question about how to compile and link with hermes, or ask a question about a specific problem with your build about including a third party. These should be 3 separate questions on this site.
how can I know what are the library name I can link to my target?
There are two things that you can target_link_libraries - you can link with libraries available on your system and link with targets defined in cmake.
You can list libraries available on your system by traversing all paths outputted by ld --verbose and listing all lib*.so files in those directories. Or https://unix.stackexchange.com/questions/43187/how-do-i-get-a-list-of-shared-library-filenames-under-linux or similar. See man ld.so.
You can list all targets defined in cmake. How do I list the defined make targets from the command line? .
These lists combined consist of all possible libraries names that you can link with.
Can I tell that from the build file structure
I do not understand. No.
I need to look into CMakeLists.txt of the external libraries
Yes, usually you want to look into any external libraries CMakeLists.txt at least to check for malicious code.
You usually look into documentation of external libraries. And, because source code is a form of documentation in open-source projects, you would look into the source code, yes.
How can I use a "grandchild" library that is part of the external library?
There are multiple ways for including external projects. Cases were outlined in Correct way to use third-party libraries in cmake project .
That said, not all CMake projects work properly with add_subdirectory() (which is called by FetchContent_MakeAvailable) - there are many cases where the third-party projects CMakeLists.txt may not work with parent project. In such cases, I would go with compiling and installing the project into a subdirectory for example before running CMake or with ExternalProject_Add, and then only including the result of compilation with hand-crafted find_library and target_include_directories from the installation directory.
Overall, including a third party is not always an easy task, and yes it requires you to become intimate with the specifics of a third party project and how it works and how it was written and decide how you want it to include in your project.
I have a project where I need to run one application on multiple boards. To do this I have two exectuables that share a main.c, but are compiled with a different board.c. The directory tree is as follows:
CMakeLists.txt
src/
├─ board/
│ ├─ board_a/
│ │ ├─ board.c
| | ├─ CMakeLists.txt
│ ├─ board_b/
│ │ ├─ board.c
| | ├─ CMakeLists.txt
├─ app/
│ ├─ main.c
├─ lib/
│ ├─ somelib/
│ │ ├─ somelib.h
│ │ ├─ somelib.c
In an attempt to make the root CMakeLists.txt smaller and more maintainable, I've created a CMakeLists.txt for both boards, and would like to compile each board as an object library for the main executable in the root CMakeLists.txt to add_subdirectory on and link in.
My problem is that board.c (as well as main.c) depends on somelib. How can I add this dependency to the subdirectory CMakeLists.txt? Is there a way I can do that without hard-coding a path to somelib? My feeling is that I should create a CMakeLists.txt in somelib, and I feel this would be easy if I were handling the library linking from the root CMakeLists.txt, but my confusion is that board is adjacent to lib. I'm relatively new to CMake and am not sure how to best structure these dependencies.
My problem is that board.c (as well as main.c) depends on somelib. How can I add this dependency to the subdirectory CMakeLists.txt? Is there a way I can do that without hard-coding a path to somelib? My feeling is that I should create a CMakeLists.txt in somelib, and I feel this would be easy if I were handling the library linking from the root CMakeLists.txt, but my confusion is that board is adjacent to lib. I'm relatively new to CMake and am not sure how to best structure these dependencies.
This is very straightforward start by linking each board_<X> to somelib's target.
# in each board_<X>/CMakeLists.txt
add_library(board_<X> OBJECT ...)
target_link_libraries(board_<X> PUBLIC proj::somelib)
then create the target for somelib:
# src/lib/somelib/CMakeLists.txt
add_library(somelib somelib.c)
add_library(proj::somelib ALIAS somelib)
target_include_directories(
somelib PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>")
As long as the top-level CMakeLists.txt includes these subdirectories via add_subdirectory (multiple levels is fine), then other targets can #include <somelib.h> and CMake will handle linking.
I am working on a project that is to be made of one executable linking with two libraries.
I'm making frequent chages to all of them and don't wish to go though a sudo make install cycle for libA and libB, so I have laid them out in the following fashion:
myProject
├── libA
│ ├── CMakeLists.txt
│ ├── include
│ │ └── libA
│ │ └── *.h
│ └── src
│ └── *.cpp
│
├── libB
│ ├── CMakeLists.txt
│ ├── include
│ │ └── libB
│ │ └── *.h
│ └── src
│ └── *.cpp
│
├── CMakeLists.txt
├── include
│ └── *.h
└── src
└── *.cpp
I'm using CMake subdirectories to automatically link in libA and libB into myProject, and it works fine. The CMakeList.txts are set up as follows:
# myProject/CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
project(myProject)
add_subdirectory(libA)
add_subdirectory(libB)
# Compiler flags, listing of the source files into ${SOURCE_FILES}, etc.
add_executable(myProject ${SOURCE_FILES})
target_link_libraries(myProject libA libB)
# myProject/libA/CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
project(libA)
# Compiler flags, listing of the source files into ${SOURCE_FILES}, etc.
add_library(libA STATIC ${SOURCE_FILES})
target_include_directories(libA PUBLIC include)
install(
TARGETS libA
ARCHIVE DESTINATION lib
)
install(
DIRECTORY include/glk
DESTINATION include
)
myProject/libB/CMakeLists.txt is similar.
Now here's the issue: libB has a dependency on libA, and includes libA's headers through #include <libA/foo.h>, and will need symbols from libA upon linking (though I guess that's a non-issue with static libraries).
How can I set up this dependency correctly so that libB finds everything it needs about this local libA, ideally by modifying only myProject's configuration?
You need to specify a link dependency of libB on libA. Yes, they might be static libraries, but CMake allows you to specify that you link one library to another even if they are static. Where the libraries are static, such a link relationship is then only used for providing transitive dependencies between the targets (i.e. those provided as PUBLIC or INTERFACE items in the various target_... commands). So I'd expect your libB's CMakeLists.txt to look similar to libA', except you'd also add the following line:
target_link_library(libB PRIVATE libA)
Or if libB uses parts of libA in its own public API, make the above link dependency PUBLIC rather than PRIVATE. By doing this, anywhere that you have a target link against libB, CMake will ensure that libA appears after it on the final command line. In your example, the top level CMakeLists.txt file links libB after libA, so without the above target_link_library() relationship, the linker would complain about symbols from libA being missing, even though libA would have been on the linker command line, just in the wrong order. Your syntax for target_link_libraries() in your top level CMakeLists.txt files is also incorrect (presumably just a typo in your example). Arguments should not be comma-separated.
The other effect of explicitly linking libB to libA is that now libB picks up PUBLIC and INTERFACE properties from libA, which in this case will be the header search path that libA defines. You should find that libB will now find libA's headers too.