How to do CMake modules - c++

I try to learn c++ with cmake. I have done a project, and cmake runs, it compiles, it works, that is nice, ok. Now I started a new project in which I want to use some classes of the first one. I have tried to read some sourcecode and I have understand that I need to make a module that I can read from both the application. So this will be my repository:
/
/cmake
/modules/Network
/software/sw1
/software/sw2
both the projects sw1 and sw2 depends on the module Netowrk. In the folder cmake there has to be the FindNetwork.cmake file, and in sw1, sw2 and Network there has to be the CMakeList.txt .
But.. I messed up with include_directories and other cmake obscurities..
Can someone point me out to a nice overview how to easily organize a repository with softwares that depend on common modules?

This link will provide some examples for you.
In terms of how you are looking at the project/infrastructure then it's best to not confuse things too much. So here is a couple of points to get you started (I hope)
In c++ a module is a library (so refer to your Network module as a
library)
To include a library you need to link it and also make the
header files available.
In cmake this is two commands target_link_libraries and include_directories respectively.
With that in mind the project structure could be
/Network/include (api here)
/Network/src
/sw1/src
/sw2/src
with an example base CmakeLists.txt file for you: (place in root dir of project)
cmake_minimum_required(VERSION 2.7) // your choice
project(myproject) // change name
add_subdirectory(Network)
add_subdirectory(sw1)
add_subdirectory(sw2)
in the Network Directory you would have this
add_library(Network net1.cc net2.cc etc.)
In the sw1 dir
include_dirs(${MYPROJECT_SOURCE_DIR}/Network/include)
link_directories(${MYPROJECT_BINARY_DIR}/Network)
add_executable (sw1prog sw1.cc sw11.cc etc.)
target_link_libraries (sw1prog Network)
In the sw2 dir
include_dirs(${MYPROJECT_SOURCE_DIR}/Network/include)
link_directories(${MYPROJECT_BINARY_DIR}/Network)
add_executable (sw2prog sw2.cc sw21.cc etc.)
target_link_libraries (sw2prog Network)
This is a very simplified version of what you may require, it removes the need for a FindXXModule.cmake file to be created and refers to the library you create implicitly. I think this is the best mechanism for you, if you did want to create a FindXXModule.cmake then I would suggest it's when you actually install your libs to the machine and wish others to be able to find it, either that or have a mechanism for multiple projects to link to each other libraries.
I hope this is a little useful, please bear in mind the cmake site has some examples and cmake --help is your friend.

Related

How to fix "Could not find a package configuration file ..." error in CMake?

I have been working on a project which uses rplidar_sdk and in the beginning, I was facing this problem:
How can I link locally installed SDK's static library in my C++ project?
Basically, the SDK generates the library in its local directory, and in its Makefile, it does not have install rules. I mean I can run make but after that, if I run sudo make install then it gives make: *** No rule to make target 'install'. Stop. error.
So, with the help of this & this answer, I was able to build my local project. So far so good.
However, the main problem is that I have to hard-code the RPLidar SDK path in CMakeLists.txt of my repo. Now, whenever someone else in my team starts working on that repo (which is quite obvious) then he/she has to update the CMakeLists.txt first. This is not a good idea/practice!
To fix this, I updated the Makefile of RPLidar SDK as follow:
.
.
.
RPLIDAR_RELEASE_LIB := $(HOME_TREE)/output/Linux/Release/librplidar_sdk.a
install: $(RPLIDAR_RELEASE_LIB)
install -d $(DESTDIR)/usr/local/lib/rplidar/Release/
install -m 644 $(RPLIDAR_RELEASE_LIB) $(DESTDIR)/usr/local/lib/rplidar/Release/
RPLIDAR_DEBUG_LIB := $(HOME_TREE)/output/Linux/Debug/librplidar_sdk.a
install: $(RPLIDAR_DEBUG_LIB)
install -d $(DESTDIR)/usr/local/lib/rplidar/Debug/
install -m 644 $(RPLIDAR_DEBUG_LIB) $(DESTDIR)/usr/local/lib/rplidar/Debug/
RPLIDAR_HEADERS := $(HOME_TREE)/sdk/include
install: $(RPLIDAR_HEADERS)
install -d $(DESTDIR)/usr/local/include/rplidar/
cp -r $(RPLIDAR_HEADERS)/* $(DESTDIR)/usr/local/include/rplidar/
RPLIDAR_HEADERS_HAL := $(HOME_TREE)/sdk/src/hal
install: $(RPLIDAR_HEADERS_HAL)
install -d $(DESTDIR)/usr/local/include/rplidar/
cp -r $(RPLIDAR_HEADERS_HAL) $(DESTDIR)/usr/local/include/rplidar/
Due to this update, now, I can run sudo make install which basically copies the header files of RPLidar SDK from the local directory to /usr/local/rplidar/ directory. It also copies the lib file to /usr/local/lib/rplidar/<Debug> or <Release>/ directory.
Now, in my local project, I updated the CMakeLists.txt to as follow:
cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR)
project(<project_name>)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
SET(CMAKE_CXX_FLAGS -pthread)
include_directories(include)
add_executable(${PROJECT_NAME} src/main.cpp src/another_src_file.cpp)
find_package(rplidar REQUIRED)
include_directories(${rplidar_INCLUDE_DIRS})
link_directories(${rplidar_LIBRARY_DIRS})
target_link_libraries(${PROJECT_NAME} ${rplidar_LIBRARY})
However, upon running cmake .. command, I'm getting this error:
.
.
.
CMake Error at CMakeLists.txt:12 (find_package):
By not providing "Findrplidar.cmake" in CMAKE_MODULE_PATH this project has
asked CMake to find a package configuration file provided by "rplidar", but
CMake did not find one.
Could not find a package configuration file provided by "rplidar" with any
of the following names:
rplidarConfig.cmake
rplidar-config.cmake
Add the installation prefix of "rplidar" to CMAKE_PREFIX_PATH or set
"rplidar_DIR" to a directory containing one of the above files. If
"rplidar" provides a separate development package or SDK, be sure it has
been installed.
-- Configuring incomplete, errors occurred!
As far as I know, RPLidar SDK does not have rplidarConfig.cmake or rplidar-config.cmake file.
How can I fix this error?
Rants from my soul:
It sucks when you have to use any library foo when the author fails to provide a foo-config.cmake for you to use easily by invoking find_package(foo). It's absolutely outrageous when a reasonably modern project still uses hand written Makefiles as its build system. I myself is stuck with a much worse constructed SDK than yours right now.
Short answer:
Since the author of the SDK fails to provide a config file to support your cmake usage, if you still insists on invoking find_package on the library (and you should!), you are required to write your own Module file to clean up their mess. (Yeah, you are doing the work for the library authors).
To truly achieve cross platform usage, you should write a Findrplidar.cmake module file to find the libraries for you.
To write a reasonable module file, you would most likely use API find_path for header files and find_library for libs. You should check out its docs and try using them, and maybe Google a few tutorials.
Here is my version of Findglog.cmake for your reference. (glog authors have updated their code and supports Config mode. Unfortunately, Ubuntu build doesn't use it, so I still have to write my own file)
find_path(glog_INCLUDE_DIR glog/logging.h)
message(STATUS "glog header found at: ${glog_INCLUDE_DIR}")
find_library(glog_LIB glog)
message(STATUS "libglog found at: ${glog_LIB}")
mark_as_advanced(glog_INCLUDE_DIR glog_LIB)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(glog REQUIRED_VARS
glog_INCLUDE_DIR
glog_LIB
)
if(glog_FOUND AND NOT TARGET glog::glog)
add_library(glog::glog SHARED IMPORTED)
set_target_properties(glog::glog PROPERTIES
IMPORTED_LINK_INTERFACE_LANGUAGES "CXX"
IMPORTED_LOCATION "${glog_LIB}"
INTERFACE_INCLUDE_DIRECTORIES
"${glog_INCLUDE_DIR}"
)
endif()
And you can use it like this:
find_package(glog)
target_link_libraries(main PRIVATE glog::glog)
Long answer:
The history of developers using cmake is an absolute nightmare. The internet is filled with bad practice/examples of how not to use cmake in your project, including the old official cmake tutorial (which still might be there). Mostly because no one really gives a **** (If I can build my project, who cares if it's cross platform). Another valid reason is that cmake documentations are really daunting to beginners.
This is why I am writing my own answer here, lest you get misguided by Googling elsewhere.
The nightmare is no more. The wait has ended. "The Messiah" of cmake (source) is come. He bringeth hope to asm/C/C++/CUDA projects written in 2020 and on. Here is The Word.
The link above points to the only way how cmake projects should be written and truly achieve cross platform once and for all. Note the material is not easy at all to follow for beginners. I myself spent an entire week to fully grasp what was covered in The Word, when I had become somewhat familiar with cmake concepts at the time (but lost in my old sinful ways).
The so-called "long answer" is actually shorter. It's just a pointer to the real answer. Good luck reading the Word. Embrace the Word, for anything against that is pure heresy.
Response of comment 1-5:
Good questions. A lot of those can be obtained from the Word. But the word is better digested when you become more familiar with CMake. Let me answer them in decreasing of relevance to your problem at hand.
For the ease of discussion, I'll just use libfoo as an example.
Let's say you always wants to use libfoo like this:
find_package(foo)
target_link_libraries(your_exe ... foo::foo)
Pretend foo is installed at the following location:
- /home/dev/libfoo-dev/
- include
- foo
- foo.h
- bar.h
- ...
- lib
- libfoo.so
- share
- foo/foo-config.cmake # This may or may not exist. See discussion.
Q: Only one .h file. Why?
A: Because in the case of libfoo (also true for glog), only one search of header location is necessary. Just like the example from libfoo,
where foo/foo.h and foo/bar.h are at the same location. So their output of find_path would be the same: /home/dev/libfoo-dev/include.
Q: Why I'm getting NOTFOUND for my headers and libs?
A: The function find_path and find_library only search locations specify in the documentations. By default they search system locations,
like /usr/include and /usr/lib respectively. Refer to the official docs for details on system locations. In the case of libfoo, however,
they reside in /home/dev/libfoo-dev. So you must specify these locations in cmake variable CMAKE_PREFIX_PATH. It's a ; seperated string.
One would do cmake -D CMAKE_PREFIX_PATH="/home/dev/libfoo-dev;/more/path/for/other/libs/;...;/even/more/path" .... on the command line.
One very important note: unlike Unix command find, find_path will only search specific paths inside /home/dev/libfoo-dev, not all the way down:
include (usually also include/{arch} where {arch} is sth like x86_64-linux-gnu for x86 Linux) for find_path; lib variant for find_library,
respectively. Unusual locations would require passing in more arguments, which is uncommon and most likely unnecessary.
For this very reason, for libfoo, calling find_path(... foo.h ...) is undesired. One would want find_path(... foo/foo.h ...). Refer to the docs
for more details. You can also try out yourself.
Also for this reason, it is desirable to organize libraries in the usual bin include lib share quad on Unix-like systems. I'm not familiar with Windows.
Q: Debug & Release
A: There are several options. The easiest one might be:
Prepare rplidar debug and release build in two different folders, /path/to/debug & /path/to/release for instance
Passing to Debug & Release build respectively (cmake -D CMAKE_PREFIX_PATH="/path/to/debugORrelease" ....)
There are definitely others ways, but perhaps requires special care in your Findrplidar.cmake script (maybe some if statements).
Q: Why glog::glog rather than glog?
A: It's just modern cmake practice, with small benefits. Not important right now. Refer to the Word if you are interested.
Q: You mentioned that you are writing rplidarConfig.cmake. Instead you should rename the file to Findrplidar.cmake.
A: CMake philosophy is as such:
Library authors should write foo-config.cmake or fooConfig.cmake
When they fail to provide one, it sucks. And according to the Messiah, it should be reported as a bug.
In this case, you as library user, should write Findfoo.cmake by guessing how to describe the dependencies for libfoo. For simple libraries, this is not so bad. For complex ones, like Boost, this sucks!
A few side note on this topic:
Note how Findfoo.cmake is written by library users, from guessing.
This is insane! Users shouldn't do this. This is the authors' fault, to put their users in this uncomfortable situation.
A foo-config.cmake file is extremely easy to write for authors of libfoo, IF they follow the Word exactly.
Extremely easy for the authors in the sense that: cmake can take care of everything. It will generate scripts automatically for the authors to use in their foo-config.cmake file.
Guaranteed to be cross-platform and easy to use by the users, thanks to cmake.
However, the reality sucks. Now you have to write Findfoo.cmake
Q: Why only find_package & target_link_libraries?
A: This is what the Word says. It's therefore good practice. Why the Word says so is something you have to find out yourself.
It's not possible for me to explain the gist of the Word in this answer, nor would it be convincing to you. I would just say the following:
It's very easy to write spaghetti CMakeLists that are next to impossible to maintain. The spirit of the Word helps you avoid that by
forcing you to carefully think about:
library structure: public vs private headers, for example. This makes you think about what to include in your headers and public APIs.
build specification: what is necessary to build a library you write (what to include; what to link)
usage requirement: what is necessary for others to use a library you write (what to include; what to link)
dependencies: what is the relationship of the library you write & its dependencies
Maybe more
If you think about it, these aspects are crucial to writing a cross-platform and maintainable library.
include_directories, link_directories and add_definitions are all very bad practice
(according to lots of sources, including the official documentations of these APIs). Bad practice tends to obscure the aspects above,
and causes problems later on when everything gets integrate together as a whole. E.g. include_directories will add -I to compiler for every
target written in the directory of that CMakeLists.txt. Read this sentence a few times and Spock will tell you it's illogical.
Don't worry. It's okay for now to use them when you are not familiar with the Word (Why else would this be in the last section). Once you know the Word, refactor your CMakeLists when you have time. Bad practice might cause problem later on, when your project becomes more complex. (In my professional experience, 5 very small groups of people is enough to cause a nightmare. By nightmare I mean hard code everything in CMakeLists; Create a git branch for every single different platform/device/environment; Fixing a bug meaning to cherry-pick one commit to each branch. I've been there before knowing the Word.)
The practice of the Word very well utilize the philosophy of modern CMake, to encapsulate build specifications and usage requirements inside
CMake targets. So when target_link_libraries is called, these properties gets propagated properly.

CMake with 3rd party libraries that need to be built along with the project

I am confused on the right way to get an external library integrated into my own Cmake project (This external project needs to be built along with my project, it's not installed separately, so we can't use find_library, or so I think)
Let's assume we have a project structure like this (simplified for this post):
my_proj/
--CMakeLists.txt
--src/
+---CMakeLists.txt
+---my_server.cpp
That is, we have a master CMakeLists.txt that basically sits at root and invokes CMakeLists for sub directories. Obviously, in this example, because its simplified, I'm not showing all the other files/directories.
I now want to include another C++ GitHub project in my build, which happens to be this C++ bycrypt implementation: https://github.com/trusch/libbcrypt
My goal:
While building my_server.cpp via its make process, I'd like to include the header files for bcrypt and link with its library.
What I've done so far:
- I added a git module for this external library at my project root:
[submodule "third_party/bcrypt"]
path = third_party/bcrypt
url = https://github.com/trusch/libbcrypt
So now, when I checkout my project and do a submodule update, it pulls down bcrypt to ${PROJ_ROOT}/third_party
Next up, I added this to my ROOT CMakeLists.txt
# Process subdirectories
add_subdirectory(third_party/bcrypt)
add_subdirectory(src/)
Great. I know see when I invoke cmake from root, it builds bcrypt inside third_party. And then it builds my src/ directory. The reason I do this is I assume this is the best way to make sure the bcrypt library is ready before my src directory is built.
Questions:
a) Now how do I correctly get the include header path and the library location of this built library into the CMakeLists.txt file inside src/ ? Should I be hardcoding #include "../third_party/bcrypt/include/bcrypt/bcrypt.h" into my_server.cpp and -L ../third_party/libcrypt.so into src/CMakeLists.txt or is there a better way? This is what I've done today and it works, but it looks odd
I have, in src/CMakeLists.txt
set(BCRYPT_LIB,"../third_party/bcrypt/libbcrypt.so")
target_link_libraries(my app ${MY_OTHERLIBS} ${BCRYPT_LIB})
b) Is my approach of relying on sequence of add_directory correct?
Thank you.
The best approach depends on what the bcrypt CMake files are providing you, but it sounds like you want to use find_package, rather than hard-coding the paths. Check out this answer, but there are a few different configurations for find_package: MODULE and CONFIG mode.
If bcrypt builds, and one of the following files gets created for you:
FindBcrypt.cmake
bcrypt-config.cmake
BcryptConfig.cmake
that might give you an idea for which find_package configuration to use. I suggest you check out the documentation for find_package, and look closely at how the search procedure is set up to determine how CMake is searching for bcrypt.

CMake not building a library when added as a subdirectory

I added the xgboost library as a git submodule of my project and I'm trying to add it to cmake as a subdirectory. Unfortunately it's not working. A simple hello world project with the following CMakeLists.txt replicates the error that I'm getting.
cmake_minimum_required(VERSION 3.2)
project(foo)
add_subdirectory(xgboost)
add_executable(${PROJECT_NAME} foo.cpp)
target_link_libraries(${PROJECT_NAME} xgboost)
After building the library there is nothing in the xgboost/lib directory so I get the following error.
clang: error: no such file or directory:
'/Users/.../myproject/xgboost/lib/libxgboost.dylib'
I think that the problem is generated in their CMakeLists file since they have two different targets. Maybe cmake is choosing the wrong target but I'm not familiar enough with cmake to figure it out. The following code is from xgboost's CMakeLists.
# Executable
add_executable(runxgboost $<TARGET_OBJECTS:objxgboost> src/cli_main.cc)
set_target_properties(runxgboost PROPERTIES
OUTPUT_NAME xgboost
)
set_output_directory(runxgboost ${PROJECT_SOURCE_DIR})
target_link_libraries(runxgboost ${LINK_LIBRARIES})
# Shared library
add_library(xgboost SHARED $<TARGET_OBJECTS:objxgboost>)
target_link_libraries(xgboost ${LINK_LIBRARIES})
set_output_directory(xgboost ${PROJECT_SOURCE_DIR}/lib)
#Ensure these two targets do not build simultaneously, as they produce outputs with conflicting names
add_dependencies(xgboost runxgboost)
My questions in order of importance are:
Is there any way to fix it without modifying xgboost's CMakeLists.txt file?
Is it reasonable to try to add xgboost to my project as a git submodule?
Is there any reason cmake is not instructing to build the library?
Note: There were several edits to this question since I tried to narrow down the problem and to provide more information.
(I would love to ask for few things beforehand in the comment section, but I have too low reputation to do so, so I will just give it a shot ;))
I have few suspects, and one of them is ${CMAKE_SOURCE_DIR} of the submodule's root CMakeLists.txt. Although the paths are set properly when you run that CMakeLists.txt alone, cmake gets confused the moment you add it as your subdirectory. Have you looked into another directories for your output binaries?
First I would suggest testing this hypothesis, and then I would suggest writing similar, but separate CMakeLists.txt file for xgboost library, and then substitute it in the project temporarily. Unfortunately the CMakeLists.txt filename is hardcoded and there is no possibility to have two files of that kind in one directory; so it seems that the answer to 1) is, that you rather have to change the file.
For the 2): as long as it does not require huge additional logic in your CMakeLists.txt, it makes sense. Other viable option is to create an install target, which you can use to install your xgboost library locally (using CMAKE_INSTALL_PREFIX(doc) variable), and then add the installation path to your CMAKE_LIBRARY_PATH(doc).

Building tensorflow serving client with cmake

I searched for the best way to do this, but I was unable to find a clear answer.
Was anyone able to build a tensorflow-serving client using cmake?
I am having difficulties with generating CPP files from proto, since they are needed for prediction service. Those proto files also include proto files from tensorflow.
so far I have come up with this:
project(serving C CXX)
find_package(Protobuf REQUIRED)
file(GLOB_RECURSE proto_files RELATIVE ${serving_SOURCE_DIR}/tensorflow/
"${serving_SOURCE_DIR}/tensorflow/*.proto")
set(PROTOBUF_GENERATE_CPP_APPEND_PATH OFF)
include_directories(${PROTOBUF_INCLUDE_DIRS})
include_directories( ${CMAKE_CURRENT_BINARY_DIR} )
PROTOBUF_GENERATE_CPP(PROTO_SRCS PROTO_HDRS ${proto_files})
add_library(tf_protos ${PROTO_SRCS} ${PROTO_HDRS})
target_link_libraries(tf_protos PUBLIC ${PROTOBUF_LIBRARIES})
Cmake builds successfully but the make command gives me an error:
No rule to make target '../tensorflow/tools/proto_text/test.proto', needed by 'tensorflow/tools/proto_text/test.pb.cc'. Stop.
To overcome the problem of .proto includes not being found I used command
set(PROTOBUF_GENERATE_CPP_APPEND_PATH OFF)
which was explained here: https://groups.google.com/forum/#!topic/protobuf/eow2fNDUHvc
My current folder structure is
serving/
CmakeLists.txt
tensorflow/
tensorflow_serving/
apis/
Folder apis contains .proto files that are needed in the client implementation and they include .proto files from the folder tensorflow.
Is this even the right way to go?
Any help/advice would be much appreciated.
I was able to get it to work in the layout you have where the CMakeLists.txt file is placed in the same level as the serving repository here. You'll need to install Tensorflow too though (using tensorflow_cc).
However, you probably don't want to muck with a fork of the official tensorflow/serving repository so I went a step further and moved the CMakeLists.txt out so you can just submodule the official repository. I made an example here
The gist is that the protobuf CMake submodule expects proto files to be laid out in the same directory from which it's called. I made some modifications to the submodules to let us call it from a level above serving and to ensure it invokes the compiler with the include paths in the right order to support the nested structure of the proto files in serving/tensorflow_serving/apis/* (and placing it accordingly in the specified build directory)
Hopefully someone else with better knowhow can make this better!
These worked for me.
https://github.com/wardsng/inception_cmake
https://github.com/FloopCZ/tensorflow_cc
You can choose a private install directory instead of the default, e.g. /usr/local/...
cmake -DCMAKE_INSTALL_PREFIX= ..

preferred cmake project structure

I would like to have the following structure A -> B -> C, where:
C is boilerplate code, wrappers for third-party libraries, very
basic code etc.
B is the common classes, functions and data
structures specific to the project's domain.
A is the project itself.
I would like to make it easy to reuse C or B(+C) in future in my other projects. In addition, I have the following requirements:
As all three projects are in-progress, I would like to have an ability to build C, C+B and C+B+A in one shot.
I would prefer the static linkage over dynamic, so that C and C+B would be static libraries, and C+B+A would be the executable
I would like to keep cmake lists and config files simple and clean. Examples which I found in the official wiki and over the internet are pretty big and monstrous.
It would be great if it won't require changing more than a couple of lines if I'd change the locations of A, B or C in the filesystem.
All these three components are using google-test, but I'm not sure if it is important for the project layout.
I am pretty new to cmake and I don't even understand is it better to write XXXConfig.cmake or FindXXX.cmake files. Also, I am not sure, how should I pass relative paths from subcomponent to the parent component using X_INCLUDE_DIRS.
First I have to admit that I agree with #Tsyvarev. Your CMake environment should fit to your processes/workflow and should take project sizes and team structure into account. Or generally speaking the environment CMake will be used in. And this tends to be - in a positive way - very alive.
So this part of your question is difficult to answer and I'll concentrate on the technical part:
CMake has to know the location of the dependencies - relative or absolute - by
having a monolithic source tree (the one you don't want anymore)
CMake share library with multiple executables
CMake: How to setup Source, Library and CMakeLists.txt dependencies?
a common directory location for includes/libraries/binaries
Custom Directory for CMake Library Output
cmake install not installing libraries on windows
getting the paths via config files/variable definitions
How can I get cmake to find my alternative boost installation?
How to add_custom_command() for the CMake build process itself?
using registration in or installation from a database provided on the host
Making cmake library accessible by other cmake packages automatically
cmake wont run build_command in ExternalProject_Add correctly
To keep your CMake files as simple as possible I would recommend to group your CMake code into separate dedicated files:
Prefer toolchain files over if(SomeCompiler) statements
Move common/repeating code parts as function() bodies into a shared CMake include file
Move complex non-target specific code parts into their own (CMake) script files
Example Code
Since you have specifically asked for the find_package() variant, taking Use CMake-enabled libraries in your CMake project and the things listed above:
MyCommonCode.cmake
cmake_policy(SET CMP0022 NEW)
function(my_export_target _target _include_dir)
file(
WRITE "${CMAKE_CURRENT_BINARY_DIR}/${_target}Config.cmake"
"
include(\"\$\{CMAKE_CURRENT_LIST_DIR\}/${_target}Targets.cmake\")
set_property(
TARGET ${_target}
APPEND PROPERTY
INTERFACE_INCLUDE_DIRECTORIES \"${_include_dir}\"
)
"
)
export(
TARGETS ${_target}
FILE "${CMAKE_CURRENT_BINARY_DIR}/${_target}Targets.cmake"
EXPORT_LINK_INTERFACE_LIBRARIES
)
export(PACKAGE ${_target})
endfunction(my_export_target)
C/CMakeLists.txt
include(MyCommonCode.cmake)
...
my_export_target(C "${CMAKE_CURRENT_SOURCE_DIR}/include")
B/CMakeLists.txt
include(MyCommonCode.cmake)
find_package(C REQUIRED)
...
target_link_libraries(B C)
my_export_target(B "${CMAKE_CURRENT_SOURCE_DIR}/include")
A/CMakeLists.txt
include(MyCommonCode.cmake)
find_package(B REQUIRED)
...
target_link_libraries(A B)
This keeps all 3 build environments separate, only sharing the relatively static MyCommonCode.cmake file. So in this approach I have so far not covered your first point, but would recommend the use of a external script to chain/trigger your build steps for A/B/C.