Syntax error in cmake code at /usr/share/cmake/Modules/CheckCSourceCompiles.cmake:59

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