CMake doesn't include header directory of submodule A within submodule B - c++

I have a CMake project that looks like this:
project/
CMakeLists.txt
subprojectA/
CMakeLists.txt
include/
headerA.hpp
src/
libraryA.cpp
subprojectB/
CMakeLists.txt
src/
mainB.cpp
The "library" subproject, A, is compiled as a static library, becoming libsubprojectA.a. The "main" project, B, is compiled as a binary and depends on the library. mainB.cpp includes a reference to headerA.hpp.
Here is subprojectA/CMakeLists.txt:
project(SubProjectA)
include_directories(include)
add_library(subprojectA STATIC src/libraryA.cpp)
set(${PROJECT_NAME}_INCLUDE_DIRS
${PROJECT_SOURCE_DIR}/include
CACHE INTERNAL "${PROJECT_NAME}: Include Directories" FORCE)
And here is subprojectB/CMakeLists.txt:
project(SubProjectB)
include_directories(${SubProjectA_INCLUDE_DIRS})
add_executable(mainBinary src/mainB.cpp)
target_link_libraries(mainBinary subprojectA)
The main Project CMakeLists.txt looks like:
project(Project)
add_subdirectory(subprojectB)
add_subdirectory(subprojectA)
Note that subprojectB, the main project, is listed before subprojectA.
Here's the problem. When I first run "cmake" on this project, ${SubProjectA_INCLUDE_DIRS} is not set within SubProjectB.
What I think is happening is that the CMakeLists for SubProjectB loads first, when ${SubProjectA_INCLUDE_DIRS} has not yet been set. It sets its own include path to an empty string as a result. However, even though libsubprojectA.a gets built successfully before mainBinary, the include path was already set empty beforehand. As a result, I get this error when trying to make mainBinary:
subprojectB/src/mainB.cpp:1:23: fatal error: headerA.hpp: No such file or directory
#include "headerA.hpp"
^
It's a workaround to put subprojectA before subprojectB in the main Project CMakeLists in the declarative world of CMake. What I really want is to know the proper way to indicate to CMake that the include_directories(${SubProjectA_INCLUDE_DIRS}) line depends on the definitions that exist inside SubProjectA's CMakeLists. Is there a better way to do this?

If you want to express that include directory subprojectA/include is an interface of the library subprojectA, attach this property to the target with target_include_directories command:
subprojectA/CMakeLists.txt:
project(SubProjectA)
add_library(subprojectA STATIC src/libraryA.cpp)
# PUBLIC adds both:
# 1) include directories for compile library and
# 2) include directories for library's interface
target_include_directories(subprojectA PUBLIC include)
So any executable(or other library) which linked with subprojectA will have this include directory automatically:
subprojectB/CMakeLists.txt:
project(SubProjectB)
add_executable(mainBinary src/mainB.cpp)
target_link_libraries(mainBinary subprojectA)
Of course, for use last command properly you need to process directory with library before one with executable:
CMakeLists.txt:
project(Project)
add_subdirectory(subprojectA)
add_subdirectory(subprojectB)

Related

Cannot link local libraries in CMake

I'm developing a c++ program on visual studio that will be deployed on linux, and it is debugged on linux through an ssh. Currently, this is the structure of my folder:
ANT
-xscommon
--xscommon_config.h
-xscontroller
-xstypes
ANT.cpp
ANT.h
CMakeLists.txt
CMakeSettings.json
hashes.h
quaternionic.h
stars.h
Currently, all the .h, .cpp, .o, .cpp.o, .a files that I think I have to link to are kept within the three xs------- directories. I am quite new to cmake, and this linking to these libraries is giving me trouble; I am able to link correctly to the includes, but there are undefined references errors thrown when I don't do linking, and when I attempt linking, it throws errors. This is my current CMakeLists.txt file:
# CMakeList.txt : CMake project for ANT, include source and define
# project specific logic h"ere.
#
cmake_minimum_required (VERSION 3.8)
project ("ANT")
link_directories(${ANT_SOURCE_DIR}/xscommon xscontroller xstypes)
add_executable(
ANT
"ANT.cpp"
"ANT.h"
"quaternionic.h"
"stars.h"
"hashes.h"
)
target_include_directories(ANT PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(ANT PUBLIC xscommon_config)
When I run this, the builder says the following:
/usr/bin/ld: cannot find -lxscommon_config
I need to look for these libraries in the directory that ANT.cpp is in, as this is where they are kept, however nothing I do (and I have messed around with configurations for hours now) will tell camke to look for these libraries in the src folder. it always goes to /usr/bin/ld.
I really just need to know what to tell CMake such that it will look in the correct place for each file, that is if I am telling it to look for the correct file (I am fairly sure I am).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Update
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
So I have remade the CMakeLists.txt file to this:
# CMakeList.txt : CMake project for ANT, include source and define
# project specific logic here.
#
cmake_minimum_required (VERSION 3.15)
project ("ANT")
#[STATIC | SHARED | MODULE]
#[STATIC | SHARED | MODULE]
#[STATIC | SHARED | MODULE]
add_library(xscommon SHARED IMPORTED)
add_library(xscontroller SHARED IMPORTED)
add_library(xstypes SHARED IMPORTED)
add_executable(
ANT
"ANT.cpp"
"ANT.h"
)
target_include_directories(ANT PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
And still get undefined references. I am going to try building the libraries instead. Additionally, I have contacted the manufacturer of the IMUs which use this SDK, as colleagues have not been able to fix this either.
The problem is you are linking to a library that has not been build.
This
# link to this directory
target_link_libraries(ANT PRIVATE xscommon)
tries to link to a library called xscommon to the target ANT but you have not build xscommon anywhere in your project.
If xscommon is a pre-build library and you just want to import it then add the library and set the IMPORTED target property:
add_library(xscommon [STATIC | SHARED | MODULE] IMPORTED)
If you want to build xscommon in your root CMakeLists.txt. Add xscommon as a library and include the location of the headers.
add_library(xscommon [STATIC | SHARED | MODULE]
xxx/xxx.cpp #list all source files that build the library - use relative path
)
target_include_directories(xscommon PRIVATE
xxx/xxx #path to the location of library header files
)
Also you don't need to add the header files when adding the executable. So this
add_executable(
ANT
"ANT.cpp"
"ANT.h"
"quaternionic.h"
"stars.h"
"hashes.h"
)
can be simplified to
add_executable(
ANT
"ANT.cpp"
)
Suppose your dir is like this:
ANT
-xscommon
--xscommon_config.h
--xscommon_config.cpp
...
First add a CMakeLists.txt file to xscommon/:
ANT
-xscommon
--CMakeLists.txt
--xscommon_config.h
--xscommon_config.cpp
...
Now in xscommon/CMakeLists.txt we will create a library, that will be imported and linked in the main CMakeLists.txt file:
xscommon/CMakeLists.txt:
#define another target, let's name it 'xscommon'
add_library(xscommon
xscommon_config.h
xscommon_config.cpp
#more sources if you want
)
Now in the main CMakeLists.txt file:
cmake_minimum_required (VERSION 3.8)
project ("ANT")
#remove this line
#link_directories(${ANT_SOURCE_DIR}/xscommon xscontroller xstypes)
add_executable(
ANT
"ANT.cpp"
"ANT.h"
"quaternionic.h"
"stars.h"
"hashes.h"
)
# add the xscommon directory, this will make the library target defined there available here
add_subdirectory(xscommon)
# link to this directory
target_link_libraries(ANT PRIVATE xscommon)
# use PUBLIC if the xscommon library will be part of the public interface of your
# library. But since it is an executable, PRIVATE is better here.
target_include_directories(ANT PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
Use the above method, you can create more libraries and link to them.
Note that it is not necessary to create separate cmake files for each subdirectory but it is considered a good practice and modularizes your code. If you want to do this in the main cmake file instead of creating a subdirectory, add this to the main cmake:
add_library(xscommon
xscommon/xscommon_config.h
xscommon/xscommon_config.cpp
#more sources if you want
)
Update according to the changes in question
Your current CMakeLists.txt:
These three lines below are not doing anything, definitely not what you think. add_library() command has the word "add" in it, I know, but it doesn't add any library, just like add_executable doesn't add any executable. It creates a library.
add_library(xscommon SHARED IMPORTED)
add_library(xscontroller SHARED IMPORTED)
add_library(xstypes SHARED IMPORTED)
How to create a library in cmake out of two file a.cpp and a.h:
add_library(myALib "a.cpp")
That's it. If you have more sources, you will include them accordingly of course. In your case you will have to add the sources of xscommon and others accordingly.
Once you have created the libraries, you need to link them to your executable. If you won't you will get undefined reference errors because compiler can locate the declarations in header files but not the definitions of your code which exists in .cpp files.
So, how do you link? Simple:
target_link_libraries(TARGET_NAME PUBLIC | PRIVATE LIBRARY_NAME)
# TARGET_NAME: can be `executable` or `library`
# PUBLIC or PRIVATE (for exe, it is usually private)
# LIBRARY_NAME: Name of library you want to link to TARGET_NAME
# So if you wanted to link "myALib" which I created above to ANT, you would do:
target_link_libraries(ANT PRIVATE myALib)
# Note: You need to add this line **after** add_executable() because target "ANT" will be created after that. You can do the linking after the "target_include_directories" command.

CMake: copy header file to output directory

I have a directory with c++ source and header files. I want to create a CMakeLists.txt to build this as a library for use in other CMake projects that include it as a sub directory.
Structure:
example/
foo.h
foo.cpp
CMakeLists.txt
The problem I run into is CMake doesn't seem to put foo.h anywhere, so getting the parent CMake to know how to find the header file is beguiling me.
Here's my current CMakeLists.txt:
cmake_minimum_required(VERSION 3.8.2)
project(example)
set (CMAKE_CXX_STANDARD 11)
# add library target foo
add_library(foo STATIC foo.cpp)
# tell cmake where to find headers for it
target_include_directories(foo PUBLIC .)
# sad attempt to get it to output the header
set_target_properties(foo PROPERTIES PUBLIC_HEADER foo.h)
I DON'T want to have to do install. The idea here is that the library would be used by other CMake projects, not by the entire system.
Ideally, the foo.h would show up next to libfoo.a in the build directory.
I've tried calling it a "FRAMEWORK", no luck; that only makes is a macOs framework.
I believe I can jury rig this, but methinks there's a best practice out there.
Open to an answer that says "here's a better way", too...
UPDATE
It might help to clarify how I think I want to pull this project into another. I've seen other projects use something like this:
add_subdirectory(<path_to_foo>/foo foo_build)
which causes the foo build to happen in a subdirectory. This allows me to refer to the library using 'foo_build', which is nice and clean. However, I still have to point at the original include directory to get the .h file, which makes me feel like I'm missing something.
It seems like cmake would have a clean solution for this.
I am fairly new to CMake but what I think you want is a 'add_custom_command'.
add_custom_command(TARGET foo.a POST_BUILD COMMAND copy foo.h ${CMAKE_LIBRARY_OUTPUT_DIRECTORY})
That might work.
What you are looking for is the following structure:
example/
- CMakeLists.txt
- src/
- main.c
- sub/
- foo/
- CMakeLists.txt
- src/
- foo/
- foo.c
- foo.h
Your CMakeLists will look like the following
example/CMakeLists.txt
# use modern target-based cmake features
cmake_minimum_required (VERSION 3.0)
# projectname
project (ff1_selfcheck)
add_subdirectory (sub/foo)
# executable to create
add_executable(${PROJECT_NAME}
src/main.c
)
# link libraries
target_link_libraries(${PROJECT_NAME}
PRIVATE
foo # imported target
)
example/sub/foo/CMakeLists.txt
# use modern target-based cmake features
cmake_minimum_required (VERSION 3.0)
# projectname
project (foo)
# executable to create
add_library(${PROJECT_NAME}
src/foo.c
)
# directories where to search for header files
target_include_directories(${PROJECT_NAME}
PUBLIC
source # the headerfiles in source are the includes
)
By using the project name foo in target_link_libraries(...) you refer to the foo library target
Furthermore, by using the PUBLIC keyword in the foo library, your headers (your include directory) is automatically propagated to every CMake project that adds this library via add_subdirectory(...).
Therefore you don't need to copy your headers! CMake >= 2.8.12 is beautiful, isn't it?
If you really want to copy files via CMake, the following would work:
file(COPY srcDir
DESTINATION dstDir
FILES_MATCHING
PATTERN .h
)
Take a look here: https://cmake.org/cmake/help/v3.2/command/file.html
As a general rule for CMake, sources are kept in the source directory and binaries and other generated files are within the build directory. So you wish is not very CMake-ish.
CMake would put headers and libraries according to your wishes when you install the project. Then you can specify what to copy where.
As you don't want to install this module, the best way is to create a package by providing a CMake config file for your project. This means that your project Foo would generate a file FooConfig.cmake which contains the paths to includes and libraries. The other CMake project would use find_package(Foo) to look for the file. By adding a hint to Foo_DIR you can make CMake find your project in a non-standard directory.
Further reading:
CMake documentation about packages
About how to use your library
Note, that configure_file is unrelated to what you wish, the confusing name has historic reasons. You can use this command, but per se it is unrelated.
UPDATE: after the update, I think that you want to use an external project. Behaves like an internal library, but pretty separated. See https://cmake.org/cmake/help/latest/module/ExternalProject.html
you should use generator expression for your "foo" include directory:
target_include_directories(foo PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR})
And since you don't want install rules not need to also add a $<INSTALL_INTERFACE:include>...
BTW you should don't care to copy the include file in the build directory (supposing you are building out of the source).
ps: if you also generate headers files simply add $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}>

Keeping file hierarchy across subdirectories in CMake

Till date I still do not really understand what the 'best practice' is for doing this for a CMake project with many subdirectories.
Say I have a project hierarchy as such and each subdirectory has source files in it...
--CMake Project Source dir
|-- SubD1
|-- SubSubD1
|-- SubD2
What I would usually do is to do add_subdirectory(SubD1) and respectively for D2 in the CMakeLists.txt of the root directory and recursively for the subdirectory in the CMakeLists.txt of the SubD1 directory, while declaring variables in each subdirectory and making them visible in the root directory with PARENT_SCOPE.
That means if a file Source2.cpp exists in `SubSubD1', I'd simply do
set(SUBSUBD1_SOURCES Source2.cpp PARENT_SCOPE)
and expect to be able to use SUBSUBD1_SOURCE in my SubD1 directory.
Subsequently, say Source.cpp exists in SubD1, I would do
set(SUBD1_SOURCES ${SUBSUBD1_SOURCES} Source.cpp PARENT_SCOPE)
so that all sources would be visible in root dir.
The problem is of course that the file paths aren't kept when the variables arrive at the root directory. What I'm currently doing is for all source files that I set, I include a ${CMAKE_CURRENT_LIST_DIR}, making it
set(SUBSUBD1_SOURCES ${CMAKE_CURRENT_LIST_DIR}/Source2.cpp PARENT_SCOPE)
and
set(SUBD1_SOURCES ${SUBSUBD1_SOURCES} ${CMAKE_CURRENT_LIST_DIR}/Source.cpp PARENT_SCOPE)
In this case, I could then say, do add_executable(myProg SUBSUBD1_SOURCES) in the root directory of my CMake project.
Are there any better ways of doing this then having to always include a CMake variable in front of all source files?
There is a fourth way if you're using newer versions of CMake.
Take a look at target_sources() command of CMake.
It seems like you are declaring your target in your CMakeLists.txt
add_executable(my_target "subd1/CMakeLists.txt" "subd2/CMakeLists.txt")
add_subdirectory(subd1)
add_subdirectory(subd2)
Instead of propagating your Source files up to the root you can depend on the target you have defined in the root CMakeLists.txt. That means subd1/CMakeLists.txt may look like:
target_sources(my_target PRIVATE "subd1/Source.cpp" "subd1/Source2.cpp")
[EDIT]
As stated in the comments you must give the relative path of the source-files to target_sources(). I use target_sources() because I do not want the explicit source file listing to pollute the targets CMakeLists.txt. Another use case is that target_sources() can be invoked with the PUBLIC or INTERFACE keyword to propagate source files to depending targets. Well I never used target_sources() that way.
[/EDIT]
If you're using IDEs like Visual Studio that support folders you make want to also declare a source_group() in the CMakeLists.txt that contains your target. So the root CMakeLists.txt may look like:
add_executable(my_target "subd1/CMakeLists.txt" "subd2/CMakeLists.txt")
add_subdirectory(subd1)
add_subdirectory(subd2)
...
source_group(subd1 REGULAR_EXPRESSION "subd1/*")
source_group(subd2 REGULAR_EXPRESSION "subd2/*")
I'm using this approach because it leads to much cleaner CMakeLists.txt files, its lesser work and I think the introduction of not needed variables only raises the complexity of your CMakeLists.txt files.
CMakeLists.txt as target sources
I currently use the CMakeLists.txt of the sub folders as source files of the target because otherwise CMake will complain that the add_executable command has no source files given.
There are 3 ways I have used before. I normally prefer the 1st way, but have already used all 3 depending on the use case:
1. You directly name the sources in your root CMakeLists.txt file
set(
SUBD1_SOURCES
"SubD1/SubSubD1/Source2.cpp"
"SubD1/Source.cpp"
)
set(
SUBD2_SOURCES
"SubD2/Source3.cpp"
)
add_executable(myProg ${SUBD1_SOURCES} ${SUBD2_SOURCES})
2. You use OBJECT intermediate libraries to collect/group your sources
SubD1/SubSubD1/CMakeLists.txt:
add_library(SubSubD1Objs OBJECT Source2.cpp)
SubD1/CMakeLists.txt:
add_subdirectory(SubSubD1)
add_library(SubD1Objs OBJECT Source.cpp)
CMakeLists.txt:
add_executable(myProg $<TARGET_OBJECTS:SubSubD1Objs> $<TARGET_OBJECTS:SubD1Objs>)
3. You write your own function() to collect the data (and do the prefixing)
CMakeLists.txt:
function(my_collect_sources)
foreach(_source IN ITEMS ${ARGN})
if (IS_ABSOLUTE "${_source}")
set(source_abs "${_source}")
else()
get_filename_component(_source_abs "${_source}" ABSOLUTE)
endif()
set_property(GLOBAL APPEND PROPERTY GlobalSourceList "${_source_abs}")
endforeach()
endfunction(my_collect_sources)
add_subdirectory(SubD1)
#add_subdirectory(SubD2)
get_property(MY_SOURCES GLOBAL PROPERTY GlobalSourceList)
add_executable(myProg ${MY_SOURCES})
SubD1/CMakeLists.txt:
add_subdirectory(SubSubD1)
my_collect_sources(Source.cpp)
SubD1/SubSubD1/CMakeLists.txt:
my_collect_sources(Source2.cpp)
In your case there's no need to use add_subdirectory since you have only one target which is created in the root CMakeLists.txt. You can simply write this:
add_executable(myProg
SubD1/Source.cpp
SubD1/SubSubD1/Source2.cpp)
Use add_subdirectory for subdirectories creating their own targets so there's no information to pass upwards.

CMake: Build Multiple Executables in one Project with Static Library

I'm working on a project that consists of 3 server executables and one library for shared code. I want it to be cross-platform, so I'm using CMake (since Xcode is being a pain anyway) to handle the build process. I'm having trouble with setting up the CMakeLists so that I can include the library from a directory at the same level when I'm building the executable.
Here's the directory structure (and the CMake files):
tethealla2.0/
CMakeLists.txt
libtethealla/
CMakeLists.txt
encryption/
utils/
patch_server/
CMakeLists.txt
login_server/
CMakeLists.txt
ship_server/
CMakeLists.txt
My top-level CMake (tethealla2.0/CMakeLists.txt, only includes the sub-project that should compile):
project(tethealla CXX)
cmake_minimum_required(VERSION 2.6)
add_subdirectory(libtethealla)
add_subdirectory(patch_server)
tethealla2.0/libtethealla/CMakeLists.txt, which generates a static library:
project(Libtethealla C)
cmake_minimum_required(VERSION 2.6)
include_directories(encryption)
set(ENC_DR encryption/)
set(ENCRYPTION_SOURCES
${ENC_DR}/psobb-crypt.c
${ENC_DR}/psogc-crypt.c
${ENC_DR}/psobb-crypt.c
${ENC_DR}/encryption.c
)
add_library(tethealla STATIC ${ENCRYPTION_SOURCES})
tethealla2.0/patch_server/CMakeLists.txt thus far:
project(patch_server CXX)
cmake_minimum_required(VERSION 2.6)
add_executable(server main.cc)
target_link_libraries(server tethealla)
So it makes more sense if I build it from the top level since tethealla2.0/CMakeLists.txt will inherit the targets from each of the subdirectories and the one in patch_server will have access to the tethealla library. However what I want is to be able to build from within these subdirectories to generate Xcode projects so that I can work on/recompile them individually. To do so I need to be able to get to the libtethealla/build directory (where CMake outputs) to access the libtethealla.a library from patch_server. Is this possible?
On kind of another note, even in building from the top-level directory my source in patch_server can't include "encryption.h", the header file for the library in encryption. Which seems to be building fine. Any thoughts on that are also greatly appreciated!
My solution is to use add_subdirectory with relative patch to shared_lib directory. I don't think that this is a perfect solution it has its caveats:
Logic very similar to a header guard must be added to library CMakeLists.txt to prevent from defining targets multiple times.
Each CMakeList.txt file must know the relative path to the library, if one want to move library all CMakeLists must be updated.
Let's assume that the directory structure looks like this:
root/
CMakeLists.txt
shared_lib/
CMakeLists.txt
inc/
foo.h
src/
foo.c
exec1/
CMakeLists.txt
main.c
exec2/
CMakeLists.txt
main.c
root/CMakeList.txt
cmake_minimum_required(VERSION 2.6)
add_subdirectory(shared_lib)
add_subdirectory(exec1)
add_subdirectory(exec2)
I have decided that shared_lib/CMakeLists.txt will export a variable named SHARED_DIR_INCLUDE_DIR. This approach helps to decouple things a little bit.
root/exec1/CMakeLists.txt
cmake_minimum_required(VERSION 2.6)
add_subdirectory(./../shared_lib shared_lib)
include_directories(${SHARED_LIB_INCLUDE_DIR})
set(SRCS main.c)
add_executable(exec1 ${SRCS})
target_link_libraries(exec1 shared_lib)
if() in the fourth line solves the issue with target's multiple definition in case the CMakeLists file is added multiple times. The second and the third lines exports the include directory for library in SHARED_LIB_INCLUDE_DIR
root/shared_lib/CMakeLists.txt
cmake_minimum_required(VERSION 2.6)
set(SHARED_LIB_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/inc)
set(SHARED_LIB_INCLUDE_DIR ${SHARED_LIB_INCLUDE_DIR} PARENT_SCOPE)
if(TARGET shared_lib)
message("shared_lib is already defined")
else()
include_directories(${SHARED_LIB_INCLUDE_DIR})
set(LIB_SRCS ./src/foo.c)
add_library(shared_lib STATIC ${LIB_SRCS})
endif()

CMake link files from subdirectories and parent directories

I have a folder net like:
main
headers
cls1.hpp cls2.hpp ...
sources
cls1.cpp cls2.cpp ...
resources
r1.hpp r2.hpp ...
I have tried more cmake methods to link the files, like
set(SRC ${SRC})
in parent dir and
set(SRC ${SRC} ${CMAKE_CURRENT_SOURCE_DIR}/cls1.cpp PARENT_SCOPE)
in subdir
OR
file(GLOB_RECURSE SRC_FILES ${PROJECT_SOURCE_DIR}/*.cpp)
file(GLOB_RECURSE HDR_FILES ${PROJECT_SOURCE_DIR}/*.hpp)
to get the .cpp and .hpp files, but when I do
#include "resources/r1.hpp"
I get error like "resources/r1.hpp" No such a file or directory
Can anyone help me pleas? How can I do linking between files in folders in my case?
To solve this specific problem, in your main CMakeLists.txt add this line before adding subfolders:
include_directories(".")
This will allow the compiler to see the file located in resources/r1.hpp starting from the root path.
However, I suggest you take a look to some CMake projects examples, because it doesn't seem you are doing things in the standard way (e.g. usually a good way is to add a sub directory that creates its own library, that then you link to your main executable)