cmake picks up LDFLAGS from environment

If LDFLAGS are set in environment, then cmake picks that up and adds it to the list of linker flags. What is the way to avoid that? I tried to define CMAKE_EXE_LINKER_FLAGS_INIT, CMAKE_CXX_LINK_FLAGS etc in the toolchain file, but it did not help.

The docs state that it happens, though setting CMAKE_EXE_LINKER_FLAGS_INIT should do it. Could you please provide a small example showing the issue?

$ cat CMakeLists.txt
project(helloworld)
add_executable(helloworld helloworld.cpp)
set(CMAKE_EXE_LINKER_FLAGS_INIT )

$ env | grep LDFLAGS
LDFLAGS=-Wl,--as-needed

If I build this I can see cmake passing LDFLAGS to the linker

[ 50%] Building CXX object CMakeFiles/helloworld.dir/helloworld.cpp.o
/usr/bin/c++     -o CMakeFiles/helloworld.dir/helloworld.cpp.o -c /usr/local/src/cmake_example/helloworld.cpp
[100%] Linking CXX executable helloworld
/usr/bin/cmake -E cmake_link_script CMakeFiles/helloworld.dir/link.txt --verbose=1
/usr/bin/c++    -Wl,--as-needed -rdynamic CMakeFiles/helloworld.dir/helloworld.cpp.o  -o helloworld
make[2]: Leaving directory '/usr/local/src/cmake_example/build'
[100%] Built target helloworld
make[1]: Leaving directory '/usr/local/src/cmake_example/build'
/usr/bin/cmake -E cmake_progress_start /usr/local/src/cmake_example/build/CMakeFiles 0

I can see that the environment variable LDFLAGS is added to CMAKE_EXE_LINKER_FLAGS irrespective of CMAKE_EXE_LINKER_FLAGS_INIT. cmake also picks up CFLAGS from the environment.

CMake initializes the flags in the project() call (which enables the C and CXX language toolchains), so your setting is too late in your example.

I thought that if you call set without a value then it clears whatever variable you’ve provided as a first argument. Try:

set(CMAKE_EXE_LINKER_FLAGS_INIT "")

Does not help to put CMAKE_EXE_LINKER_FLAGS_INIT before project.

Does not help to do

set(CMAKE_EXE_LINKER_FLAGS_INIT "")

Also tried to put CMAKE_EXE_LINKER_FLAGS_INIT in a toolchain file but cmake still picks up environment variable.

I can confirm that the behavior differs from the documentation, so either the docs or implementation is buggy.

The content of CMAKE_EXE_LINKER_FLAGS_INIT and LDFLAGS seems to be concatened and used for initialization of CMAKE_EXE_LINKER_FLAGS. Only the latter one is stored in the cache file, the _INIT is not.

The easiest solution for you is to delete the environment variables. So you don’t need to know exactly how it is used by CMake.

set(ENV{LDFLAGS})

Wait, that’s a cache variable so you need the word CACHE in your set call. Or you can add them in your generation call with -D I believe…

For this one both works. The last one defined seems to win.

Please try and play with this self-contained example:

cmake_minimum_required(VERSION 3.7)

set(ENV{LDFLAGS} "--illegal-flag-via-env")
set(CMAKE_EXE_LINKER_FLAGS_INIT "--illegal-flag-via-cache" CACHE STRING "")
set(CMAKE_EXE_LINKER_FLAGS_INIT "--illegal-flag-via-var")

project(helloworld LANGUAGES CXX)
add_executable(helloworld helloworld.cpp)
file(GENERATE OUTPUT helloworld.cpp CONTENT "")

Calling the following command

cmake . ; cat CMakeCache.txt | grep illegal

will fail at the compiler check and you can see --illegal-flag-via-var --illegal-flag-via-env used on the command line by CMake.
This is also the value used in the cache file for CMAKE_EXE_LINKER_FLAGS

I think the *_INIT variables are never written to the cache file by CMake itself, because they are used only once on the first configure for initializing the cache variable without the _INIT suffix. That makes them irrelevant for future configure calls.

I removed the environment variables CFLAGS and LDFLAGS to avoid them being picked up by cmake.

This cmake behavior is quite problematic and undesirable. In my case a build machine had these environment variables defined giving a different build result. This kind of behavior is not good for reproducible builds and can be hard to track down. A toolchain file should be the input to the compiler and linker flags.

Please file an issue with your example. However, pulling {C,CXX,CPP,LD}FLAGS from the environment has a long history. Changing this will almost certainly need a policy (and docs/tests to demonstrate the old and new behaviors).

Rather than changing the behavior and requiring a policy, another choice is to add support for a new variable which can be set to true in a toolchain file to say “Don’t pick up flags like CFLAGS, LDFLAGS, etc. from the environment, ignore them”.

A variable telling cmake to not pick up compiler and linker flags from environment sounds good. Although I thought that was the intent with the variable CMAKE_EXE_LINKER_FLAGS_INIT ? Maybe fixing or describing how those variables work is sufficient?

Fixing the docs is certainly required, but I don’t think that gets you the behavior you’re looking for. That is what will require a policy (or just a brand-new mechanism since policy manipulations in toolchain files are…probably also a little odd).

Created issue

https://gitlab.kitware.com/cmake/cmake/-/issues/22674