Skip to content
Quinton Miller edited this page Dec 18, 2023 · 55 revisions

Note

This page is still a work in progress. An archive of the previous instructions can be found here: Porting to Windows (old)

This page will give some help and rules for developing Crystal on Windows. Ongoing efforts will be tracked and coordinated in #5430 and the Windows Support project.

Building Crystal natively

The following development tools are needed if you want to rebuild Crystal entirely on Windows. Unless otherwise noted, they should all be accessible in your current user's %PATH% environment variable.

Crystal

To rebuild Crystal, you need an existing installation of Crystal. Any installer or portable package on the Releases page should work, but since Windows support is still in a flux, the most recent stable release is preferred.

Microsoft Visual Studio

Microsoft Visual C++ is currently the only toolchain supported by Crystal on Windows. The Desktop development with C++ workload should be enabled in Visual Studio Installer. Also the following individual components should be checked:

  • MSVC v14x - VS 20xx C++ x64/x86 build tools (also available individually)
  • Windows 10 / 11 SDK (also available individually)
  • C++ ATL for latest v14x build tools (x86 & x64) (required for building LLVM)

The file %ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe should be available after a successful installation. The Crystal compiler relies on its existence to be able to build executables outside the Developer Command Prompt.

GNU Make

Crystal uses GNU Make as a command runner for various development tasks.

Git for Windows

Crystal and its third-party dependencies use Git as the version control system.

CMake

CMake is needed to build LLVM and some of Crystal's dependencies.

  • Website: https://cmake.org/
  • WinGet package: Kitware.CMake
  • Visual Studio Installer component: C++ CMake tools for Windows

LLVM

Crystal uses LLVM as its code generation backend. LLVM must be built from source, never installed using their release packages, because llvm-config.exe is only available via the former. The following build instructions, which should work on the stock PowerShell console that comes with Windows, are taken from .github/workflows/win.yml:

iwr https://github.com/llvm/llvm-project/releases/download/llvmorg-x.y.z/llvm-x.y.z.src.tar.xz -OutFile llvm.tar.xz
7z x llvm.tar.xz
7z x llvm.tar
mv llvm-* llvm-src

iwr https://github.com/llvm/llvm-project/releases/download/llvmorg-x.y.z/cmake-x.y.z.src.tar.xz -OutFile cmake.tar.xz
7z x cmake.tar.xz
7z x cmake.tar
mv cmake-* cmake

mkdir llvm-build
cd llvm-build
cmake ..\llvm-src -Thost=x64 -DLLVM_TARGETS_TO_BUILD="X86;AArch64" -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded -DBUILD_SHARED_LIBS=OFF -DCMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH=OFF -DLLVM_INCLUDE_BENCHMARKS=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_ENABLE_ZSTD=OFF
cmake --build . --config Release -j8
cmake "-DCMAKE_INSTALL_PREFIX=$(pwd)\..\llvm" "-DCMAKE_INSTALL_CONFIG_NAME=Release" -P cmake_install.cmake

where x.y.z is the LLVM version number, e.g. 17.0.6. It assumes that 7-Zip is installed and available in %PATH%; very new versions of Windows 11 can decompress those archives natively on the Windows Explorer without external tools. A successful build should produce a file called ...\llvm\bin\llvm-config.exe. The %LLVM_CONFIG% environment variable should point to this executable, so that Crystal knows which version of LLVM it is building against.

Warning

The %LLVM_CONFIG% environment variable must be defined. There are no plans to support src/llvm/ext/find-llvm-config on Windows.

Netwide Assembler (optional)

NASM is only required to build OpenSSL from source, and is normally not necessary since Crystal's Windows releases already include OpenSSL.

Strawberry Perl (optional)

Same as above, Strawberry Perl is only required to build OpenSSL from source. Since Strawberry Perl comes with its own MinGW tree, it is recommended to use its portableshell.bat to set up the environment on demand, rather than exposing its binary directories to %PATH%.

Inno Setup (optional)

Crystal's Windows installers are built using Inno Setup 6.

Cross-compiling Crystal

It is possible to cross-compile Crystal for Windows from a non-Windows system. Note that the Crystal repository has no provisions for cross-compiling Crystal's third-party dependencies.

(TODO)

Development

All commands in this section should be run inside an x64 Native Tools Command Prompt for VS 20xx.

Building a local development compiler

This is more or less the same as Linux, except the Makefile is always Makefile.win. The batch script bin/crystal.bat or PowerShell script bin/crystal.ps1 will use the standard library at the repository itself and pick up the local compiler if it exists.

git clone -c core.autocrlf=false https://github.com/crystal-lang/crystal.git
cd crystal
make -fMakefile.win crystal format release=1

Warning

The %CRYSTAL_PATH% environment variable should never be set, because Makefile.win populates the freshly built compiler with the same defaults as other platforms. If you have defined it system-wide in order to make previous compiler versions work, now is the time to unset it.

Building third-party dependencies

These steps are also taken from .github/workflows/win.yml. Start a PowerShell session within the Developer Command Prompt, then run the following at the repository's root directory:

.\etc\win-ci\build-gc.ps1 -BuildTree deps\gc -Version 8.2.4 -AtomicOpsVersion 7.8.0
.\etc\win-ci\build-pcre.ps1 -BuildTree deps\pcre -Version 8.45
.\etc\win-ci\build-pcre2.ps1 -BuildTree deps\pcre2 -Version 10.42
.\etc\win-ci\build-iconv.ps1 -BuildTree deps\iconv
.\etc\win-ci\build-ffi.ps1 -BuildTree deps\ffi -Version 3.3
.\etc\win-ci\build-z.ps1 -BuildTree deps\z -Version 1.2.13
.\etc\win-ci\build-mpir.ps1 -BuildTree deps\mpir
.\etc\win-ci\build-yaml.ps1 -BuildTree deps\yaml -Version 0.2.5
.\etc\win-ci\build-xml2.ps1 -BuildTree deps\xml2 -Version 2.11.3
.\etc\win-ci\build-openssl.ps1 -BuildTree deps\openssl -Version 3.1.0

Dynamic libraries are produced if -Dynamic is also supplied, otherwise static libraries are produced. The above will place the static or import libraries under libs/, and DLLs under dlls/.

Note

As mentioned above, NASM and Strawberry Perl are needed to build OpenSSL. Additionally the file libs/openssl_VERSION will contain the OpenSSL version number determined at build time. This file is required on Windows because there isn't a pkg-config equivalent to obtain this information.

Building an installer

To build the Windows installer, all the necessary files should be prepared under etc/win-ci/portable with the same structure as a portable package:

  • docs/*: standard library documentation
  • examples/*: sample programs
  • lib/*: static or import libraries
  • src/*: standard library
  • crystal.exe: the compiler
  • crystal.pdb: the compiler's debug symbols
  • LICENSE.txt: compiler license
  • README.md: compiler readme
  • shards.exe: the dependency manager
  • *.dll: dynamic libraries

Then run iscc.exe crystal.iss inside etc/win-ci/. Refer to the GitHub workflow jobs for details.

Dynamic linking

(TODO)

Guidelines

  • Conditionally compiled code should use flag?(:win32) for code using the Win32 functions, flag?(:windows) for any code on the Windows platform (potentially including non-MSVC toolchains in the future), and flag?(:msvc) for code directly related to the MSVC tools. When in doubt, flag?(:win32) will usually do.
  • Bindings for Win32 and C runtime APIs should be placed under src/lib_c/x86_64-windows-msvc/c. They should use LibC and their filenames should follow the original C headers where they are defined. (The bindings make no distinction between the shared, ucrt, and um subdirectories in the Windows SDK.)
  • All bindings should follow their original names as closely as possible. In particular, fun names should not be snakecased.
  • Specs should be marked with pending_win32 if they are supposed to work on Windows, but do not yet for reasons. Specs that aren't supposed to work at all should be disabled with {% unless flag?(:win32) %} instead. (There are only very few of these remaining in the whole repository.)