Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Basic MinGW-w64 cross-compilation support #15070

Merged
merged 11 commits into from
Oct 11, 2024

Conversation

HertzDevil
Copy link
Contributor

@HertzDevil HertzDevil commented Oct 9, 2024

Resolves part of #6170. These series of patches allow --cross-compile --target=x86_64-windows-gnu to mostly work:

  • The @[ThreadLocal] annotation and its corresponding LLVM attribute seem to break when targetting x86_64-windows-gnu, so Win32 TLS is used instead. This is only needed for Thread.current.
  • Since MinGW uses libgcc, and Crystal relies on the underlying C++ ABI to raise exceptions, we use the Itanium ABI's _Unwind_* functions, along with a thin personality function wrapper. (GCC itself does the same. See also Language-specific handler from the Windows x64 ABI docs.)
  • MinGW binaries now come with DWARF debug information, so they work under GDB, maybe under LLDB, and probably poorly under the Visual Studio Debugger.
  • There is no need to mangle symbol names the same way MSVC binaries do.
  • --cross-compile now prints ld-style linker flags, rather than MSVC ones. This is still incomplete and includes remnants of the MSVC toolchain like the /ENTRY flag; they will be fixed later once we get to native compiler builds.
  • src/lib_c/x86_64-windows-gnu is now a symlink to src/lib_c/x86_64-windows-msvc, since both toolchains are targetting the same Win32 APIs. (This is not Cygwin nor MSYS2's MSYS environment.)
  • Lib funs now use the Win32 C ABI, instead of the SysV ABI.
  • On MinGW we use GMP proper, and there is no need for MPIR.

After building a local compiler, bin\crystal build --cross-compile --target=x86_64-windows-gnu will generate an object file suitable for linking under MinGW-w64. At a minimum, this object file depends on Boehm GC and libiconv, although they can be skipped using -Dgc_none and -Dwithout_iconv respectively. Then we could use MSYS2's UCRT64 environment to link the final executable:

$ pacman -Sy mingw-w64-ucrt-x86_64-gc mingw-w64-ucrt-x86_64-pcre2 mingw-w64-ucrt-x86_64-libiconv mingw-w64-ucrt-x86_64-gmp
$ cc test.obj `pkg-config bdw-gc iconv libpcre2-8 gmp --libs` -lDbgHelp -lole32

Stack traces do not work correctly yet. Also note that MSYS2's DLL names are different from the ones distributed with MSVC Crystal, and that cross-compilation never copies the DLL dependencies to the output directory. To make the executable run outside MSYS2, use dumpbin /dependents from the MSVC developer prompt to obtain the dependencies, then copy them manually from the MSYS2 /ucrt64/bin folder.

@HertzDevil HertzDevil added kind:feature platform:windows Windows support based on the MSVC toolchain platform:x86 labels Oct 9, 2024
@crysbot
Copy link

crysbot commented Oct 9, 2024

This pull request has been mentioned on Crystal Forum. There might be relevant details there:

https://forum.crystal-lang.org/t/port-to-windows-gnu/1801/7

@straight-shoota straight-shoota added this to the 1.15.0 milestone Oct 9, 2024
@straight-shoota straight-shoota merged commit d5600eb into crystal-lang:master Oct 11, 2024
66 checks passed
@HertzDevil HertzDevil deleted the feature/mingw-w64 branch October 11, 2024 12:42
straight-shoota pushed a commit that referenced this pull request Oct 19, 2024
This is a continuation of #15070 that allows a compiler built with MinGW-w64 to itself build programs correctly. Resolves part of #6170.

* Because linker flags for GCC may now be executed on a Windows environment, we use the correct form of argument quoting. We also drop `-rdynamic` since that only makes sense for ELF executables.
* Targetting `x86_64-windows-gnu`, including normal compilations from such a Crystal compiler, will not copy dependent DLLs to the output directory. Crystal itself and programs built under MSYS2 will just work as long as the proper environment is used. You are on your own here, although `ldd` exists on MSYS2 so that you don't need the MSVC build tools for this.
* The correct GCC compiler flag to select `wmain` over `main` as the C entry point is `-municode`. (The system entry point is presumably `_start` now.)
* `legacy_stdio_definitions.obj` doesn't exist on MinGW-w64, so we disable it outside MSVC.
* For build command lines that are too long on Windows, we use GCC's response file support.

To build a MinGW-w64 compiler:

```cmd
@Rem on the MSVC developer prompt
make -fMakefile.win crystal
bin\crystal build --cross-compile --target=x86_64-windows-gnu src\compiler\crystal.cr -Dwithout_interpreter
```

```sh
# on MSYS2's UCRT64 environment
pacman -Sy \
  mingw-w64-ucrt-x86_64-gc mingw-w64-ucrt-x86_64-pcre2 mingw-w64-ucrt-x86_64-libiconv \
  mingw-w64-ucrt-x86_64-zlib mingw-w64-ucrt-x86_64-openssl mingw-w64-ucrt-x86_64-llvm
cc crystal.obj -o crystal \
  $(pkg-config bdw-gc libpcre2-8 iconv zlib openssl --libs) \
  $(llvm-config --libs --system-libs --ldflags) \
  -lDbgHelp -lole32 -lWS2_32
export CRYSTAL_PATH='lib;$ORIGIN\src'
export CRYSTAL_LIBRARY_PATH=''
```

Now you can run or build a considerable number of files from here, such as `./crystal.exe samples/2048.cr` and `./crystal.exe spec spec/std/regex_spec.cr`. Notable omissions are OpenSSL and LLVM, as fixing their version detection macros is a bit complicated.

The interpreter is not supported. Most likely, `Crystal::Loader` would have a GCC-style `.parse`, but the rest of the functionality would be identical to the MSVC `LoadLibraryExW`-based loader.

~~Also, some invocations like `./crystal.exe spec spec/std/json` will fail since the whole command line string is too long. Similar to MSVC, [GCC also handles response files starting with `@`](https://gcc.gnu.org/onlinedocs/gcc/Overall-Options.html), so this can be implemented later; a workaround is to use `--single-module`.~~

For reference, here are all the useful MSYS2 packages and their corresponding pkg-config names:

| MSYS2 package name             | pkg-config name |
|-|-|
| mingw-w64-ucrt-x86_64-gc       | bdw-gc     |
| mingw-w64-ucrt-x86_64-pcre2    | libpcre2-8 |
| mingw-w64-ucrt-x86_64-libiconv | iconv      |
| mingw-w64-ucrt-x86_64-gmp      | gmp        |
| mingw-w64-ucrt-x86_64-zlib     | zlib       |
| mingw-w64-ucrt-x86_64-libxml2  | libxml-2.0 |
| mingw-w64-ucrt-x86_64-libyaml  | yaml-0.1   |
| mingw-w64-ucrt-x86_64-openssl  | openssl    |
| mingw-w64-ucrt-x86_64-libffi   | libffi     |
| mingw-w64-ucrt-x86_64-llvm     | _(use llvm-config instead)_ |
@HertzDevil HertzDevil added platform:windows-gnu Windows support based on the MinGW-w64 toolchain + MSYS2 and removed platform:windows Windows support based on the MSVC toolchain labels Oct 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind:feature platform:windows-gnu Windows support based on the MinGW-w64 toolchain + MSYS2 platform:x86
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

3 participants