Cannot build with vanilla clang on windows

I’m trying to build a CMake JUCE project using Ninja with clang (not clang-cl), with LLVM 12 on Windows.
While clang-cl does work, i’d like to unify the compiler flags between MacOS and Windows.
However, the building of juceaide fails, it seems like it tries to compile something with MSVC compiler flags.
This happens with the current juce-develop tip (commit 3fd4f7a2315ea05eabd1551e6603eb4fae17cdc4).

Error message:

-- The C compiler identification is Clang 12.0.0 with GNU-like command-line
-- The CXX compiler identification is Clang 12.0.0 with GNU-like command-line
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: C:/Program Files/LLVM/bin/clang.exe - 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: C:/Program Files/LLVM/bin/clang++.exe - skipped
-- Configuring juceaide
-- Building juceaide
CMake Error at extras/Build/juceaide/CMakeLists.txt:86 (message):
  Failed to build juceaide

  [0/2] Re-checking globbed directories...

  [1/8] Building CXX object
  extras/Build/juceaide/CMakeFiles/juceaide.dir/Main.cpp.obj

  FAILED: extras/Build/juceaide/CMakeFiles/juceaide.dir/Main.cpp.obj

  C:\PROGRA~1\LLVM\bin\CLANG_~1.EXE -DDEBUG=1
  -DJUCE_DISABLE_JUCE_VERSION_PRINTING=1
  -DJUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1
  -DJUCE_MODULE_AVAILABLE_juce_build_tools=1
  -DJUCE_MODULE_AVAILABLE_juce_core=1
  -DJUCE_MODULE_AVAILABLE_juce_data_structures=1
  -DJUCE_MODULE_AVAILABLE_juce_events=1
  -DJUCE_MODULE_AVAILABLE_juce_graphics=1
  -DJUCE_MODULE_AVAILABLE_juce_gui_basics=1 -DJUCE_STANDALONE_APPLICATION=1
  -DJUCE_USE_CURL=0 -D_CONSOLE=1 -D_DEBUG=1
  -Iextras/Build/juceaide/juceaide_artefacts/JuceLibraryCode
  -I../../extras/Build -I../../modules /bigobj /Od /EHsc /W4 -MD -MT
  extras/Build/juceaide/CMakeFiles/juceaide.dir/Main.cpp.obj -MF
  extras\Build\juceaide\CMakeFiles\juceaide.dir\Main.cpp.obj.d -o
  extras/Build/juceaide/CMakeFiles/juceaide.dir/Main.cpp.obj -c
  ../../extras/Build/juceaide/Main.cpp

  CLANG_~1: error: no such file or directory: '/bigobj'

  CLANG_~1: error: no such file or directory: '/Od'

  CLANG_~1: error: no such file or directory: '/EHsc'

  CLANG_~1: error: no such file or directory: '/W4'

Steps to reproduce:

git clone git@github.com:juce-framework/JUCE.git
mkdir JUCE/build && cd JUCE/build
cmake .. -G Ninja -DCC="C:\Program Files\LLVM\bin\clang.exe" -DCXX="C:\Program Files\LLVM\bin\clang++.exe"

Is there any way around this, or am i missing something?

I can reproduce the issue, but I’m not sure why it’s happening. It seems that the CMAKE_CXX_COMPILER_ID is set to MSVC even for Clang 11 with GNU-like command-line, at least with CMake 3.20. There’s not a great deal of documentation for CMAKE_CXX_COMPILER_ID so it’s not clear what the expected behaviour should be in this case, or whether there’s a recommended way of checking for Clang variants on Windows. I suspect that the meaning of “drop in replacement” in the docs refers to ABI, rather than to the commandline interface.

There’s also CMAKE_CXX_COMPILER_FRONTEND_VARIANT which is set to GNU in the case above, but this has even less documentation than CMAKE_CXX_COMPILER_ID so I’m not sure whether it’s alright to depend on it.

So i just tested the behaviour of CMAKE_CXX_COMPILER_ID and CMAKE_CXX_SIMULATE_ID on my machine, with the following result:

cmake .. -G "Visual Studio 16 2019"
CMAKE_CXX_COMPILER_ID: MSVC
CMAKE_CXX_SIMULATE_ID:
CMAKE_CXX_COMPILER_FRONTEND_VARIANT:

cmake .. -G "Visual Studio 16 2019" -T ClangCL
CMAKE_CXX_COMPILER_ID: Clang
CMAKE_CXX_SIMULATE_ID: MSVC
CMAKE_CXX_COMPILER_FRONTEND_VARIANT: MSVC

cmake .. -G Ninja -DCC="C:\Program Files\LLVM\bin\clang.exe" -DCXX="C:\Program Files\LLVM\bin\clang++.exe"
CMAKE_CXX_COMPILER_ID: Clang
CMAKE_CXX_SIMULATE_ID: MSVC
CMAKE_CXX_COMPILER_FRONTEND_VARIANT: GNU

cmake .. -G Ninja -DCC="C:\Program Files\LLVM\bin\clang-cl.exe" -DCXX="C:\Program Files\LLVM\bin\clang-cl.exe"
CMAKE_CXX_COMPILER_ID: Clang
CMAKE_CXX_SIMULATE_ID: MSVC
CMAKE_CXX_COMPILER_FRONTEND_VARIANT: GNU

This almost seems like a bug in CMake, clang.exe shouldn’t report any CMAKE_CXX_SIMULATE_ID, otherwise how should one distinguish between the two!

1 Like

Quoting a comment from a CMake issue:

Clang has two front-ends that both simulate MSVC (define _MSC_VER ): The MSVC-like front-end clang-cl and the GNU-like front-end clang / clang++ .

i.e. CMAKE_CXX_SIMULATE_ID would mean "define _MSC_VER", and not “can handle MSVC compiler flags”.

If this is correct, then the JUCE code needs to be adapted.

Thanks to both of you for the extra information. I’ve pushed this change which substitutes CMAKE_CXX_SIMULATE_ID for CMAKE_CXX_COMPILER_FRONTEND_VARIANT.

2 Likes

Notice that you can pass GNU flags to clang-cl. I took that approach. I will post my workaround the in case you don’t want to maintain own patches or use bleeding edge CMake.

This is a reduced example of how I’m doing:

cmake_minimum_required(VERSION 3.12.0)

...

if ("${CMAKE_BUILD_TYPE}" MATCHES "Release")
    set (ARTV_DSP_COMPILE_OPTIONS -O2 -ffast-math -ftree-vectorize -msse2)
    set (ARTV_PLAIN_COMPILE_OPTIONS -Os)
else()
    set (ARTV_DSP_COMPILE_OPTIONS -Wall)
    set (ARTV_PLAIN_COMPILE_OPTIONS  -Wall)
endif()

if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
    if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
        list (TRANSFORM ARTV_DSP_COMPILE_OPTIONS   PREPEND "/clang:")
        list (TRANSFORM ARTV_PLAIN_COMPILE_OPTIONS PREPEND "/clang:")
    endif()
endif()

add_library (plain_target_compile_options INTERFACE)
add_library (dsp_target_compile_options INTERFACE)

target_compile_options(
    plain_target_compile_options INTERFACE ${ARTV_PLAIN_COMPILE_OPTIONS}
    )
target_compile_options(
    dsp_target_compile_options INTERFACE ${ARTV_DSP_COMPILE_OPTIONS}
    )

This approach is using clang-cl and linking my libraries with interface libraries that provide the compile flags. This way:

  • Projects with old CMakeLists that automatically consider Windows->MSVC still keep working. Not everyone is so fast to patch as JUCE. It is rare at this point of time for imported projects to work right with GNU flags on Windows.
  • The global build flags are not modified. This allows imported projects to keep working normally (e.g. some require no ffast-math). Notice “/clang:-flag” flags are of higher precedence than those passed through the CL frontend. The compiler flag interface libraries have to be linked as the last one.

I don’t know if there is a better way of doing this.

Awesome!
I actually realized that i was using the wrong variables to change to compiler, and indeed the FRONTEND_VARIANT seems to be the correct switch:

cmake .. -G "Visual Studio 16 2019"
-- Selecting Windows SDK version 10.0.18362.0 to target Windows 10.0.19042.
-- The C compiler identification is MSVC 19.28.29914.0
-- The CXX compiler identification is MSVC 19.28.29914.0
-- Check for working C compiler: C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.28.29910/bin/Hostx64/x64/cl.exe - skipped
-- Check for working CXX compiler: C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.28.29910/bin/Hostx64/x64/cl.exe - skipped
CMAKE_CXX_COMPILER_ID: MSVC
CMAKE_CXX_SIMULATE_ID:
CMAKE_CXX_COMPILER_FRONTEND_VARIANT:

cmake .. -G "Visual Studio 16 2019" -T ClangCL
-- Selecting Windows SDK version 10.0.18362.0 to target Windows 10.0.19042.
-- The C compiler identification is Clang 11.0.0 with MSVC-like command-line
-- The CXX compiler identification is Clang 11.0.0 with MSVC-like command-line
-- Check for working C compiler: C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/Llvm/x64/bin/clang-cl.exe - skipped
-- Check for working CXX compiler: C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/Llvm/x64/bin/clang-cl.exe - skipped
CMAKE_CXX_COMPILER_ID: Clang
CMAKE_CXX_SIMULATE_ID: MSVC
CMAKE_CXX_COMPILER_FRONTEND_VARIANT: MSVC

cmake .. -G Ninja -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang
-- The C compiler identification is Clang 12.0.0 with GNU-like command-line
-- The CXX compiler identification is Clang 12.0.0 with GNU-like command-line
-- Check for working C compiler: C:/Program Files/LLVM/bin/clang.exe - skipped
-- Check for working CXX compiler: C:/Program Files/LLVM/bin/clang.exe - skipped
CMAKE_CXX_COMPILER_ID: Clang
CMAKE_CXX_SIMULATE_ID: MSVC
CMAKE_CXX_COMPILER_FRONTEND_VARIANT: GNU

cmake .. -G Ninja -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl
-- The C compiler identification is Clang 12.0.0 with MSVC-like command-line
-- The CXX compiler identification is Clang 12.0.0 with MSVC-like command-line
-- Check for working C compiler: C:/Program Files/LLVM/bin/clang-cl.exe - skipped
-- Check for working CXX compiler: C:/Program Files/LLVM/bin/clang-cl.exe - skipped
CMAKE_CXX_COMPILER_ID: Clang
CMAKE_CXX_SIMULATE_ID: MSVC
CMAKE_CXX_COMPILER_FRONTEND_VARIANT: MSVC

I’m still fighting with the linker, but this is a different story i guess :wink:
Thank you for the quick fix!

What linker problems are you having? I’m currently setting up clang with gnu-command-line on our CI system and it looks like clang’s LTO is incompatible with the MSVC linker. I’m working around this by passing -D CMAKE_CXX_FLAGS=-fuse-ld=lld-link.exe, which allows Release builds with LTO to link successfully.

Currently i’m using the VS2019 developer powershell, because otherwise some of the libraries (oldnames.lib and msvcrtd.lib to be specific) weren’t found, but this is just a search-path issue i guess.
The libs are found correctly in the dev-powershell, but the linker still encounters undefined symbols.
Did you encounter anything similar already? Maybe my setup is just broken, hehe.

cmake .. -G Ninja -D CMAKE_C_COMPILER=clang -D CMAKE_CXX_COMPILER=clang++
-- The C compiler identification is Clang 12.0.0 with GNU-like command-line
-- The CXX compiler identification is Clang 12.0.0 with GNU-like command-line
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - failed
-- Check for working C compiler: C:/Program Files/LLVM/bin/clang.exe
-- Check for working C compiler: C:/Program Files/LLVM/bin/clang.exe - broken
...

    Run Build Command(s):C:/ProgramData/chocolatey/bin/ninja.exe cmTC_3fdcc && [1/2] Building C object CMakeFiles/cmTC_3fdcc.dir/testCCompiler.c.obj
    [2/2] Linking C executable cmTC_3fdcc.exe
    FAILED: cmTC_3fdcc.exe
    cmd.exe /C "cd . && C:\PROGRA~1\LLVM\bin\clang.exe -fuse-ld=lld-link -nostartfiles -nostdlib -g -Xclang -gcodeview -O0 -D_DEBUG -D_DLL -D_MT -Xclang --dependent-lib=msvcrtd -fuse-ld=lld-link   -Xlinker /subsystem:console CMakeFiles/cmTC_3fdcc.dir/testCCompiler.c.obj -o cmTC_3fdcc.exe -Xlinker /implib:cmTC_3fdcc.lib -Xlinker /pdb:cmTC_3fdcc.pdb -Xlinker /version:0.0   -lkernel32 -luser32 -lgdi32 -lwinspool -lshell32 -lole32 -loleaut32 -luuid -lcomdlg32 -ladvapi32 -loldnames && cd ."
    lld-link: error: <root>: undefined symbol: mainCRTStartup
    clang: error: linker command failed with exit code 1 (use -v to see invocation)
    ninja: build stopped: subcommand failed.

Yes, I think this can happen when no runtime library is specified. If your cmake_minimum_required is set to 3.15 or greater, you can set the property MSVC_RUNTIME_LIBRARY or the variable CMAKE_MSVC_RUNTIME_LIBRARY to explicitly request a specific runtime library.

Just tried this, but it didn’t resolve the problem. But this is more of an cmake issue than a juce issue, i’ll seek help in the proper channels :slight_smile:

For the reference, this is the thread on the cmake-discorse forum:

I’ve finally tracked down the problem.
I’ve been using the Developer Powershell for VS 2019 to try the compilation using clang, since there all paths for the SDKs are set up correctly. But it seems like the powershell was configured for x86 compilation, which led to the linker errors i’ve encountered.
Using the x64 Native Tools Command Prompt for VS 2019, compilation works fine now.

1 Like