How not to have dollar sign in target_link_options mangled?

If I have this CMake script:

add_executable(main main.cpp)


add_executable(main main.cpp)
target_link_options(main PUBLIC --preload-file=temp$.txt)

and an arbitrary file named temp$.txt, it doesn’t find the txt file because the dollar sign gets changed into something like \$$ in the actual compiler invocation.

(Don’t mind the actual link option, it’s an Emscripten specific one, but the problem I believe is not specific to Emscripten development.)

What should I do to make it find my file which has a dollar sign in its name? This doesn’t help:

target_link_options(main PUBLIC [=[--preload-file=temp$.txt]=])

Do you really need to have $ in the name? Is there no other character that can work? CMake doesn’t guarantee that arbitrary text can make it through untouched (e.g., a filename with ; in it is going to have a horrible time). I think $ may just be in the same bucket as I believe it is important to all non-Xcode generators in some way.

I observe that using CMAKE_EXE_LINKER_FLAGS is not affected by the same escaping behavior. Consider:

cmake_minimum_required(VERSION 3.21)

set(CMAKE_EXE_LINKER_FLAGS "--preload-file=temp$.txt")

add_executable(main main.cpp)  # main.cpp is empty for demo

Then at the terminal, we can see:

$ cmake -G Ninja -S . -B build -DCMAKE_BUILD_TYPE=Release
-- The C compiler identification is GNU 9.3.0
-- The CXX compiler identification is GNU 9.3.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /path/to/build
$ cmake --build build/ -- -nv
[1/2] /usr/bin/c++   -O3 -DNDEBUG -MD -MT CMakeFiles/main.dir/main.cpp.o -MF CMakeFiles/main.dir/main.cpp.o.d -o CMakeFiles/main.dir/main.cpp.o -c /home/alex/test2/main.cpp
[2/2] : && /usr/bin/c++ -O3 -DNDEBUG --preload-file=temp$.txt CMakeFiles/main.dir/main.cpp.o -o main   && :

This also works with Unix Makefiles, though it is obscured in the command output.

Escaping with \$$ looks like a bug, though. Depending on the backend, I would expect to see either \$ or $$, not the two laid on top of each other. Also, shouldn’t CMAKE_*_LINKER_FLAGS* escape the same way as target_link_options, add_link_options, etc.?

Hmm. Good point. Unfortunately, fixing any of this is going to be a policy. The CMAKE_<LANG>_LINKER_FLAGS are “old” and probably don’t have the same codepaths as the usage requirement-using target_link_* bits.

1 Like

A \$$ looks perfectly fine inside a Makefile or Ninja using a shell. The \ quotes the dollar sign for the shell command line and the $$ is a single dollar sign in Make/Ninja syntax.

But the Makefile generated by CMake does not call the linker directly. Instead it generates a link.txt file which is given to cmake -E cmake_link_script. In that case doing a $$ quoting is a bug, the working string is \$. Most probably introduced with the cmake_link_script feature.

And a Ninja file generated by CMake file contains \$$$$ which is also a bug. The working string is \$$.

1 Like

Thanks for the context, there. I figured (incorrectly) that Ninja was calling the commands directly and not going through the shell.

Are you a CMake dev? Do I need file a bug report? Or is one of these related?