diff --git a/Makefile b/Makefile index e56a14a27c1c..b39c089bef99 100644 --- a/Makefile +++ b/Makefile @@ -48,7 +48,8 @@ CRYSTAL_CONFIG_BUILD_COMMIT ?= $(shell git rev-parse --short HEAD 2> /dev/null) CRYSTAL_CONFIG_PATH := '$$ORIGIN/../share/crystal/src' CRYSTAL_VERSION ?= $(shell cat src/VERSION) SOURCE_DATE_EPOCH ?= $(shell (cat src/SOURCE_DATE_EPOCH || (git show -s --format=%ct HEAD || stat -c "%Y" Makefile || stat -f "%m" Makefile)) 2> /dev/null) -ifeq ($(shell command -v ld.lld >/dev/null && uname -s),Linux) +check_lld := command -v ld.lld >/dev/null && case "$$(uname -s)" in MINGW32*|MINGW64*|Linux) echo 1;; esac +ifeq ($(shell $(check_lld)),1) EXPORT_CC ?= CC="$(CC) -fuse-ld=lld" endif EXPORTS := \ @@ -60,11 +61,20 @@ EXPORTS_BUILD := \ CRYSTAL_CONFIG_LIBRARY_PATH=$(CRYSTAL_CONFIG_LIBRARY_PATH) SHELL = sh LLVM_CONFIG := $(shell src/llvm/ext/find-llvm-config) -LLVM_VERSION := $(if $(LLVM_CONFIG),$(shell $(LLVM_CONFIG) --version 2> /dev/null)) +LLVM_VERSION := $(if $(LLVM_CONFIG),$(shell "$(LLVM_CONFIG)" --version 2> /dev/null)) LLVM_EXT_DIR = src/llvm/ext LLVM_EXT_OBJ = $(LLVM_EXT_DIR)/llvm_ext.o CXXFLAGS += $(if $(debug),-g -O0) +# MSYS2 support (native Windows should use `Makefile.win` instead) +ifeq ($(OS),Windows_NT) + CRYSTAL_BIN := crystal.exe + WINDOWS := 1 +else + CRYSTAL_BIN := crystal + WINDOWS := +endif + DESTDIR ?= PREFIX ?= /usr/local BINDIR ?= $(DESTDIR)$(PREFIX)/bin @@ -74,9 +84,9 @@ DATADIR ?= $(DESTDIR)$(PREFIX)/share/crystal INSTALL ?= /usr/bin/install ifeq ($(or $(TERM),$(TERM),dumb),dumb) - colorize = $(shell printf >&2 "$1") + colorize = $(shell printf "%s" "$1" >&2) else - colorize = $(shell printf >&2 "\033[33m$1\033[0m\n") + colorize = $(shell printf "\033[33m%s\033[0m\n" "$1" >&2) endif DEPS = $(LLVM_EXT_OBJ) @@ -119,7 +129,7 @@ interpreter_spec: $(O)/interpreter_spec ## Run interpreter specs .PHONY: smoke_test smoke_test: ## Build specs as a smoke test -smoke_test: $(O)/std_spec $(O)/compiler_spec $(O)/crystal +smoke_test: $(O)/std_spec $(O)/compiler_spec $(O)/$(CRYSTAL_BIN) .PHONY: all_spec all_spec: $(O)/all_spec ## Run all specs (note: this builds a huge program; `test` recipe builds individual binaries and is recommended for reduced resource usage) @@ -136,7 +146,7 @@ docs: ## Generate standard library documentation cp -av doc/ docs/ .PHONY: crystal -crystal: $(O)/crystal ## Build the compiler +crystal: $(O)/$(CRYSTAL_BIN) ## Build the compiler .PHONY: deps llvm_ext deps: $(DEPS) ## Build dependencies @@ -151,9 +161,9 @@ generate_data: ## Run generator scripts for Unicode, SSL config, ... $(MAKE) -B -f scripts/generate_data.mk .PHONY: install -install: $(O)/crystal man/crystal.1.gz ## Install the compiler at DESTDIR +install: $(O)/$(CRYSTAL_BIN) man/crystal.1.gz ## Install the compiler at DESTDIR $(INSTALL) -d -m 0755 "$(BINDIR)/" - $(INSTALL) -m 0755 "$(O)/crystal" "$(BINDIR)/crystal" + $(INSTALL) -m 0755 "$(O)/$(CRYSTAL_BIN)" "$(BINDIR)/$(CRYSTAL_BIN)" $(INSTALL) -d -m 0755 $(DATADIR) cp -av src "$(DATADIR)/src" @@ -171,9 +181,16 @@ install: $(O)/crystal man/crystal.1.gz ## Install the compiler at DESTDIR $(INSTALL) -d -m 0755 "$(DESTDIR)$(PREFIX)/share/fish/vendor_completions.d/" $(INSTALL) -m 644 etc/completion.fish "$(DESTDIR)$(PREFIX)/share/fish/vendor_completions.d/crystal.fish" +ifeq ($(WINDOWS),1) +.PHONY: install_dlls +install_dlls: $(O)/$(CRYSTAL_BIN) ## Install the compiler's dependent DLLs at DESTDIR (Windows only) + $(INSTALL) -d -m 0755 "$(BINDIR)/" + @ldd $(O)/$(CRYSTAL_BIN) | grep -iv ' => /c/windows/system32' | sed 's/.* => //; s/ (.*//' | xargs -t -i $(INSTALL) -m 0755 '{}' "$(BINDIR)/" +endif + .PHONY: uninstall uninstall: ## Uninstall the compiler from DESTDIR - rm -f "$(BINDIR)/crystal" + rm -f "$(BINDIR)/$(CRYSTAL_BIN)" rm -rf "$(DATADIR)/src" @@ -210,7 +227,7 @@ $(O)/compiler_spec: $(DEPS) $(SOURCES) $(SPEC_SOURCES) @mkdir -p $(O) $(EXPORT_CC) $(EXPORTS) ./bin/crystal build $(FLAGS) $(SPEC_WARNINGS_OFF) -o $@ spec/compiler_spec.cr --release -$(O)/primitives_spec: $(O)/crystal $(DEPS) $(SOURCES) $(SPEC_SOURCES) +$(O)/primitives_spec: $(O)/$(CRYSTAL_BIN) $(DEPS) $(SOURCES) $(SPEC_SOURCES) @mkdir -p $(O) $(EXPORT_CC) ./bin/crystal build $(FLAGS) $(SPEC_WARNINGS_OFF) -o $@ spec/primitives_spec.cr @@ -219,12 +236,15 @@ $(O)/interpreter_spec: $(DEPS) $(SOURCES) $(SPEC_SOURCES) @mkdir -p $(O) $(EXPORT_CC) ./bin/crystal build $(FLAGS) $(SPEC_WARNINGS_OFF) -o $@ spec/compiler/interpreter_spec.cr -$(O)/crystal: $(DEPS) $(SOURCES) +$(O)/$(CRYSTAL_BIN): $(DEPS) $(SOURCES) $(call check_llvm_config) @mkdir -p $(O) @# NOTE: USE_PCRE1 is only used for testing compatibility with legacy environments that don't provide libpcre2. @# Newly built compilers should never be distributed with libpcre to ensure syntax consistency. - $(EXPORTS) $(EXPORTS_BUILD) ./bin/crystal build $(FLAGS) -o $@ src/compiler/crystal.cr -D without_openssl -D without_zlib $(if $(USE_PCRE1),-D use_pcre,-D use_pcre2) + $(EXPORTS) $(EXPORTS_BUILD) ./bin/crystal build $(FLAGS) -o $(if $(WINDOWS),$(O)/crystal-next.exe,$@) src/compiler/crystal.cr -D without_openssl -D without_zlib $(if $(USE_PCRE1),-D use_pcre,-D use_pcre2) + @# NOTE: on MSYS2 it is not possible to overwrite a running program, so the compiler must be first built with + @# a different filename and then moved to the final destination. + $(if $(WINDOWS),mv $(O)/crystal-next.exe $@) $(LLVM_EXT_OBJ): $(LLVM_EXT_DIR)/llvm_ext.cc $(call check_llvm_config) diff --git a/bin/crystal b/bin/crystal index 2282cbefec80..e8abdff30ee8 100755 --- a/bin/crystal +++ b/bin/crystal @@ -184,9 +184,18 @@ fi # with symlinks resolved as well (see https://github.com/crystal-lang/crystal/issues/12969). cd "$(realpath "$(pwd)")" -if [ -x "$CRYSTAL_DIR/crystal" ]; then - __warning_msg "Using compiled compiler at ${CRYSTAL_DIR#"$PWD/"}/crystal" - exec "$CRYSTAL_DIR/crystal" "$@" +case "$(uname -s)" in + CYGWIN*|MSYS_NT*|MINGW32_NT*|MINGW64_NT*) + CRYSTAL_BIN="crystal.exe" + ;; + *) + CRYSTAL_BIN="crystal" + ;; +esac + +if [ -x "$CRYSTAL_DIR/${CRYSTAL_BIN}" ]; then + __warning_msg "Using compiled compiler at ${CRYSTAL_DIR#"$PWD/"}/${CRYSTAL_BIN}" + exec "$CRYSTAL_DIR/${CRYSTAL_BIN}" "$@" elif !($PARENT_CRYSTAL_EXISTS); then __error_msg 'You need to have a crystal executable in your path! or set CRYSTAL env variable' exit 1 diff --git a/src/compiler/crystal/compiler.cr b/src/compiler/crystal/compiler.cr index aa11ef1dc47e..6c7664bacc25 100644 --- a/src/compiler/crystal/compiler.cr +++ b/src/compiler/crystal/compiler.cr @@ -476,6 +476,7 @@ module Crystal {DEFAULT_LINKER, %(#{DEFAULT_LINKER} "${@}" -o #{Process.quote_posix(output_filename)} #{link_flags} #{program.lib_flags(@cross_compile)}), object_names} elsif program.has_flag?("win32") && program.has_flag?("gnu") link_flags = @link_flags || "" + link_flags += " -Wl,--stack,0x800000" lib_flags = program.lib_flags(@cross_compile) lib_flags = expand_lib_flags(lib_flags) if expand cmd = %(#{DEFAULT_LINKER} #{Process.quote_windows(object_names)} -o #{Process.quote_windows(output_filename)} #{link_flags} #{lib_flags}) diff --git a/src/llvm/ext/find-llvm-config b/src/llvm/ext/find-llvm-config index 5f40a1f2e6a3..5aa381aaf13b 100755 --- a/src/llvm/ext/find-llvm-config +++ b/src/llvm/ext/find-llvm-config @@ -16,7 +16,14 @@ if ! LLVM_CONFIG=$(command -v "$LLVM_CONFIG"); then fi if [ "$LLVM_CONFIG" ]; then - printf %s "$LLVM_CONFIG" + case "$(uname -s)" in + MINGW32_NT*|MINGW64_NT*) + printf "%s" "$(cygpath -w "$LLVM_CONFIG")" + ;; + *) + printf "%s" "$LLVM_CONFIG" + ;; + esac else printf "Error: Could not find location of llvm-config. Please specify path in environment variable LLVM_CONFIG.\n" >&2 printf "Supported LLVM versions: $(cat "$(dirname $0)/llvm-versions.txt" | sed 's/\.0//g')\n" >&2 diff --git a/src/llvm/lib_llvm.cr b/src/llvm/lib_llvm.cr index 4c7ed49e7900..8b6856631b55 100644 --- a/src/llvm/lib_llvm.cr +++ b/src/llvm/lib_llvm.cr @@ -1,5 +1,5 @@ {% begin %} - {% if flag?(:win32) && !flag?(:static) %} + {% if flag?(:msvc) && !flag?(:static) %} {% config = nil %} {% for dir in Crystal::LIBRARY_PATH.split(Crystal::System::Process::HOST_PATH_DELIMITER) %} {% config ||= read_file?("#{dir.id}/llvm_VERSION") %} @@ -21,7 +21,7 @@ lib LibLLVM end {% else %} - {% llvm_config = env("LLVM_CONFIG") || `#{__DIR__}/ext/find-llvm-config`.stringify %} + {% llvm_config = env("LLVM_CONFIG") || `sh #{__DIR__}/ext/find-llvm-config`.stringify %} {% llvm_version = `#{llvm_config.id} --version`.stringify %} {% llvm_targets = env("LLVM_TARGETS") || `#{llvm_config.id} --targets-built`.stringify %} {% llvm_ldflags = "`#{llvm_config.id} --libs --system-libs --ldflags#{" --link-static".id if flag?(:static)}#{" 2> /dev/null".id unless flag?(:win32)}`" %}