I'm trying to define a preprocessor variable via CMake, but unfortunately it does not seem to work.
The following is my c++ code:
#ifdef hehe_test
#define it_exists "it_exists"
#endif
#ifndef hehe_test
#define it_exists "it_doesnt_exist"
#endif
int main(int argc, char** argv)
{
printf("%s\n",it_exists);
return 0;
}
And this is my CMakeLists.txt:
project(LibraryTester)
add_definitions(hehe_test)
file(GLOB src "*.h" "*.cpp")
include_directories(${CMAKE_SOURCE_DIR}/include)
add_executable( ${PROJECT_NAME} ${src})
When i run my program it outputs: it_doesnt_exist
indicating "hehe_test" has not been defined.
What am i doing wrong here?
The following form won't do what you expect in any case:
add_definitions(hehe_test)
According to the documentation the form is:
add_definitions(-DFOO -DBAR ...)
In your case it means:
add_definitions(-Dhehe_test)
You can also give them a value if required:
// statically defined
add_definitions(-Dfoo=bar)
// use a cmake or user-defined variable
add_definitions(-Dbar=${FOO})
// use "" if you intend to pass strings with spaces
add_definitions(-Dxxx="${YYY}")
Note that you can put all of them together in the same add_definitions call.
In order to get the C/C++ effect of :
#define _MYSYMBOL
From the command line you can :
cmake . -DCMAKE_C_FLAGS=" -D_MYSYMBOL " -DCMAKE_CXX_FLAGS=" -D_MYSYMBOL "
From within CMakeLists.txt:
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_MYSYMBOL")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_MYSYMBOL")
These add flags to the c/c++ compiler command directly.
-D is define symbol
You can also use cmake's add_definitions command
add_definitions ( -D_MYSYMBOL1 -D_MYSYMBOL2 )
add_definitions makes some slight assumptions as described here
Related
Background
I'm running through the CMake tutorial and found the solution for TODO #10, i.e:
TutorialConfig.h.in
#define Tutorial_VERSION_MAJOR #Tutorial_VERSION_MAJOR#
#define Tutorial_VERSION_MINOR #Tutorial_VERSION_MINOR#
seems to conflict with the manual for the configure_file command
configure_file(
[NO_SOURCE_PERMISSIONS | USE_SOURCE_PERMISSIONS |
FILE_PERMISSIONS ...]
[COPYONLY] [ESCAPE_QUOTES] [#ONLY]
[NEWLINE_STYLE [UNIX|DOS|WIN32|LF|CRLF] ])
Copies an file to an file and substitutes variable
values referenced as #VAR# or ${VAR} in the input file content. Each
variable reference will be replaced with the current value of the
variable, or the empty string if the variable is not defined.
Furthermore, input lines of the form
#cmakedefine VAR ...
will be replaced with either
#define VAR ...
or
/* #undef VAR */
depending on whether VAR is set in CMake to any value not considered a
false constant by the if() command. The "..." content on the line
after the variable name, if any, is processed as above.
in that the #cmakedefine preprocessor directive should be used in the input file an turned into a #define directive in the output file rather than what is done in the solution (using the #define directive in the input file). I tried using the #cmakedefine directive before looking at the solution and it resulted in an undefined _VERSION_MINOR i.e. here is the generated header file:
TutorialConfig.h
#define Tutorial_VERSION_MAJOR 1
/* #undef Tutorial_VERSION_MINOR */
whereas when I use the #define directive within the input file (TutorialConfig.h.in), both Tutorial_VERSION_MAJOR and Tutorial_VERSION_MINOR are defined in the generated header file. i.e.
TutorialConfig.h
#define Tutorial_VERSION_MAJOR 1
#define Tutorial_VERSION_MINOR 0
So what is the difference here and why would one properly grab the defined Tutorial_VERSION_MINOR and another not? Is there specific documentation regarding the #cmakedefine and #cmakedefine01 directives other than what is shown in the configure_file documentation?
Based on a comment received highlighting the following text from the configure_file documentation:
"[...] depending on whether VAR is set in CMake to any value not considered a false constant by the if() command".
I'm wondering if the only difference between using the #cmakedefine and #define directives is just that the #cmakedefine one goes through an if() statement and thus any value of 0 would result in the output header file showing an undefined variable rather than a string of 0 as intended.
cmake version 3.22.1
Minimal Working Example
A full working minimal example below or alternatively can be found here:
tutorial.cxx
#include <cstdlib>
#include <iostream>
#include <string>
#include "TutorialConfig.h"
int main()
{
std::cout << "Version: " << Tutorial_VERSION_MAJOR << "." << Tutorial_VERSION_MINOR << std::endl;
return 0;
}
TutorialConfig.h.in
#define Tutorial_VERSION_MAJOR #Tutorial_VERSION_MAJOR#
#define Tutorial_VERSION_MINOR #Tutorial_VERSION_MINOR#
CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(Tutorial
VERSION 1.0)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
configure_file(TutorialConfig.h.in
TutorialConfig.h
#ONLY)
add_executable(Tutorial
tutorial.cxx)
target_include_directories(Tutorial
PUBLIC
${PROJECT_BINARY_DIR})
Steps to reproduce this:
from the directory containing the above 3 files (e.g. Step1) make a build directory with mkdir build
cd build
cmake ..
make
./Tutorial and note it prints Version: 1.0 as expected
cd ..
change the #define directives in TutorialConfig.h.in to #cmakedefine, i.e.
TutorialConfig.h.in:
#cmakedefine Tutorial_VERSION_MAJOR #Tutorial_VERSION_MAJOR#
#cmakedefine Tutorial_VERSION_MINOR #Tutorial_VERSION_MINOR#
repeat steps 2-4 and note the compile error:
Step1/tutorial.cxx:8:64: error: ‘Tutorial_VERSION_MINOR’ was not declared in this scope; did
you mean ‘Tutorial_VERSION_MAJOR’?
8 | std::cout << "Version: " << Tutorial_VERSION_MAJOR << "." << Tutorial_VERSION_MINOR << std::endl;
| ^~~~~~~~~~~~~~~~~~~~~~
| Tutorial_VERSION_MAJOR
As Friedrich already mentioned in the comments, CMake is doing exactly what it's documented to do.
You wrote:
project(Tutorial
VERSION 1.0)
, so in the following
#cmakedefine Tutorial_VERSION_MINOR #Tutorial_VERSION_MINOR#
, for the first "Tutorial_VERSION_MINOR", CMake looks at what the value of that CMake variable is, and sees that it is a value considered false by the if() command. Yes- Tutorial_VERSION_MINOR is a CMake variable. See the <PROJECT_NAME>_VERSION_MINOR docs. The specific docs that say 0 is considered as a falsy value in the if() command docs is under the "basic expressions" section:
False if the constant is 0, OFF, NO, FALSE, N, IGNORE, NOTFOUND, the empty string, or ends in the suffix -NOTFOUND. Named boolean constants are case-insensitive.
And as the documentation you linked states, #cmakedefine VAR ... is replaced with /* #undef VAR */ when "VAR is set in CMake to any value considered a false constant by the if() command."
seems to conflict with the manual for the configure_file command [...] in that the #cmakedefine preprocessor directive should be used in the input file an turned into a #define directive in the output file rather than what is done in the solution (using the #define directive in the input file)
uhh no. The tutorial just uses the approach that will actually work here. This kind of scenario where you want to create a macro definition with the same name as a CMake variable where the CMake variable might be a CMake-falsy value is the weakness scenario of #cmakedefine that one can avoid by instead using #define and configure_file's variable substitution facilities- namely #VAR# and ${VAR}.
There's nothing special about #define with respect to configure_file. It just treats it like any other text. #cmakedefine, #cmakedefine01 and variable substitution with #VAR# and ${VAR} are the only things configure_file does special things for.
I'm new to cmake, so correct me if I've messed things up and this should be solved using something other than cmake.
I have main_program, that requires multiple other subprograms in form of bindata to be specified at build phase. Right now I build it by running
cmake -DBINDATA1="\xde\xad..." -DBINDATA2="\xbe\xef" -DBINDATA3="..."
and in code I use them as:
// main_program.cpp
int main() {
#ifdef BINDATA1
perform_action1(BINDATA1);
#endif
#ifdef BINDATA2
perform_action2(BINDATA2);
#endif
[...]
This is rather unclean method as any time I'm changing one of subprograms I have to generate bindata from it and pass it to cmake command.
What I would like to do, is have a project structure:
/
-> main_program
-> subprograms
-> subprogram1
-> subprogram2
-> subprogram3
and when I run cmake, I would like to
compile each of subprograms
generate shellcode from each of them, by running generate_bindata program on them
build main_program passing bindatas from step 2
and when I run cmake, I would like to
compile each of subprograms
generate shellcode from each of them, by running generate_shellcode program on them
build main_program passing shellcodes from step 2
Then let's do that. Let's first write a short script to generate a header:
#!/bin/sh
# ./custom_script.sh
# TODO: Find out proper quoting and add `"` is necessarily. Ie. details.
# Prefer to use actual real variables like `static const char *shellcode[3]`
# instead of raw macro defines.
cat > "$1" <<EOF
#define SHELLCODE1 $(cat "$2")
#define SHELLCODE2 $(cat "$3")
#define SHELLCODE3 $(cat "$4")
EOF
To be portable, write this script in cmake. This script will be run at build phase to generate the header needed for compilation. Then, "model dependencies" - find out what depends on what exactly. Then write it in cmake:
add_executable(subprogram1 sources.c...)
add_executable(subprogram2 sources.c...)
add_executable(subprogram3 sources.c...)
for(i IN ITEMS 1 2 3)
add_custom_target(
COMMENT Generate shellcode${i}.txt with the content of shellcode
# TODO: redirection in COMMAND should be removed, or the command
# should be wrapped in `sh -c ...`.
COMMAND $<TARGET_FILE:subprogram${i}> | generate_shellcode > ${CMAKE_CURRENT_BINARY_DIR}/shellcode${i}.txt
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/shellcode${i}.txt
DEPENDS $<TARGET_FILE:subprogram${i}> generate_shellcode
)
endfor()
add_custom_command(
COMMENT Generate shellcodes.h from shellcode1.txt shellcode2.txt and shellcode3.txt
COMMAND sh custom_script.sh
${CMAKE_CURRENT_BINARY_DIR}/shellcodes.h
${CMAKE_CURRENT_BINARY_DIR}/shellcode1.txt
${CMAKE_CURRENT_BINARY_DIR}/shellcode2.txt
${CMAKE_CURRENT_BINARY_DIR}/shellcode3.txt
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/shellcodes.h
DEPENDS
${CMAKE_CURRENT_BINARY_DIR}/shellcode1.txt
${CMAKE_CURRENT_BINARY_DIR}/shellcode2.txt
${CMAKE_CURRENT_BINARY_DIR}/shellcode3.txt
)
# Then compile the final executable
add_executable(main main.c ${CMAKE_CURRENT_BINARY_DIR}/shellcodes.h)
# Don't forget to add includes!
target_include_directories(main PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
# or you may add dependency to a single file instead of target
# Like below only to a single shellcodeswrapper.c file only
# This should help build parallelization.
set_source_files_properties(main.c OBJECT_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/shellcodes.h)
# Or you may add a target for shelcodes header file and depend on it
add_custom_target(shellcodes DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/shellcodes.h)
add_executable(main main.c)
target_include_directories(main PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
add_dependencies(main shellcodes)
Then your main file:
#include <shellcodes.h> // compiler will find it in BINARY_DIR
int main() {
perform_action1(SHELLCODE1);
perform_action2(SHELLCODE2);
}
So that all your source files are not recompiled each time, I suggest to write a wrapper:
// shellcodewrapper.c
#include <shellcodes.h>
// preserve memory by not duplicating code in each TU
static const char shellcode1[] = SHELLCODE1;
// only this file will be recompiled when SHELLCODE changes
const char *get_shellcode1(void) {
return shellcode1;
}
// shellcodewrapper.h
const char *get_shellcode1(void);
// main.c
#include <shellcodewrapper.h>
int main() {
perform_action1(get_shellcode1());
perform_action2(get_shellcode2());
}
That way when you change the "SHELLCODE" generators, only shellcodewrapper.c will be compiled, resulting in super fast compilation times.
Note how dependency is transferred and how it works - I used files inside BINARY_DIR to transfer result from one command to another, then these files track what was changed and transfer dependency below in the chain. Track dependencies in DEPENDS and OUTPUT in add_custom_command and cmake will properly compile in proper order.
Use case: I'm trying to compile a test program that probes for a list of TrueType(tm) fonts using SDL2_ttf (with SDL2, Freetype, PNG and Zlib). The SDL2_ttf::SDL2_ttf interface library exists and links successfully with target executables. My problem is how to get check_c_source_runs() to pick up the definitions, include directories and libraries. I'd rather not have to manually extract everything from properties, as in the following code fragment:
include(CheckCSourceRuns)
get_property(defs TARGET SDL2_ttf::SDL2_ttf PROPERTY INTERFACE_COMPILE_DEFINITIONS)
get_property(incs TARGET SDL2_ttf::SDL2_ttf PROPERTY INTERFACE_INCLUDE_DIRECTORIES)
get_property(libs TARGET SDL2_ttf::SDL2_ttf PROPERTY INTERFACE_LINK_LIBRARIES)
## Transform the definitions with "-D"
if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.12")
list(TRANSFORM defs PREPEND "-D")
list(TRANSFORM incs PREPEND "-I")
else ()
## Code that does what list(TRANSFORM...) does in less capable CMake
## versions.
endif ()
set(CMAKE_REQUIRED_DEFINITIONS ${defs})
set(CMAKE_REQUIRED_INCLUDES ${incs})
set(CMAKE_REQUIRED_LIBRARIES ${libs})
check_c_source_runs("
#include <stdint.h>
#include <SDL.h>
#include <SDL_ttf.h>
int main(int argc, char *argv[])
{
const char *fonts[] = {\"DejaVuSans.ttf\", \"LucidaSansRegular.ttf\", \"FreeSans.ttf\", \"AppleGothic.ttf\", \"tahoma.ttf\"};
size_t i, cnt = 0;
SDL_Init(SDL_INIT_VIDEO);
TTF_Init();
for (i = 0; i < sizeof(fonts)/sizeof(fonts[0]); ++i) {
TTF_Font *ttf = TTF_OpenFont(fonts[i], 10);
if (ttf != NULL) {
fputs(fonts[i], stderr);
if (cnt++ > 0) {
fputc(';', stderr);
}
TTF_CloseFont(ttf);
}
}
TTF_Quit();
SDL_Quit();
return 0;
}" ttfprobe_run)
Link libraries are hairy, since there are interface libraries referenced from within SDL2_ttf::SDL2_ttf, e.g. FreeType::FreeType.
Suggestions?
Functions try_compile and try_run and everything which is based on them (e.g. check_c_source_runs) are actually build some other CMake project. Because you cannot pass targets to the CMake project, you have two ways:
Extract all needed target's properties to the variables and pass them to the newly generated project. As you already do.
Write CMakeLists.txt for other project manually, and use calls to find_package and other package-discovery functions in it.
E.g., you may write CMakeLists.txt for other project like that:
# Source file is in SOURCE_FILE parameter,
# resulted executable is copied into the file pointed by EXE_FILE parameter.
cmake_minimum_required(...)
project(test_project)
# This will create 'SDL2_ttf::SDL2_ttf' target
find_package(SDL2_ttf REQUIRED)
add_executable(test_exe ${SOURCE_FILE})
target_link_libraries(test_exe SDL2_ttf::SDL2_ttf)
add_custom_command(OUTPUT ${EXE_FILE}
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:test_exe> ${EXE_FILE}
DEPENDS $<TARGET_FILE:test_exe>
)
add_custom_target(copy_exe ALL DEPENDS ${EXE_FILE})
The main challenge is to pass as many variables to the other project as needed for it to be built in the same "environment" as the main project.
Example below handles only variables which could affect on find_package(SDL2_ttf) call:
# Main project
# Somewhere you have this call too.
find_package(SDL2_ttf REQUIRED)
# List of arguments for the subproject
set(SUBPROJECT_ARGS
# This affects on searching for possible `FindSDL2_ttf.cmake` script
-DCMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}
# This affects on searching for `find_*` calls in find script.
-DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH}
)
if (SDL2_ttf_DIR)
# This is a directory with `SDL2_ttfConfig.cmake` script
list(APPEND SUBPROJECT_ARGS -DSDL2_ttf_DIR=${SDL2_ttf_DIR})
endif()
# build subproject
try_compile(TTF_TEST_RESULT # Variable which will contain result of building the subproject
${CMAKE_CURRENT_BINARY_DIR}/ttf_test # Build directory for the subproject
<src-dir> # Source directory for the subproject, where its `CMakeLists.txt` resides.
test_project # Project name of the subproject
CMAKE_FLAGS
-DSOURCE_FILE=<src-file> # Source file
-DEXE_FILE=<exe-file> # Path to the resulted executable file
${SUBPROJECT_ARGS} # The rest of arguments for subproject
OUTPUT_VAR TTF_TEST_OUTPUT # Variable which will contain output of the build process
)
if (TTF_TEST_RESULT)
# Subproject has been built successfully, now we can try to execute resulted file
...
endif()
Tricky? Yes. But this is how CMake works...
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.
I want to use CMake to create a simple static library Maths, and then link to that library to create a simple executable Alpha.
Here's my directory structure:
/Maths
/build
/
/CMakeLists.txt
/maths.cpp
/maths.h
/Alpha
/build
/
/CMakeLists.txt
/main.cpp
And here are the contents of those files:
* CMakeLists.txt (for Maths) *
cmake_minimum_required(VERSION 2.6)
project(Maths)
add_library(maths STATIC maths.cpp)
* maths.cpp *
int Double(int x)
{
int y = x * 2;
return y;
}
* maths.h *
int Double(int x);
* CMakeLists.txt (for Alpha) *
cmake_minimum_required(VERSION 2.8)
project(Alpha)
add_executable(alpha main.cpp ../Maths/maths.h)
target_link_libraries(maths ../Maths/build/libmaths.a)
* main.cpp *
#include <iostream>
#include "maths.h"
using namespace std;
int main()
{
int x = 5;
int y = Triple(x);
cout << "The answer = " << y << endl;
return 0;
}
So, I enter the directory /Maths/build, and run cmake ... Then, I run make. This creates the library file libmaths.a in the directory /Maths/build.
Then, I enter the directory /Alpha/build, and run cmake ... Then, I run make. However, this gives me the error message:
/usr/bin/ld: cannot find -lmaths
Why can make not find the library file that I just created?
I also tried copyiing libmaths.a and maths.h into the directory /Alpha, and changing Alpha's CMakeLists.txt to:
cmake_minimum_required(VERSION 2.8)
project(Alpha)
add_executable(alpha main.cpp maths.h)
target_link_libraries(maths libmaths.a)
But this gave the same error.
The essential problem here is that libmaths.a is not placed in one of the default linker directories.
The bigger problem is that you passed libmaths.a to target_link_libraries in the first place.
This is bad for several reasons, but the most obvious is that it's completely non-portable. A different compiler toolchain might use a different ending than .a for static libraries, so you won't be able to build there. Also, should you ever decide to switch to dynamic linking in the future, it means refactoring your existing CMake scripts.
The proper solution is to not pass hardcoded filenames to target_link_libraries. There are two roads that you can take to avoid this.
Use a find script. Essentially, you call find_library(MY_MATHLIB maths [...]) and then do target_link_libraries(alpha ${MY_MATHLIB}) instead. This will determine the absolute path of the .a file in a portable way and pass that to the linker. The biggest disadvantage of this approach is that the .a file must already be there for the find call to succeed. So if you want to build maths after running the CMake for alpha or build it as part of alpha, this won't work.
Since both libraries are under your control, consider using CMake's packaging mechanism. This is by far the most convenient approach for building, but unfortunately also the most difficult to set up. If you are willing to spend some time digging through the CMake docs and experimenting with the build, I encourage you to give it a try, but if you just want something that works quickly, you might want to stick with option 1.
Use a CMakeLists.txt in the root directory
/CMakeLists.txt:
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
PROJECT( MyProject )
ADD_SUBDIRECTORY( Maths )
ADD_SUBDIRECTORY( Alpha )
in Alpha/CMakeLists.txt:
TARGET_LINK_LIBRARIES( alpha maths )
Run CMake on the top-level CMakeLists.txt instead of the individual subdirectory ones