Change exported target name in cmake (install alias to a target) - c++

I have a project which consists of many smaller subprojects. For ease of use we are building everything as one big project - each subproject is add_subdirectory. This way all targets are visible, we can use EXCLUDE_FROM_ALL and build only necessary dependencies, it greatly improves build times. On the other hand, it is possible to unclude some of those subprojects as a platform for another project. In this case we would want to install necessary targets. Here comes the tricky part. Target names have to be unique within the whole cmake invocation, thus out target names tend to be component_subcomponent_module. On the other hand, while we are exporting them, it makes sense to use namespace component::subcomponent. Here is a problem, it still requires using target name, so the user would have to use component::subcomponent::component_subcomponent_module. Is there a way to support both including and importing the project and using namespaced names? I want to export component_subcomponent_module as a module in component::subcomponent namespace. I understand that I can create such alias within the project but I can't find any way to change (alias) the exported name.
add_library(component_subcomponent_mocks ...)
install(
TARGETS component_subcomponent_mocks
EXPORT FindComponentSubcomponentMocks
ARCHIVE DESTINATION lib
PUBLIC_HEADER DESTINATION include
)
install(
EXPORT FindComponentSubcomponentMocks
NAMESPACE component::subcomponent
DESTINATION cmake
)
You see where it is going, imported name will become component::subcomponent::component_subcomponent_mocks.
On the other hand I can't create just:
add_library(mocks ...)
install(
TARGETS mocks
...
}
If I do it this way, then adding subprojects with add_subdirectory will end up with clashing project names. One option is not to use namespaces at all. If it already has to be unique within the whole project, then what is the point of adding namespaces? I don't like this idea but this is where it is all going to.
I've seen solutions like this: Cmake: Exporting targets with the same name in the build tree
This does not solve my issues at all. I could configure it this way but the whole point of using subprojects is to avoid implementing additional layer of dependency tracking. Either we'd have to call make && make install on each (of a few hundred) dependency before proceeding to our build, or we'd have to believe that none of them has changed which leads to many mistakes. For imported projects we use this approach and it works fine but is unnecessarily slow. Just running cmake on each of them to update them in case of changes is taking a few minutes. All of this for 10 seconds of running tests you are currently developing. That's why the support for adding subprojects as subdirectories is must have for us.
In my understanding, the easiest solution would be installing target it an alias but there is no such option by default. For example:
install(
TARGETS component_subcomponent_mocks
EXPORT FindComponentSubcomponentMocks
...
)
install(
EXPORT FindComponentSubcomponentMocks
NAMESPACE component::subcomponent
ALIAS mocks
...
This way installed target would be component::subcomponent::mocks and target used by add_subdirectory would be component_subcomponent_mocks which I can easily aliast to the same namespaced version. Everything would be neat and consistent except it is not supported by default. Is there any alternative to it?
There were similar but not detailed questions to this, all without a particular answer.
Change name of exported target
cmake install xxxTargets.cmake with a different name from the original target
The whole problem is that I have to keep my target names unique, so they already include their "namespace" in the name and including it once again seems to be pointless.

You can set the EXPORT_NAME property on your targets.
The following setup ensures consistent names (e.g. component::subcomponent::target) both in cmake superprojects as well as in exported configurations.
add_library(component_subcomponent_mylib ...)
set_property(TARGET component_subcomponent_mylib PROPERTY EXPORT_NAME mylib)
add_library(component::subcomponent::mylib ALIAS component_subcomponent_mylib)
install(TARGETS component_subcomponent_mylib
EXPORT FindComponentSubcomponentTargets
DESTINATION lib
)
install(EXPORT FindComponentSubcomponentTargets
DESTINATION cmake
NAMESPACE component::subcomponent::
)

Related

Controlling build options (tests, etc) in included libraries in a modern CMake fashion

What would the best way to replace global variables and instead move towards using target properties to control things like building tests in included libraries from the main applications CMakeLists.txt file?
Background
We have a c++ application, A, that uses, amongst others, inhouse libraries: lib B and lib C, that are included as git submodules in A:s repository. All respositories (a, B and C) share a common structure internal structure with all of them using Gtest as a unit test framework. This leads to all projects having a "unit_tests" target as well as a "gtest" target.
As a consequence, when including lib B and lib C in A:s CMakeLists.txt using add_subfolder() there is a conflict, as CMake requires target names to be unique and there is a total of 3 "unit_tests" targets in these 3 repos. There are other test targets as well, but they are so far unique. Renaming the test targets to b_unit_tests and a_unit_test would cure this, but doesnt feel right, and we would also need to rename the gtest targets to a_gtest, b_gtest...
Currently we have solved it by having global CMake variables B_BUILD_TESTS, C_BUILD_TESTS that are set to false in A:s CMakeLists.txt and control the inclusison of test in B and Cs CMakeLists.txt files using add_submodule(unit_tests). This has not been a real problem as we don't want to build and run unit tests for lib B and C when building app A. Doing so would have been mostly a waste of time.
Having watched Daniel Pfeifers ccpnow talk and read other blog posts on using CMake in a declarative way, I've started rewriting our CMake system with modern CMake practices in mind. So now the use of setting a global flag for every included library feels like an anti pattern I would like to avoid. Ideally I feel that tests would be best controlled by setting a property on an included targets. Something like below.
add_subdirectory(B)
set_target_properties(B PROPERTIES BUILD_TESTS false)
target_link_libraries(A PRIVATE B)
Ideally this would still include bs test targets, only make them not depend on b:s main target. But this seems very difficult to do without renaming all of bs targets with a b_ prefix. Technically b_unit_tests is still a different target than b, though from the context of A you would think of it as being part of target B.
Unfortunately I can't get the above to work as is seems like CMake already has a set of pre-defined properties for targets and adding new one doesn't seem fully supported. There is define_property and set_property, but from what I can see you cant use them to define properties on targets.
Suggestion how to rewrite the CMakeLists.txt in a more modern way with clear separation and configuration without resorting to global variables are greatly appreciated.
It seems that for modern CMake, libraries should export their paths and sources and in the export process you can add a namespace. Though I don't really get if this should also apply to libraries that are included as submodules and built together with the application? Is this the route I should go with libs A and
Why don't just rename test targets so that they wouldn't conflict with each other? You can also rule them with single BUILD_TESTS variable and set it to ON or OFF by default depending on how a library is built - as a standalone project, or as part of other one.

Isolating gitsubmodule projects in CMake

I'm trying to manage my C++ project dependencies using CMake and gitsubmodules. I'm following the layout described here: http://foonathan.net/blog/2016/07/07/cmake-dependency-handling.html and it's worked really well for me on smaller projects. But I've started to use it on much larger projects and I'm hitting some issue with CMake.
My current setup
All my external build dependencies are in a contrib/ subfolder inside my main project. Each is a submodule and has its own separate directory.
/contrib
- /eigen
- /curl
- /leapserial
- /zlib
- /opencv
etc.
The contrib/CMakeListst.txt simply initializes the submodule and adds the subdirectory for each external dependency
# EIGEN
execute_process(COMMAND git submodule update --recursive --init -- eigen
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
# options..
add_subdirectory(eigen EXCLUDE_FROM_ALL)
# CURL
execute_process(COMMAND git submodule update --recursive --init -- curl
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
# Initialize cache with CMake build options..
add_subdirectory(curl EXCLUDE_FROM_ALL)
# LEAP SERIAL
execute_process(COMMAND git submodule update --recursive --init -- leapserial
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
# Initialize cache with CMake build options..
add_subdirectory(leapserial EXCLUDE_FROM_ALL)
# ZLIB
execute_process(COMMAND git submodule update --recursive --init -- zlib
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
# Initialize cache with CMake build options..
add_subdirectory(zlib EXCLUDE_FROM_ALL)
# OPENCV
execute_process(COMMAND git submodule update --recursive --init -- opencv
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
# Initialize cache with CMake build options..
add_subdirectory(opencv EXCLUDE_FROM_ALL)
This setup has worked fantastically for me:
It's system/packagemanager independent. You don't need to install any libraries to get started developing
I can maintain the exact versions of my dependencies by setting the submodule to a particular commit. There are no surprises with some external library breaking your build
Adding the libraries to my build in the root CMakeListst.txt is trivial. Since I have the target available I just have something like
add_executable(someProjectImWorkingOn
${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp )
target_link_libraries(someProjectImWorkingOn
opencv_world
eigen
zlib
etc.)
when you hook up an existing library target to your own target executable/library CMake will automatically (through the target interface) add include directories to your target and add any other necessary options the library target requires for it to be used
I can pick a toolchain/compiler-option/build-type in the root CMakeLists.txt and it'll propogate to all the subprojects (I need to build for multiple systems. So this is a big big deal)
Since it's all in one "mega-project" it makes it very easy to hook up to rtags/kdevelop/clion to navigate not on your own code, but also the library code
Some issues that I can't resolved:
1
Subdirectories will define targets with the same name. In the example I gave, both Eigen OpenCV as well as another library define an 'uninstall' target
I tried to update the
add_subdirectory(blah)
to
add_subdirectory(blah EXCLUDE_FROM_ALL)
but this doesn't fix the issue for some reason
Enabling the variable ALLOW_DUPLICATE_CUSTOM_TARGETS kinda works.. but this is a hack, only work with Make files, and the libraries are essentially still "mixing" so it's still an issue
2
The second issue came up in LeapSerial but illustrates a bigger issue. The project no longer knows it's own name. LeapSerial tried to determine the version of LeapSerial, but when it asks for the project version it's getting the root project version. Ie. when cmake code in a subproject asks for "what project am I in" it's getting the root project, and not the immediate project it's in.
So again, the parent "namespace"s are leaking everywhere. This is bound to create more and more issues down the line. I need to the submodules to be self-contained
Is there are a cleaner solution?
ExternalProjectAdd might solve some of these problems, but has a lot more issues of its own. It's a real non-starter b/c it doesn't do most of what I've listed. The central issue is that it doesn't expose the sub-project's targets - and just vomits back variables that you then have to juggle
As the asker said in the comments, they resolved their issue by using the Hunter package manager. The rest of this answer is about actually answering the question as posed.
Concerning your first issue with target name clashes when using add_subdirectory-based approaches to using dependencies, a very similar (or essentially the same?) question has also since been asked here: How to avoid namespace collision when using CMake FetchContent?. When the clashes are between targets from different project dependencies, there's nothing you can do right now except politely ask the project maintainers to consider modifying their non-import/export target names to be namespaced like.
For example, for a library target, that might look like:
add_library(projectname_targetnamepart)
add_library("importexportnamespacename::targetnamepart" ALIAS projectname_targetnamepart)
set_target_properties(projectname_targetnamepart PROPERTIES EXPORT_NAME targetnamepart)
set_target_properties(projectname_targetnamepart PROPERTIES OUTPUT_NAME targetnamepart)
install(TARGETS projectname_targetnamepart EXPORT projectname_targets ...)
install(EXPORT projectname_targets NAMESPACE "importexportnamespacename::" ...)
There's a Kitware issue ticket by Craig Scott proposing a CMake feature for Project-level namespaces. Here's an excerpt:
A common problem when pulling in subprojects using add_subdirectory() is that target names must be unique, but different subprojects might try to use the same target name, which results in a name clash. This issue proposes to introduce the concept of a project namespace to address this and related name uniqueness problems (this is the primary goal of this issue).
Sometimes the upstream maintainer will just decline to support the add_subdirectory / FetchContent use-case. That's the case with OpenCV, as shown in this issue ticket (#16896). As for eigen, there's an open ticket that hasn't had any activity in a while (#1892).
Concerning your second issue, there's not enough detail in your question post to confidently troubleshoot. What is LeapSerial? Are you referring to the leapmotion/leapserial GitHub repo? What version and what commit are you referring to? At the latest commit before the time of your question post, 41515db, it's not immediately obvious what's wrong.
Variables in CMake are scoped by directory, so even if a project is added by add_subdirectory and doesn't used the <PROJECT-NAME>_VERSION variable and instead uses the more general PROJECT_VERSION variable, it should be okay. It just shouldn't attempt to use the CMAKE_PROJECT_VERSION variable to get its own version, since that one is fixed to refer to the top-level project's version.

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.

How to have CMake show headers-that are not part of any binary target-in the IDE?

In our workflow, we can have a module A that is composed of several header files, module A not producing any binary (side note: it will obviously be used by other modules, that include some of the headers from module A to produce binaries).
A good example would be a header-only library, for which CMake 3 introduces a good support thanks to the notion of INTERFACE library (see this SO answer, and CMake's documentation of the feature).
We can make an interface library target out of module A:
add_library(module_A INTERFACE)
That gives us all the nice features of CMakes targets (it is possible to use it as another target's dependency, to export it, to transitively forward requirements etc.)
But in this case, the headers in module A do not show up in our IDE (Xcode, yet we expect it to be the same with most/every other IDE).
This proves to be a major drawback in the workflow, since we need the files composing module A to be shown in the IDE for edition. Is it possible to achieve that ?
Several months down the line, I did not find a way to directly list the header files for an INTERFACE library.
Since the question still has some views, here is what I ended up doing (i.e. what appears like the lesser hack currently available).
Imagine module A is a header only library. In the CMakeLists.txt declaring its target:
# Define 'modA_headers' variable to list all the header files
set(modA_headers
utility.h
moreUtilities.h
...)
add_library(moduleA INTERFACE) # 'moduleA' is an INTERFACE pseudo target
#
# From here, the target 'moduleA' can be customised
#
target_include_directories(moduleA ...) # Transitively forwarded
install(TARGETS moduleA ...)
#
# HACK: have the files showing in the IDE, under the name 'moduleA_ide'
#
add_custom_target(moduleA_ide SOURCES ${modA_headers})
I do not accept this answer, since I expect further releases of CMake to offer a more semantically correct approach, which will then be accepted : )
You can use the new target_sources command in CMake 3.1.
add_library(moduleA INTERFACE)
target_include_directories(moduleA INTERFACE ...)
target_sources(moduleA INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}/utility.h
${CMAKE_CURRENT_SOURCE_DIR}/moreUtilities.h
)
It is also transitive.
http://www.cmake.org/cmake/help/v3.1/command/target_sources.html#command:target_sources
The limitation of not being able to export targets which have INTERFACE_SOURCES has been lifted for CMake 3.3.

CMake building targets conditionally based on library existence

I have a large cross-platform project which needs to build in various places; in some places, different UI toolkits, sound APIs, etc. may be available, and I am trying to figure out the best way to automatically configure which targets get configured based on which libraries are present.
The code I am trying for that is, for example:
find_library(PC_EGL EGL)
find_library(PC_GLESv2 GLESv2)
find_library(PC_Xxf86vm Xxf86vm)
if (DEFINED PC_EGL AND DEFINED PC_GLESv2 AND DEFINED PC_Xxf86vm)
add_executable(foo foo.cpp)
target_link_libraries(foo ${PC_EGL} ${PC_GLESv2} ${PC_Xxf86vm})
endif()
However, in the case that I build this on a system which doesn't have libGLESv2 available, I get the error:
CMake Error: The following variables are used in this project, but they are set to NOTFOUND.
Please set them or make sure they are set and tested correctly in the CMake files:
PC_GLESv2
linked by target "foo" in directory /path/to/platform
The find_library documentation implies that the variable PC_EGL_NOTFOUND should be getting set, but it isn't (CMake 2.8.5). So, what is the appropriate way to use find_library to determine whether a target should be made to exist at all? It seems like using
if (NOT PC_EGL MATCH "-NOTFOUND")
is a bit fragile and fiddly, so is there a better mechanism for determining a CMake command path based on wheter a library was found at all?
It's simply
if(PC_EGL AND PC_GLESv2 AND PC_GLESv2)
CMake treats 0, FALSE, OFF, ANYTHING-NOTFOUND as false.