CMake: Include vs add_subdirectory: Relative header file path - c++

I have a c++ project with several subdirectories, e.g.
src/
CMakeLists.txt
main.cpp
module1/
CMakeLists.txt
code.cpp
code.h
module2/
CMakeLists.txt
code2.cpp
It seems that the two ways to deal with this in cmake is to either use add_subdirectory(module1) or include(module1) in my src/CMakeLists.txt. Somewhere I read, that the include use is regarded legacy/deprecated. My src/module1/CMakeLists.txt looks like this:
include_directories(${CMAKE_CURRENT_LIST_DIR})
set( SRCS
${SRCS}
${CMAKE_CURRENT_LIST_DIR}/code.cpp
)
set( QT_FILE_HEADERS
${QT_FILE_HEADERS} code.h
)
If I try to use the add_subdirectory method and want to usecode.h in main.cpp I have to write #include "module1/code.h". If I do the include method of adding module1, I can simply write #include "code.h". I would prefer not to specify the relative path of the include files when I use them somewhere else, is there a way to achieve this using the add_subdirectory method? I thought the include_directories line should have taken care of that.

This is not how you should make modules -- sure you can do it this way, it works, but it is not very useful. Given your layout, simply reference module1/code.cpp in the main CMakeLists.txt file.
However, if you want to use modules, make each one a separate static library. This will really simplify things!
In src/CMakeLists.txt write:
add_subdirectory(module1)
add_executable(myprogram main.cpp)
target_link_libraries(myprogram module1)
In src/module1/CMakeLists.txt write:
add_library(module1 STATIC code.cpp)
target_include_directories(module1 PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
This way, you are only passing one single thing back from the module1 CMake script: the module1 target. Your main doesn't need to know anything about what happens inside there. If the code in module1 requires specific external libraries, link them there, the main CMake script won't need to know about it. Simply by linking to module1, all the magic will happen behind the scenes, your program will be compiled with the right include directories and linked to the right libraries.

Related

CMake: Can we specify include directories for a specific set of files not forming an executable or lib?

If I have this source tree:
C:\app:
src:
CMakeLists.txt
main.cpp --> #include "acme/header_only_lib/api.h"
D:\3rdparty\acme\header_only_lib:
api.h --> #include "detail.h"
detail.h
Without using symlink tricks, and without adding files to the 3rdparty folders, if I must retain #include "acme/header_only_lib/api.h" in main.cpp, how should I specify the include directories in CMake such that api.h can see detail.h, without adding global include directory of D:\3rdparty\acme\header_only_lib? The header-only-lib is not an executable nor library target, and its code is not modifiable by me. I also don't want to pollute my global include directories by adding D:\3rdparty\acme\header_only_lib because the filenames inside there are too common and will easily clash with other libraries/future code.
Is there a CMake mechanism where I can say:
Only for D:\3rdparty\acme\header_only_lib\api.h, add D:\3rdparty\acme\header_only_lib as the include directory?
To add a directory to the global list of include directories, you use e.g.
include_directories( ${CMAKE_SOURCE_DIR}/3rdparty )
Note that you should not hardcode absolute paths (like D:\) into your CMakeLists.txt as that makes it impossible to build your project in any other location. CMake offers variables like ${CMAKE_SOURCE_DIR} and ${CMAKE_BINARY_DIR} for just that purpose.
If you want to add a directory to the list of include directories for a specific part of your build only, you use e.g.
target_include_directories( app PRIVATE ${CMAKE_SOURCE_DIR}/3rdparty )
This adds the directory only for the target (executable / library) app.
Note that, if your acme/header_only_lib is supposed to be installed alongside with the app binaries, this approach won't work, as your acme headers would need to "see" each other on the client's machine, which is unlikely if they reside in the acme/header_only_lib subdirectory but address each other without subdirs. That would require your client (who isn't using your CMakeLists.txt for his builds) to add acme/header_only_lib to the include paths for your header lib to work -- you should not do that.
Use include_directories or target_include_directories:
include_directories("D:\3rdparty")
OR with target_include_directories if you want to make this change only for the main target:
add_executable(main main.cpp)
target_include_directories(main PUBLIC "D:\3rdparty")
Then you can just #include "acme/header_only_lib/api.h" or #include <acme/header_only_lib/api.h>
Header only lib
If you want to do this for header-only-lib only, you need to do this in it's CMake file. First add the library as INTERFACE with no source files:
add_library(header-only INTERFACE)
Then include directories for it:
target_include_directories(header-only INTERFACE include-dir-for-header-only)
Then link it to the main target
find_library(HeaderOnly
NAMES header-only
HINTS "D:\3rdparty\path-to-lib"
)
target_link_libraries(main header-only)

Add interface library as SYSTEM in modern CMake

I'm currently transforming my C++ project from simply using a Makefile to using CMake. As I'm unfamiliar with CMake, I try and avoid learning "old" CMake and stick to "modern CMake" best practices, e.g. described here.
However, I could not figure out how to include a simple, header-only, third party library without getting bothered with warnings about that library.
Say I have a project structure like this:
CMakeLists.txt
src/
CMakeLists.txt
main.cpp
third-party-lib/
foo/
foo.h
Say main.cpp uses foo: #include <foo.h>
The problem is, I use a bunch of warning flags and foo.h introduces some warnings that I don't care about, as I'm not the author and it's nothing serious.
In a classic Makefile I would just have written -isystem third-party-lib/foo somewhere appropriate and be done.
In CMake, I achieved this by a include_directories(SYSTEM PRIVATE third-party-lib/foo) in the top-level CMakeLists.txt.
Now, according to the link above, I should keep my hands off include_directories() to avoid hidden dependencies and I think that this is a good idea. I'd rather specify for each target that actually needs foo to include that library and treat it as 'don't warn me here'. As I understand it, the approach would be:
find foo target in top-level CMakeLists.txt
target_link_libraries(main PRIVATE foo) or so in src/CMakeLists.txt
How would I achieve that, i.e. what is the command for finding, and where to specify the SYSTEM property? Or any other suggestions for how to approach this?
To add a header-only library in modern CMake, you can use target_include_directories with SYSTEM INTERFACE. You can place this in your top-level CMake file, before processing the src/CMakeLists.txt file:
add_library(foo INTERFACE)
target_include_directories(foo SYSTEM INTERFACE
${CMAKE_SOURCE_DIR}/third-party-lib/foo
)
Your friend is target_include_directories. It works like include_directories but on a per-target basis.
add_executable(foo foo.c)
target_include_directories(foo SYSTEM
PRIVATE sys_inc
)

CMake how to include headers without sources?

This is probably a dummy question but I have literally looked at the two first pages of google without success.
I'm writing a header only library and I'm unable to set up correctly the CMake configuration in order that when I build my solution a given main.cpp finds the proper includes.
How can this be accomplished?
EDIT
So I probably should give a little more detailed explanation.
Lets say I have a ./src folder with: ./src/core and ./src/wrappers. Inside each folder I have .h files that needs to be included in a main.cpp file:
#include <src/core/reader.h>
Still when I put in CMakeList.txt something like:
include_directories(src/core)
add_executable(main main.cpp)
I receive a message like: src/core/reader.h no such file or directory.
To be able to use that path, you should refer to the parent directory of src.
Assuming the top level CMakeLists.txt is at the same level of src, you can use this instead:
include_directories(${CMAKE_SOURCE_DIR})
As from the documentation of CMAKE_SOURCE_DIR:
The path to the top level of the source tree.
If src is directly in the top level directory, this should let you use something like:
#include <src/whatever/you/want.h>
That said, a couple of suggestions:
I would rather add this:
include_directories(${CMAKE_SOURCE_DIR}/src)
And use this:
#include <whatever/you/want.h>
No longer src in your paths and restricted search area.
I would probably use target_include_directories instead of include_directories and specify the target to be used for that rules:
target_include_directories(main ${CMAKE_SOURCE_DIR}/src)
This must be put after the add_executable, otherwise the target is not visible.
Another option which might make sense in some situations would be to create a dedicated target for the header-only library:
add_library(headerlib INTERFACE)
target_include_directories(headerlib INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
And then to use it:
target_link_libraries(mytarget headerlib)
This has the advantage that if you want to use it in multiple targets, it's easy to do so.
Just add a include_directories() directive in order to find where your header only library can be found by the target project.
According to your edit. To find
#include <src/core/reader.h>
You need to add
include_directories(/full_parent_path_of_src)
If I understand your question correctly then in your CMakeLists.txt you need to add include_directories(<DIRECTORY>) for every directory of your header library.

How to order/design a shared library with CMake that use higher includes

At the moment I have a shared library with around ~30 classes. My idea is to group them into subdirectories, means a subdirectory contains classes that are more or less equals/have the same content (ui, utils, backend etc).
But I ran into a typical problem: I have some export defines/constants that are shared across the project and my first question was: How to include this header from a header in a subdirectory ?
I often see the #include ../Header.h solution but my personal opinion is, that this is quite ugly.
So what I did:
Application/
main.cpp
MyLibrary/
Header.h
foo/
Foo.h (#include <Header.h>
bar/
Bar.h (#include <Header.h> #include <foo/Foo.h>)
And in my library CMakeList I add and include_directory with the root directory of the MyLibrary directory.
My application executable CMakeLists.txt looks something like this (pseudo CMake)
project(application)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../)
add_executable(application main.cpp)
target_link_libraries(application mylibrary)
And in the main.cpp I include like this:
#include <MyLibrary/Header.h>
#include <MyLibrary/bar/Bar.h>
The problem now is, that the compiler doesn't know where to look for the included <> INSIDE the library, for example the Header.h includation inside foo.bar points to ${CMAKE_CURRENT_SOURCE_DIR}/Header.h - but CMAKE_CURRENT_SOURCE_DIR is the path of the application and NOT of the library
So I have to add the directory of the library that the compiler can found the other headers of the library (Header in the library that includes another Header in the library via <> and not "")
So I end up with something like this:
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../ ${CMAKE_CURRENT_SOURCE_DIR}/../MyLibrary)
And now Bar.h can find foo/Foo.h, otherwise something goes wrong. And this is even more ugly
So my question: Is there a good/tricky way to solve this problem in CMake ? Or can someone give me tipps about a better project structure ? The solution has to run under MSVC, GCC and clang.
If you think about how your library will look after install you may get some hints about structure. So for example:
<install-prefix>/Foo/foo.hpp -> has `#include <Boo/boo.hpp>`
<install-prefix>/Boo/boo.hpp -> has `#include <Bar/bar.hpp>`
<install-prefix>/Bar/bar.hpp -> has `#include <Bar/details/utils.hpp>`
<install-prefix>/Bar/details/utils.hpp
this is possible layout before install done:
<source-dir>/Sources/Foo/foo.hpp
<source-dir>/Sources/Boo/boo.hpp
<source-dir>/3rdParty/Bar/bar.hpp
<source-dir>/3rdParty/Bar/details/utils.hpp
hence CMakeLists.txt must contains (or better target_include_directories):
include_directories(<source-dir>/Sources)
include_directories(<source-dir>/3rdParty)
so for your case, IMHO it must be something like that:
Application/
main.cpp
MyLibrary/
Header.h
foo/
Foo.h (#include <MyLibrary/Header.h>
bar/
Bar.h (#include <MyLibrary/Header.h> #include <MyLibrary/foo/Foo.h>)
What I've done to solve this problem is have a top level CMakeLists.txt that sets variables that will point to the top of the project directory. In your case this will be the directory containing Application and MyLibrary.
ProjectRoot
CMakeLists.txt // top
Application/
CMakeLists.txt
main.cpp
MyLibrary/
CMakeLists.txt
Header.h
foo/
Foo.h (#include <Header.h>
bar/
Bar.h (#include <Header.h> #include <foo/Foo.h>)
# Top level cmakelists
project (myproject)
set (my_app_src_top "${CMAKE_CURRENT_SOURCE_DIR}" )
set (my_app_build_top "${CMAKE_CURRENT_BINARY_DIR}" )
add_subdirectory( MyLibrary )
add_subdirectory( Application )
Then in your subdirectory CmakeLists, you can add include paths like this:
include_directories( ${my_app_src_top}/MyLibrary )
I also just define a project at the top level CMakeLists.txt. In this case you may be able to reference ${PROJECT_SOURCE_DIR}, but if you define projects in sub directories, then I think this will get reset based on the last sub directory you built.
I think with this structure you should be able to
#include <foo/Foo.h>
#include <bar/Bar.h>
Plus you may want to add ./ to the include_directories for each sub dir.

Using cmake's automoc on external header files without knowing their filenames

In essence I want to be able to moc header files that are not part of any target in cmake with the additional difficulty that I don't know the filenames of those headers.
The actual project is quite complex and part of an automated build system. The following is an easy example.
Consider a project structured like this:
CMakeLists.txt
src/lib/source.cpp
src/lib/CMakeLists.txt
src/include/some_header.hpp # which is included in source.cpp
Content of main CMakeLists.txt:
cmake_mimimum_required(VERSION 2.8.6)
project("automoctest")
add_subdirectory(src/lib)
Content of src/lib/CMakeLists.txt:
include_directories(${CMAKE_HOME_DIRECTORY}/src/include)
find_package(Qt4 4.8 REQUIRED QtCore)
include(UseQt4)
include_directories(${CMAKE_CURRENT_BINARY_DIR})
add_library(foo SHARED source.cpp)
target_link_libraries(foo ${QT_LIBRARIES})
set_target_properties(foo PROPERTIES AUTOMOC true)
Inside source.cpp the file some_header.hpp is included like this:
#include "some_header.hpp"
The Problem:
The file some_header.hpp includes a Q_OBJECT and has some signals, so moc needs to work its magic. But as the header is not inside the actual project the header will not get mocked. I don't even know the filename of some_header.hpp so I can't add it specifically to be mocked. Obviously AUTOMOC does not check the include_directories for mockable files even when a source file includes one of them.
What I tried (unsuccessfully):
use #include moc_some_header.cpp in source.cpp as it is described in the cmake documentation. This leads to an error in which cmake complains that it could not find some_header{.h,.hpp,.hxx,.H}
setting CMAKE_AUTOMOC_RELAXED_MODE to true. Even though it's not clear from the doc what this actually does. Made no difference anyway.
setting AUTOMOC_MOC_OPTIONS to -Isrc/include or -I/path/to/project/src/include or -I${CMAKE_HOME_DIRECTORY}/src/include Doesn't do anything that I could see.
The great thing about AUTOMOC is that I don't need to know which files actually need to be mocked. In this case however I would need to know all the headers that might have a Q_OBJECT inside, that are included by my source files.
Is there a way to tell AUTOMOC where exactly to look for mockable files?
Did you truly set AUTOMOC_MOC_OPTIONS to -Isrc/include, or to -I/path/to/myproject/src/include? The former probably doesn't exist.
I have always used the MOC macros; it looks like AUTOMOC is something new that is built into CMake.
I usually include all headers when creating a library or executable - i.e.
add_library(foo SHARED source.cpp ../include/some_header.hpp )
I assume that this will allow AUTOMOC to work. It will have the added benefit of causing make to work as expected - rebuilding your library when the header changes.
If you are truly unable to add the file to the add_library command ahead of time, then I think you'll have to write a cmake function to search for #include statements within source.cpp, locate those files, and search them for Q_OBJECT. If they match, add them to a variable - perhaps EXTRA_MOCS. Then, the add_library line becomes
add_library(foo SHARED source.cpp ${EXTRA_MOCS} )