Windows Support (2025)
This PR is a new attempt to make codon work on Windows (#69 ) It isn't a continuation of the (very) dated #403 .
At the moment this PR doesn't work yet, serving primarily as visible progress and for communication. The commits marked as WIP should be removed/squashed before this draft PR becomes a real PR.
The setup I'm using is MinGW (13.2), LLVM (20.1.8), CMake (4.1.0), all installed via chocolatey.
The value of CODON_SYSTEM_LIBRARIES is currently irrelevant for Windows and the functionality might have to be adjusted slightly.
#675 might help a lot for making codon work well on Windows, as non-system shared libraries are a bit iffy on Windows, and well, actually even system ones due to some rare few breaking changes.
TODO: [x] no compile errors [ ] no linking errors [ ] tests pass [ ] lib copying works
Thank you for your pull request. We require contributors to agree to our Contributor License Agreement (https://exaloop.io/legal/cla), and we don't have @K0lb3 on file. In order for us to review and merge your code, please email [email protected] to get yourself added.
My first milestone for this PR is that the codon target compiles successfully. Therefore I "disabled" via comments for now.
llvm and MinGW from choco might be "incompatible" for this project, as the llvm package seems to be targeting msvc. I'm going to try llvm-mingw as toolchain tomorrow.
@cla-bot check
The cla-bot has been summoned, and re-checked this pull request!
Small update.
GCC x LLVM is quite the pain on Windows.
For GCC and its libs MinGW can be used just fine, but LLVM in combination with that is annoying. As far as I can see there is no with mingw precompiled llvm with cmake files for Windows. ~~Due to that either the relevant cmake logic would have to be "ported" to make use of the precompiled llvm-mingw-ucrt, or llvm would have to be compiled with cmake file generation.~~
~~I'm leaning towards the later as I don't want to bother with even more cmake madness.~~
llvm-mingw doesn't include some header files used by codon, so llvm compiling it is....
Well, more fun.
llvm-openmp uses an masm format asm for Windows (32x and 64x), and MinGW doesn't ship an masm assembler. So another requirement for Windows has to be added, a masm compiler... Let's see if JWasm works.
At the very least arm isn't affected by this, as llvm seems to use a (linux) gcc based asm for it. ~~How comes this somehow starts to look like a hellhole?~~
Oh, and to add on top, cmake seems to detect the asm compiler incorrectly for MinGW, it tries to use gcc instead of as....
long long story
My latest compile history is this (2025/7/18).
exaloop/codon v0.17, MSVC
- Side-projects can be fixed by adjust
CPM.cmake, or manually add needed libraries. codon/CMakeLists.txtcan be adjusted.- The part need fix is the exceptions in
codon/runtime/exc.cppwhere it usesunwind. Unwind is supported on macOS/Linux, but not supported in Windows UCRT and MinGW/MSYS2.
I also started to adjust some of the dependencies in the cmake/deps.cmake as TaiXeflar mentioned.
Using read.c & allloc.c instead of mmapio.c & mmap.c for backtrace,
switching from exaloop/bdwgc to a newer bdwgc/bdwgc,
using exaloop/openmp as openmp library for __kmpc_set_gc_callbacks.
@K0lb3 Do you joined exaloop/codon's DC server? maybe we can have a chat there
Well, more fun.
llvm-openmp uses an masm format asm for Windows (32x and 64x), and MinGW doesn't ship an masm assembler. So another requirement for Windows has to be added, a masm compiler... Let's see if JWasm works.
At the very least arm isn't affected by this, as llvm seems to use a (linux) gcc based asm for it. ~How comes this somehow starts to look like a hellhole?~
Oh, and to add on top, cmake seems to detect the asm compiler incorrectly for MinGW, it tries to use gcc instead of as....
@K0lb3 have you tried using MSYS2? I see they have the package mingw-w64-x86_64-llvm-openmp in their repository. If you install base-devel package (its dependencies are installed automatically) it provides you with a *nix development environment, but produces native Windows executables. You can install both GCC and clang.
P.S.: don't forget to check this page: Environments.
@MaD70
There's a assembly compile target at openmp/runtime/src/z_Windows_NT-586_asm.asm requires Macro Assembler where GCC toolchain doesn't have this. So is not any environment problems, it is GCC doesn't have Macro Assembler.
Available Macro Assembler is JWasm @K0lb3 using or by MSVC ml64.exe.
@MaD70 There's a assembly compile target at
openmp/runtime/src/z_Windows_NT-586_asm.asmrequires Macro Assembler where GCC toolchain doesn't have this. So is not any environment problems, it is GCC doesn't have Macro Assembler.Available Macro Assembler is JWasm @K0lb3 using or by MSVC
ml64.exe.
@TaiXeflar obviously you don't know what MSYS2 is and didn’t bother to look at the links I included in my message, did you?
MSYS2 packages are usually precompiled binaries. I quote from MSYS2 home page:
Our package repository contains more than 3500 pre-built packages ready to install.
Follow this link to mingw-w64-x86_64-llvm-openmp package: you'll see there is a separate link to sources and under "Build Dependencies" you'll see there another MSYS2 package listed, mingw-w64-x86_64-uasm, a free MASM-compatible assembler based on JWasm (mingw-w64). If required, the software can be rebuilt from sources from within MSYS2.
Super interesting @K0lb3 ! +1 for trying msys2 and a clang only toolchain. Any reason you did not start with such a setup? Any thoughts/ideas/recommendations on using MSVC?
I quickly tried with clang and this is what I get in the end. Any clues? Looks like missing symbols in an external lib, which is weird.
ld.lld: error: undefined symbol: GC_allow_register_threads
>>> referenced by CMakeFiles/codonrt.dir/codon/runtime/lib.cpp.obj:(seq_init)
ld.lld: error: undefined symbol: GC_register_my_thread
>>> referenced by CMakeFiles/codonrt.dir/codon/runtime/lib.cpp.obj:(seq_init)
ld.lld: error: undefined symbol: GC_remove_roots
>>> referenced by CMakeFiles/codonrt.dir/codon/runtime/lib.cpp.obj:(seq_init)
>>> referenced by CMakeFiles/codonrt.dir/codon/runtime/lib.cpp.obj:(seq_gc_remove_roots)
ld.lld: error: undefined symbol: __kmpc_set_gc_callbacks
>>> referenced by CMakeFiles/codonrt.dir/codon/runtime/lib.cpp.obj:(seq_init)
ld.lld: error: undefined symbol: GC_wait_for_reclaim
>>> referenced by libgc.a(alloc.c.obj):(GC_collect_a_little_inner)
>>> referenced by libgc.a(alloc.c.obj):(GC_collect_a_little_inner)
>>> referenced by libgc.a(alloc.c.obj):(GC_try_to_collect_inner)
>>> referenced 1 more times
ld.lld: error: undefined symbol: GC_stop_world
>>> referenced by libgc.a(alloc.c.obj):(GC_stopped_mark)
>>> referenced by libgc.a(misc.c.obj):(GC_stop_world_external)
ld.lld: error: undefined symbol: GC_start_world
>>> referenced by libgc.a(alloc.c.obj):(GC_stopped_mark)
>>> referenced by libgc.a(alloc.c.obj):(GC_stopped_mark)
>>> referenced by libgc.a(misc.c.obj):(GC_start_world_external)
ld.lld: error: undefined symbol: GC_allocate_ml
>>> referenced by libgc.a(alloc.c.obj):(.refptr.GC_allocate_ml)
>>> referenced by libgc.a(reclaim.c.obj)
>>> referenced by libgc.a(misc.c.obj)
ld.lld: error: undefined symbol: GC_need_to_lock
>>> referenced by libgc.a(alloc.c.obj):(.refptr.GC_need_to_lock)
>>> referenced by libgc.a(reclaim.c.obj)
>>> referenced by libgc.a(misc.c.obj)
ld.lld: error: undefined symbol: GC_thr_init
>>> referenced by libgc.a(misc.c.obj):(GC_init)
ld.lld: error: undefined symbol: GC_start_mark_threads_inner
>>> referenced by libgc.a(misc.c.obj):(GC_init)
>>> referenced by libgc.a(misc.c.obj):(GC_start_mark_threads)
ld.lld: error: undefined symbol: GC_init_parallel
>>> referenced by libgc.a(misc.c.obj):(GC_init)
ld.lld: error: undefined symbol: GC_do_blocking_inner
>>> referenced by libgc.a(misc.c.obj):(GC_do_blocking)
ld.lld: error: undefined symbol: GC_in_thread_creation
>>> referenced by libgc.a(misc.c.obj):(.refptr.GC_in_thread_creation)
ld.lld: error: undefined symbol: GC_push_all_stacks
>>> referenced by libgc.a(os_dep.c.obj):(GC_default_push_other_roots)
ld.lld: error: undefined symbol: GC_push_thread_structures
>>> referenced by libgc.a(mark_rts.c.obj):(GC_push_roots)
ld.lld: error: undefined symbol: GC_mark_thread_local_free_lists
>>> referenced by libgc.a(mark_rts.c.obj):(GC_push_roots)
ld.lld: error: undefined symbol: GC_acquire_mark_lock
>>> referenced by libgc.a(mark.c.obj):(GC_mark_some)
>>> referenced by libgc.a(mark.c.obj):(GC_wait_for_markers_init)
>>> referenced by libgc.a(mark.c.obj):(GC_mark_local)
>>> referenced 8 more times
ld.lld: error: undefined symbol: GC_notify_all_marker
>>> referenced by libgc.a(mark.c.obj):(GC_mark_some)
>>> referenced by libgc.a(mark.c.obj):(GC_mark_some)
>>> referenced by libgc.a(mark.c.obj):(GC_mark_local)
>>> referenced 3 more times
ld.lld: error: undefined symbol: GC_wait_marker
>>> referenced by libgc.a(mark.c.obj):(GC_mark_some)
>>> referenced by libgc.a(mark.c.obj):(GC_help_marker)
>>> referenced by libgc.a(mark.c.obj):(GC_mark_local)
I quickly tried with clang and this is what I get in the end. Any clues? Looks like missing symbols in an external lib, which is weird.
ld.lld: error: undefined symbol: GC_allow_register_threads >>> referenced by CMakeFiles/codonrt.dir/codon/runtime/lib.cpp.obj:(seq_init) ld.lld: error: undefined symbol: GC_register_my_thread >>> referenced by CMakeFiles/codonrt.dir/codon/runtime/lib.cpp.obj:(seq_init) ld.lld: error: undefined symbol: GC_remove_roots >>> referenced by CMakeFiles/codonrt.dir/codon/runtime/lib.cpp.obj:(seq_init) >>> referenced by CMakeFiles/codonrt.dir/codon/runtime/lib.cpp.obj:(seq_gc_remove_roots) ld.lld: error: undefined symbol: __kmpc_set_gc_callbacks >>> referenced by CMakeFiles/codonrt.dir/codon/runtime/lib.cpp.obj:(seq_init) ld.lld: error: undefined symbol: GC_wait_for_reclaim >>> referenced by libgc.a(alloc.c.obj):(GC_collect_a_little_inner) >>> referenced by libgc.a(alloc.c.obj):(GC_collect_a_little_inner) >>> referenced by libgc.a(alloc.c.obj):(GC_try_to_collect_inner) >>> referenced 1 more times ld.lld: error: undefined symbol: GC_stop_world >>> referenced by libgc.a(alloc.c.obj):(GC_stopped_mark) >>> referenced by libgc.a(misc.c.obj):(GC_stop_world_external) ld.lld: error: undefined symbol: GC_start_world >>> referenced by libgc.a(alloc.c.obj):(GC_stopped_mark) >>> referenced by libgc.a(alloc.c.obj):(GC_stopped_mark) >>> referenced by libgc.a(misc.c.obj):(GC_start_world_external) ld.lld: error: undefined symbol: GC_allocate_ml >>> referenced by libgc.a(alloc.c.obj):(.refptr.GC_allocate_ml) >>> referenced by libgc.a(reclaim.c.obj) >>> referenced by libgc.a(misc.c.obj) ld.lld: error: undefined symbol: GC_need_to_lock >>> referenced by libgc.a(alloc.c.obj):(.refptr.GC_need_to_lock) >>> referenced by libgc.a(reclaim.c.obj) >>> referenced by libgc.a(misc.c.obj) ld.lld: error: undefined symbol: GC_thr_init >>> referenced by libgc.a(misc.c.obj):(GC_init) ld.lld: error: undefined symbol: GC_start_mark_threads_inner >>> referenced by libgc.a(misc.c.obj):(GC_init) >>> referenced by libgc.a(misc.c.obj):(GC_start_mark_threads) ld.lld: error: undefined symbol: GC_init_parallel >>> referenced by libgc.a(misc.c.obj):(GC_init) ld.lld: error: undefined symbol: GC_do_blocking_inner >>> referenced by libgc.a(misc.c.obj):(GC_do_blocking) ld.lld: error: undefined symbol: GC_in_thread_creation >>> referenced by libgc.a(misc.c.obj):(.refptr.GC_in_thread_creation) ld.lld: error: undefined symbol: GC_push_all_stacks >>> referenced by libgc.a(os_dep.c.obj):(GC_default_push_other_roots) ld.lld: error: undefined symbol: GC_push_thread_structures >>> referenced by libgc.a(mark_rts.c.obj):(GC_push_roots) ld.lld: error: undefined symbol: GC_mark_thread_local_free_lists >>> referenced by libgc.a(mark_rts.c.obj):(GC_push_roots) ld.lld: error: undefined symbol: GC_acquire_mark_lock >>> referenced by libgc.a(mark.c.obj):(GC_mark_some) >>> referenced by libgc.a(mark.c.obj):(GC_wait_for_markers_init) >>> referenced by libgc.a(mark.c.obj):(GC_mark_local) >>> referenced 8 more times ld.lld: error: undefined symbol: GC_notify_all_marker >>> referenced by libgc.a(mark.c.obj):(GC_mark_some) >>> referenced by libgc.a(mark.c.obj):(GC_mark_some) >>> referenced by libgc.a(mark.c.obj):(GC_mark_local) >>> referenced 3 more times ld.lld: error: undefined symbol: GC_wait_marker >>> referenced by libgc.a(mark.c.obj):(GC_mark_some) >>> referenced by libgc.a(mark.c.obj):(GC_help_marker) >>> referenced by libgc.a(mark.c.obj):(GC_mark_local)
Missing GC static libraries. This library is avail on macOS/Linux, not installed on Windows by default. And available toolchains(VS20XX/oneAPI/HIP/Strawberry) didn't provide GC libraries.
I quickly tried with clang and this is what I get in the end. Any clues? Looks like missing symbols in an external lib, which is weird.
ld.lld: error: undefined symbol: GC_allow_register_threads >>> referenced by CMakeFiles/codonrt.dir/codon/runtime/lib.cpp.obj:(seq_init) ld.lld: error: undefined symbol: GC_register_my_thread >>> referenced by CMakeFiles/codonrt.dir/codon/runtime/lib.cpp.obj:(seq_init) [snipped]
@adam-urbanczyk I guess you don't have the Boehm Garbage Collector library available in your path. If you are using MSYS2, it is available as package mingw-w64-x86_64-gc. Just install it with:
pacman -S mingw-w64-x86_64-gc
and then try to recompile.
Hm, AFAICT bdwgc is compiled as a submodule of this project via CPM. Maybe wrong configuration then?
__kmpc_set_gc_callbacks seems to be explained here: https://github.com/exaloop/codon/issues/671#issuecomment-3152309213
If I understand this correctly, I need to at least build a custom OpenMP runtime and link against it.
@adam-urbanczyk oh yes, I now see that in deps.cmake and they are using a local fork in exaloop/bdwgc, not the canonical source in bdwgc/bdwgc.
They also have exaloop/llvm-project, that is an LLVM fork based on LLVM 20 (instruction to build it) and exaloop/openmp, a LLVM OpenMP fork with GC hooks. But at the moment I can't find how this custom version of OpenMP is imported by Codon.
Is it an option to we get windows compiling without openmp at first? So the scope is smaller and the task is more likely to be done .. ever?