I am trying to build a correct CMake structure for a simple project with several nested submodules. Similarly to this post, I am facing a situation where the main executable and one of the submodules both depend on another submodule:
executable_A/
CMakeListst.txt
library_B/
CMakeLists.txt
library_C/
CMakeLists.txt
library_C/
CMakeLists.txt
Multiple builts of the same target would then result in a cmake error:
add_library cannot create target "library_C" because another target with the
same name already exists. The existing target is an interface library
created in source directory ".....".
See documentation for policy CMP0002 for more details.
The issue had been closed with the following solution, that consists in cheking if the concerned target had already been built before building it again:
# When include 'C' subproject
if(NOT TARGET library_C)
add_subdirectory(C)
endif()
I agree with one of the commenters of the original posts in thinking that it is not a satisfactory solution in every case: in the (unlikely) case of executable_A and library_B depending on different versions of library_C, a mismatch would occur. Is there a way, using submodules, of avoiding this scenario ? Is it possible, for example, to "rename" the library_C target built from library_B to library_C_B, so that no naming conflicts occur ?
Related
I would like a simple explanation of what a target is in CMake. The current terminology is something I am not all too familiar with and I am seeking a simple explanation of the word "target" used primarily with add_executable and add_library.
Target is a name for something to do. Execute or build or create, depending on the target. Typically, target names represent an executable file or library file.
The term "target" comes from make command. make takes targets names as arguments and builds them using rules that specify what to do to create a specific target/file. In make a "target" is just a path to a file to produce, for example an actual executable name. https://www.gnu.org/software/make/manual/make.html#Rule-Introduction
The build system generated by CMake "updates a target" (executes the commands needed to build the executable file or library file) when the prerequisite files have been modified since the target was last modified or the target does not exist ("the target" = the file that is the result of building the target).
A target represents either an executable (functions and data in a binary that has a main-function/entrypoint (a program)), or a library (functions and data in a binary with no main-function/entrypoint) in a buildsystem. For more information, see the docs on binary targets, and the docs for add_executable and for add_library.
Targets can be part of a project (in which case they have source files and need to be built as part of the buildsystem), or IMPORTED, in which case they already exist in binary form and have no sources and do not need to be built (they were already built outside the buildsystem to be generated for a project).
Targets can have dependencies between them. Ex. An executable target depending on a library target to use its functions and data. For more info, see the docs for target_dependencies, which can have implications for linking binaries together, and for build order: making sure dependent targets are re-built when depended-upon targets need to be rebuilt.
There are some target types which are not IMPORTED which also have nothing to build (Ex. interface libraries, and other types you can read about in the add_library docs, but those are more "advanced" features).
Suppose I'm writing an app, and managing its build with CMake; and I also want to use a library, mylib, via the FetchContent mechanism.
Now, my own CMakeLists.txt defines a bunch of targets, and so does mylib's CMakeLists.txt. If I were to install mylib, then find_package(mylib), I would only get its exported targets, and even those would be prefixed with mylib:: (customarily, at least). But with FetchContent, both my app's and mylib's (internal and export-intended) targets are in the "global namespace", and may clash.
So, what can I do to separate those targets - other than meticulously name all of my own app's targets defensively?
I would really like it if it were possible to somehow "shove" all the mylib targets into a namespace of my choice.
Note: Relates to: How to avoid namespace collision when using CMake FetchContent?
In the current CMake (<=3.24) world, there are no features for adjusting the names of the targets in other black-box projects, whether included via find_package, add_subdirectory, or FetchContent. Thus, for now, it is incumbent on you to avoid name-clashes in targets, install components, test names, and anywhere else this could be a problem.
Craig Scott says as much in his (very good) talk at CppCon 2019, see here: https://youtu.be/m0DwB4OvDXk?t=2186
The convention he proposes is to use names that are prefixed with SomeProj_. He doesn't suggest to literally use ${PROJECT_NAME}_, and I wouldn't either, because doing so makes the code harder to read and grep (which is extremely useful for understanding a 3rd-party build).
To be a good add_subdirectory or FetchContent citizen, however, it is not enough to simply namespace your targets as SomeProj_Target; you must also provide an ALIAS target SomeProj::Target. There are a few reasons for this:
Your imported targets from find_package will almost certainly be named SomeProj::Target. It should be possible for consumers of your library to switch between FetchContent and find_package easily, without changing other parts of their code. The ALIAS target lets you expose the same interface in both cases. This will become especially pressing when CMake 3.24 lands with its new find_package-to-FetchContent redirection features.
CMake's target_link_libraries function treats names that contain :: as target names always and will throw configure-time error if the target does not exist. Without the ::, it will be treated as a target preferentially, but will turn into a linker flag if the target doesn't exist. Thus, it is preferable to link to targets with :: in their names.
Yet, only IMPORTED and ALIAS targets may have :: in their names.
Points (2) and (3) are good enough for me to define aliases.
Unfortunately, many (most?) CMake builds are not good FetchContent citizens and will flaunt this convention. Following this convention yourself reduces the chance of integration issues between your project and any other, but obviously does nothing to prevent issues between two third party projects that might define conflicting targets. In these cases, you're just out of luck.
An example of defining a library called Target that will play nice with FetchContent:
add_library(SomeProj_Target ${sources})
add_library(SomeProj::Target ALIAS SomeProj_Target)
set_target_properties(
SomeProj_Target
PROPERTIES
EXPORT_NAME Target
OUTPUT_NAME Target # optional: makes the file libTarget.so on disk
)
install(TARGETS SomeProj_Target EXPORT SomeProj_Targets)
install(EXPORT SomeProj_Targets NAMESPACE SomeProj::)
For a more complete example that plays nice with install components, include paths, and dual shared/static import, see my blog post.
See these upstream issues to track the progress/discussion of these problems.
#22687 Project-level namespaces
#16414 Namespace support for target names in nested projects
As #AlexReinking , and, in fact, Craig Scott, suggest - there's no decent current solution.
You can follow the following CMake issues through which the solution will likely be achieved:
#22687 Project-level namespaces (more current)
#16414 Namespace support for target names in nested projects (longer discussion which influenced the above, recommended reading)
I have a question regarding CMake and I need help to solve the following error I'm getting:
CMake Error at :::: (add_custom_target):
add_custom_target cannot create target "generate" because another target
with the same name already exists. The existing target is a custom target
created in source directory :::::.
Here the target names of the two same level CMakeLists.txt are the same and I want to keep them identical, without any conflict. Can anyone help me out?
According with CMake policy CMP0002 (introduced by CMake 2.6, emphasis mine):
Targets names created with add_executable, add_library, or add_custom_target are logical build target names. Logical target names must be globally unique [...]
The following note deserves a mention and could probably help you anyway:
Custom targets must simply have globally unique names (unless one uses the global property ALLOW_DUPLICATE_CUSTOM_TARGETS with a Makefiles generator).
It means that there exists a global property named ALLOW_DUPLICATE_CUSTOM_TARGETS that is probably what you are looking for. It has a limited use and you should read carefully the documentation, but it's worth a try.
The most relevant part follows:
Makefile generators are capable of supporting duplicate custom target names. [...] However, setting this property will cause non-Makefile generators to produce an error and refuse to generate the project.
To be able to use duplicate custom targets put the following line in your CMakeLists.txt:
set(ALLOW_DUPLICATE_CUSTOM_TARGETS TRUE)
If it solves your issue mainly depends on the actual problem, so I cannot say.
This could be a good help:
OUTPUT_NAME sets the real name of a target when it is built and can be used to help create two targets of the same name even though CMake requires unique logical target names.
https://cmake.org/cmake/help/v3.0/command/set_target_properties.html
In our project, we want to use a third-party library (A) which is built using autotools and which generates an object file (B) which we need # link time of one of our libraries (C).
external_project(
A
...
)
set_source_files_properties(B PROPERTIES DEPEND A)
add_library(C ... A)
add_dependency(C B)
I had the impression that this should do the trick, but the cmake command fails by stating that it cannot find file A during the check for add_library.
Any fixes or alternative solutions would be greatly appreciated! (changing the third-party library is not an option)
thanks!
There are a few issues here:
external_project should be ExternalProject_Add
Source files have no property called DEPEND available - the set_source_files_properties command has no effect here. (Here are the properties available on source files)
add_library expects a list of source files to be passed, not another CMake target (which A is)
add_dependency should be add_dependencies
Apart from those 4 lines it's all OK :-)
So the issue is going to be that you want to include the object file B in the add_library call, but it's not going to be available at configure-time (when CMake is invoked), only at build time.
I think you're going to have to do something like:
ExternalProject_Add(
A
...
)
set_source_files_properties(
${B} PROPERTIES
EXTERNAL_OBJECT TRUE # Identifies this as an object file
GENERATED TRUE # Avoids need for file to exist at configure-time
)
add_library(C ... ${B})
I am wanting to create CMakeLists.txt files that are more specifically named such as "CMakeLists_nightly.txt", "CMakeLists_weekly.txt" and so forth. The reason I want to do this is to cut down on the folder hierarchy clutter of my project. I could easily put each of these files in their own folder with the postfix I showed above but I do not want to do this.
Can I tell cmake to take a CMakeLists.txt file by another name? I have seen this question asked before on another forum (http://www.cmake.org/pipermail/cmake/2007-August/016036.html) but it was back in 2007 and the answer was no. Does the current version of CMake provide this capability?
Not really, but you can emulate this by putting CMakeLists.txt in separate directories, e.g. continous/CMakeLists.txt and nightly/CMakeLists.txt. Use INCLUDE to include the appropriate scripts for each of the build configs.
Consider if this really is the right approach - completely separating the nightly and continuous script is a really bad idea as that will lead to duplication and a very bug prone build setup.
Answer, which came into my mind, while I was reading an answer from larsmoa and thinking about it little bit longer:
(this is not exactly the answer to the question about different name for CMakeLists.txt, but rather, to "how to have two different CMake configuraiton files in the same directory")
You can avoid creating multiple directories and storing there CMakeLists.txt (it may also be problematic, if you want your script to be the parent of everything). My Idea is, that you can have two "include" cmake files with any names you like. And then in CMakeLists.txt you may have an set(CACHE), which controlls, which include-script should be actually included.
With this setup you can have two build directories: one configured with one value of the option, and another - with another. Depending on that, in which build-directory you do the build, corresponding build definition will be used.
It can look something like this:
CMakeLists.txt:
set(
MY_BUILD_KIND BUILD_A CACHE STRING
"Select build kind: BUILD_A or BUILD_B"
)
if ( MY_BUILD_KIND strequal "BUILD_A" )
include(build_a.cmake)
elseif (MY_BUILD_KIND strequal "BUILD_B")
include(build_b.cmake)
else ()
message ( FATAL_ERROR "Unknown build kind: ${MY_BUILD_KIND}" )
endif ()
Background (why do I need it?): My situation is kind of exotic, I guess. I have a C++ project, different parts of which use two different compilers. And there is a part of it, which needs to be built by each of them. So the directory structure is like this:
Projects
CompilerAProjects
CompilerBProjects
CommonProjects
Here "CommonProjects" are included as Part of "CompilerAProjects" and also as part of "CompilerBProjects". Now we try to integrate cmake and I was thinking, how can we keep the structure, but do the build with CMake. If I put CMakeLists.txt in the root directory, then I don't understand, how to differentiate between two compilers. And if I don't have the root project, then it is not clear, how to refer to "sibling" project. So I came to the idea, that I can included sub-directories basing on the current compiler. And then I decided, that actually it is not necessary, that compiler is the driving factor, we can use set(CACHE) instead. And we are not restricted to select, which sub-directory we select, but can also include ".cmake" files.