CMake: circular dependency on different targets - cmake

My project has a CMakeLists in a libs/ directory like this:
add_subdirectory(foo)
add_subdirectory(bar)
With bar depending on foo.
I recently added new functionalities to foo, and appropriate tests. However those tests need functionalities from bar. This means I currently need to run CMake twice to be able to compile.
Is there a way to get around this issue or am I stuck with re-running cmake multiple times?
Is there a way to declare my dependencies in a specific way maybe? ie right now I do this:
add_library(${BAR} ${SRC_FILES} ${PUBLIC_HEADERS} ${PRIVATE_HEADERS})
which means that linking against ${BAR} in a test (located in the foo module) will expand the variable to an empty string. Can I tell CMake that this should be a target and not just a target name?

Related

Predeclare search location for anticipated find_library()-call

I want to include an external library as a subproject into my own project and link its target(s) statically against my own lib.
The said project somewhere in its CMake calls the following find-functions:
find_library(MBEDTLS_LIBRARY mbedtls)
find_library(MBEDX509_LIBRARY mbedx509)
find_library(MBEDCRYPTO_LIBRARY mbedcrypto)
The subproject expects mbedtls to already be installed somewhere on the system, but it didn't consider the fact that I want to link statically. My approach is to now FetchContent mbedtls and provide the find_library() calls with the prebuilt static libraries.
Now I need a way provide those find_library-calls with the right search directory, of course without modifying its source code. Can I somehow set a prefix path? I know I could probably set CMAKE_PREFIX_PATH but that seems like an ugly hack and it would probably affect other find_library()-calls within the project which also exist. Is there a more "constrained" way?
Can I somehow set a prefix path?
Setting a prefix path won't help find_library to locate the library, because command find_library searches the file at configuration stage, but the library is built only on build stage.
Instead, you may write the target name to the CACHE variable, which is passed to find_library as the first argument:
When find the result variable to be already set, find_library won't search the library file.
In most cases a project uses result of find_library in the call to target_link_libraries, so having the library target in the result variable will fit to the project's expectations.
Example:
FetchContent_Declare(mbedtls ...)
FetchContent_MakeAvailable(mbedtls)
set(MBEDTLS_LIBRARY MbedTLS::mbedtls CACHE INTERNAL "mbedtls library target")
With such setup the following
find_library(MBEDTLS_LIBRARY mbedtls)
will do nothing, since the variable MBEDTLS_LIBRARY is already set.
And if the project will use this variable like
target_link_libraries(<executable> ${MBEDTLS_LIBRARY})
then it effectively gets
target_link_libraries(<executable> MbedTLS::mbedtls)
Name of the target which should be assigned to the variable could sometime be found from the project's documentation, but otherwise you need to look into the project's sources (CMakeLists.txt).
E.g. in case of mbedtls project, the library target mbedtls is created with add_library() call, and MbedTLS::mbedtls is created as ALIAS for it.

CMake: How to manage multiple dependency to the same submodule?

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 ?

Is CMake "set(XXXX CACHE ...)" a bug when used in a library?

I'm using CMake in my project, as does one of my project's third-party library (included with add_subdirectory() for convenience). I have been having strange build issues, and I think I've tracked them down to the following line in the third-party library's top-level CMakeLists.txt:
set(CMAKE_DEBUG_POSTFIX "d" CACHE STRING "")
This sets CMAKE_DEBUG_POSTFIX for my entire project (except for any subtrees where it's explicitly set), which breaks my build. Worse, the build behavior is order- and time-dependent, with the value being changed in the middle of my build after a clean build, and not after a rebuild. (Lots of fun to track down.)
The library sets lots of cache variables whose name begins with "XXXX_", where "XXXX" is the name of the library. That's fine by me, as it's unlikely these variables will be used by others' code. But, it seems antisocial to set commonly-used variables globally when your code is meant to be a component of someone else's project. It also is fragile; if I use set(XXXX <aValue>) in my top-level CMakeLists.txt then the library's set(XXXX CACHE...) statements will be ignored.
Instead, the library should just use set(CMAKE_DEBUG_POSTFIX "d"), which sets the variable for all of the library's code tree, and nobody else's.
Is this a bug in the library's build code? Should libraries that aim to be good CMake citizens avoid CACHE variables except for their clearly-named private variables?
For libraries that intend to be usable as a subproject (via add_subdirectory or FetchContent), I would say it is a bug to set such cache variables without a check that the project is top-level. On the other hand, a project that does not intend to be usable this way should explicitly check and issue a fatal error (or maybe an author warning) in this case. So either way, I would argue there's a bug and you should notify the maintainers.
In CMake 3.21+ the variable PROJECT_IS_TOP_LEVEL works. In earlier versions, you can write:
string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}" PROJECT_IS_TOP_LEVEL)
to get the same variable. Then check:
if (PROJECT_IS_TOP_LEVEL)
# Either this: (AUTHOR_WARNING acceptable, too)
message(FATAL_ERROR "Subproject inclusion not supported")
# or:
set(CMAKE_* ... CACHE ...)
endif ()

CMake: Remove header dependency

Is there any way to make CMake "forget" about a file in the dependency tree? My original problem (to avoid the XY situation) is the following: I want to timestamp the build of a set of tools which have complicated dependencies among them and to other tools. Right now, I want to use a pure timestamp, but later I might want add some info from the repository (SVN). Whatever system I end up implementing needs to have the following characteristics (my "X"):
No unnecessary rebuilding: the executables should not be rebuilt on every make if the only change would be the timestamp.
Update on any change: if any tool is going to be rebuilt or relinked, either by changes to its code or to one of its dependencies, the timestamp needs to be updated.
My current solution goes along the lines of creating a custom command+target that invokes CMake at make time (so the command calls CMake itself with -P script.cmake) to generate a timestamp.h file. The main files of my tools would include that file, and the projects would depend on the target so that it gets rebuilt first.
However, this has its drawbacks: if I do update the timestamp file on every call to make, then CMake's dependency scanner would know about that file even if I do not list it as an explicit dependency of my tools. Thus, every make would trigger at least a recompilation of the respective "main" files and the corresponding relink. With tens of tools, this means slowing down the build when I may be working on just two or three of them at once.
So, I was thinking that my solution would be to somehow make CMake forget about that file when building its dependency tree for the "main" file of each tool. I would keep the dependency on the custom target that does depend on the file, so that it would be regenerated first on each call to make. However, the build tool would not consider that file as relevant to determine whether it is necessary to actually rebuild each individual tool. Thus, tools only with other changes would be rebuilt (satisfying my first criterion), and any change that causes a rebuild of a tool would obviously use the version just generated (fulfilling the second criterion).
To my chagrin, I have not found a way to make the dependency scanner forget about this file, so my solution cannot be put to use. How would I go about doing such a thing? Is it even possible, or is it completely the wrong way to go about this? I am using CMake 3.4, and my code is currently C++, but I would like a solution that did not rely on C/C++ specifics, since I have a different project (written in Fortran) in which I would also like to have build timestamping.
I've had almost the same problem than you are. Simply solved by pushing the timestamp header file into standalone target containing only this header generator command. After that you have several choices:
1.. Exclude that project from the build by the IDE you are using. For example, for the Visual Studio you can do it by several ways:
1.1. Project->Project Dependencies...->uncheck project with that header (not always works: Error while removing project dependency in VS2010)
1.2. Build->Configuration Manager...->uncheck project with that header
2.. Create an environment variable and use the condition with that variable around the add_dependencies command in the CMakeLists.txt file.
3.. Generate 2 standalone solutions through the cmake generator with included and with excluded add_dependencies in the CMakeLists.txt file.
I've used particulary [1.2]. When i need build and debug, then i uncheck the dependecy. By default, dependecy always checked, so there is no problem to miss timestamp build for a build server.
Note:
The timestamp header will be included in all projects you want to include that header (for example, through the add_library and add_executable) and you still can observe it in the IDE under a project item menu even if a project depends on the timestamp project indirectly. This is useful if you don't want to search for the timestamp project with the header to open it from there and want to open it from any project which has included that header.
So, in case of removing the timestamp header from the add_library or add_executable you won't have that opportunity.

Defining Variable in Custom Target for CMake

I have a CMake file with two targets (say, target1 and target2) defined via "add_custom_target" at the top level of my source directory. I have some external projects in lower level directory. In the extneral project, a "TEST_COMMAND ${Test_Command_Variable}" is defined as part of an ExternalProject_Add().
I would like to change that vaiarble ${Test_Command_Variable} depending on whether I am using custom target1 or target2. Currently, ${Test_Command_Variable} is defined at the top level CMakeLists.txt before either custom targets are defined. I simply want to vary that variable depending on whether target1 or target2 is called. Is there any way to redefine that variable? Maybe do a conditional if statement dependent on whether target1 or target 2 is chosen (this seems like a trivial thing, but I can't find the way to access the "name" of the custom target!).
To clarify: I have two collections of tests. I want to type "make target1" and it runs my first collection of tests. I want to type "make target2" and it tests my second collection of tests. The problem is I also have an external project, where some of the tests are there. The external projects have a TEST_COMMAND(test_command_variable) that does not differentiate between target1 tests and target2 tests. I would like to be able to change that variable depending on whether I run "make target1" or "make target2".
Well, I got it working finally. I did something completely different to meet my objective. I abandoned the goal of trying to toggle variables depending on the target. To be clear, here was my objective:
"make target1" runs only tests in Group 1.
"make target2" runs only tests in Group 2.
I configured each of the tests as per as per this question I asked earlier. My problem was that I had external projects I was trying to pass that variable down so it would toggle Group1 or Group 2 tests depending on the configuration defined at the top level. I knew passing ctest -C group1_configuration flag (or group2_configuration flag for group2 tests)would work, but I couldn't seem to pass down to my external projects. Fortunately, my solution was "simple".
I wrote a shell script that would execute the correct ctest ... -C group1_configuration (or ctest ... -C group2_configuration) and called it test_script1.sh and test_script2.sh. These scripts would move through the build directory and call the aforementioned ctest command in each of the external projects build directories. Then, in my custom target's I defined in the CMakeLists.txt I have it call the shell script and each custom target executes the right shell script, which move through the build directories calling the correct ctest command with the correct configuration flag in each project and external project.