I am invoking a software from CMAKE to generate required files for build.Is it possible to print the version of software invoked in the build window..?
As said in the comments, the exact way to output the version will depend on the executable itself.
Lets assume it is <executable> --version.
Then it CMake it will look like:
find_program(EXECUTABLE_RUNTIME <executable>)
if ("${EXECUTABLE_RUNTIME}" STREQUAL "EXECUTABLE_RUNTIME-NOTFOUND")
message(FATAL_ERROR "<executable> runtime could not be found!")
else()
execute_process(COMMAND "${EXECUTABLE_RUNTIME}" --version
OUTPUT_VARIABLE EXECUTABLE_VERSION
OUTPUT_STRIP_TRAILING_WHITESPACE)
message(STATUS "Found <executable> runtime at ${EXECUTABLE_VERSION}, version ${EXECUTABLE_VERSION}")
endif()
A possible footgun is to include the command arguments in the quotes (e.g. "${EXECUTABLE_RUNTIME} --version", if you do this the output variable will be empty.
OUTPUT_STRIP_TRAILING_WHITESPACE will remove the new line which is very often present after the version.
Related
Goals
Make cmake select clang++ if its version is above 10; otherwise, use g++. If clang++ is below 10 and the default g++ is below 10.1, use g++-10 (REQUIRED).
This is to achieve compiler fallback.
Background
As kindly noted in this answer, one should set the default compiler before the project keyword.
Instead of using
-DCMAKE_CXX_COMPILER=clang++
or
set(CMAKE_CXX_COMPILER clang++)
I used the following statements with reference to the answer here:
find_program(CMAKE_CXX_COMPILER
NAMES $ENV{CXX} clang++ PATHS ENV PATH NO_DEFAULT_PATH)
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 10)
find_program(CMAKE_CXX_COMPILER
NAMES $ENV{CXX} g++ PATHS ENV PATH NO_DEFAULT_PATH)
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 10.1)
find_program(CMAKE_CXX_COMPILER
NAMES $ENV{g++-10} g++-10 PATHS ENV PATH NO_DEFAULT_PATH REQUIRED)
endif()
endif()
Problem
The compiler is not set to g++ when the version of clang++ is less than 10, but I could check that cmake passes the branch
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 10)
I do not understand this behavior. And are there any alternative to this without using command line arguments?
UPDATE
This question was asked in acknowledgment of the fact that setting a compiler in CMakeLists.txt is discouraged.
#KamilCuk suggested two ways to achieve what I stated, and one may take those suggestions if they want to.
Here's what I did.
TL;DR
Do not specify a compiler in CMakeLists.txt.
My suggestion
This is contrary to what I have previously done in this question.
I scrapped all the configuration logic from CMakeLists.txt and decided to create a shell file that extracts compiler versions and select an appropriate compiler. This way of configuration is quite well-known, which can be found in many popular repositories such as ImageMagick and tensorflow.
If you want to write the logic in cmake, you could write something along:
cmake_minimum_required(VERSION 3.11)
# Before project()!!!
execute_process(COMMAND clang++ --version OUTPUT_VARIABLE result)
string(REGEX REPLACE "[^0-9]* ([0-9]+).*" "\\1" clang_version "${result}") # extract the version with some regex
execute_process(COMMAND g++ --version OUTPUT_VARIABLE result)
string(REGEX REPLACE "[^0-9]* ([0-9]+).*" "\\1" gcc_version "${result}") # extract the version with some regex
# write your super condition here and set CMAKE_CXX_COMPILER depending on it
set(CMAKE_CXX_COMPILER g++)
if (gcc_version LESS 10)
set(CMAKE_CXX_COMPILER clang++)
endif()
project(blabla) # uses CMAKE_CXX_COMPILER and configures cmake
You could also use include(CMakeDetermineCXXCompiler) to determine the version, or use the template files from cmake from CMakeCXXCompilerId.cpp.in from cmake installation path that is used by CMakeDetermineCXXCompiler internally.
I would like to link my Debug executable with external library built in Release version using FetchContent.
I'm able to link my Debug executable with Debug built library and similar with Release and Release using:
project(CMakeDemo)
set(FETCHCONTENT_QUIET OFF)
FetchContent_Declare(
ZLIB
URL https://zlib.net/zlib-1.2.11.tar.gz
)
FetchContent_MakeAvailable(ZLIB)
add_executable(CMakeDemo main.cpp)
target_link_libraries(CMakeDemo ZLIB)
So when I execute from a build directory on Windows:
cmake ../
cmake --build .
Then zlib and my executable is built in Debug version and my executable is linked with that zlib Debug version.
But how to enhance the CMake to build my executable in Debug version but zlib in Release version and link my Debug executable with the zlib Release version? How to achieve that using FetchContent_Declare?
(I believe this has to be some common approach because for example when someone wants to use Google Test framework or zlib in a project then for sure he wants to use these external libraries in Release version always)
FetchContent() will integrate the dependency, here ZLIB, in your worktree like an add_subdirectory() so flags will be identical (if ZLIB is correctly configured to be use as subproject, spoiler: this is not the case you'll need to patch it...).
If you really want to build it in Release, you should try to use ExternalProject() and execute_process()
to build and install it at configure time then you can use find_package() to retrieve this pre-installed dependency.
I know that the question is old, but as I have been struggling a whole day with the same issue, and wanted to share my progress. Please bear in mind I'm a cmake newbie, and I'm not sure if what I'm doing conforms to best practices, and the example code I provide might have mistakes. It can also definitely be significantly improved - but I believe is sufficient to get someone off the ground, if they're having the same issue.
For my use case, I wanted the external libraries to be built at cmake configure time, instead of build time. To that effect, I used execute_process to configure and build the external libraries. This is the function I created for that effect:
function(fetch_and_build_lib)
set(options "")
set(oneValueArgs LIB_NAME GIT_REPOSITORY GIT_TAG PACKAGE_NAME)
set(multiValueArgs CONFIG_ARGS CONFIG_TYPES BUILD_ARGS COMPONENTS)
cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if (NOT DEFINED ARG_LIB_NAME)
message(FATAL_ERROR "fetch_and_build_lib called without LIB_NAME")
endif()
if (NOT DEFINED ARG_GIT_REPOSITORY)
message(FATAL_ERROR "fetch_and_build_lib called without GIT_REPOSITORY")
endif()
if (NOT DEFINED ARG_GIT_TAG)
message(FATAL_ERROR "fetch_and_build_lib called without GIT_TAG")
endif()
if (DEFINED ARG_PACKAGE_NAME)
set(PACKAGE_NAME ${ARG_PACKAGE_NAME})
else()
set(PACKAGE_NAME ${ARG_LIB_NAME})
endif()
include(FetchContent)
FetchContent_Declare(${ARG_LIB_NAME}
GIT_REPOSITORY "${ARG_GIT_REPOSITORY}"
GIT_TAG "${ARG_GIT_TAG}"
)
FetchContent_GetProperties(${ARG_LIB_NAME}
POPULATED LIB_POPULATED
)
message(STATUS "Checking if ${ARG_LIB_NAME} is populated: ${LIB_POPULATED}")
if (NOT LIB_POPULATED})
message(STATUS "Populating ${ARG_LIB_NAME}...")
FetchContent_Populate(${ARG_LIB_NAME})
set(LIB_BINARY_DIR ${${ARG_LIB_NAME}_BINARY_DIR})
set(LIB_SOURCE_DIR ${${ARG_LIB_NAME}_SOURCE_DIR})
message(STATUS "Configuring ${ARG_LIB_NAME}...")
execute_process(
COMMAND ${CMAKE_COMMAND}
"-S ${LIB_SOURCE_DIR}"
"-B ${LIB_BINARY_DIR}"
"-DCMAKE_GENERATOR=${CMAKE_GENERATOR}"
"-DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}"
${ARG_CONFIG_ARGS}
WORKING_DIRECTORY "${LIB_BINARY_DIR}"
COMMAND_ECHO STDOUT
RESULT_VARIABLE result_config
)
if(result_config)
message(FATAL_ERROR "Failed to config ${ARG_LIB_NAME} exited with result: ${result_config}")
else()
message(STATUS "${ARG_LIB_NAME} configuring complete!")
endif()
foreach(config_type IN LISTS ARG_CONFIG_TYPES)
set(${ARG_LIB_NAME}_CONFIG_TYPE "${config_type}" CACHE INTERNAL "Config/build type for ${ARG_LIB_NAME}")
message(STATUS "Building ${ARG_LIB_NAME}... with CONFIG: ${config_type}")
execute_process(
COMMAND ${CMAKE_COMMAND}
--build "${LIB_BINARY_DIR}"
--config "${config_type}"
WORKING_DIRECTORY "${LIB_BINARY_DIR}"
COMMAND_ECHO STDOUT
${ARG_BUILD_ARGS}
RESULT_VARIABLE result_build
)
endforeach()
if(result_build)
message(FATAL_ERROR "Failed to build ${ARG_LIB_NAME} with result: ${result_build}")
else()
message(STATUS "${ARG_LIB_NAME} build complete!")
endif()
endif()
if (DEFINED ARG_COMPONENTS)
find_package(${PACKAGE_NAME} REQUIRED
COMPONENTS ${ARG_COMPONENTS}
PATHS ${LIB_BINARY_DIR}
NO_DEFAULT_PATH
)
else()
find_package(${PACKAGE_NAME} REQUIRED
PATHS ${LIB_BINARY_DIR}
NO_DEFAULT_PATH
)
endif()
endfunction()
What it does is as follows:
Downloads from git a repository with a specific tag
Configures the build system of the downloaded library
Builds the library as part of the main project configure stage
Makes sure that FindPackage can discover the newly built library
Then, in my CMakeLists.txt for my project, I use it like so:
add_executable(main main.cpp)
fetch_and_build_lib(
LIB_NAME "mylib"
GIT_REPOSITORY "https://github.com/myRepo/myLib.git"
GIT_TAG "1.2.3"
CONFIG_ARGS "-DMYLIB_DOC=OFF;-DMYLIB_TEST=OFF"
CONFIG_TYPES "debug, release"
PACKAGE_NAME "MyLIB"
)
find_package(MyLIB REQUIRED)
#Use Release version of library for Debug, MinSizeRel, RelWithDebInfo build types:
set_target_properties(MyLIB PROPERTIES
MAP_IMPORTED_CONFIG_MINSIZEREL Release
MAP_IMPORTED_CONFIG_RELWITHDEBINFO Release
MAP_IMPORTED_CONFIG_DEBUG Release
)
target_link_libraries(main myLib::myLib)
This invokes the previously defined function that downloads, configures and builds the library. It assumes that the library correctly exports its targets, and then remaps the imported configs to the current project config.
This is what actually ends up linking the debug lib with the release executable.
CMake documentation for MAP_IMPORTED_CONFIG_
Mandatory disclaimer:
Mixing release libs and debug executables is filled with pitfalls, both for shared and static libraries. I'm not discussing whether this is a good idea or not, or what the pitfalls are. Be sure you are aware of the limitations and what is safe and what is not safe to do when doing such a mix.
I am having trouble telling cmake to find openssl on my mac. Let me say, I am an amateur at using cmake, as my acquaintance with it, is very recent and occasional. That said, I can do simple things in cmake such as create small projects build them and run them. Some commands (find_library, find_package, ...) always perplex me.
I downloaded and installed openssl (./configure, make and make install) and it has put the files in /opt/openssl
I was googling around to see help for a simple inclusion openssl in my program.
One search result points to a command FindOpenSSL here (https://cmake.org/cmake/help/v3.6/module/FindOpenSSL.html). When i used FindOpenSSL, cmake emitted error saying FindOpenSSL is not a command.
I also tried find_library and find_package commands.
But if I check the variable OPENSSL_FOUND, it is still undefined.
It looks like I have to hardcode (or pass through -D options to cmake) the variables for openssl. What is the best practice here? The reference manual of cmake seems to be like piece of puzzle, as I don't understand the true difference between find_library and find_package, or when to use in preference of the other.
Am I the only one facing this, or more souls around here that are confused and struggling?
Thanks to anyone pointing me the right direction and helping me out of this confusion.
My current CMakeLists.txt looks like below:
ravindranaths-MacBook-Pro:crypt_handlers ravindranath$ cat CMakeLists.txt
cmake_minimum_required (VERSION 3.5.1)
# search for CPP_HOME. If found use this as the install_root dir.
# else, use /usr/local
message (STATUS "Searching for environment var CPP_HOME ...")
if (DEFINED ENV{CPP_HOME})
message (STATUS "Found CPP_HOME: " $ENV{CPP_HOME})
set (CPP_HOME $ENV{CPP_HOME})
else()
message (STATUS "Could not find. Treating /usr/local as CPP_HOME...")
set (CPP_HOME /usr/local)
endif()
set(Boost_USE_STATIC_LIBS ON)
###########################################################
set(CMAKE_INCLUDE_PATH /usr/local/ssl/include/openssl)
set(OPENSSL_USE_STATIC_LIBS ON)
############################################################
find_package(Boost 1.45.0 COMPONENTS system )
find_package (Threads)
set (CMAKE_CXX_STANDARD 11)
include_directories(../../include)
include_directories(../../include/ext)
include_directories(../../include/ext/spdlog)
include_directories(${CPP_HOME}/externals/plog/include)
add_executable(crypt app.cpp)
link_directories(../../build/src)
target_link_libraries (crypt ${CMAKE_THREAD_LIBS_INIT})
find_library(OpenSSL_LIB libcrypto.a libssl.a)
#find_package(OpenSSL)
if (OPENSSL_FOUND)
message (STATUS "OPENSSL found")
message (STATUS "OpenSSL INclude directories:" OPENSSL_INCLUDE_DIR)
else()
message (FATAL_ERROR "OpenSSL Not found.")
endif()
include_directories(${OPENSSL_INCLUDE_DIR})
target_link_libraries(crypt ${CMAKE_DL_LIBS})
TARGET_LINK_LIBRARIES(crypt ${OPENSSL_LIBRARIES} ${LIBYAML_LIBRARIES} pthread -ldl)
#target_link_libraries(crypt /usr/local/ssl/lib/libcrypto.a /usr/local/lib/libssl.a)
target_link_libraries(crypt /usr/local/lib/libcrypto.a /usr/local/lib/libssl.a)
if(Boost_FOUND)
include_directories(${include_directories} ${Boost_INCLUDE_DIRS})
target_link_libraries(crypt nettu ${Boost_LIBRARIES})
endif()
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/dist/bin)
UPDATE
now, I just use find_package(OpenSSL), instead of find_libraries(...). And, I am running cmake as below:
cmake -DCMAKE_BUILD_TYPE=Debug -DOPENSSL_ROOT_DIR=/opt/openssl/ -DOPENSSL_LIBRARIES=/opt/openssl/lib/ ..
cmake succeeds by writing the makefiles. However, I get a linker error when running make. The error points to missing libssl.a like below:
make[2]: *** No rule to make target `/usr/local/lib/libssl.a', needed by `examples/crypt_handlers/crypt'. Stop.
What I don't understand is that the make is still looking for libssl.a in "/usr/local/lib/.." instead of /opt/openssl/lib.
How to fix this?
You can change as follows
#find_library(OpenSSL_LIB libcrypto.a libssl.a)
find_package(OpenSSL)
And run
cmake -DOPENSSL_ROOT_DIR=/opt/openssl
Where OPENSSL_ROOT_DIR is hint for FindOpenSSL.cmake standard module where to find root for OpenSSL installation.
I want to find GTest via:
find_package(GTest REQUIRED)
But it is not found:
Error:Could NOT find GTest (missing: GTEST_LIBRARY GTEST_INCLUDE_DIR GTEST_MAIN_LIBRARY)
I know from this link that GTest should be distributed via standard CMake.
Can you tell me what I did wrong?
If you're on Ubuntu, you should read /usr/share/doc/libgtest-dev/README.Debian. It says:
The Google C++ Testing Framework uses conditional compilation for some
things. Because of the C++ "One Definition Rule", gtest must be
compiled with exactly the same flags as your C++ code under test.
Because this is hard to manage, upstream no longer recommends using
precompiled libraries
So you should compile and install your own version of the gtest library with the exactly same Compiler Options, and set the GTEST_LIBRARY or GTEST_ROOT variable accordingly.
For example, I did the following:
$ mkdir -p ExternalLibs/gTest
$ cd ExternalLibs/gTest
$ cmake /usr/src/gtest
$ make
Then I added the following lines in my CMakeLists.txt:
set (GTEST_ROOT ${CMAKE_SOURCE_DIR}/ExternalLibs/gTest)
find_package(GTest REQUIRED)
find_package does not look in CMake's installation directory. It only evaluates the PATH and CMAKE_PREFIX_VARIABLES. Just add the path to CMake's GTest to the latter variable, clear your CMake cache and re-run CMake.
If you have gtest installed you can just do:
add_subdirectory("/usr/src/gtest" ${CMAKE_BINARY_DIR}/gtest)
enable_testing()
include_directories(${GTEST_INCLUDE_DIRS})
add_executable(test test.cpp)
target_link_libraries(test gtest gtest_main)
add_test(AllTests test)
When using some libraries like OpenCV with C/C++, variables like OpenCV_LIBS are used to point the compiler/linker to the relevant directories.
Examples using cmake:
include_directories( ${OpenCV_INCLUDE_DIRS} )
target_link_libraries( project_name ${OpenCV_LIBS} )
How can I check where such variables point at? I've tried typing set or printenv in terminal but it shows only some system variables. Also how can I set/change such variables?
Those variables are determined by cmake (see OpenCVConfig.cmake for a more detailed description of opencv CMake variables available).
To see those values you can add message() calls after the find_package(OpenCV) call to your project's CMakeLists.txt:
find_package(OpenCV)
message(STATUS "OpenCV_INCLUDE_DIRS = ${OpenCV_INCLUDE_DIRS}")
message(STATUS "OpenCV_LIBS = ${OpenCV_LIBS}")
Alternatively you can run find_package via a CMake command line option.
Here are a few examples (the CMAKE_PREFIX_PATH part is optional if CMake is not able to find your libraries installation path automatically):
MODE=COMPILE giving include directories (e.g. with MSVC compiler)
$ cmake
--find-package
-DNAME=OpenCV
-DCOMPILER_ID=MSVC -DMSVC_VERSION=1700
-DLANGUAGE=CXX
-DMODE=COMPILE
-DCMAKE_PREFIX_PATH:PATH=/path/to/your/OpenCV/build
MODE=LINK giving link libraries (e.g. with GNU compiler)
$ cmake
--find-package
-DNAME=OpenCV
-DCOMPILER_ID=GNU
-DLANGUAGE=CXX
-DMODE=LINK
-DCMAKE_PREFIX_PATH:PATH=/path/to/your/OpenCV/build
Note: This CMake call will create a CMakeFiles sub-directory in your current working directory.
References
Using OpenCV with gcc and CMake
CMakeFindPackageMode CMake module documentation