use set_tests_properties from test in other directory - cmake

(related to add standard libraries to RPATH for build tree executables)
I have the following directory structure as minimal working example
.
├── baz.cc
├── baz.h
├── CMakeLists.txt
└── sub
├── CMakeLists.txt
└── foo.cc
sub is in practice a git submodule and should not be edited for my specific corner case but remain generic.
sub/CMakeLists.txt defines a test that does not run in my setup because a shared library is not added to the RPATH by cmake. To reproduce this behavior, it can be thought be
enable_testing()
add_executable(foo foo.cc)
set_property(TARGET foo PROPERTY SKIP_BUILD_RPATH TRUE)
target_link_libraries(foo ${CMAKE_CURRENT_BINARY_DIR}/../libbaz.so)
include_directories(..)
add_test(NAME footest COMMAND foo)
(foo.cc can be:
#include "baz.h"
int main() {
return bazinga() - 42;
}
let's assume a shared library with the bazinga symbol will always be available, e.g. from /usr/lib)
My top level CMakeLists.txt is
add_subdirectory(sub)
add_library(baz SHARED baz.cc)
I want to get the footest running by manipulating its LD_LIBRARY_PATH as suggested here. This would be by adding
set_tests_properties(footest PROPERTIES ENVIRONMENT "LD_LIBRARY_PATH=/path_where_libbaz.so_is_on_my_target_system")
This line does the job when I add it to sub/CMakeLists.txt, but fails with the following error when I edit the top level CMakeLists.txt
CMake Error at CMakeLists.txt:3 (set_tests_properties):
set_tests_properties Can not find test to add properties to: footest
given the above reason, I want to do the fixup without editing anything in the subdirectory.
Is there a way to set properties of tests that are defined in other directories than the current one?

Related

CMAKE: Circular ... dependency dropped

I have a 3rd-party (prebuilt) executable (named other) located in imported folder of my project's source tree. Basically, the project's structure looks like this:
.
├── CMakeLists.txt
├── imported
│   ├── libother.so
│   └── other
└── main.cpp
In order for my application to operate properly, that other executable must be copied next to my executable, what I achieve with this script:
cmake_minimum_required(VERSION 3.21)
project(myproject)
add_executable(myexe main.cpp)
set(path_in ${CMAKE_SOURCE_DIR}/imported/other)
set(path_out ${CMAKE_BINARY_DIR}/other)
add_executable(other_i IMPORTED)
set_target_properties(other_i PROPERTIES IMPORTED_LOCATION ${path_in})
add_custom_command(OUTPUT ${path_out}
COMMAND ${CMAKE_COMMAND} -E copy ${path_in} ${path_out}
DEPENDS other_i
)
add_custom_target(other DEPENDS ${path_out})
add_dependencies(myexe other)
The problem is whenever the project is being built, on Linux, I am getting weird messages from gmake:
$ cmake -S . -B .build
...
$ cmake --build .build
gmake[2]: Circular CMakeFiles/other <- other dependency dropped.
gmake[2]: Circular other <- other dependency dropped.
While the other is copied all right, it is being copied ALWAYS whenever I re-issue a build command (even without any changes). This is undesirable and what is bothering me most is that it works perfectly with MSVC on Windows and also with shared libraries on Linux. E.g. this works fine:
cmake_minimum_required(VERSION 3.21)
project(myproject)
add_executable(myexe main.cpp)
set(path_in ${CMAKE_SOURCE_DIR}/imported/libother.so)
set(path_out ${CMAKE_BINARY_DIR}/libother.so)
add_library(other_i SHARED IMPORTED)
set_target_properties(other_i PROPERTIES IMPORTED_LOCATION ${path_in})
add_custom_command(OUTPUT ${path_out}
COMMAND ${CMAKE_COMMAND} -E copy ${path_in} ${path_out}
DEPENDS other_i
)
add_custom_target(other DEPENDS ${path_out})
add_dependencies(myexe other)
I am at wits end on what is going on. Is it a CMAKE's bug or I am missing something?
Any insights would be greatly appreciated.
PS: Please do not suggest using POST_BUILD on a main target, as this approach has it's own drawbacks.
The line
add_custom_target(other DEPENDS ${path_out})
creates a dependency of other from the ${CMAKE_BINARY_DIR}/other.
But from the view of Make these are the same things: both refers to the file other under the build directory (where Makefile is located). Because of that you get the message
gmake[2]: Circular other <- other dependency dropped.
The core of the problem is that unlike to CMake, Make doesn't have separation of targets (pure names) and files (located on the disk). From the view of Make all targets (even .PHONY ones) are files.
Using another name for target other (e.g. other_exe) would eliminate the problem.
This appears to be a bug in CMake! I worked around it like this:
cmake_minimum_required(VERSION 3.21)
project(myproject)
set(path_in "${CMAKE_CURRENT_SOURCE_DIR}/imported/other")
set(path_out "${CMAKE_CURRENT_BINARY_DIR}/other")
add_custom_command(
OUTPUT "${path_out}"
COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${path_in}" "${path_out}"
DEPENDS "${path_in}"
)
add_custom_target(other_update DEPENDS "${path_out}")
add_executable(myproject::other IMPORTED)
set_target_properties(myproject::other PROPERTIES IMPORTED_LOCATION "${path_out}")
add_dependencies(myproject::other other_update)
add_executable(myexe main.cpp)
add_dependencies(myexe myproject::other)
There were a couple other issues with specifying your dependencies, so I cleaned those up. Even so, I observe that naming an executable target (even if imported) the same as the output file trips the bug. You should open an issue with CMake upstream.
In general, it is a good idea to utilize the fact that CMake always treats names containing :: as target names.

CMake build target without specifying target_include_directories

I have a project with the following file layout:
Project
├── CMakeLists.txt
├── app
│ ├── CMakeLists.txt
│ └── main.cpp
└── ext
├──CMakeLists.txt
└── lib
├── CMakeLists.txt
├── include
│ └── foo.h
└── foo.cpp
foo is a 3rd party library that I downloaded the source code of that I want to use in main.cpp. I can build main.cpp using a cmake file like:
add_executable(app main.cpp)
target_include_directories(app PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/extern/foo/include)
target_link_libraries(app foo)
However I feel it should be the responsibility of the foo lib to specify what files should be included when using it. Is there a way to make this work without the target_include_directories call?
Just add
target_include_directories(foo PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
into extern/foo/CMakeLists.txt.
That way the include directory will be accessible when you build foo library and for anyone who links with that library.
Note, that automatic propagation of include directory and other library properties works only when you link with the library target:
# Assuming you have 'add_library(foo)' somewhere,
# PUBLIC and INTERFACE properties of the `foo` library will be propagated
# to the 'app'.
target_link_libraries(app PUBLIC foo)
This would work even if add_library(foo) is issued after target_link_libraries call.
Propagation won't work when link with the library file:
# Propagation won't work with a name of the library file:
target_link_libraries(app PUBLIC foo.a)
# Propagation won't work with a full path to the library file:
target_link_libraries(app PUBLIC /path/to/foo.a)
# If 'add_library(foo)' in inaccessible from the project,
# then linking with 'foo' means linking with a library file,
# so propagation won't work.
target_link_directories(app PUBLIC /path/to/)
target_link_libraries(app PUBLIC foo)

CMake: Linking a static library (.a) to small c++ script

I am trying to link a prebuilt static library, leveldb, to a small c++ script however I am getting a file not found error when trying to include the .h file from the lib. I don't know CMake that well so any help is appreciated.
//script.cc
#include <iostream>
#include "leveldb/db.h" // <<<<<<<< " fatal error: leveldb/db.h: No such file or directory"
int main(int argc, char** argv){
std::cout << "hello \n";
return 0;
}
As per other stack overflow posts static linking is done with target_link_libraries().
This is my current cmake file
CMakeLists.txt
cmake_minimum_required(VERSION 3.13.4)
project(my_project)
link_directories(${CMAKE_SOURCE_DIR}/third_party/)
add_executable(runscript script.cc)
target_link_libraries(runscript ${CMAKE_SOURCE_DIR}/third_party/libleveldb.a)
this is essentially what my project directory looks like
.
├── CMakeLists.txt
├── script.cc
└── third_party
└── libleveldb.a
To link with another cmake project, usually the way is to:
Clone the repo somewhere. For example, if your project is meant to be under git control, most people add the third_party repos as git submodules. Note: that is also ExternalProject_Add.
Then add add_subdirectory(the_dir_with_the_repo). This will source the third_party CMakeLists.txt file and include all the targets that are there.
After it, you can just target_link_libraries(runscript PUBLIC leveldb) use the targets from the third_party CMakeLists.txt like your own.
A static library, a file with .a extension, is just a archive of object files, that's all. Object files do not contain the information from header files, function declarations, external variables declarations, macros, etc. In C language headers are meant to be shipped separately, as entirely independent files.
Solution. (Thanks to KamilCuk's comment above)
CMakeLists.txt
cmake_minimum_required(VERSION 3.13.4)
project(my_proj)
add_subdirectory(third_party/leveldb)
add_executable(runscript script.cc)
target_link_libraries(runscript PUBLIC leveldb)
.
├── CMakeLists.txt
├── script.cc
└── third_party
└── [leveldb repo]

Automatically add linking options to dependants of shared lib

Let's say I have a shared lib, A, and something else that links against it, B. They are in two separate projects.
In my current setup, for A's functionality to be able to work properly, B needs to add -rdynamic to its linker options, like this:
target_link_libraries(B -rdynamic)
The thing is that there may be many, many dependants of A, so having to explicitly include the line above for each of them is a hassle.
Is there a way for A to have all its dependants automatically use -rdynamic?
Edit: here's a more detailed explanation of my situation.
I have a shared library, mylib. It cannot be a static library. It is defined in a CMake project of the same name.
mylib
├── CMakeLists.txt
└── src
   └── ...
Its CMakeLists.txt looks like this:
# mylib/CMakeLists.txt
project(mylib)
...
add_library(mylib SHARED ${SOURCES})
...
In separate projects, there are executables, client_i, that use/link against mylib, sometimes indirectly (i.e. link against something that links against mylib).
client_0
├── CMakeLists.txt
└── src
   └── ...
client_1
├── CMakeLists.txt
└── src
   └── ...
client_2
├── CMakeLists.txt
└── src
   └── ...
...
One of those CMakeLists.txt looks like:
# client_i/CMakeLists.txt
project(client_i)
...
add_executable(client_i ${SOME_OTHER_SOURCES})
target_link_libraries(client_i mylib -rdynamic)
...
Notice the -rdynamic. Without this option, some functionality provided by mylib doesn't work. I need it.
My problem is that there may be thousands of different client_is, each defined in its own project by different people/users of mylib, which I provide (and may be used as a binary).
I'd like to avoid having to add -rdynamic to each of the client_is, since some users might not know about it (since it may be used indirectly), some might forget about it, it might lead to headaches, etc.
Ideally, the CMakeLists.txt for mylib would look like:
# mylib/CMakeLists.txt
project(mylib)
...
add_library(mylib SHARED ${SOURCES})
target_link_libraries(mylib -rdynamic)
...
And a client_i CMakeLists.txt would simply be:
# client_i/CMakeLists.txt
project(client_i)
...
add_executable(client_i ${SOME_OTHER_SOURCES})
target_link_libraries(client_i mylib)
...
And it would have -rdynamic in its linker options. But I have tried this and it does not seem to work.
This is a simple solution you can try:
# Get a list of A's direct library dependencies.
get_target_properties(LIB_DEPENDENCIES A LINK_LIBRARIES)
# Loop through each library, adding the link option to each.
foreach(LIB ${LIB_DEPENDENCIES})
target_compile_options(${LIB} PRIVATE "-rdynamic")
endforeach()
This can be expanded, similar to the answer #KamilCuk linked here, to account for static/shared/imported libraries and recursion depending on the variety and complexity of your dependencies.
How to link A against B when A and B are in different projects?
If A is a different project than B, the you have to export package A and use find_package in project B to find A. Here is a good tutorial show-casing the use-case.

Produce .lib of header-only library that depends on external resources

EDIT: I've read up and understood the initial issue was caused by scanning-header-only not having cpp files and thus a lib file not being generated. Edited the question to reflect that extra understanding:
My current project folder structure and relevant CMakeLists content:
leveling
├── CMakeLists.txt: add_subdirectory(deps)
└── deps
├── CMakeLists.txt: add_subdirectory(scanning-header-only)
└── scanning
├── CMakeLists.txt: add_subdirectory(deps)
│ add_library(scanning-header-only file.h)
│ target_include_directories(scanning-header-only PUBLIC ${CMAKE_CURRENT_LIST_DIR}/deps/tinyxml2)
│ target_link_libraries(scanning-header-only PUBLIC tinyxml2)
└── deps
├── CMakeLists.txt: add_subdirectory(tinyxml2)
└── tinyxml2
But a scanning-header-only library file is not being generated, and thus the root project can't target_link_libraries(leveling scanning-header-only) and has had to target_include_directories(leveling ${CMAKE_CURRENT_LIST_DIR}/deps/scanning-header-only/deps/tinyxml2)
Is it possible to target_link_library a header-only library that depends on external resources?
I see that a header-only library without external resource dependency could be add_library(.. INTERFACE), but I'm failing to do so with the dependency on tinyxml2
A dirty workaround is adding and empty cpp file to scanning-header-only so a lib file is generated, but is there a correct way to do this?
Here is minimal example v1: https://www.dropbox.com/s/r1lbajz3xoat1bg/leveling-header-only-test%20v1.zip?dl=0
leveling CMakeLists.txt:
cmake_minimum_required(VERSION 3.8)
set(LEVELING_NAME leveling)
project(${LEVELING_NAME})
#
# To put tinyxml.dll next to the executable, to workaround having to make tinyxml2.dll reachable in PATH
#
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
math(EXPR platform_bits "${CMAKE_SIZEOF_VOID_P} * 8")
set(platform_dir bin/${CMAKE_SYSTEM_NAME}-${platform_bits})
foreach(config DEBUG RELEASE RELWITHDEBINFO MINSIZEREL)
foreach(var
CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${config}
CMAKE_LIBRARY_OUTPUT_DIRECTORY_${config}
CMAKE_RUNTIME_OUTPUT_DIRECTORY_${config}
)
set(${var} "${CMAKE_BINARY_DIR}/${platform_dir}/${config}")
string(TOLOWER "${${var}}" ${var})
endforeach()
endforeach()
#
# ----------------------------------------------------------------------
#
add_subdirectory(deps)
add_executable(${LEVELING_NAME} main.cpp)
target_include_directories(${LEVELING_NAME} PUBLIC
${CMAKE_CURRENT_LIST_DIR}/deps/scanning
)
target_link_libraries(${LEVELING_NAME}
xml-reading
)
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ${LEVELING_NAME}) # Set Startup Project in VS. Implemented in CMake v3.6
set_target_properties(${LEVELING_NAME} PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}") # Set Working Directory of project in VS. Implemented in CMake v3.8
scanning CMakeLists.txt
cmake_minimum_required(VERSION 3.8)
set(XML_NAME xml-reading)
project(${XML_NAME})
#
# To put tinyxml.dll next to the executable, to workaround having to make tinyxml2.dll reachable in PATH
#
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
math(EXPR platform_bits "${CMAKE_SIZEOF_VOID_P} * 8")
set(platform_dir bin/${CMAKE_SYSTEM_NAME}-${platform_bits})
foreach(config DEBUG RELEASE RELWITHDEBINFO MINSIZEREL)
foreach(var
CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${config}
CMAKE_LIBRARY_OUTPUT_DIRECTORY_${config}
CMAKE_RUNTIME_OUTPUT_DIRECTORY_${config}
)
set(${var} "${CMAKE_BINARY_DIR}/${platform_dir}/${config}")
string(TOLOWER "${${var}}" ${var})
endforeach()
endforeach()
#
# ----------------------------------------------------------------------
#
add_subdirectory(deps)
add_library(${XML_NAME} INTERFACE CamerasXML.h)
target_include_directories(${XML_NAME} INTERFACE
${CMAKE_CURRENT_LIST_DIR}/deps/tinyxml2
)
target_link_libraries(${XML_NAME}
INTERFACE tinyxml2
)
which yields
CMake Error at deps/scanning/CMakeLists.txt:33 (add_library):
add_library INTERFACE library requires no source arguments.
A .lib is when you create a STATIC (.lib) or SHARED (.lib and .dll) library on Windows. What you want is an INTERFACE library and it generates no files. http://mariobadr.com/creating-a-header-only-library-with-cmake.html has an example. Then you can use the following commands listed here, https://cmake.org/cmake/help/latest/command/add_library.html#interface-libraries, to populate the interface. Notice that it uses INTERFACE not PUBLIC.
target_link_libraries(INTERFACE),
target_link_options(INTERFACE),
target_include_directories(INTERFACE),
target_compile_options(INTERFACE),
target_compile_definitions(INTERFACE), and
target_sources(INTERFACE),
I've never actually used this but I assume it works as documented.
A simple add_library(${XML_NAME} INTERFACE) (not specifying any source files), while having target_include_directories(${XML_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR}/deps/tinyxml2) and target_link_libraries(${XML_NAME} INTERFACE tinyxml2) will do the trick.
The tinyxml2 includes are made available to the parent project, and the tinyxml2 library is linked in the parent project.