Issue
I have been experimenting a bit with cmake recently and came across the project
definition, where you can specify the name, version, description and languages.
I then learned that you can pass this information to the code, to print for example when the program starts, with the following lines:
add_compile_definitions( PROJECT_NAME="${PROJECT_NAME}" )
add_compile_definitions( PROJECT_VERSION="${PROJECT_VERSION}" )
add_compile_definitions( PROJECT_DESCRIPTION="${PROJECT_DESCRIPTION}" )
I have been wondering how I can expand this to include the author's name(s) and/or license information. I started with the following:
set (
PROJECT_AUTHORS
fname1 lname1
fname2 lname2
)
add_compile_definitions( PROJECT_AUTHORS="${PROJECT_AUTHORS}" )
This resulted in:
$ make src/main.i
Preprocessing C source to CMakeFiles/cmake_test.dir/src/main.c.i
/usr/bin/sh: -c: line 0: unexpected EOF while looking for matching `"'
/usr/bin/sh: -c: line 1: syntax error: unexpected end of file
make[1]: *** [CMakeFiles/cmake_test.dir/build.make:87: CMakeFiles/cmake_test.dir/src/main.c.i] Error 1
make: *** [Makefile:187: src/main.c.i] Error 2
If I then change the definition line to add_compile_definitions( PROJECT_AUTHORS=${PROJECT_AUTHORS} )
(removed the "
's around the variable).
This results to just fname1
in the code (as plain text, not a string)...
I then changed the CMakeLists.txt
to:
set (
PROJECT_AUTHORS
"fname1 lname1"
"fname2 lname2"
)
add_compile_definitions( PROJECT_AUTHORS="${PROJECT_AUTHORS}" )
Which resulted in:
$ make src/main.i
Preprocessing C source to CMakeFiles/cmake_test.dir/src/main.c.i
/usr/bin/sh: -c: line 0: unexpected EOF while looking for matching `"'
/usr/bin/sh: -c: line 1: syntax error: unexpected end of file
make[1]: *** [CMakeFiles/cmake_test.dir/build.make:87: CMakeFiles/cmake_test.dir/src/main.c.i] Error 1
make: *** [Makefile:187: src/main.c.i] Error 2
Any ideas or suggestions on how I could go about this?
For information:
cmake --version
->cmake version 3.19.2
gcc --version
->gcc.exe (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 8.1.0
bash --version
->GNU bash, version 4.4.23(1)-release (x86_64-pc-msys)
Solution
I think a simple way would be to use a configure_file
. Create a file:
# authors.h.in
#cmakedefine PROJECT_AUTHORS "@PROJECT_AUTHORS@"
Then:
$ cat CMakeLists.txt
cmake_minimum_required(VERSION 3.11)
project(test)
set(PROJECT_AUTHORS "fname1 lname1" "fname2 lname2")
configure_file(authors.h.in ${CMAKE_CURRENT_BINARY_DIR}/gen/authors.h)
add_executable(main main.c)
target_include_directories(main PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/gen)
include(CTest)
add_test(NAME main COMMAND main)
and:
#include <authors.h>
#include <stdio.h>
#include <string.h>
int main() {
char authors[] = PROJECT_AUTHORS;
for (char *author = strtok(authors, ";"); author; author = strtok(0, ";")) {
printf("%s\n", author);
}
}
and after compilation:
$ build_dir/main
fname1 lname1
fname2 lname2
The problem is the ;
character that comes from concatenating the list variable in cmake into a string. Whereas ;
is the separator of arguments in CMake. You could replace ;
for example by \;
and of course pass a value that is a C string literal. For example:
cmake_minimum_required(VERSION 3.11)
project(test)
set(PROJECT_AUTHORS "fname1 lname1" "fname2 lname2")
# TODO: I think also escape "" or for example newline
# ie preprocess the value so that all is nicely parsed
string(REGEX REPLACE ";" "\\\\;" val "${PROJECT_AUTHORS}")
add_executable(main main.c)
target_compile_definitions(main PRIVATE "PROJECT_AUTHORS=\"${val}\"")
include(CTest)
add_test(NAME main COMMAND main)
with same main.c
except #include <authors.h>
and it also works fine:
$ cmake -S . -B _build -G Ninja && cmake --build _build --verbose && ( cd _build && ctest -V )
...
[1/2] /usr/bin/cc -DPROJECT_AUTHORS="\"fname1 lname1;fname2 lname2\"" -MD -MT CMakeFiles/main.dir/main.c.o -MF CMakeFiles/main.dir/main.c.o.d -o
....
1: Test command: /tmp/10/_build/main
1: Test timeout computed to be: 1500
1: fname1 lname1
1: fname2 lname2
1/1 Test #1: main ............................. Passed 0.00 sec
Answered By - KamilCuk