Skip to content

An option to set default C flags and C++ flags via the CFLAGS and CXXFLAGS environment variables #271

@kjcamann

Description

@kjcamann

The problem

I use CMake toolchain files a lot, and this package interacts badly with a certain way of using CMake toolchain files. I have a proposed fix, but I want to get people's feedback on if it should be turned into a PR.

Toolchain files with essential compiler flags

I would like Rust to select the C and C++ compiler via a toolchain file, e.g.

CMAKE_TOOLCHAIN_FILE=$HOME/share/cmake-toolchains/musl-clang19-x64v4-libstdcxx.cmake cargo build

Here is what a toolchain file like this does:

set(CMAKE_C_COMPILER /usr/lib/llvm/19/bin/clang)
set(CMAKE_CXX_COMPILER /usr/lib/llvm/19/bin/clang++)
set(CMAKE_ASM_FLAGS_INIT -march=x86-64-v4)
set(CMAKE_C_FLAGS_INIT "-march=x86-64-v4 --gcc-toolchain=/opt/gnu/gcc-15.2 --target=x86_64-pc-linux-musl")
set(CMAKE_CXX_FLAGS_INIT "-march=x86-64-v4 -stdlib=libstdc++ --gcc-toolchain=/opt/gnu/gcc-15.2 --target=x86_64-pc-linux-musl")

Note the setting of the CMAKE_<LANG>_FLAGS_INIT variables. Because this clang19 uses a non-system gcc (to pick up its newer standard library), and also a musl libc, the compiler invocation always needs special C and C++ flags to function correctly.

Inconsistent behavior between CMake CFLAGS environment expansion and -DCMAKE_C_FLAGS

CMake does something odd here.

If you were to specify additional, project-wide C flags via an environment variable (i.e., CFLAGS), CMake would merge the values of CMAKE_C_FLAGS_INIT and the CFLAGS environment variable. This is (I think) almost always what you want: the _INIT value still provides the essential parameters for the toolchain to operate, but you let people inject top-level override stuff too.

However, if you use instead pass -DCMAKE_C_FLAGS via a command line, this will completely overwrite the flags in CMAKE_C_FLAGS_INIT, and the toolchain will no longer function correctly.

I think perhaps the no_default_flags feature was added to fix this problem but there are two issues with it:

  • It doesn't actually work (it removes most of the flags but somewhere else, it still injects a -w and overwrites the toolchain's flags)
  • Perhaps I want to keep whatever cc-rs thinks are good default flags; I just want my CMAKE_C_FLAGS_INIT stuff too

Potential solution

An easy way to fix this would be to allow the user (via a configuration option) to choose to pass compiler flags via environment variables instead of command line parameters, which has better mergeability behavior inside CMake. Perhaps it could even be configurable in the following way:

  • I want to use CMAKE_C_FLAGS
  • I want to use default CFLAGS as a passing mechanism, but containing only the values as determined by the cc-rs package
  • I want to do the above, and ${CFLAGS} is a union of the values computed by cc-rs package plus the value that CFLAGS has in the calling environment

The ugly workaround I am using now

In case someone stumbles upon this issue and is desperate to fix their problem, here is a workaround that works right now. Add the following to your Rust CMake build configuration:

.configure_arg("-UCMAKE_ASM_FLAGS")
.configure_arg("-UCMAKE_C_FLAGS")
.configure_arg("-UCMAKE_CXX_FLAGS")

Undefining these value on the command line after they are defined somehow by cmake-rs will cause CMake to ignore them, preserving the intended value from the toolchain file.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions