Compute real number in CMake - cmake

I am using CMake to configure some scripts required to build my project using configure_file.
Some of these scripts do not have any math functionality, so I need to compute derived values in my CMake script using the math macro, e.g.:
math(EXPR EXAMPLE_BLOCK_SIZE "${EXAMPLE_SIZE} / ${EXAMPLE_BLOCK_COUNT}")
However, it seems that math does not support floating point arithmetic. If I set -DEXAMPLE_SIZE=2.5 and -DEXAMPLE_BLOCK_COUNT=2 CMake throws an error:
math cannot parse the expression: "2.5 * 2": syntax error, unexpected
exp_NUMBER, expecting $end (2)
Is there any way to compute a real number directly using CMake macros?
If not, what would be a portable way to achieve this?

First of all, floating-point math is not supported on CMake as far as I know. It is possible to compute variables, however, using shell commands. The following CMake function should do the trick:
function(floatexpr expr output)
execute_process(COMMAND awk "BEGIN {print ${expr}}" OUTPUT_VARIABLE __output)
set(${output} ${__output} PARENT_SCOPE)
endfunction()
Example usage:
set(A 2.0)
set(B 3.0)
floatexpr("${A} / ${B}" RESULT)
message(${RESULT})
NOTE: More advanced computations (such as trigonometric functions) can be computed using the command bc -l <<< 'EXPRESSION', but it's not part of the standard system packages and may not be installed.
EDIT: A completely portable (albeit much slower) solution would be to use the try_run functionality to compile a C file that runs the expression:
function(floatexpr expr output)
SET(FLOAT_EXPR_SRC "
#include <stdio.h>
int main(int argc, char *argv[]){printf(\"%f\\n\", ${expr})\; return 0\;}
")
FILE(WRITE ${CMAKE_CURRENT_BINARY_DIR}/CMakeFloatExpr.c ${FLOAT_EXPR_SRC})
try_run(RESULT COMPRES ${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_BINARY_DIR}/CMakeFloatExpr.c
RUN_OUTPUT_VARIABLE __output)
set(${output} ${__output} PARENT_SCOPE)
endfunction()

Related

Use value from C/C++ macro in CMake

What is the easiest way to get the value of a C/C++ macro into a CMake variable?
Given I check for a library libfoo with the header foo.h. I know foo.h contains the macro #define FOO_VERSION_MAJOR <version> where version is an integer or string value. To extract the major version of the found library, I want to use the value from this macro.
As a bonus, if the macro is not found, this could indicate a version older then a specific version introducing the version macro.
I'd go with file(READ ...) to read the header followed by string(REGEX ...) to extract desired define.
Example code:
file(READ "foo.h" header)
string(REGEX MATCH "#define FOO_MAJOR_VERSION [0-9]+" macrodef "${header}")
string(REGEX MATCH "[0-9]+" FooMajorVersion "${macrodef}")
With try_compile and the right pragma it is possible to output the value of a pre-processor macro during compile time. CMake can parse the output to get the desired value.
CMake snippet:
try_compile(result "${CMAKE_BINARY_DIR}"
SOURCES "${CMAKE_SOURCE_DIR}/foo-version.cpp"
OUTPUT_VARIABLE fooversion)
string(REGEX MATCH ": [0-9]+" fooversionshort "${fooversion}")
string(REGEX MATCH "[0-9]+" FooMajorVersion "${fooversionshort}")
foo-version.cpp:
#include "foo.h"
/* definition to expand macro then apply to pragma message */
#define VALUE_TO_STRING(x) #x
#define VALUE(x) VALUE_TO_STRING(x)
#pragma message(VALUE(FOO_MAJOR_VERSION))
int main()
{
return 0;
}
Good:
Actual value from the variable, which might be calculated.
Bad:
Output of macros is only support by some newer compilers.
Parsing of output might break for untested compilers, as the format changes from compiler version to compiler version.
Kind of complicated code, verbose code which is difficult to read.
The macro expansion can be extracted by using the C preprocessor.
I used this method to extract specific typedef's without needing to know the exact location of the define in the file hierarchy.
Let say that we have this macro defined somewhere in foo.h
#define FOO_VERSION_MAJOR 666
You need to create a helper file helper.c with this content
#include "foo.h"
int __READ_FOO_MAJOR__ = FOO_VERSION_MAJOR ;
Note that I used a specific pattern __READ_FOO_MAJOR__ that I will use later as the pattern for a grep command
And from CMakeLists.txt you have to call the C (C++, etc..) preprocessor and filter its output like this
cmake_minimum_required(VERSION 3.0)
execute_process(
COMMAND bash "-c" "${CMAKE_C_COMPILER} -E ${CMAKE_CURRENT_SOURCE_DIR}/helper.cpp | grep __READ_FOO_MAJOR__ | awk '{ print $4}'"
OUTPUT_VARIABLE FOO_VERSION_MAJOR )
message("From CMake: FOO_VERSION_MAJOR=${FOO_VERSION_MAJOR}")
Note that awk '{ print $4}' extract the 4th word on the selected line.
When running cmake we get this result
From CMake: FOO_VERSION_MAJOR=666
The short shel pipeline used is built with Unix system V base commands and should run everywhere.

Dealing with lots and lots of escaped characters in add_custom_command

I have a file that contains a bunch of data. I want to turn it into a C++ string literal, because I need to compile this data into the binary - I cannot read it from disk.
One way of doing this is to just generate a C++ source file that declares a string literal with a known name. The CMake code to do this is straightforward, if somewhat awful:
function(make_literal_from_file dest_file source_file literal_name)
add_custom_command(
OUTPUT ${dest_file}
COMMAND printf \'char const* ${literal_name} = R\"\#\(\' > ${dest_file}
COMMAND cat ${source_file} >> ${dest_file}
COMMAND printf \'\)\#\"\;\' >> ${dest_file}
DEPENDS ${source_file})
endfunction()
This works and does what I want (printf is necessary to avoid a new line after the raw string introducer). However, the amount of escaping going on here makes it very difficult to see what's going on. Is there a way to write this function such that it's actually readable?
Note that I cannot use a file(READ ...)/configure_file(...) combo here because source_file could be something that is generated by CMake at build time and so may not be present at configuration time.
I would recommend writing a script to do this. You could write it in CMake, but I personally prefer a better language such as Python:
# Untested, just to show roughly how to do it
import sys
dest_file, source_file, literal_name = sys.argv[1:]
with open(dest_file) as dest, open(source_file) as source:
literal_contents = source.read()
dest.write(f'char const* {literal_name} = R"({literal_contents})";\n')
Corresponding CMake code:
# String interpolation came in Python 3.6, thus the requirement on 3.6.
# If using CMake < 3.12, use find_package(PythonInterp) instead.
find_package(Python3 3.6 COMPONENTS Interpreter)
# Make sure this resolves correctly. ${CMAKE_CURRENT_LIST_DIR} is helpful;
# it's the directory containing the current file (this cmake file)
set(make_literal_from_file_script "path/to/make_literal_from_file.py")
function(make_literal_from_file dest_file source_file literal_name)
add_custom_command(
OUTPUT "${dest_file}"
COMMAND
"${Python3_EXECUTABLE}" "${make_literal_from_file_script}"
"${dest_file}"
"${source_file}"
"${literal_name}"
DEPENDS "${source_file}")
endfunction()
If you don't want the dependency on Python, you could use C++ (only the CMake code shown):
add_executable(make_literal_from_file_exe
path/to/cpp/file.cpp
)
function(make_literal_from_file dest_file source_file literal_name)
add_custom_command(
OUTPUT "${dest_file}"
COMMAND
make_literal_from_file_exe
"${dest_file}"
"${source_file}"
"${literal_name}"
DEPENDS "${source_file}")
endfunction()

Get full C++ compiler command line

In CMake, the flags for the C++ compiler can be influenced in various ways: setting CMAKE_CXX_FLAGS manually, using add_definitions(), forcing a certain C++ standard, and so forth.
In order to compile a target in the same project with different rules (a precompiled header, in my case), I need to reproduce the exact command that is used to compile files added by a command like add_executable() in this directory.
Reading CMAKE_CXX_FLAGS only returns the value set to it explicitly, CMAKE_CXX_FLAGS_DEBUG and siblings only list default Debug/Release options. There is a special functions to retrieve the flags from add_definitions() and add_compiler_options(), but none seem to be able to return the final command line.
How can I get all flags passed to the compiler into a CMake variable?
To answer my own question: It seems like the only way of getting all compiler flags is to reconstruct them from the various sources. The code I'm working with now is the following (for GCC):
macro (GET_COMPILER_FLAGS TARGET VAR)
if (CMAKE_COMPILER_IS_GNUCXX)
set(COMPILER_FLAGS "")
# Get flags form add_definitions, re-escape quotes
get_target_property(TARGET_DEFS ${TARGET} COMPILE_DEFINITIONS)
get_directory_property(DIRECTORY_DEFS COMPILE_DEFINITIONS)
foreach (DEF ${TARGET_DEFS} ${DIRECTORY_DEFS})
if (DEF)
string(REPLACE "\"" "\\\"" DEF "${DEF}")
list(APPEND COMPILER_FLAGS "-D${DEF}")
endif ()
endforeach ()
# Get flags form include_directories()
get_target_property(TARGET_INCLUDEDIRS ${TARGET} INCLUDE_DIRECTORIES)
foreach (DIR ${TARGET_INCLUDEDIRS})
if (DIR)
list(APPEND COMPILER_FLAGS "-I${DIR}")
endif ()
endforeach ()
# Get build-type specific flags
string(TOUPPER ${CMAKE_BUILD_TYPE} BUILD_TYPE_SUFFIX)
separate_arguments(GLOBAL_FLAGS UNIX_COMMAND
"${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_${BUILD_TYPE_SUFFIX}}")
list(APPEND COMPILER_FLAGS ${GLOBAL_FLAGS})
# Add -std= flag if appropriate
get_target_property(STANDARD ${TARGET} CXX_STANDARD)
if ((NOT "${STANDARD}" STREQUAL NOTFOUND) AND (NOT "${STANDARD}" STREQUAL ""))
list(APPEND COMPILER_FLAGS "-std=gnu++${STANDARD}")
endif ()
endif ()
set(${VAR} "${COMPILER_FLAGS}")
endmacro ()
This could be extended to also include options induced by add_compiler_options() and more.
Easiest way is to use make VERBOSE=1 when compiling.
cd my-build-dir
cmake path-to-my-sources
make VERBOSE=1
This will do a single-threaded build, and make will print every shell command it runs just before it runs it. So you'll see output like:
[ 0%] Building CXX object Whatever.cpp.o
<huge scary build command it used to build Whatever.cpp>
There actually is a fairly clean way to do this at compile time using CXX_COMPILER_LAUNCHER:
If you have a script print_args.py
#!/usr/bin/env python
import sys
import argparse
print(" ".join(sys.argv[1:]))
# we need to produce an output file so that the link step does not fail
p = argparse.ArgumentParser()
p.add_argument("-o")
args, _ = p.parse_known_args()
with open(args.o, "w") as f:
f.write("")
You can set the target's properties as follows:
add_library(${TARGET_NAME} ${SOURCES})
set_target_properties(${TARGET_NAME} PROPERTIES
CXX_COMPILER_LAUNCHER
${CMAKE_CURRENT_SOURCE_DIR}/print_args.py
)
# this tells the linker to not actually link. Which would fail because output file is empty
set_target_properties(${TARGET_NAME} PROPERTIES
LINK_FLAGS
-E
)
This will print the exact compilation command at compile time.
Short answer
It's not possible to assign final value of compiler command line to variable in CMake script, working in all use cases.
Long answer
Unfortunately, even solution accepted as answer still not gets all compiler flags. As gets noted in comments, there are Transitive Usage Requirements. It's a modern and proper way to write CMake files, getting more and more popular. Also you may have some compile options defined using generator expressions (they look like variable references but will not expand when needed).
Consider having following example:
add_executable(myexe ...);
target_compile_definitions(myexe PRIVATE "PLATFORM_$<PLATFORM_ID>");
add_library(mylib ...);
target_compile_definitions(mylib INTERFACE USING_MY_LIB);
target_link_libraries(myexe PUBLIC mylib);
If you try to call proposed GET_COMPILER_FLAGS macro with myexe target, you will get resulting output -DPLATFORM_$<PLATFORM_ID> instead of expected -DPLATFORM_Linux -DUSING_MY_LIB.
This is because there are two stages between invoking CMake and getting build system generated:
Processing. At this stage CMake reads and executes commands from cmake script(s), particularly, variable values getting evaluated and assigned. At this moment CMake just collecting all required info and being prepared to generate build system (makefiles).
Generating. CMake uses values of special variables and properties, being left at end of processed scripts to finally decide and form generated output. This is where it constructs final command line for compiler according to its internal algorithm, not avaliable for scripting.
Target properties which might be retrieved at processing stage with get_target_property(...) or get_property(... TARGET ...) aren't complete (even when invoked at the end of script). At generating stage CMake walks through each target dependency tree (recursively) and appends properties values according to transitive usage requirements (PUBLIC and INTERFACE tagged values gets propagated).
Although, there are workarounds, depending on what final result you aiming to achieve. This is possible by applying generator expressions, which allows use final values of properties of any target (defined at processing stage)... but later!
Two general possibilites are avaliable:
Generate any output file based on template, which content contains variable references and/or generator expressions, and defined as either string variable value, or input file. It's not flexible due to very limited support of conditional logic (i.e. you cannot use complex concatenations available only with nested foreach() loops), but has advantages, that no further actions required and content described in platform-independent way. Use file(GENERATE ...) command variant. Note, that it behaves differently from file (WRITE ...) variant.
Add custom target (and/or custom command) which implements further usage of expanded value. It's platform dependent and requires user to additionally invoke make (either with some special target, or include to all target), but has advantage, that it's flexible enough because you may implement shell script (but without executable bit).
Example demonstrating solution with combining these options:
set(target_name "myexe")
file(GENERATE OUTPUT script.sh CONTENT "#!/bin/sh\n echo \"${target_name} compile definitions: $<TARGET_PROPERTY:${target_name},COMPILE_DEFINITIONS>\"")
add_custom_target(mycustomtarget
COMMAND echo "\"Platform: $<PLATFORM_ID>\""
COMMAND /bin/sh -s < script.sh
)
After calling CMake build directory will contain file script.sh and invoking make mycustomtarget will print to console:
Platform: Linux
myexe compile definitions: PLATFORM_Linux USING_MY_LIB
Use
set(CMAKE_EXPORT_COMPILE_COMMANDS true)
and get compile_commands.json

Using value of __GLIBCXX__ in cmake

There are plenty of examples of using cmake to set a preprocessor value. I'm having the reverse problem -- I want to find the value of __GLIBCXX__ and then perform other cmake commands conditionally based on the result.
Up until now, I had been using the GCC version as a surrogate for libstdc++ functionality, like this:
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.6)
....
# other gcc versions
....
endif()
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
# and so on
endif()
The problem I'm now having is the fallout from a known issue with libstdc++ and gcc-4.8 c++11 regex support, and the fact on many setups clang reuses the system libstdc++, therefore inheriting the same problem. Under these circumstances, there's no version test for clang that will help, since it's specifically related to libstdc++, and my surrogate method of using the compiler version no longer works.
In order to fallback on Boost.Regex or PCRE if either clang or gcc are using the libstdc++ distributed with gcc-4.8 or earlier, the best way I can think of is to check if __GLIBCXX__ <= 20140404, but I can't see how to get cmake to do it in a straight-forward way, since clang might not always be using libstdc++, e.g. most OS X systems.
CheckVariableExists doesn't seem to help, I suppose for at least two reasons; firstly, a preprocessor macro isn't a variable, and secondly, it doesn't give the value, only indicates its presence.
You could use CHECK_CXX_SOURCE_COMPILES to compile a specific test which fails when your condition is not met:
INCLUDE (CheckCXXSourceCompiles)
CHECK_CXX_SOURCE_COMPILES(
"
#include <cstdio>
#ifdef __GLIBCXX__
#if __GLIBCXX__ <= 20140404
#error test failed
#endif
#endif
int main() { return 0;}
" GLIBCXX_TEST)
IF(NOT GLIBCXX_TEST)
MESSAGE(STATUS "__GLIBCXX__ test failed")
ENDIF()
Based on m.s.'s idea and taking Marc Glisse's observation about __GLIBCXX__ not being a reliable way to test for this, I wrote a CMake module to test for broken implementations of regex support. In order for this test to pass, the compiler will need to be targetting C++11 or higher.
Gist is here: https://gist.github.com/anonymous/7520ce6f64c63e2f8e79
Sample use:
include(CheckForRegex)
check_cxx_native_regex_works(USE_NATIVE_REGEX)
add_definitions("-DUSE_NATIVE_REGEX=${USE_NATIVE_REGEX}")
if (NOT USE_NATIVE_REGEX)
find_package(Boost REQUIRED COMPONENTS regex)
endif()

CMake generator expression is not evaluated

I cannot understand what I'm doing wrong.
I'm always getting the string "$<TARGET_FILE:tgt1>" instead of the path to the library.
I've created the dummy project.
Here is my root CMakeLists.txt
cmake_minimum_required (VERSION 3.0) # also tried 2.8 with the same result
set(PROJECT_NAME CMP0026)
add_subdirectory(src)
set(TGT_PATH $<TARGET_FILE:tgt1>)
message(STATUS "${TGT_PATH}")
Here is my src/CMakeLists.txt
add_library(tgt1 a.c)
File a.c is created and is empty
I've tried the following generators: Visual Studio 2013 Win64, Ninja and MingW Makefile. I've used Android toolchain for the last two, downloaded from here
I expect that the last message(STATUS command would print full path to the library. However, all variants print the string $<TARGET_FILE:tgt1>.
Generator expressions are not evaluated at configure time (when CMake is parsing CMakeLists, executing commands like add_target() or message() etc.). At this time, a generator expression is just a literal string - the character $ followed by <, then T, then ...
Evaluation of generator expressions happens at generate time (that's why they are called "generator expressions"). Generate time occurs after all CMake code is parsed and processed, and CMake is starting to act on the data therein to produce buildsystem files. Only then does it have all the information necessary to evaluate generator expressions.
So you can only really use generator expressions for things which occur at generate time or later (such as build time). A contrived example would be this:
add_custom_target(
GenexDemo
COMMAND ${CMAKE_COMMAND} -E echo "$<TARGET_FILE:tgt1>"
VERBATIM
)
At configure time, CMake will record the literal string $<TARGET_FILE:tgt1> as the argument of COMMAND. Then at generate time (when the location of tgt1 is known for each configuration and guaranteed not to change any more), it will substitute it for the generator expression.