How to add IF ESLE ENDIF between ExternalProject_Add commands in CMake - cmake

Is there any way to add IF ELSE ENDIF between ExternalProject_Add lines?
For example
ExternalProject_Add(my_lib
URL "https://github.com/nlohmann/json/archive/refs/tags/v3.11.2.zip"
CMAKE_ARGS
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_INSTALL_PREFIX}
IF(WIN32)
# Some Commands Here #
ELSE()
# Another Some Commands Here #
ENDIF(WIN32)
)
You see some errors because of above lines.
Or I must create different commands for each of my conditions?

Use a temporary variable.....
if(WIN32)
set(ARGS some arguments)
else()
set(ARGS other arguments)
endif()
ExternalProject_Add(my_lib
URL "https://github.com/nlohmann/json/archive/refs/tags/v3.11.2.zip"
CMAKE_ARGS
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_INSTALL_PREFIX}
${ARGS}
)

In cases where you need to deal with multiple target systems you could introduce variables with names containing CMAKE_SYSTEM_NAME and simply refer to the suitable variable in the command:
set(ARGS_Windows some arguments)
set(ARGS_Linux other arguments)
set(ARGS_Darwin other other arguments)
ExternalProject_Add(my_lib
URL "https://github.com/nlohmann/json/archive/refs/tags/v3.11.2.zip"
CMAKE_ARGS
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_INSTALL_PREFIX}
${ARGS_${CMAKE_SYSTEM_NAME}} # evaluates to the var value corresponding to the target system
)

Related

Using cmake to create protobuf / grpc cc files

If I want to recreate the following protoc command in cmake:
protoc -I ../proto/ --cpp_out=. service.proto
I use the following lines in cmake:
file(GLOB ProtoFiles "${CMAKE_CURRENT_SOURCE_DIR}/*.proto")
PROTOBUF_GENERATE_CPP(ProtoSources ProtoHeaders ${ProtoFiles})
If I instead want to recreate the protoc command below:
protoc -I ../proto/ --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` service.proto
In the case above I am not able to determine how to change the cmake file, please help!
The Question is how do I address the:
--plugin=EXECUTABLE Specifies a plugin executable to use.
Normally, protoc searches the PATH for
plugins, but you may specify additional
executables not in the path using this flag.
Additionally, EXECUTABLE may be of the form
NAME=PATH, in which case the given plugin name
is mapped to the given executable even if
the executable's own name differs.
I have been reading the PROTOBUF_GENERATE_CPP documentation, but did not find an answer!
Module findProtobuf.cmake defines functions-wrappers only for common protoc calls: PROTOBUF_GENERATE_CPP - for --cpp_out and PROTOBUF_GENERATE_PYTHON - for --py_out. But you can implement your own function-wrapper for needed plugin. Code below is based on PROTOBUF_GENERATE_CPP implementation.
find_program(GRPC_CPP_PLUGIN grpc_cpp_plugin) # Get full path to plugin
function(PROTOBUF_GENERATE_GRPC_CPP SRCS HDRS)
if(NOT ARGN)
message(SEND_ERROR "Error: PROTOBUF_GENERATE_GRPC_CPP() called without any proto files")
return()
endif()
if(PROTOBUF_GENERATE_CPP_APPEND_PATH) # This variable is common for all types of output.
# Create an include path for each file specified
foreach(FIL ${ARGN})
get_filename_component(ABS_FIL ${FIL} ABSOLUTE)
get_filename_component(ABS_PATH ${ABS_FIL} PATH)
list(FIND _protobuf_include_path ${ABS_PATH} _contains_already)
if(${_contains_already} EQUAL -1)
list(APPEND _protobuf_include_path -I ${ABS_PATH})
endif()
endforeach()
else()
set(_protobuf_include_path -I ${CMAKE_CURRENT_SOURCE_DIR})
endif()
if(DEFINED PROTOBUF_IMPORT_DIRS)
foreach(DIR ${Protobuf_IMPORT_DIRS})
get_filename_component(ABS_PATH ${DIR} ABSOLUTE)
list(FIND _protobuf_include_path ${ABS_PATH} _contains_already)
if(${_contains_already} EQUAL -1)
list(APPEND _protobuf_include_path -I ${ABS_PATH})
endif()
endforeach()
endif()
set(${SRCS})
set(${HDRS})
foreach(FIL ${ARGN})
get_filename_component(ABS_FIL ${FIL} ABSOLUTE)
get_filename_component(FIL_WE ${FIL} NAME_WE)
list(APPEND ${SRCS} "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.grpc.pb.cc")
list(APPEND ${HDRS} "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.grpc.pb.h")
add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.grpc.pb.cc"
"${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.grpc.pb.h"
COMMAND ${Protobuf_PROTOC_EXECUTABLE}
ARGS --grpc_out=${CMAKE_CURRENT_BINARY_DIR}
--plugin=protoc-gen-grpc=${GRPC_CPP_PLUGIN}
${_protobuf_include_path} ${ABS_FIL}
DEPENDS ${ABS_FIL} ${Protobuf_PROTOC_EXECUTABLE}
COMMENT "Running gRPC C++ protocol buffer compiler on ${FIL}"
VERBATIM)
endforeach()
set_source_files_properties(${${SRCS}} ${${HDRS}} PROPERTIES GENERATED TRUE)
set(${SRCS} ${${SRCS}} PARENT_SCOPE)
set(${HDRS} ${${HDRS}} PARENT_SCOPE)
endfunction()
Usage is same as for PROTOBUF_GENERATE_CPP:
file(GLOB ProtoFiles "${CMAKE_CURRENT_SOURCE_DIR}/*.proto")
PROTOBUF_GENERATE_GRPC_CPP(ProtoGRPCSources ProtoGRPCHeaders ${ProtoFiles})
Starting at version 3.12, protobuf_generate supports a PLUGIN argument
https://github.com/protocolbuffers/protobuf/blob/v3.12.0/cmake/protobuf-config.cmake.in#L46
so you could try something along the line:
PROTOBUF_GENERATE_CPP(ProtoSources ProtoHeaders ${ProtoFiles} PLUGIN protoc-gen-grpc=${GRPC_CPP_PLUGIN_PATH})
For me the blogpost https://www.falkoaxmann.de/dev/2020/11/08/grpc-plugin-cmake-support.html lead to success because it provides a full example (thanks #powerpete).
I'm putting the code here so it is available as an answer and not just as a comment:
project(my-service VERSION 1.0 LANGUAGES CXX C)
find_package(protobuf CONFIG REQUIRED)
find_package(gRPC CONFIG REQUIRED)
find_package(Threads)
set(PROTO_FILES
MyService.proto
)
# protobuf source files go into the lib just like any other CPP source file
add_library(my-service ${PROTO_FILES})
target_link_libraries(my-service
PUBLIC
protobuf::libprotobuf
gRPC::grpc
gRPC::grpc++
)
target_include_directories(my-service
PUBLIC
${CMAKE_CURRENT_BINARY_DIR}
)
get_target_property(grpc_cpp_plugin_location gRPC::grpc_cpp_plugin LOCATION)
# compile the message types
protobuf_generate(TARGET my-service LANGUAGE cpp)
# compile the GRPC services
protobuf_generate(
TARGET
my-service
LANGUAGE
grpc
GENERATE_EXTENSIONS
.grpc.pb.h
.grpc.pb.cc
PLUGIN
"protoc-gen-grpc=${grpc_cpp_plugin_location}"
)

How to print all the properties of a target in cmake?

With the following cmake scrpt:
get_cmake_property(_variableNames VARIABLES)
foreach (_variableName ${_variableNames})
message(STATUS "${_variableName}=${${_variableName}}")
endforeach()
We can print all the variables in the CMake project. Then my question is: is there a function that can print all the properties of a target?
I have had limited success with the following work-around to the apparent lack of ability to dynamically query for the properties of a target.
I invoke the cmake command to list all properties, and then try each one on the target.
# Get all propreties that cmake supports
if(NOT CMAKE_PROPERTY_LIST)
execute_process(COMMAND cmake --help-property-list OUTPUT_VARIABLE CMAKE_PROPERTY_LIST)
# Convert command output into a CMake list
string(REGEX REPLACE ";" "\\\\;" CMAKE_PROPERTY_LIST "${CMAKE_PROPERTY_LIST}")
string(REGEX REPLACE "\n" ";" CMAKE_PROPERTY_LIST "${CMAKE_PROPERTY_LIST}")
endif()
function(print_properties)
message("CMAKE_PROPERTY_LIST = ${CMAKE_PROPERTY_LIST}")
endfunction()
function(print_target_properties target)
if(NOT TARGET ${target})
message(STATUS "There is no target named '${target}'")
return()
endif()
foreach(property ${CMAKE_PROPERTY_LIST})
string(REPLACE "<CONFIG>" "${CMAKE_BUILD_TYPE}" property ${property})
# Fix https://stackoverflow.com/questions/32197663/how-can-i-remove-the-the-location-property-may-not-be-read-from-target-error-i
if(property STREQUAL "LOCATION" OR property MATCHES "^LOCATION_" OR property MATCHES "_LOCATION$")
continue()
endif()
get_property(was_set TARGET ${target} PROPERTY ${property} SET)
if(was_set)
get_target_property(value ${target} ${property})
message("${target} ${property} = ${value}")
endif()
endforeach()
endfunction()
This isn't really intended to be a competing answer, just an expansion on the answer by AlwaysTraining (and edited by Dawid Drozd)!
If the target is an INTERFACE_LIBRARY, this will print out a whole bunch of errors about whitelisted properties. Unfortunately, there doesn't seem to be a good way to dynamically query what the whitelisted properties are - by checking the source for cmTargetPropertyComputer::WhiteListedInterfaceProperty I was able to come up with a regexp, but it could change from version to version (I was looking at the source for cmake-3.11). Anyway, here's my revised version, with support for INTERFACE_LIBRARYtargets - I also removes duplicate, and made the filtering of LOCATION properties happen once, outside of the loop.
(I've made a suggested edit to the original question, but this is here in case it isn't accepted...)
Thanks AlwaysTraining and Dawid Drozd!
# Get all propreties that cmake supports
execute_process(COMMAND cmake --help-property-list OUTPUT_VARIABLE CMAKE_PROPERTY_LIST)
# Convert command output into a CMake list
STRING(REGEX REPLACE ";" "\\\\;" CMAKE_PROPERTY_LIST "${CMAKE_PROPERTY_LIST}")
STRING(REGEX REPLACE "\n" ";" CMAKE_PROPERTY_LIST "${CMAKE_PROPERTY_LIST}")
# Fix https://stackoverflow.com/questions/32197663/how-can-i-remove-the-the-location-property-may-not-be-read-from-target-error-i
list(FILTER CMAKE_PROPERTY_LIST EXCLUDE REGEX "^LOCATION$|^LOCATION_|_LOCATION$")
# For some reason, "TYPE" shows up twice - others might too?
list(REMOVE_DUPLICATES CMAKE_PROPERTY_LIST)
# build whitelist by filtering down from CMAKE_PROPERTY_LIST in case cmake is
# a different version, and one of our hardcoded whitelisted properties
# doesn't exist!
unset(CMAKE_WHITELISTED_PROPERTY_LIST)
foreach(prop ${CMAKE_PROPERTY_LIST})
if(prop MATCHES "^(INTERFACE|[_a-z]|IMPORTED_LIBNAME_|MAP_IMPORTED_CONFIG_)|^(COMPATIBLE_INTERFACE_(BOOL|NUMBER_MAX|NUMBER_MIN|STRING)|EXPORT_NAME|IMPORTED(_GLOBAL|_CONFIGURATIONS|_LIBNAME)?|NAME|TYPE|NO_SYSTEM_FROM_IMPORTED)$")
list(APPEND CMAKE_WHITELISTED_PROPERTY_LIST ${prop})
endif()
endforeach(prop)
function(print_properties)
message ("CMAKE_PROPERTY_LIST = ${CMAKE_PROPERTY_LIST}")
endfunction(print_properties)
function(print_whitelisted_properties)
message ("CMAKE_WHITELISTED_PROPERTY_LIST = ${CMAKE_WHITELISTED_PROPERTY_LIST}")
endfunction(print_whitelisted_properties)
function(print_target_properties tgt)
if(NOT TARGET ${tgt})
message("There is no target named '${tgt}'")
return()
endif()
get_target_property(target_type ${tgt} TYPE)
if(target_type STREQUAL "INTERFACE_LIBRARY")
set(PROP_LIST ${CMAKE_WHITELISTED_PROPERTY_LIST})
else()
set(PROP_LIST ${CMAKE_PROPERTY_LIST})
endif()
foreach (prop ${PROP_LIST})
string(REPLACE "<CONFIG>" "${CMAKE_BUILD_TYPE}" prop ${prop})
# message ("Checking ${prop}")
get_property(propval TARGET ${tgt} PROPERTY ${prop} SET)
if (propval)
get_target_property(propval ${tgt} ${prop})
message ("${tgt} ${prop} = ${propval}")
endif()
endforeach(prop)
endfunction(print_target_properties)
Improved #AlwaysTraining's and #PaulMolodowitch's answers.
Solves problem with INTERFACE_LIBRARY described in kyb's comment
Functionally my implementation does at most the same as #PaulMolodowitch's implementation. But it is more laconic.
## https://stackoverflow.com/questions/32183975/how-to-print-all-the-properties-of-a-target-in-cmake/56738858#56738858
## https://stackoverflow.com/a/56738858/3743145
## Get all properties that cmake supports
execute_process(COMMAND cmake --help-property-list OUTPUT_VARIABLE CMAKE_PROPERTY_LIST)
## Convert command output into a CMake list
STRING(REGEX REPLACE ";" "\\\\;" CMAKE_PROPERTY_LIST "${CMAKE_PROPERTY_LIST}")
STRING(REGEX REPLACE "\n" ";" CMAKE_PROPERTY_LIST "${CMAKE_PROPERTY_LIST}")
list(REMOVE_DUPLICATES CMAKE_PROPERTY_LIST)
function(print_target_properties tgt)
if(NOT TARGET ${tgt})
message("There is no target named '${tgt}'")
return()
endif()
foreach (prop ${CMAKE_PROPERTY_LIST})
string(REPLACE "<CONFIG>" "${CMAKE_BUILD_TYPE}" prop ${prop})
get_target_property(propval ${tgt} ${prop})
if (propval)
message ("${tgt} ${prop} = ${propval}")
endif()
endforeach(prop)
endfunction(print_target_properties)
This version uses ideas from David Drozd's and Paul Molodowitch's answers but includes processing for <CONFIG> and <LANG> text returned by cmake --help-property-list:
# Sets the AVAILABLE_CONFIGURATION_TYPES variable to the default available configurations
# (For some reason, CMAKE_CONFIGURATION_TYPES tends to be empty)
function(get_available_configuration_types)
# Get all variables that cmake cache defines by default
execute_process(COMMAND cmake -LAH -N OUTPUT_VARIABLE CMAKE_CACHE_VARIABLE_LIST)
# Convert command output into a CMake list
string(REGEX REPLACE ";" "[:semicolon:]" CMAKE_CACHE_VARIABLE_LIST "${CMAKE_CACHE_VARIABLE_LIST}")
string(REGEX REPLACE "\n" ";" CMAKE_CACHE_VARIABLE_LIST "${CMAKE_CACHE_VARIABLE_LIST}")
# filter down to the variables
list(FILTER CMAKE_CACHE_VARIABLE_LIST EXCLUDE REGEX "^$|^//.*$|^\-\-$")
# Get the configuration types
set(AVAILABLE_CONFIGURATION_TYPES ${CMAKE_CACHE_VARIABLE_LIST})
list(FILTER AVAILABLE_CONFIGURATION_TYPES INCLUDE REGEX "^CMAKE_CONFIGURATION_TYPES")
list(GET AVAILABLE_CONFIGURATION_TYPES 0 AVAILABLE_CONFIGURATION_TYPES)
string(REGEX REPLACE ".*=" "" AVAILABLE_CONFIGURATION_TYPES "${AVAILABLE_CONFIGURATION_TYPES}")
string(REPLACE "[:semicolon:]" ";" AVAILABLE_CONFIGURATION_TYPES "${AVAILABLE_CONFIGURATION_TYPES}")
string(TOUPPER "${AVAILABLE_CONFIGURATION_TYPES}" AVAILABLE_CONFIGURATION_TYPES)
# Add the current build type if it isn't already there
string(TOUPPER ${CMAKE_BUILD_TYPE} BUILD_TYPE)
list(FILTER AVAILABLE_CONFIGURATION_TYPES EXCLUDE REGEX ${BUILD_TYPE})
list(APPEND AVAILABLE_CONFIGURATION_TYPES ${BUILD_TYPE})
list(SORT AVAILABLE_CONFIGURATION_TYPES)
# make AVAILABLE_CONFIGURATION_TYPES available to parent
set(AVAILABLE_CONFIGURATION_TYPES ${AVAILABLE_CONFIGURATION_TYPES} PARENT_SCOPE)
endfunction()
# Sets the CMAKE_PROPERTY_LIST and CMAKE_WHITELISTED_PROPERTY_LIST variables to
# the list of properties
function(get_cmake_property_list)
# See https://stackoverflow.com/a/44477728/240845
set(LANGS ASM-ATT ASM ASM_MASM ASM_NASM C CSHARP CUDA CXX FORTRAN HIP ISPC JAVA OBJC OBJCXX RC SWIFT)
get_available_configuration_types()
# Get all propreties that cmake supports
execute_process(COMMAND cmake --help-property-list OUTPUT_VARIABLE CMAKE_PROPERTY_LIST)
# Convert command output into a CMake list
string(REGEX REPLACE ";" "\\\\;" CMAKE_PROPERTY_LIST "${CMAKE_PROPERTY_LIST}")
string(REGEX REPLACE "\n" ";" CMAKE_PROPERTY_LIST "${CMAKE_PROPERTY_LIST}")
# Populate "<CONFIG>" with AVAILBLE_CONFIG_TYPES
set(CONFIG_LINES ${CMAKE_PROPERTY_LIST})
list(FILTER CONFIG_LINES INCLUDE REGEX "<CONFIG>")
list(FILTER CMAKE_PROPERTY_LIST EXCLUDE REGEX "<CONFIG>")
foreach(CONFIG_LINE IN LISTS CONFIG_LINES)
foreach(CONFIG_VALUE IN LISTS AVAILABLE_CONFIGURATION_TYPES)
string(REPLACE "<CONFIG>" "${CONFIG_VALUE}" FIXED "${CONFIG_LINE}")
list(APPEND CMAKE_PROPERTY_LIST ${FIXED})
endforeach()
endforeach()
# Populate "<LANG>" with LANGS
set(LANG_LINES ${CMAKE_PROPERTY_LIST})
list(FILTER LANG_LINES INCLUDE REGEX "<LANG>")
list(FILTER CMAKE_PROPERTY_LIST EXCLUDE REGEX "<LANG>")
foreach(LANG_LINE IN LISTS LANG_LINES)
foreach(LANG IN LISTS LANGS)
string(REPLACE "<LANG>" "${LANG}" FIXED "${LANG_LINE}")
list(APPEND CMAKE_PROPERTY_LIST ${FIXED})
endforeach()
endforeach()
# no repeats
list(REMOVE_DUPLICATES CMAKE_PROPERTY_LIST)
# Fix https://stackoverflow.com/questions/32197663/how-can-i-remove-the-the-location-property-may-not-be-read-from-target-error-i
list(FILTER CMAKE_PROPERTY_LIST EXCLUDE REGEX "^LOCATION$|^LOCATION_|_LOCATION$")
list(SORT CMAKE_PROPERTY_LIST)
# Whitelisted property list for use with interface libraries to reduce warnings
set(CMAKE_WHITELISTED_PROPERTY_LIST ${CMAKE_PROPERTY_LIST})
# regex from https://stackoverflow.com/a/51987470/240845
list(FILTER CMAKE_WHITELISTED_PROPERTY_LIST INCLUDE REGEX "^(INTERFACE|[_a-z]|IMPORTED_LIBNAME_|MAP_IMPORTED_CONFIG_)|^(COMPATIBLE_INTERFACE_(BOOL|NUMBER_MAX|NUMBER_MIN|STRING)|EXPORT_NAME|IMPORTED(_GLOBAL|_CONFIGURATIONS|_LIBNAME)?|NAME|TYPE|NO_SYSTEM_FROM_IMPORTED)$")
# make the lists available
set(CMAKE_PROPERTY_LIST ${CMAKE_PROPERTY_LIST} PARENT_SCOPE)
set(CMAKE_WHITELISTED_PROPERTY_LIST ${CMAKE_WHITELISTED_PROPERTY_LIST} PARENT_SCOPE)
endfunction()
get_cmake_property_list()
function(print_target_properties tgt)
if(NOT TARGET ${tgt})
message("There is no target named '${tgt}'")
return()
endif()
get_target_property(target_type ${tgt} TYPE)
if(target_type STREQUAL "INTERFACE_LIBRARY")
set(PROPERTIES ${CMAKE_WHITELISTED_PROPERTY_LIST})
else()
set(PROPERTIES ${CMAKE_PROPERTY_LIST})
endif()
foreach (prop ${PROPERTIES})
#message ("Checking ${prop}")
get_property(propval TARGET ${tgt} PROPERTY ${prop} SET)
if (propval)
get_target_property(propval ${tgt} ${prop})
message ("${tgt} ${prop} = ${propval}")
endif()
endforeach(prop)
endfunction(print_target_properties)

cmake generator expression for runtime output directory

Is there a generator expression for CMAKE_RUNTIME_OUTPUT_DIRECTORY?
I want to copy a directory to the runtime output directory, which I currently do like this:
add_custom_target(copy_target ALL)
add_custom_command(TARGET copy_target POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_CURRENT_SOURCE_DIR}/foo
$<TARGET_FILE_DIR:other_target>/foo)
Just copying to CMAKE_RUNTIME_OUTPUT_DIRECTORY will not work for multi-config generators (Visual Studio, XCode) that create subdirectories for each configuration.
The example above works but it creates an unnecesary dependency of copy_target on other_target. I would prefer something like ${CMAKE_RUNTIME_OUTPUT_DIRECTORY_$<CONFIG>} but this was not accepted (unexpected <). Also, the solution should work with single-config generators as well, which ${CMAKE_RUNTIME_OUTPUT_DIRECTORY_$<CONFIG>} probably wouldn't.
Also $<TARGET_FILE_DIR:copy_target> does not work, because copy_target is a dummy, not a binary.
The variable CMAKE_CFG_INTDIR may be helpful, e.g.:
add_custom_command(TARGET copy_target POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_CURRENT_SOURCE_DIR}/foo
${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_CFG_INTDIR}/foo)
I see that sakra answered your question, and I do not have a better answer to your specific question. But I do have a suggestion for simplifying working with both multi-configuration builds (for Visual Studio) and single-configuration builds (for Linux makefiles). I like to collapse the output directories to a flat structure with all configuration artifacts having unique postfixes so they do not collide. I do this with the following init_output_directories macro:
# initialize the variables defining output directories
#
# Sets the following variables:
#
# - :cmake:data:`CMAKE_ARCHIVE_OUTPUT_DIRECTORY`
# - :cmake:data:`CMAKE_LIBRARY_OUTPUT_DIRECTORY`
# - :cmake:data:`CMAKE_RUNTIME_OUTPUT_DIRECTORY`
#
# plus the per-config variants, ``*_$<CONFIG>``
#
# #public
#
macro(init_output_directories)
# Directory for output files
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib
CACHE PATH "Output directory for static libraries.")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib
CACHE PATH "Output directory for shared libraries.")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
CACHE PATH "Output directory for executables and DLL's.")
foreach( OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES} )
string( TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG )
set( CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG} "${CMAKE_BINARY_DIR}/bin" CACHE PATH "" FORCE)
set( CMAKE_LIBRARY_OUTPUT_DIRECTORY_${OUTPUTCONFIG} "${CMAKE_BINARY_DIR}/lib" CACHE PATH "" FORCE)
set( CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${OUTPUTCONFIG} "${CMAKE_BINARY_DIR}/lib" CACHE PATH "" FORCE)
endforeach()
endmacro()

How to check if find_package found the package (boost)

I want to not add boost.cxx if cmake find_package found no boost installed. Does find_package return something that I can wrap in condition to compile boost.cxx or not. Here is my current cmake file:
add_executable (complex complex.cxx lexer.cxx boost.cxx ../../src/lili.cxx ../../src/lilu.cxx)
# Make sure the compiler can find all include files
include_directories (../../src)
include_directories (.)
# Make sure the linker can find all needed libraries
# rt: clock_gettime()
target_link_libraries(complex rt)
# Install example application
install (TARGETS complex
RUNTIME DESTINATION bin)
IF(UNIX)
find_package(Boost COMPONENTS system filesystem REQUIRED)
## Compiler flags
if(CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS "-O2")
set(CMAKE_EXE_LINKER_FLAGS "-lsqlite3 -lrt -lpthread")
endif()
target_link_libraries(complex
${Boost_FILESYSTEM_LIBRARY}
${Boost_SYSTEM_LIBRARY}
#${PROTOBUF_LIBRARY}
)
ENDIF(UNIX)
The FindXXX scripts are supposed to set a variable <Packagename>_FOUND to TRUEif the package was found. So in your case, it will set Boost_FOUND if boost was found.
When compiling your Boost.cxx, I assume that you will need Boost headers as well, so you should adjust your include directories as well.*
look for Boost before creating your executable. Furhtermore, you need to set your include directories before adding the executable.
IF(UNIX)
find_package(Boost COMPONENTS system filesystem REQUIRED)
# IF( Boost_FOUND ) # checking this variable isnt even necessary, since you added
# REQUIRED to your call to FIND_PACKAGE
SET( BOOST_SRC_FILES boost.cxx )
INCLUDE_DIRECTORIES( ${Boost_INCLUDE_DIRS} ) # you could move this down as well
# as ${Boost_INCLUDE_DIRS} will be
# empty if Boost was not found
# ENDIF()
ENDIF()
add_executable (complex complex.cxx lexer.cxx ${BOOST_SRC_FILES} ../../src/lili.cxx ../../src/lilu.cxx)
# Make sure the compiler can find all include files
include_directories (../../src)
include_directories (.)
# INCLUDE_DIRECTORIES( ${Boost_INCLUDE_DIRS} ) # alternative location to
# add include dirs, see above
# Make sure the linker can find all needed libraries
# rt: clock_gettime()
target_link_libraries(complex rt)
# Install example application
install (TARGETS complex
RUNTIME DESTINATION bin)
IF(UNIX)
## Compiler flags
if(CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS "-O2")
set(CMAKE_EXE_LINKER_FLAGS "-lsqlite3 -lrt -lpthread")
endif()
target_link_libraries(complex
${Boost_FILESYSTEM_LIBRARY}
${Boost_SYSTEM_LIBRARY}
#${PROTOBUF_LIBRARY}
)
ENDIF(UNIX)
Afternote: Since you use the REQUIRED flag when looking for Boost (since you only need it on Unix platform) it is even sufficient to use the optional-source-files-in-a-variable trick.
(*) Thanks to your question, I just found out that it doesn't matter whether include_directories(...) is called before or after creating the target with ADD_EXECUTABLE or ADD_LIBRARY since the directories are added to all targets in the same project.
Yes, if the find_package(Boost COMPONENTS system filesystem REQUIRED) succeeds, Boost_FOUND will be true.
Also, there will be component-specific versions, so Boost_date_time_FOUND, Boost_filesystem_FOUND, etc.
For further info, run
cmake --help-module FindBoost
Yes, it sets variable Boost_FOUND. Example from FindBoost.cmake:
== Using actual libraries from within Boost: ==
#
# set(Boost_USE_STATIC_LIBS ON)
# set(Boost_USE_MULTITHREADED ON)
# set(Boost_USE_STATIC_RUNTIME OFF)
# find_package( Boost 1.36.0 COMPONENTS date_time filesystem system ... )
#
# if(Boost_FOUND)
# include_directories(${Boost_INCLUDE_DIRS})
# add_executable(foo foo.cc)
# target_link_libraries(foo ${Boost_LIBRARIES})
# endif()

How to make temp directory in CMake?

I need the CMake analog of mktemp command in linux. What macro provides this?
Was looking for this too to evaluate expressions as suggested in the CMake Wiki. Wrote some macros and an example for generating temp file names and executing them:
#!/usr/bin/cmake -P
macro(temp_name fname)
if(${ARGC} GREATER 1) # Have to escape ARGC to correctly compare
set(_base ${ARGV1})
else(${ARGC} GREATER 1)
set(_base ".cmake-tmp")
endif(${ARGC} GREATER 1)
set(_counter 0)
while(EXISTS "${_base}${_counter}")
math(EXPR _counter "${_counter} + 1")
endwhile(EXISTS "${_base}${_counter}")
set(${fname} "${_base}${_counter}")
endmacro(temp_name)
# Evaluate expression
# Suggestion from the Wiki: http://cmake.org/Wiki/CMake/Language_Syntax
# Unfortunately, no built-in stuff for this: http://public.kitware.com/Bug/view.php?id=4034
macro(eval expr)
temp_name(_fname)
file(WRITE ${_fname} "${expr}")
include(${_fname})
file(REMOVE ${_fname})
endmacro(eval)
# Examples
eval("message(\"Hai\")")
set(funcs a;b)
macro(test_a arg)
message("A: ${arg}")
endmacro(test_a)
macro(test_b arg)
message("B: ${arg}")
endmacro(test_b)
foreach(func ${funcs})
set(func_name test_${func})
eval("${func_name}(\"Test\")")
endforeach(func)
Output:
Hai
A: Test
B: Test
Note that in Linux you can set this script to executable and run it using cmake -P. Useful for testing stuff out.
There is no direct CMake analog of "mktemp".
From inside a CMake script or CMakeLists.txt file, your best bet is to use the
file(MAKE_DIRECTORY "/path/to/dir/name")
command, and give it a name of a directory that you know you have write access to. Help for the file command is found here: https://cmake.org/cmake/help/latest/command/file.html
You could also possibly simply use
$ENV{TMP}
if there is an environment variable that points you to a system-provided temp directory.
If you are invoking CMake directly, you could also use
cmake -E make_directory /path/to/dir/name
Finally, see also the execute_process command, which allows you to call arbitrary command line tools from within a cmake script or CMakeLists file and capture the output. That may prove useful if you have another tool that you can call that gives you mktemp functionality. https://cmake.org/cmake/help/latest/command/execute_process.html
I implemented the following macro:
#!/usr/bin/cmake -P
include(CMakeParseArguments)
function(MKTEMP)
set(options CREATE_FOLDER CREATE_FILE)
set(oneValueArgs PREFIX PARENT OUTPUT_VARIABLE)
cmake_parse_arguments(MKTEMP "${options}" "${oneValueArgs}" "" ${ARGN})
if(NOT DEFINED MKTEMP_CREATE_FOLDER)
set(MKTEMP_CREATE_FOLDER FALSE)
endif()
if(NOT DEFINED MKTEMP_CREATE_FILE)
set(MKTEMP_CREATE_FILE FALSE)
endif()
if(MKTEMP_CREATE_FOLDER AND MKTEMP_CREATE_FILE)
# Can not create folder and file with the same name
message(FATAL_ERROR "Both flags CREATE_FOLDER and CREATE_FILE are set")
endif()
if(NOT DEFINED MKTEMP_PREFIX)
set(MKTEMP_PREFIX "tmp")
endif()
if(NOT DEFINED MKTEMP_PARENT)
set(MKTEMP_PARENT "$ENV{TMP}")
endif()
set(_COUNTER 0)
while(EXISTS "${MKTEMP_PARENT}/${MKTEMP_PREFIX}${_COUNTER}")
math(EXPR _COUNTER "${_COUNTER} + 1")
endwhile()
set(_NAME "${MKTEMP_PARENT}/${MKTEMP_PREFIX}${_COUNTER}")
set(${MKTEMP_OUTPUT_VARIABLE} "${_NAME}" PARENT_SCOPE)
if(MKTEMP_CREATE_FOLDER)
file(MAKE_DIRECTORY "${_NAME}")
elseif(MKTEMP_CREATE_FILE)
file(WRITE "${_NAME}" "")
endif()
endfunction()
Usage:
# only generate name - with default prefix ("tmp")
MKTEMP(OUTPUT_VARIABLE TMPONLYNAME)
message("TMPONLYNAME is ${TMPONLYNAME}")
# only generate name - with custom prefix ("myapp")
MKTEMP(PREFIX "myapp" OUTPUT_VARIABLE TMPONLYNAME)
message("TMPONLYNAME is ${TMPONLYNAME}")
# only generate name - use current folder as temp
MKTEMP(PARENT "." OUTPUT_VARIABLE TMPONLYNAME)
message("TMPONLYNAME is ${TMPONLYNAME}")
# create file
MKTEMP(PREFIX "myapp" OUTPUT_VARIABLE TMPFILE CREATE_FILE)
message("TMPFILE is ${TMPFILE}")
# ... work with file ...
file(REMOVE "${TMPFILE}")
# create folder
MKTEMP(PREFIX "myapp" OUTPUT_VARIABLE TMPFOLDER CREATE_FOLDER)
message("TMPFOLDER is ${TMPFOLDER}")
# ... work with folder ...
file(REMOVE_RECURSE "${TMPFOLDER}")
Example of output on my Windows environment ("myapp7" the same because of deletion):
TMPONLYNAME is C:\Users\msuslov\AppData\Local\Temp\tmp1
TMPONLYNAME is C:\Users\msuslov\AppData\Local\Temp\myapp7
TMPONLYNAME is .\tmp0
TMPFILE is C:\Users\msuslov\AppData\Local\Temp\myapp7
TMPFOLDER is C:\Users\msuslov\AppData\Local\Temp\myapp7