Expanding a variable, #cmakedefine, and generator expression in template file - cmake

I have the following template
config.c.in
#define QPID_DISPATCH_VERSION "${QPID_DISPATCH_VERSION}"
#define QPID_DISPATCH_LIB "$<TARGET_FILE_NAME:qpid-dispatch>"
#cmakedefine01 USE_MEMORY_POOL
And I wish to obtain the following, by expanding the variable, #cmakedefine, and generator expression.
config.c
#define QPID_DISPATCH_VERSION "1.6.0-SNAPSHOT"
#define QPID_DISPATCH_LIB "libqpid-dispatch.so"
#define USE_MEMORY_POOL 1
The problem is, there does not seem to be a CMake function which can expand all three.
What I can do is
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h.tmp)
file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/config.h INPUT ${CMAKE_CURRENT_BINARY_DIR}/config.h.tmp)
Is there a way to accomplish this without creating a temporary file? Cleaning up the file is tricky, because file GENERATE does not run immediately.

Based on #Tsyvarev's comment, I now have
file(READ "${CMAKE_CURRENT_SOURCE_DIR}/config.h.in" CONFIG_H_IN)
string(CONFIGURE "${CONFIG_H_IN}" CONFIG_H_TMP)
file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/config.h" CONTENT "${CONFIG_H_TMP}")

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.

What is the #cmakedefine preprocessor directive?

I am looking through the source-code for ZeroMQ, which I want to build from source. Inside I found platform.hpp.in, which contains:
...
#cmakedefine ZMQ_HAVE_SO_PEERCRED
#cmakedefine ZMQ_HAVE_LOCAL_PEERCRED
#cmakedefine ZMQ_HAVE_SOCK_CLOEXEC
#cmakedefine ZMQ_HAVE_SO_KEEPALIVE
#cmakedefine ZMQ_HAVE_TCP_KEEPCNT
...
I assume these cmakedefine macros are used as templates to generate a header file, but how exactly do they work in CMake? How can I determine what are valid values? How are values set by the user when building the project?
It's part of a file that's processed by CMake's configure_file command. When configure_file is called for the file, #cmakedefine FOO is replaced by:
#define FOO - if the CMake variable FOO is set to ON or TRUE.
/* #undef FOO */ - otherwise.
And that is one way to pass values from CMake into C or C++ source code: The result of configure_file() is a C/C++ header file, which is included by the code you want to respect the CMake variable values.

CMake configure_file replace ON/OFF with 1/0

Suppose I have two libraries ABC, XYZ and two variables ABC_FOUND XYZ_FOUND in CMakeLists.txt. In a config.h.in file, I have the following lines
#define __USE_ABC__ #ABC_FOUND#
#define __USE_XYZ__ #XYZ_FOUND#
If I do configure_file(config.h.in config.h), the output config.h file will be changed to
#define __USE_ABC__ ON
#define __USE_XYZ__ OFF
My question is how can I convert the ON/OFF options into 1/0, in other word, my desired output config.h should be
#define __USE_ABC__ 1
#define __USE_XYZ__ 0
Define ABC_FOUND to either 0 or 1 with
#cmakedefine01 #ABC_FOUND#
Use an intermediate variable in either environment to change it to USE_ABC.
Likely, you should use #cmakedefine instead of hard defines (see examples e.g. here)
But please remember that in the case of #cmakedefine you get a configuration header where "enabled features" (in terms of CMake if() command) are #define-d to a supplied value, while "disabled features" are #undef-ined. So you should test them not with #if <something> but rather with #ifdef <something>.

How to create a C #define for a certain target using CMake?

I feel a little stupid right now. After recently converting a few smaller projects to use CMake, I decided to also get rid of a few "Platform_Config.h" files. These files contain a few preprocessing directives like #define USE_NEW_CACHE and control compilation.
How would I 'convert' these defines to be controlled with CMake? Ideally by using these "cache" variables the user can easily edit.
There are two options. You can use the add_definitions method to pass defines as compiler flags: E.g. somewhere in your projects cmakelists.txt:
add_definitions( -DUSE_NEW_CACHE )
CMake will make sure the -D prefix is converted to the right flag for your compiler (/D for msvc and -D for gcc).
Alternatively, check out configure_file. It is more complex, but may be better suited to your original approach with a Platform_Config file.
You can create an input-file, similar to your original Platform_Config.h and add "#cmakedefine" lines to it.
Let's call in Platform_Config.h.in:
// In Platform_Config.h.in
#cmakedefine USE_NEW_CACHE
// end of Platform_Config.h.in
When then running
configure_file( ${CMAKE_SOURCE_DIR}/Platform_Config.h.in ${CMAKE_BINARY_DIR}/common/Platform_Config.h )
it will generate a new Platform_Config file in your build-dir. Those variables in cmake which are also a cmakedefine will be present in the generated file, the other ones will be commented out or undefed.
Of course, you should make sure the actual, generated file is then correctly found when including it in your source files.
option command might provide what you are looking for.
use it with the COMPILE DEFINITIONS property on the target and i think you are done.
To set the property on the target, use the command set target properties
option(DEBUGPRINTS "Prints a lot of debug prints")
target(myProgram ...)
if(DEBUGPRINTS)
set_target_properties(myProgram PROPERTIES COMPILE_DEFINITIONS "DEBUGPRINTS=1")
endif()
edit:
The option i wrote in the example shows up as a checkbox in the CMake GUI.
In case you want to set defines per target: Since 2.8.11 you can use target_compile_definitions.
In earlier versions you probably don't want to use set_target_properties as is, since it overwrites any defines you set previously. Call get_target_property first instead, then merge with previous values. See add_target_definitions here.
Use target_compile_options. Do not quote your define or it not be detected as a define. CMake parses off the /define and adds the actual define to the DefineConstants section of the csproj, if there are quotes it will put the entire quoted string in the AdditionalOptions section of the csproj.
An example from one of my projects that uses generator expressions:
target_compile_options( ${LIBRARY_NAME} PRIVATE
$<${IS_ART_ITERATION_BUILD}:/define:ART_ITERATION_BUILD>
)
An example without generator expressions:
target_compile_options( ${LIBRARY_NAME} PRIVATE
/define:GRAPHICS_VULKAN
)

Generating a VERSION_STRING from major/minor/patch/build defines for use in a plist

I have a plist that is processed with a precompiled header file and in it there is the "variable" VERSION_STRING used a few times in such fields as CFBundleGetInfoString, ie the value for the key CFBundleGetInfoString is: MyProduct VERSION_STRING Copyright © 2009 MyCorp
In MyHeader.h (which is the set as the Info.plist prefix header I would like to be able to build VERSION_STRING into the form MAJOR.MINOR.PATCH.BUILD where I have
#define MAJOR 1
#define MINOR 0
#define PATCH 0
#define BUILD 23
For some reason I seem to be incapable of doing this. I might just be having one of those moments
Turns out there is actually an Apple Tech Note on this and a solution to the very problem I was having. So, for anyone that may come across this and is having the same problems I was check out Technical Note TN2175: Preprocessing Info.plist files in Xcode Using the C Preprocessor
Take your plist file and rename it with an extra extension (perhaps a P?). Add #include "MyHeader.h" to the beginning of the file, and preprocess it in your build with the C preprocessor (usually cpp). You may need to filter out extra # lines, but I don't see why it wouldn't work.
By default, cpp should output to stdout, so adding a command like this might work fine:
cpp myplist.plist.P | grep -v '^#' > myplist.plist