c++cmake

x64-linux becomes x64-1 when defining a macro?


I need to get a specific path of where something is installed, and then use that path to tell a program where to search at runtime. This is working as expected on Windows, but on Linux (specifically Ubuntu running in WSL2), something odd is happening.

In my CMake I run the following:

find_package(PROJ CONFIG REQUIRED)
find_path(PROJ_DATA_DIR
    NAMES proj.db
    PATHS "${PROJ_DIR}"
    NO_DEFAULT_PATH
    REQUIRED
)
add_compile_definitions(MY_PROJ_DIR=${PROJ_DATA_DIR})
message(${PROJ_DATA_DIR})

This prints out:

/mnt/c/Users/cgnam/Source/Repos/vira/out/build/ubuntu/vcpkg_installed/x64-linux/share/proj

which is the path I am expecting it to be. In my c++ code however, when I inspect the value of the defined macro using:

#define STRINGIFY2(X) #X
#define STRINGIFY(X) STRINGIFY2(X)

...

std::cout << STRINGIFY(MY_PROJ_DIR);

it prints out:

/mnt/c/Users/cgnam/Source/Repos/vira/out/build/ubuntu/vcpkg_installed/x64-1/share/proj

This is nearly entirely correct, except the x64-linux has been replaced by x64-1.

This exact code works completely as expected when being built on Windows, so I am at a loss for why it is doing this when being built on Ubuntu. Its especially odd to me that linux is being replaced by the integer 1, but this also feels intentional as if I am missing something.


Solution

  • By default, for both GCC and Clang under Linux, linux is a predefined macro:

    $ cpp -dM /dev/null | grep linux
    #define __linux 1
    #define __gnu_linux__ 1
    #define linux 1
    #define __linux__ 1
    

    So, for example

    #include <iostream>
    
    #define MY_PROJ_DIR /mnt/c/Users/cgnam/Source/Repos/vira/out/build/ubuntu/vcpkg_installed/x64-linux/share/proj
    
    #define STRINGIFY2(X) #X
    #define STRINGIFY(X) STRINGIFY2(X)
    
    int main()
    {
        std::cout << STRINGIFY(MY_PROJ_DIR);
    }
    

    prints

    /mnt/c/Users/cgnam/Source/Repos/vira/out/build/ubuntu/vcpkg_installed/x64-1/share/proj
    

    The "linux" part of the path is replaced with "1", as that is the value of the linux macro.

    If you instead do add_compile_definitions(MY_PROJ_DIR="${PROJ_DATA_DIR}"), then your macro will be defined as if by

    #define MY_PROJ_DIR "/mnt/c/Users/cgnam/Source/Repos/vira/out/build/ubuntu/vcpkg_installed/x64-linux/share/proj"
    

    As that is a string, the preprocessor won't perform macro replacement.

    You also won't need the STRINGIFY macro and can just use MY_PROJ_DIR directly.


    NOTE: some compiler flags will suppress the linux flag. According to gcc's documentation:

    When the -ansi option, or any -std option that requests strict conformance, is given to the compiler, all the system-specific predefined macros outside the reserved namespace are suppressed. The parallel macros, inside the reserved namespace, remain defined.

    For instance:

    $ cpp -std=c99 -dM /dev/null | grep linux
    #define __linux 1
    #define __gnu_linux__ 1
    #define __linux__ 1