GBenchmark and CMake - cmake

From relevant search on the web, I got an impression that
google-benchmark is not easily incorporated into a CMake-project.
One way I could do that is adding as external project,
replicating verbatim the corresponding text for GTest in google-test's readme:
# adding google-benchmark as external git project
#=======================================================
configure_file(extern/googlebenchmark/CMakeLists.txt.in googlebenchmark-download/CMakeLists.txt)
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
RESULT_VARIABLE result
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googlebenchmark-download)
if(result)
message(FATAL_ERROR "CMake step for googlebenchmark failed: ${result}")
endif()
execute_process(COMMAND ${CMAKE_COMMAND} --build .
RESULT_VARIABLE result
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googlebenchmark-download )
if(result)
message(FATAL_ERROR "Build step for googlebenchmark failed: ${result}")
endif()
# Prevent overriding the parent project's compiler/linker
# settings on Windows
set(gbenchmark_force_shared_crt ON CACHE BOOL "" FORCE)
add_subdirectory(${CMAKE_CURRENT_BINARY_DIR}/googlebenchmark-src
${CMAKE_CURRENT_BINARY_DIR}/googlebenchmark-build
EXCLUDE_FROM_ALL)
if (CMAKE_VERSION VERSION_LESS 2.8.11)
include_directories("${gbenchmark_SOURCE_DIR}/include")
endif()
and having CMakeLists.txt.in with the contents as in
How to build and link google benchmark using cmake in windows
This however has a huge downside: every time we change something in the CMakeLists.txt -- the topmost one -- that hosts all this, it starts building google-benchmark from scratch,
and running all the "tests", whatever they are. Thus compilation times become longer.
Without a root access to a Linux server, is there any more-less portable way
of having the benchmark code installed in one's home directory, while being
able to link towards it in a CMake-project?
EDIT: I must say I've been able to git clone the benchmark code and successfully built it in my home directory.
EDIT: Answering my own question. I am not sure if this warrants a close, and leave it to the patrons to decide, but I've solved the problem as follows. In the CMakeLists.txt one can have the following contents:
cmake_minimum_required(VERSION 3.15)
set(CMAKE_CXX_STANDARD 17)
add_executable(sample_bench sample_bench.cpp)
target_link_libraries(sample_bench PUBLIC benchmark benchmark_main pthread)
target_link_directories(sample_bench PUBLIC ~/local/benchmark/build/src)
target_include_directories(sample_bench PUBLIC
~/local/benchmark/include)
The key here is target_link_directories, which is specified with -L in the example in https://github.com/google/benchmark

Answering my own question. I've solved the problem as follows. In the CMakeLists.txt one can have the following contents:
cmake_minimum_required(VERSION 3.15)
set(CMAKE_CXX_STANDARD 17)
add_executable(sample_bench sample_bench.cpp)
target_link_libraries(sample_bench PUBLIC benchmark benchmark_main pthread)
target_link_directories(sample_bench PUBLIC ~/local/benchmark/build/src)
target_include_directories(sample_bench PUBLIC
~/local/benchmark/include)
The key here is target_link_directories, which is specified with -L in the example in https://github.com/google/benchmark
This is enough to run the sample benchmark, at least -- haven't tried others.
That is, once you have built benchmark somewhere -- even in your home directory, as in the example -- you can point CMake to the designated locations,
in order for your code to compile.

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 rule that depends on the output of a command

How do I generate source files during the pre-build step if and only if they need to be regenerated?
One of my project's dependencies is a library (libfoo) which expensive to relink (several minutes) and even more expensive to rebuild (less than an hour). Generating the source files for this dependency is inexpensive (several seconds), but using out-of-date sources would render the resultant application suite useless. I have a command check_foo.sh that will exit with a non-zero status when the sources must be regenerated, but I haven't been able to determine how to convince CMake to run check_foo.sh during every build and only rebuild libfoo when check_foo.sh returns nonzero.
In trying to create a simple proof, the closest I have gotten is as follows, although only ever runs generate_foo_if.sh once. The ultimate goal is that generate_foo_if.sh gets run unconditionally, but libfoo is only rebuilt when generate_foo_if.sh modifies foo.cpp.
CMakeLists.txt
cmake_minimum_required(VERSION 3.8 FATAL_ERROR)
project(my_project
VERSION 1.0.0.0
LANGUAGES CXX)
add_custom_command(OUTPUT foo.cpp
COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/generate_foo_if.sh" "${CMAKE_CURRENT_BINARY_DIR}/foo.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/check_foo.sh"
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
COMMENT "Generating foo.cpp..."
)
add_library(foo STATIC foo.cpp)
add_executable(main main.cpp)
target_link_libraries(main foo)
main.cpp
#include "foo.hpp"
int main(int,char**){
return foo::exit_status;
}
foo.hpp
#pragma once
namespace foo {
extern const int exit_status;
}
check_foo.sh
#!/usr/bin/env bash
exit $(((${RANDOM} % 2 )))
generate_foo_if.sh
#!/usr/bin/bash
CHECK=${2:-./check_foo.sh}
if [ ${CHECK} -eq 0 ]; then
exit 0
fi
msg=$(cat <<__EOF
#include "foo.hpp"
namespace foo {
const int exit_status = 1;
}
__EOF
)
echo "${msg}" >${1:-foo.cpp}
Turns out, I wasn't very far off the mark.
The critical difference seems to be in creating a custom target rather than a custom command, using BYPRODUCTS rather than OUTPUT, and explicitly adding the dependency. Updated CMakeLists.txt is below, and results in the desired behavior (i.e. libfoo is only regenerated if it should be).
cmake_minimum_required(VERSION 3.8 FATAL_ERROR)
project(my_project
VERSION 1.0.0.0
LANGUAGES CXX)
add_custom_target(generate_foo
COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/generate_foo_if.sh" "${CMAKE_CURRENT_BINARY_DIR}/foo.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/check_foo.sh"
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
BYPRODUCTS foo.cpp
)
add_library(foo STATIC foo.cpp)
add_dependencies(foo generate_foo)
include_directories("${CMAKE_CURRENT_SOURCE_DIR}")
add_executable(main main.cpp)
target_link_libraries(main foo)
While I can't really say I know why it works, this does what I want.

Getting a list of tests in CMake

My CMake project is structured with a submodule that is controlled by a third party. They have a test that is broken, and I'd like to disable it.
I thought this would be something like
add_subdirectory(thirdparty)
set_tests_properties(their_broken_test PROPERTIES DISABLED true)
But not so simple, CMake tells me this test doesn't exist.
So I tried to step back and get a list of all of the tests that CMake does think exists. But this is even more baffling:
project(cmaketest)
cmake_minimum_required(VERSION 3.5.0 FATAL_ERROR)
enable_testing()
add_test(ls /bin/ls)
get_property(LIST_OF_TESTS DIRECTORY . PROPERTY TESTS)
message(STATUS "Here are the tests: ${LIST_OF_TESTS}")
On CMake 3.10.2, this shows no tests. But on CMake 3.12.1, it shows the ls test.
Is this a bug? Or is there a way to do it with the older CMake? (3.10.2 is what is in the Ubuntu Bionic repos.)
The TESTS property was added in CMake 3.12 (release notes):
A TESTS directory property was added to hold the list of tests defined by the add_test() command.
You can do something like this to collect a list of test names as they're added.
if(${CMAKE_VERSION} VERSION_LESS "3.12.0")
function(add_test)
_add_test(${ARGN})
# There are two signatures to the add_test function to handle
if(ARGV0 STREQUAL "NAME")
set(TEST_NAME "${ARGV1}")
else()
set(TEST_NAME "${ARGV0}")
endif()
list(APPEND ALL_TESTS "${TEST_NAME}")
endfunction()
endif()
However, it does not appear you can set properties on a test defined in another directory.

Can not generate a CMake command

My CMakeLists.txt:
cmake_minimum_required(VERSION 2.6)
project(main)
SET(MAIN main)
SET(MAIN_OUT "${CMAKE_CURRENT_BINARY_DIR}/out.txt")
add_executable(${MAIN} main.cpp)
# command is unknown
add_custom_command(OUTPUT ${MAIN_OUT}
POST_BUILD
COMMAND ./${MAIN} > ${MAIN_OUT}
DEPENDS ${MAIN}
)
After compiling, I just want to be able to type
make out.txt
However, cmake seems to be unaware of this target ("no rule"). In the build directory, a call of
grep out.txt -r *
finds no files containing out.txt. How can I make my target callable? I know this has probably asked before, but I have not found it.
If you want to be able to type "make out.txt", you probably want add_custom_target instead of add_custom_command. This creates a target which can be built, and in building executes the specified commands.
Rather than call this target "out.txt" which would misleadingly make it look like a text file instead of a target, I'd recommend something more like "RunMain" or "GetOutputOfMain".
If you can specify a recent version of CMake as the minimum, you can use "generator expressions" within the command part of your add_custom_target call. This isn't documented for add_custom_target, but you can read about generator expressions in the docs for add_custom_command. I'm not sure what the minimum required version of CMake should be set to in order to have generator expressions available.
So, your CMakeLists.txt could be changed to something like:
cmake_minimum_required(VERSION 2.8.10)
project(Test)
add_executable(MyExe main.cpp)
set(MainOut "${CMAKE_CURRENT_BINARY_DIR}/out.txt")
add_custom_target(RunMain $<TARGET_FILE:MyExe> > ${MainOut}
COMMENT "Running MyExe with output redirected to ${MainOut}")
# Ensure MyExe is built before trying to build the custom target
add_dependencies(RunMain MyExe)
Then just do make RunMain to generate out.txt.
If you don't want to specify such a high minimum version, you can use the obsolete LOCATION target property instead:
get_target_property(MyExeLocation MyExe LOCATION)
add_custom_target(
RunMain ${MyExeLocation} > ${MainOut}
COMMENT "Running ${MyExeLocation} with output redirected to ${MainOut}")

"make dist" equivalent in CMake

According to FAQ, CMake doesn't create a make dist target and source package can be created using CPack. But CPack just makes a tarball of the source directory with all files that don't match patterns in CPACK_SOURCE_IGNORE_FILES.
On the other hand, make dist generated by autotools bundles only files it knows about, mostly sources needed for compilation.
Anyone has a smart way of making a source package with only files that are specified in CMakeLists.txt (and its dependencies)?
I've been thinking about this for a while and I won't pretend I can simulate a make dist without having this directly supported by CMake itself.
The problem is that you can add a lot of file dependencies with CMake on the one side (e.g. to pre-build libraries) and on the other side CMake does not know about dependencies directly checked by the generated build environment itself (e.g. any header dependencies).
So here is a code that just collects all CMakeList.txt and source files given with any build targets:
function(make_dist_creator _variable _access _value _current_list_file _stack)
if (_access STREQUAL "MODIFIED_ACCESS")
# Check if we are finished (end of main CMakeLists.txt)
if (NOT _current_list_file)
get_property(_subdirs GLOBAL PROPERTY MAKE_DIST_DIRECTORIES)
list(REMOVE_DUPLICATES _subdirs)
foreach(_subdir IN LISTS _subdirs)
list(APPEND _make_dist_sources "${_subdir}/CMakeLists.txt")
get_property(_targets DIRECTORY "${_subdir}" PROPERTY BUILDSYSTEM_TARGETS)
foreach(_target IN LISTS _targets)
get_property(_sources TARGET "${_target}" PROPERTY SOURCES)
foreach(_source IN LISTS _sources)
list(APPEND _make_dist_sources "${_subdir}/${_source}")
endforeach()
endforeach()
endforeach()
add_custom_target(
dist
COMMAND "${CMAKE_COMMAND}" -E tar zcvf "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.tar.gz" -- ${_make_dist_sources}
COMMENT "Make distribution ${PROJECT_NAME}.tar.gz"
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
)
message("_make_dist_sources = ${_make_dist_sources}")
else()
# else collect subdirectories in my source dir
file(RELATIVE_PATH _dir_rel "${CMAKE_SOURCE_DIR}" "${_value}")
if (NOT _dir_rel MATCHES "\.\.")
set_property(GLOBAL APPEND PROPERTY MAKE_DIST_DIRECTORIES "${_value}")
endif()
endif()
endif()
endfunction()
variable_watch("CMAKE_CURRENT_LIST_DIR" make_dist_creator)
Note: The used BUILDSYSTEM_TARGETS property needs at least CMake version 3.7
I see the code above as an starting point and prove of concept. You could add libraries, headers, etc. on a need-by basis, but you should probably just tweak cpack to do your bidding.
As a starting point see e.g. the link #usr1234567 provided in the comments.
References
Get all source files a target depends on in CMake
Simon is correct above but does not give a full answer. With git you can generate a compatible tar ball archive with the git archive command.
This example with the version is compatible with make dist of yesteryear.
git archive --format=tar.gz -o my-repo-0.01.tar.gz --prefix=my-repo-0.01/ master
See: https://gist.github.com/simonw/a44af92b4b255981161eacc304417368