I am attempting to determine if a particular issue exists during Generate time.
I have checked that the source code itself is correct, and quotes, etc, are correctly escaped. The resultant string dumped by the call to message() will compile without warning
But, I continue to get this error:
CMake Error at /usr/share/cmake/Modules/CheckCSourceCompiles.cmake:52 (cmake_check_source_compiles):
Syntax error in cmake code at
/usr/share/cmake/Modules/CheckCSourceCompiles.cmake:59
when parsing string
Here is the CMake snippet that is failing:
# Older versions of musl libc don't unescape entries in /etc/mtab
# Try to detect this behaviour, and work around, if necessary.
set(DETECT_GETMNTENT_NEEDS_UNESCAPE_WARNING "
#define _GNU_SOURCE
#include <mntent.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define dir_space_tab \"dir\\040space\\011tab\"
int main()
{
const char *fake_mtab = \"name \" dir_space_tab \" type opts 0 0\\n\";
FILE *f = fmemopen((void *)fake_mtab, strlen(fake_mtab) + 1, \"r\");
struct mntent *entp = getmntent(f);
fclose(f);
if(NULL == entp)
exit(EXIT_FAILURE);
if (0 == strcmp(entp->mnt_dir, dir_space_tab))
printf(\"needs escaping\\n\");
else
printf(\"no need to escape\\n\");
}
")
message("${DETECT_GETMNTENT_NEEDS_UNESCAPE_WARNING}")
set(CMAKE_REQUIRED_QUIET FALSE)
check_c_source_compiles("${DETECT_GETMNTENT_NEEDS_UNESCAPE_WARNING}"
CHECK_GETMNTENT_NEEDS_UNESCAPE
FAIL_REGEX "(?!needs escaping)" )
if(CHECK_GETMNTENT_NEEDS_UNESCAPE)
set(GETMNTENT_NEEDS_UNESCAPE ON)
else()
message(WARNING "getmntent might require unescape")
endif()
1 Like
I ran your code locally. Thanks for the easy to repro example. I got the following error:
Invalid character escape '\0'.
Call Stack (most recent call first):
CMakeLists.txt:74 (check_c_source_compiles)
This is caused by the following line of code:
#define dir_space_tab \"dir\\040space\\011tab\"
Looks like this is for UNIX. Any reason to not use forward slashes instead?
Also FWIW you can use the bracket argument syntax to clean things up.
set(DETECT_GETMNTENT_NEEDS_UNESCAPE_WARNING [=[
#define _GNU_SOURCE
#include <mntent.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define dir_space_tab "dir/040space/011tab"
int main()
{
const char *fake_mtab = "name " dir_space_tab " type opts 0 0\n";
FILE *f = fmemopen((void *)fake_mtab, strlen(fake_mtab) + 1, "r");
struct mntent *entp = getmntent(f);
fclose(f);
if(NULL == entp)
exit(EXIT_FAILURE);
if (0 == strcmp(entp->mnt_dir, dir_space_tab))
printf("needs escaping\n");
else
printf("no need to escape\n");
}
]=])
I figured out the route cause of the issue. check_c_source_compiles
is implemented as a macro.
macros
re-parse their bodes after substituting placeholders: https://gitlab.kitware.com/cmake/cmake/-/issues/19281
Iâve made an issue specifically for check_c_source_compiles to track this:
https://gitlab.kitware.com/cmake/cmake/-/issues/25803
Your analysis is awesome !
I should really have played around trying to make the classic âsimplest possible exampleâ, except that the error didnât really help - although I did wonder if the issue was with the escaped â0â (i.e. found in this string âdir\040space\011tabâ)
It wasnât at all clear why that would be a problem, and other escaped characters not; so I couldnât decide where I should start.
1 Like
Your suggestion for the other variable syntax appears to allow for a solution to the complicated escape sequences required.
For completeness, I provide the working solution:
# Older versions of musl libc don't unescape entries in /etc/mtab
# Try to detect this behaviour, and work around, if necessary.
set(COMPILE_ME [=[
#include <mntent.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define dir_space_tab "dir/040space/011tab"
int main(void)
{
const char *fake_mtab = "name " dir_space_tab " type opts 0 0\\n";
FILE *f = fmemopen((void *)fake_mtab, strlen(fake_mtab) + 1, "r");
struct mntent *entp = getmntent(f);
fclose(f);
if(NULL == entp)
exit(EXIT_FAILURE);
if (0 == strcmp(entp->mnt_dir, dir_space_tab))
return 99; // printf("needs escaping\n");
else
return 100; //printf("no need to escape\n");
}
]=])
try_run(
RUN_RESULT
COMPILE_RESULT
SOURCE_FROM_VAR "compile_me.cpp" COMPILE_ME
LINK_OPTIONS -lmount
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMPILE_OUTPUT_VARIABLE COMPILE_OUT_TXT
RUN_OUTPUT_VARIABLE RUN_OUT_TXT
)
if(NOT COMPILE_RESULT)
message(WARNING "Compiling test for getmntent failed with output: ${COMPILE_OUT_TXT}")
else()
# Check the result of try_run using regex
if(RUN_RESULT EQUAL 100)
message(STATUS "getmntent does NOT require escaping")
elseif(RUN_RESULT EQUAL 99)
message(WARNING "getmntent REQUIRES escaping")
else()
message(WARNING "Unknown error returned from testing for getmntent. Value: ${RUN_RESULT}.\nOutput ${RUN_OUT_TXT}")
endif()
endif()
1 Like