Can't compile VST3 with LLVM 18 on ARM Mac

I build my JUCE project on ARM Mac with the LLVM toolchain so I can use newer C++ features, since Apple clang lags very far behind.

Things have generally worked great with LLVM 17, but LLVM 18 has been out for quite a while and LLVM 19 is on its way. I’ve been trying to upgrade to LLVM 18 for a very long time, and recently finally figured out how to (mostly) get my JUCE project building with LLVM 18 on ARM Mac. There’s some info about it here: Unable to catch exceptions with clang++ 18.1.5 on macos ARM (try-catch does nothing) · Issue #92121 · llvm/llvm-project · GitHub

For reference, basically I just needed to add this to the correct place in the CMakeLists.txt:

if (CMAKE_SYSTEM_NAME MATCHES "Darwin" AND CMAKE_SYSTEM_PROCESSOR MATCHES "arm64"
        AND CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 18)
    link_libraries(/opt/homebrew/opt/llvm/lib/libunwind.dylib)
    set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L/opt/homebrew/opt/llvm/lib/c++")
endif ()

This resolves two problems. For some reason in LLVM 18 I needed to start explicitly specifying the lib c++ installed with LLVM. I guess I’ve been using the Apple lib c++ with LLVM 17 and that worked, but it doesn’t work with v18. The second fix (the libunwind piece) is a workaround for subclasses of std::exception not being catche-able (this might be Homebrew bug, which was used to install LLVM, but I’m not sure).

Anyway, I can build JUCE Standalone apps this way now, and everything seems to work as intended. But when I try to build a VST3, I get this error:

ld: Undefined symbols:
  std::exception_ptr::__from_native_exception_pointer(void*), referenced from:
      std::exception_ptr std::make_exception_ptr[abi:ne180100]<std::__1::future_error>(std::__1::future_error) in libMinimal Plugin Example_SharedCode.a[12](juce_core.mm.o)
  ___cxa_init_primary_exception, referenced from:
      std::exception_ptr std::make_exception_ptr[abi:ne180100]<std::__1::future_error>(std::__1::future_error) in libMinimal Plugin Example_SharedCode.a[12](juce_core.mm.o)
clang++: error: linker command failed with exit code 1 (use -v to see invocation)
ninja: build stopped: subcommand failed.

I am hoping there is a simple thing I can add to CMakeLists.txt to resolve this issue as well, but I have no idea how to figure it out. I’ve been Googling around and I’m not finding any information to help me.

Any ideas?

I’d also be grateful for any advice about how I can learn more about debugging and resolving linker errors like this. This aspect of C++ is still a huge mystery to me and I don’t even know where to begin. Maybe I am just missing a linker flag or link_libraries command? I’m not even sure if it’s an LLVM issue, CMake issue, Homebrew installation issue, etc, so I’m not even sure where to turn besides this JUCE forum to ask for help. Maybe I should just try opening an issue again LLVM in github, but I don’t want to come off as a complete newb who wastes these developers’ time.

I can publish a minimal CMake JUCE project to Github if anyone is willing to dig into this with me.

I think this is a known issue with the Brew-provided LLVM:

The workaround is to add this flag:

-L"$(brew --prefix llvm)"/lib/c++

Which you already do in your set(CMAKE_EXE_LINKER_FLAGS) modification - but maybe the prefix needs to be stated explicitly rather than through a symbolic (homebrew) link, i.e. using brews’ “–prefix” flag:

set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L"$(brew --prefix llvm)"/lib/c++")

… but pay closer attention to the followups in that Github bug report - there are more details there that could give you a clue - speficially the clang-using-header-files-installed-by-brew/but-using-system-provided-libc++ thing …

(PS - it goes without saying, but you do “brew update && brew upgrade && brew cleanup && brew doctor” at least once a week, right …? Because if you’re not, you need to: homebrew needs regular attendance…)

EDIT: This is an interesting clue about the problem, also:

Thank you for the reply @ibisum! I really appreciate it. I won’t have time to dig into this for a few days, but I will be taking a close look at all the info you provided. I will report back here.

I’ve been unable to resolve this. For the most part, the workarounds (adding -L"$(brew --prefix llvm)"/lib/c++ etc) work in my MacOS/LLVM C++ projects, but it doesn’t avoid the error when compiling JUCE projects for VST or AU. JUCE standalone apps work fine.

I guess it is some interaction because the LLVM + Homebrew + MacOS issues and difference in the build steps for JUCE plugins vs standalone.

I had to step back and start asking why I am spending time on this and why I care. I want to learn modern C++, so I am trying to use compilers with the latest features. I am going through books and doing exercises with those latest features. But I am not actually using any very new C++ features in my JUCE project.

Therefore, I decided to stop wasting time and only develop my JUCE projects against Apple clang or LLVM17 (the latest version I can get to work properly) for now. I have learned how to manage multiple C++ toolchains and switch to LLVM18 or 19 when I’m working through a modern C++ book separately from my JUCE project.

This is good enough for me for now. If anyone comes across this thread, has the same issue, and figures it out, please share!

I think personally it’s too risky using any compiler but AppleClang on Mac for deployment.

That’s because many times modern C++ features would just break in strange ways at runtime on older MacOS versions. AppleClang would tell you that during compilation but non-Apple LLVM would just let you compile and fail on a users machine.

Many STL features require an OS call for the implementation and it’s possible that call won’t exist unless the user is on the same OS as your build machine.

Having that said there are many equivalents available that would give you alternative implementations until AppleClang adds that construct.

So for example you can search GH for alternatives to std::filesystem, etc, that won’t compile depends on the OS you deploy to.

1 Like

Yeah, that sounds very reasonable. I think I will be doing this too. Thanks for the perspective, it’s very helpful.