This is an archive of the discontinued LLVM Phabricator instance.

[CMake][WinMsvc] Fix user passed compiler/linker flags
ClosedPublic

Authored by ychen on Jan 5 2022, 4:26 PM.

Details

Summary

Users could pass flags by environment variables like CFLAGS/CXXFLAGS/LDFLAGS
or by using CMAKE_<LANG>_FLAGS_INIT/CMAKE_<t>_LINKER_FLAGS_INIT. So this
toolchain file should append to INIT flags instead. Otherwise, user
flags would be discarded here by assigning to CMAKE_<LANG>_FLAGS
directly.

Diff Detail

Event Timeline

ychen created this revision.Jan 5 2022, 4:26 PM
ychen requested review of this revision.Jan 5 2022, 4:26 PM
Herald added a project: Restricted Project. · View Herald TranscriptJan 5 2022, 4:26 PM
ychen updated this revision to Diff 397742.Jan 5 2022, 4:45 PM
  • update. ready for review

User could pass flags by environment variables like CFLAGS/CXXFLAGS/LDFLAGS or by using CMAKE_<LANG>_FLAGS_INIT/CMAKE_<t>_LINKER_FLAGS_INIT. So this toolchain file should append to INIT flags instead. Otherwise, user flags would be discarded here by assigning to CMAKE_<LANG>_FLAGS directly.

Can you elaborate a bit more here so I follow properly (with more explanation of exactly what cmake does here)? If the user has set e.g. CXXFLAGS, that does end up picked up by cmake and set in CMAKE_CXX_FLAGS_INIT - is that correct? What happened before, when we appended things into CMAKE_CXX_FLAGS without taking CMAKE_CXX_FLAGS_INIT into account - were the flags from CXXFLAGS and/or CMAKE_CXX_FLAGS_INIT missed/forgotten/overridden? Can you explain the implicit cmake behavious that are at play here?

Also, for the native tool build, target flags should not be used.

Flags specified to the main cmake build (CMAKE_<LANG>_FLAGS*) shouldn't be propagated automatically to the inner native cmake build, or am I missing something?

ychen added a comment.EditedJan 6 2022, 1:52 PM

User could pass flags by environment variables like CFLAGS/CXXFLAGS/LDFLAGS or by using CMAKE_<LANG>_FLAGS_INIT/CMAKE_<t>_LINKER_FLAGS_INIT. So this toolchain file should append to INIT flags instead. Otherwise, user flags would be discarded here by assigning to CMAKE_<LANG>_FLAGS directly.

Can you elaborate a bit more here so I follow properly (with more explanation of exactly what cmake does here)? If the user has set e.g. CXXFLAGS, that does end up picked up by cmake and set in CMAKE_CXX_FLAGS_INIT - is that correct? What happened before, when we appended things into CMAKE_CXX_FLAGS without taking CMAKE_CXX_FLAGS_INIT into account - were the flags from CXXFLAGS and/or CMAKE_CXX_FLAGS_INIT missed/forgotten/overridden? Can you explain the implicit cmake behavious that are at play here?

cmake uses CMAKE_<LANG>_FLAGS_INIT (INIT flags themselves are initialized by environment variables like CFLAGS/CXXFLAGS/LDFLAGS, or by a cmake toolchain file) to initialize CMAKE_<LANG>_FLAGS. If CMAKE_<LANG>_FLAGS is set by the user directly, cmake will not initialize it from CMAKE_<LANG>_FLAGS_INIT anymore, so the user-specified flags are lost. Docs on CMAKE_<LANG>_FLAGS_INIT suggests that the preferred behavior is for a toolchain file to manipulate INIT flags.

Also, for the native tool build, target flags should not be used.

Flags specified to the main cmake build (CMAKE_<LANG>_FLAGS*) shouldn't be propagated automatically to the inner native cmake build, or am I missing something?

That's right except when users specify flags by environment vars which will be picked up by sub-cmake through the above-described initialization process. Setting CMAKE_<LANG>_FLAGS to empty here is to avoid that.

mstorsjo accepted this revision.Jan 6 2022, 11:57 PM

Thanks for the explanations! LGTM.

This revision is now accepted and ready to land.Jan 6 2022, 11:57 PM
smeenai added inline comments.Jan 7 2022, 5:53 AM
llvm/cmake/platforms/WinMsvc.cmake
254–258

I'm not a fan of this. A pattern I like to follow is to pass a CMAKE_TOOLCHAIN_FILE to CROSS_TOOLCHAIN_FLAGS_NATIVE, so that the native build flags get configured via a toolchain file instead of ad-hoc variable setting. Pre-set cached copies of these variables will interfere with that toolchain file's ability to set flags, and in particular, it'll break the toolchain file's use of the *_INIT variables, which is kinda ironic given the purpose of this diff :)

Would unsetting the env vars in the toolchain file instead accomplish the same goal?

282–283

I think we can avoid having the two separate variables, and just do a direct setting, if we make sure this toolchain file is only evaluated once per CMake invocation. Now that we've raised our minimum CMake version, just adding

include_guard(GLOBAL)

at the top ought to do the trick.

It's fine to leave that for a follow-up change (or not do it at all).

ychen updated this revision to Diff 398262.Jan 7 2022, 3:45 PM
  • Use include guard
  • Add comment about NATIVE cmake build flags.
ychen added inline comments.Jan 7 2022, 3:49 PM
llvm/cmake/platforms/WinMsvc.cmake
254–258

I see what happened now. The native cmake config time is cross-build's build time. I should make sure these env vars are not set during the cross-build's build time.

282–283

Thanks. This works great.

smeenai accepted this revision.Jan 7 2022, 4:27 PM

Looks great! Thanks for cleaning this up :)

ychen edited the summary of this revision. (Show Details)Jan 7 2022, 4:34 PM

Somehow this change broke cross compiling for 32-bit windows using WinMsvc.cmake. Cmake ends up adding /machine:x64 to the linker flags during one of its first checks, and that fails.

Herald added a project: Restricted Project. · View Herald TranscriptMar 10 2022, 4:13 PM
ychen added a comment.Mar 10 2022, 4:24 PM

Somehow this change broke cross compiling for 32-bit windows using WinMsvc.cmake. Cmake ends up adding /machine:x64 to the linker flags during one of its first checks, and that fails.

Thanks for reporting this. What's your env output look like during config and build time? How do I reproduce it locally?

Somehow this change broke cross compiling for 32-bit windows using WinMsvc.cmake. Cmake ends up adding /machine:x64 to the linker flags during one of its first checks, and that fails.

Hmm. Are you passing any CMAKE_C_FLAGS or CMAKE_CXX_FLAGS directly as part of your configuration? That'd make the CMAKE_*_FLAGS_INIT in this file be ignored.

The only variables I'm passing: CMAKE_C_COMPILER_TARGET, CMAKE_CXX_COMPILER_TARGET, CMAKE_ASM_COMPILER_TARGET, CMAKE_BUILD_TYPE, LLVM_ENABLE_ASSERTIONS, LLVM_CONFIG_PATH, COMPILER_RT_DEFAULT_TARGET_ONLY, CMAKE_TOOLCHAIN_FILE, LLVM_NATIVE_TOOLCHAIN, MSVC_BASE, WINSDK_BASE, WINSDK_VER, HOST_ARCH.

CMakeCCompilerId.c ends up being compiled with no flags at all.

It still happens when I cut down to CMAKE_TOOLCHAIN_FILE, LLVM_NATIVE_TOOLCHAIN, MSVC_BASE, WINSDK_BASE, WINSDK_VER and HOST_ARCH.

It turns out CMAKE_*_FLAGS is only initialized from CMAKE_*_FLAGS_INIT _after_ CMAKE_DETERMINE_COMPILER_ID is done.