Search code examples
c++cmakepreprocessor-directive

How to pass the CMake binary path to C++ on Linux?


My project has this simplified directory structure:

+-src
| +-CMakeLists.txt
+-example
| +-CMakeLists.txt
+-_builds
| +-win
| | +-src
| | +-example
| +-linux
| | +-src
| | +-example
+-CMakeLists.txt

In example/CMakeLists.txt I compile the shaders with a platform-dependent compiler into the appropriate _builds/*/example directory, then I pass the path to the C++ code like this:

target_compile_definitions(vulkan_tutorial PRIVATE EXAMPLE_BIN_DIR=${CMAKE_CURRENT_BINARY_DIR})

In C++ I do this:

#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)
#define GET_EXAMPLE_BIN_DIR() TOSTRING(MYLIB_EXAMPLE_BIN_DIR)

std::cout << GET_EXAMPLE_BIN_DIR() << std::endl;

On Windows this works as I expect, and it prints D:/Dev/mylib/_builds/win/example.

However, on Ubuntu virtual machine it prints /mnt/hgfs/dev/mylib/_builds/1/example. So the linux word is replaced with a 1. Why is that? How to solve it?

I named my project mylib only for this question. Its real name is different, just in case it matters. It is simple, and contains only a few core ASCII characters, underscore letters only.

I checked the binary directory structure whether it contains a folder named 1. It doesn't. It contains what is listed above. I searched for similar CMake variables but did not find any solution. I also realized that in case of a syntax error CMake shows the whole command. Abusing this, I checked my CMake build command, and found that it looks good. It contains this:

-DEXAMPLE_BIN_DIR=/mnt/hgfs/dev/mylib/_builds/linux/example

The whole command line looks like this (I did not simplify this one, not to hide any possible errors):

/bin/x86_64-linux-gnu-g++-9 -DMYLIB_ASSERT_TO_CERR -DMYLIB_ENABLE_EXCEPTIONS -DMYLIB_EXAMPLE_BIN_DIR=/mnt/hgfs/dev/mylib/_builds/linux/example -DMYLIB_LINKED_WITH_VULKAN -DMYLIB_LOGLEVEL=3 -DMYLIB_PRINT_CONFIG -I/mnt/hgfs/dev/mylib/include -I/mnt/hgfs/dev/mylib/third_party/glad2/include -I/mnt/hgfs/dev/mylib/_builds/linux/_deps/glfw-src/include -I/mnt/hgfs/dev/mylib/_builds/linux/_deps/glm-src -g -std=gnu++17 -MD -MT example/CMakeFiles/vulkan_tutorial.dir/vulkan.cpp.o -MF example/CMakeFiles/vulkan_tutorial.dir/vulkan.cpp.o.d -o example/CMakeFiles/vulkan_tutorial.dir/vulkan.cpp.o -c /mnt/hgfs/dev/mylib/example/vulkan.cpp


Solution

  • linux is defined to 1, the tokens in MYLIB_EXAMPLE_BIN_DIR get expanded before they are stringified. If you want a macro to be a string just pass it to the compiler as a string to start with, you can then skip the stringify and are guaranteed not to get any weird expansions:

    -DMYLIB_EXAMPLE_BIN_DIR=\"/mnt/hgfs/dev/mylib/_builds/linux/example\"
    
    #include <iostream>
    
    #define GET_EXAMPLE_BIN_DIR() MYLIB_EXAMPLE_BIN_DIR
    
    int main()
    {
        std::cout << GET_EXAMPLE_BIN_DIR() << std::endl;
    }
    

    https://godbolt.org/z/ev4aq4eMx To do this in cmake:

    target_compile_definitions(vulkan_tutorial PRIVATE MYLIB_EXAMPLE_BIN_DIR="${CMAKE_CURRENT_BINARY_DIR}")
    

    https://godbolt.org/z/515hx5bM1