CMake Include Directories Collision - c++

In my project, I have two versions of the same system library: SysLib1.0 and SysLib2.0. These two libraries are used by other components of the system heavily.
SysLib1.0 headers are located in some directory: /project/include. Here's an example of the contents of the project include directory:
/project/include/syslib/
/project/include/awesomelib/
/project/include/coollib/
So naturally, in CMake, other components use include_directories(/project/include) to gain access to system and other component headers. C++ source code could access the headers like so:
#include <syslib/importantheader.hpp>
SysLib2.0 is installed in a separate location in order to avoid linking issues. SysLib2.0's headers are stored here:
/opt/SysLib2.0/include
So naturally, in CMake, other components which require SysLib2.0 use include_directories(/opt/SysLib2.0/include). C++ source code could access the headers like so:
#include <syslib/importantheader.hpp>
Now we have run into a problem. A new component I'm writing needs access to /project/include in order to access awesomelib, BUT also needs SysLib2.0. This involves including /opt/SysLib2.0/include as well. Now when I say #include <syslib/importantheader.hpp>, that could refer to either version of the library. The compiler yells at me with some redefinition errors, as it should.
Even worse, SysLib1.0 and SysLib2.0 both refer to themselves as syslib/... when looking for headers within their own library, which is just as ambiguous.
Does anyone have an idea of how I could exclude a particular directory from an include path? Even if I am including a parent directory as shown in my example? Any solutions or suggestions are appreciated.

You can create a tree-like directory structure for your project with nested CMakeLists.txt files and include separate directories in different leafs:
Given a directory structure:
A:
|main.cpp
|
|CMakeLists.txt
|
|B-----|
| |CMakeLists.txt
| |b.cpp
|
|C-----|
|CMakeLists.txt
|c.cpp
A/CMakeLists.txt:
add_subdirectory(B)
add_subdirectory(C)
add_executable(exe main.cpp)
target_link_libraries(exe a b)
B/CMakeLists.txt:
include_directories(/project/include)
add_library(b b.cpp)
C/CMakeLists.txt:
include_directories(/opt/SysLib2.0/include)
add_library(c c.cpp)
This way you can include different directories for different source files; pack them up in libraries and link the final target with the libraries

I don't like using one include path for all includes. I would rather the following structure.
include - your own headers
include/awesomelib
include/coollib
3rd - third party libs
3rd/syslib-1.0/include
3rd/syslib-1.0/src
3rd/syslib-2.0/include
3rd/syslib-2.0/src
src - your source
src/awesomelib (depends on syslib-1.0, includes 3rd/syslib-1.0/include)
src/coollib (depends on syslib-2.0, includes 3rd/syslib-2.0/include)
Then you can specify which syslib to use when building a library.

Related

target_include_directories in merged source tree structure for private headers

I am working on a project that is based on the merged header placement (headers, sources and tests are in the same directory).
I would like to link another directory to this, however using the target_include_directory will include the private header files as well. I am wondering how can i exclude certain headers file from being accessible by libraries that are linked to my current target
I have tried to read through the documents of cmake and seems that i am not able to achieve this quite easily. I am quite new to cmake so sorry if this is a trivial question. I have also searched for example of this structure in github and could not find any example project.
The reason I did not choose the separate structure is that the current project i am working on is very modularized. There are many nested subdirectories 5-6 level and having the project structure repeated 3 time in src test and include was very annoying to navigate. That is why we have switched to the merged header placement structure to have only a single nested directory.
You can switch to a split header layout and put public headers under include/ and private headers under src/. Then target_include_directories() include/ for your target.
As far as I'm aware, the merged header placement problem can't be solved by CMake for the build tree (what you use at development time). It's a "limitation" due to how include directory flags work on all major compilers: If you put a directory in the include path, then everything under it can be path-resolved through it (thought that's not to say everything that can be path-resolved is valid source code just that it can be path-resolved). But it can be solved for the install tree for private headers that don't need to be installed: Just don't install those ones.
Unless there's something I'm missing about how CMake and compiler include paths work, if you really want to make your private headers non-reachable by dependent targets at build time, you'll need to just bit the bullet and switch to a layout where the private headers are under a separate tree than the public ones.
The Pitchfork Layout Spec even alludes to this in its section on Separate Header Placement:
Consumers of a library using separated header layout should be given the path to the § 2.2 include/ directory as the sole include search directory for the library’s public interface. This prevents users from being able to #include paths which exist only in the src/ directory.

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)

CMake/make apply -D flags on header files

I'm (cross-)compiling a shared C library with support for many different platforms which is handled by an hierarchy of CMakeLists files. In those files, several platform specific compiler flags are conditionally produced (with add_definitions()). I can successfully compile and link the source code leading to an appropriate .so file.
But to use the library in any project, I need to provide the right header files, too. The following install command of CMake selects the right header files to copy but does not apply the replacement of preprocessor defines/includes:
install(FILES ${headers} DESTINATION include/mylibrary)
So how can I generate/install the "post-compiled" header files?
What I thought of so far:
As add_definitions() should stack my -D's in the COMPILE_DEFINITIONS variable, maybe running a foreach loop on the copied raw headers and replace the define/include placeholders?
Using add_custom_command() to apply some logic before copying?
Edit: As pointed out by Tsyvarev, there is an answer quite near to my needs here, but unfortunately not quite it. In summary, the answer gives 2 options:
Include a special 'config' header in all of the library's headers and leverage the cmakedefine command to call configure_file() on this header. I can't use this approach because I don't want to alter the library headers.
Create a target-specific .cmake file which helps external projects in including the right headers together with all necessary -D defines. I can't use this approach either, because my external projects do not use cmake for building. Plus, I wish to create a library that is as easy to include as possible.
Any other thoughts?
Edit 2: I may have to elaborate on my statement, that the install command of CMake is not replacing defines. Take the following example:
//sampleheader.hpp
#ifndef SAMPLEHEADER_HPP_
#define SAMPLEHEADER_HPP_
#include OS_SPECIFIC_HEADER
//...
Now I have a CMakeLists.txt file that does something like this:
# ...
if (${OS} MATCHES "arm-emblinux")
add_definitions(-DOS_SPECIFIC_HEADER="emblinuxHeader.hpp")
elseif (${OS} MATCHES "linux")
add_definitions(-DOS_SPECIFIC_HEADER="linuxHeader.hpp")
endif()
# ...
Everything compiles fine, but when the install command above gets called, I have a header file in my ../include/ directory still with OS_SPECIFIC_HEADER placeholder in it. And of course, this cannot be properly included in any development project.

Tell CLion to use header include path with prefix

I have a project layout as follows:
workspace
project_a
project_a -> .h files here
Root -> .cxx files here
project_b
project_b -> .h files here
Root -> .cxx files here
I cannot change the directory layout due to the build system that we're using.
Headers are included as
#include "project_a/some_header.h
also from the corresponding .cxx file.
I've created a CMakeLists.txt file in the root directory, that adds all my projects via include_directories(project_a project_b), which should be the path prefixed before the one given in the #include. CLion does not manage to find and index any of my files.
Additionally, I have an automatically generated directory of headers of structure
include
lib_a -> .h files
lib_b -> .h files
and I've set them up accordingly, but it also does not work.
Does CLion not manage to resolve the prefixed path in the #include or why is this not working?
In CMakeList.txt, which should be located in parent folder, "workspace" folder in that situation, add
set(INCLUDE_DIRECTORIES ./)
If, for example, there is a parent folder, that holds include files:
workspace
includes_folder
project_a
a.h
b.h:
#include <project_a/a.h>
Then CMakeList.txt should contain
set(INCLUDE_DIRECTORIES ./)
include_directories(includes_folder)
If the only thing that doesn't work is Clion's interpretation of your headers then you should check out the Clion FAQ. If Clion's not working the way you expect then your CMake project might not be set up correctly or you might be doing something unintentionally. You should show us your CMakeLists.txt.
From the Clion FAQ:
Q: CLion fails to find some of my headers. Where does it search for them?
A: CLion searches through the same places CMake does. Set the INCLUDE_DIRECTORIES variable in CMake to provide the headers path to the IDE.
By the way, in this wiki you can find a lot of useful CMake variables with descriptions that can be especially helpful if you are new to CMake.
Q: Refactorings are not working and highlighting is wrong, even though the project can be compiled correctly. What’s happened?
A: Most probably CLion is unaware of some files in your project (these files are grayed out in the project tree):
non-project-files
It gets this information from the CMakeLists.txt files in the following way:
set(SOURCE_FILES main.cpp)
add_executable(my_exec ${SOURCE_FILES})
This is how CLion now knows that main.cpp is included in your project. As for now, header files (in case their names differ from the appropriate .cpp files already added to the SOURCE_FILES variable) should also be included in the project in that way.

CMake: ordering of include directories (How to mix system- and user-based include paths?)

I've got a CMake project that includes and links against two libraries, say A and B (actually it's more than two and one of them is boost stuff, but that doesn't really matter here). Both are located via FindSomething.cmake scripts that (correctly) populate the standard CMake variables such that include directories are added via
INCLUDE_DIRECTORIES(${A_INCLUDE_DIRS})
INCLUDE_DIRECTORIES(${B_INCLUDE_DIRS})
and linking is later done via
TARGET_LINK_LIBRARIES(mytarget ${A_LIBRARIES} ${B_LIBRARIES})
Now, the problem is that both libraries can either reside in a user based location or in the system directories (I'm on linux by the way, CMake 2.8.2) - or in both. Let's say A is only in $HOME/usr/include and $HOME/usr/lib while B (boost in my case) resides in both the system paths (/usr/include and /usr/lib) AND in the user based paths - in different versions. The find scripts can be made to find either the system or the user-based library B, this works.
The trouble starts when I want to link against B from the system paths.${B_INCLUDE_DIRS} and ${B_LIBRARIES} correctly point to the system-wide locations of the headers and libraries. But there is still ${A_INCLUDE_DIRS} that points to a non-system include directory and ultimately also the headers for library B are taken from this location, while the linking for B uses the version from the system paths (via ${B_LIBRARIES}) which leads to conflicts, i.e. linking errors.
Changing the order of the INCLUDE_DIRECTORIES statements does not seem to change anything. I checked the origin of the symbols that cause the linking errors via nm --line-numbers on the object files.
What can I do? Is there a trick to
force the ordering of the include directories (even if this would mean to give precedence to a system path although there is also a user-based location specified)?
tell CMake to use ${A_INCLUDE_DIRS} for all headers from A and ${B_INCLUDE_DIRS} for all headers from B?
Here's what CMake says about include_directories():
include_directories([AFTER|BEFORE] [SYSTEM] dir1 [dir2 ...])
You can specify that you want to have include directories searched before or after the system include directories at the time that you tell it about those directories.
You may also be specific to a target:
target_include_directories(target [SYSTEM] [BEFORE] [items1...] [ [items2...] ...])
If A and B are different libraries containing different header files and paths, there should be no problem doing what you are doing right now.
That being said, if A and B are similar libraries containing header files of the same name at the same location, that is problematic. In that case, the order of the include_directory() call is important. I ran a little test where I had three copies of a header file. The first copy is located in my system path (say /usr/include). The other copies are located in two user-defined locations (say /tmp/include1 and /tmp/include2). The file in /tmp/include1 is found and used first if I put the include_directory() call in the following order:
include_directory("/tmp/include1")
include_directory("/tmp/include2")
The file in /tmp/include2 is found and used first if I put the include_directory() call in the following order:
include_directory("/tmp/include2")
include_directory("/tmp/include1")
If I put no include_directory() statement, then the header in the system path is found and used.
You may want to re-check how your FindSomething.cmake are written. The search order of the find_*() CMake commands can be found in the CMake documentation,
As far as I can remember, there is now way of telling CMake to use ${A_INCLUDE_DIRS} for all headers from A and ${B_INCLUDE_DIRS} for all headers from B if the header file can be found in both location. It all depends in what order the include_directory() call are made. If the FindSomething.cmake are written properly, if the CMAKE_MODULE_PATH (this is the location where CMake will look for the Find*.cmake files) is set properly and all the paths are good, then you should be good to go. If not, I will need more information on your current CMake/library setup.
When using third party libraries, I would always do this
Library A + B header files:
third_party/include/libA_name/ <-- put header files in there
third_party/include/libB_name/ <-- put header files in there
In source files you would always use it like this
#include "libA_name/file.h" <-- no ambiguity possible
#include "libB_name/file.h" <-- no ambiguity possible
Then you can still use -I "third_party/include" as the include folder and no ambiguity in ordering will happen in source files.
This also disambiguates custom header files from system header files which could clash from time to time from 3rd party libs.
For me this worked fine:
INCLUDE_DIRECTORIES(BEFORE ${A_INCLUDE_DIRS})
INCLUDE_DIRECTORIES(${B_INCLUDE_DIRS})