Skip to content

Commit 2fd6333

Browse files
committed
Makefile: support dist-based cross builds with WASM/JS configuration
Add infrastructure for running stage3 cross-compilation from a pre-built dist artifact (DIST_BUILD=1 mode), as used in CI where build and cross-compilation run in separate jobs. Makefile changes: - Add DIST_BUILD guards to skip stage1/stage2/hackage targets when building from a dist artifact (only stage3 cross-build runs) - Add overridable tool paths: GHC_TOOLCHAIN_BIN, DERIVE_CONSTANTS_BIN, GENAPPLY_BIN, HAPPY_TEMPLATE_DIR for dist-based builds - Copy cross-compilation tools (ghc-toolchain-bin, deriveConstants, genapply, cabal, happy-lib templates) into dist artifact - Add CPP/HS_CPP/CMM_CPP settings for JS (emcc) and WASM (wasm32-wasi-clang) cross-compilation targets - Use stamp files as prerequisites instead of phony stage targets to avoid redundant rebuilds after intermediate cleanup - Use $(abspath ...) for tool paths passed to ghc-toolchain cabal.project.stage3: - Disable `shared: True` under `if os(wasi)` — this setting applies to BUILD packages too (e.g. happy-lib), causing -dynamic-too which fails because the build compiler's dist lacks .dyn_hi files. Re-enable once cabal supports separate BUILD/HOST package settings.
1 parent 7c25bcf commit 2fd6333

2 files changed

Lines changed: 72 additions & 13 deletions

File tree

Makefile

Lines changed: 61 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,13 @@ endif
152152

153153
GHC_TOOLCHAIN_ARGS = --disable-ld-override
154154

155+
# Cross-compilation tool paths: default to stage1 build tree; can be
156+
# overridden for dist-based cross builds (CI) where only the dist
157+
# artifact is available.
158+
GHC_TOOLCHAIN_BIN ?= $(STAGE1_PATH)/bin/ghc-toolchain-bin
159+
DERIVE_CONSTANTS_BIN ?= $(STAGE1_PATH)/bin/deriveConstants
160+
GENAPPLY_BIN ?= $(STAGE1_PATH)/bin/genapply
161+
155162
#
156163
# Build directories and paths
157164
#
@@ -589,6 +596,7 @@ STAGE1_CABAL_BUILD = \
589596
--with-build-compiler=$(GHC0) \
590597
--ghc-options "-ghcversion-file=$(call NORMALIZE_FP,$(CURDIR)/rts/include/ghcversion.h)"
591598

599+
ifndef DIST_BUILD
592600
stage1: STAGE=stage1
593601
stage1: stable-cabal $(CONFIGURE_SCRIPTS) $(CONFIGURED_FILES) cabal.project.stage1 cabal.project.common libraries/ghc-boot-th-next | hackage
594602
$(call PHASE_START,stage1)
@@ -613,7 +621,12 @@ endif
613621
$(call PHASE_END_OK,stage1)
614622
@touch $(STAGE1_STAMP)
615623

616-
$(addprefix $(STAGE1_PATH)/bin/,$(STAGE1_EXECUTABLES)) : stage1
624+
# Stage1 binaries are produced by the stage1 target (which touches STAGE1_STAMP
625+
# on success). Use the stamp as prerequisite so Make doesn't re-run the full
626+
# stage1 build when the binaries already exist (e.g. after intermediate cleanup
627+
# in CI removes the cabal build cache but preserves the store).
628+
$(addprefix $(STAGE1_PATH)/bin/,$(STAGE1_EXECUTABLES)) : $(STAGE1_STAMP)
629+
endif # DIST_BUILD (stage1)
617630

618631
# ____ _ ____
619632
# / ___|| |_ __ _ __ _ ___ |___ \
@@ -709,9 +722,10 @@ STAGE2_CABAL_BUILD = \
709722
$(foreach dir,$(STAGE2_EXTRA_LIB_DIRS),--extra-lib-dirs=$(dir)) \
710723
$(foreach dir,$(STAGE2_EXTRA_INCLUDE_DIRS),--extra-include-dirs=$(dir))
711724

725+
ifndef DIST_BUILD
712726
stage2: STAGE=stage2
713727
stage2: TARGET_PLATFORM:=$(HOST_PLATFORM)
714-
stage2: $(GHC1) stable-cabal $(CONFIGURE_SCRIPTS) $(CONFIGURED_FILES) cabal.project.stage2 cabal.project.stage2.settings cabal.project.common libraries/ghc-boot-th-next | stage1
728+
stage2: $(GHC1) stable-cabal $(CONFIGURE_SCRIPTS) $(CONFIGURED_FILES) cabal.project.stage2 cabal.project.stage2.settings cabal.project.common libraries/ghc-boot-th-next | $(STAGE1_STAMP)
715729
$(call PHASE_START,stage2)
716730
$(call LOG,Starting build of $(STAGE))
717731

@@ -771,12 +785,28 @@ endif
771785
@cp -rfp driver/ghc-usage.txt $(DIST_DIR)/lib/
772786
@cp -rfp driver/ghci-usage.txt $(DIST_DIR)/lib/
773787

788+
# Copy stage1 tools needed for cross-compilation from dist
789+
$(call LOG,Copying cross-compilation tools to $(DIST_DIR)/bin)
790+
@cp -fp $(STAGE1_PATH)/bin/ghc-toolchain-bin $(DIST_DIR)/bin/ghc-toolchain-bin
791+
@cp -fp $(STAGE1_PATH)/bin/deriveConstants $(DIST_DIR)/bin/deriveConstants
792+
@cp -fp $(STAGE1_PATH)/bin/genapply $(DIST_DIR)/bin/genapply
793+
794+
# Copy stage0 cabal (needed for cross-compilation from dist, has -W flag)
795+
$(call LOG,Copying cabal to $(DIST_DIR)/bin)
796+
@cp -fp $(CABAL) $(DIST_DIR)/bin/cabal
797+
798+
# Copy happy-lib templates (needed for cross-compilation from dist)
799+
$(call LOG,Copying happy-lib templates to $(DIST_DIR)/share)
800+
@mkdir -p $(DIST_DIR)/share/happy-lib/data
801+
@cp -rfp _build/stage2/src/happy-lib-2.1.5/data/* $(DIST_DIR)/share/happy-lib/data/
802+
774803
$(call LOG,Finished building $(STAGE) in $(DIST_DIR))
775804
$(call PHASE_END_OK,stage2.dist)
776805
$(call PHASE_END_OK,stage2)
777806
@touch $(STAGE2_STAMP)
778807

779-
$(addprefix $(STAGE2_PATH)/bin/,$(STAGE2_EXECUTABLES)) : stage2
808+
$(addprefix $(STAGE2_PATH)/bin/,$(STAGE2_EXECUTABLES)) : $(STAGE2_STAMP)
809+
endif # DIST_BUILD (stage2)
780810

781811
# ____ _ _____
782812
# / ___|| |_ __ _ __ _ ___ |___ /
@@ -864,6 +894,9 @@ STAGE3_javascript-unknown-ghcjs_CC = emcc
864894
STAGE3_javascript-unknown-ghcjs_CC_OPTS =
865895
STAGE3_javascript-unknown-ghcjs_CXX = em++
866896
STAGE3_javascript-unknown-ghcjs_CXX_OPTS =
897+
STAGE3_javascript-unknown-ghcjs_CPP = emcc
898+
STAGE3_javascript-unknown-ghcjs_HS_CPP = emcc
899+
STAGE3_javascript-unknown-ghcjs_CMM_CPP = emcc
867900
STAGE3_javascript-unknown-ghcjs_EXTRA_INCLUDE_DIRS =
868901
STAGE3_javascript-unknown-ghcjs_EXTRA_LIB_DIRS =
869902
STAGE3_javascript-unknown-ghcjs_LD = emcc
@@ -876,6 +909,9 @@ STAGE3_wasm32-unknown-wasi_CC = wasm32-wasi-clang
876909
STAGE3_wasm32-unknown-wasi_CC_OPTS = -fno-strict-aliasing -Wno-error=int-conversion -Oz -msimd128 -mnontrapping-fptoint -msign-ext -mbulk-memory -mmutable-globals -mmultivalue -mreference-types
877910
STAGE3_wasm32-unknown-wasi_CXX = wasm32-wasi-clang++
878911
STAGE3_wasm32-unknown-wasi_CXX_OPTS = $(STAGE3_wasm32-unknown-wasi_CC_OPTS) -fno-exceptions
912+
STAGE3_wasm32-unknown-wasi_CPP = wasm32-wasi-clang
913+
STAGE3_wasm32-unknown-wasi_HS_CPP = wasm32-wasi-clang
914+
STAGE3_wasm32-unknown-wasi_CMM_CPP = wasm32-wasi-clang
879915
STAGE3_wasm32-unknown-wasi_AR = wasm32-wasi-ar
880916
STAGE3_wasm32-unknown-wasi_RANLIB = wasm32-wasi-ranlib
881917
STAGE3_wasm32-unknown-wasi_EXTRA_INCLUDE_DIRS =
@@ -885,15 +921,19 @@ STAGE3_wasm32-unknown-wasi_GHC_TOOLCHAIN_ARGS = $(GHC_TOOLCHAIN_ARGS) --merge-ob
885921

886922
TARGET_DIR = $(DIST_DIR)/lib/targets/$(TARGET_PLATFORM)
887923

924+
# Happy template directory: defaults to stage2's unpacked happy-lib source; can
925+
# be overridden for dist-based cross builds where stage2 build tree is unavailable.
926+
HAPPY_TEMPLATE_DIR ?= _build/stage2/src/happy-lib-2.1.5/data/
927+
888928
# NOTE: disable-library-for-ghci is repeated here but it should be sufficient
889929
# to put it in cabal.project.stage3
890930

891931
define stage3
892932

893933
STAGE3_$(1)_CABAL_BUILD = \
894934
env \
895-
DERIVE_CONSTANTS=$$(call NORMALIZE_FP,$$(CURDIR)/$$(STAGE1_PATH)/bin/deriveConstants) \
896-
GENAPPLY=$$(call NORMALIZE_FP,$$(CURDIR)/$$(STAGE1_PATH)/bin/genapply) \
935+
DERIVE_CONSTANTS=$$(call NORMALIZE_FP,$$(abspath $$(DERIVE_CONSTANTS_BIN))) \
936+
GENAPPLY=$$(call NORMALIZE_FP,$$(abspath $$(GENAPPLY_BIN))) \
897937
NM=$$(STAGE3_$(1)_NM) \
898938
OBJDUMP=$$(STAGE3_$(1)_OBJDUMP) \
899939
$$(CABAL_BUILD) \
@@ -902,27 +942,39 @@ STAGE3_$(1)_CABAL_BUILD = \
902942
--ghc-options "-ghcversion-file=$$(call NORMALIZE_FP,$$(CURDIR)/rts/include/ghcversion.h)" \
903943
--with-hsc2hs=$$(call NORMALIZE_FP,$$(CURDIR)/$$(DIST_DIR)/bin/$(1)-hsc2hs) \
904944
--hsc2hs-options='-x' \
945+
--happy-options="--template=$$(abspath $$(HAPPY_TEMPLATE_DIR))" \
905946
--with-gcc $$(STAGE3_$(1)_CC) \
906947
$$(foreach dir,$$(STAGE3_$(1)_EXTRA_LIB_DIRS),--extra-lib-dirs=$$(dir)) \
907948
$$(foreach dir,$$(STAGE3_$(1)_EXTRA_INCLUDE_DIRS),--extra-include-dirs=$$(dir))
908949

950+
# In DIST_BUILD mode, stage2 binaries come from a pre-built dist artifact;
951+
# skip the $(GHC2) prerequisite (it points into the stage2 store, not dist).
952+
ifdef DIST_BUILD
953+
STAGE3_$(1)_PREREQS = $$(GHC_TOOLCHAIN_BIN) $(CONFIGURE_SCRIPTS) $(CONFIGURED_FILES) libraries/ghc-boot-th-next cabal.project.common cabal.project.stage3 stage3-$(1)-additional-files
954+
else
955+
STAGE3_$(1)_PREREQS = $(GHC2) $$(GHC_TOOLCHAIN_BIN) $(CONFIGURE_SCRIPTS) $(CONFIGURED_FILES) libraries/ghc-boot-th-next cabal.project.common cabal.project.stage3 stage3-$(1)-additional-files
956+
endif
957+
909958
.PHONY: stage3-$(1)
910959
stage3-$(1): STAGE=stage3
911960
stage3-$(1): TARGET_PLATFORM=$(1)
912-
stage3-$(1): $(GHC2) $$(STAGE1_PATH)/bin/ghc-toolchain-bin $(CONFIGURE_SCRIPTS) $(CONFIGURED_FILES) libraries/ghc-boot-th-next cabal.project.common cabal.project.stage3 stage3-$(1)-additional-files
961+
stage3-$(1): $$(STAGE3_$(1)_PREREQS)
913962
$$(call PHASE_START,stage3-$(1))
914963
$$(call LOG,Linking executables)
915964
$$(foreach exe,$$(STAGE3_EXECUTABLES),$(LN_SF) $$(exe) $(DIST_DIR)/bin/$(1)-$$(exe);)
916965

917966
@mkdir -p $$(TARGET_DIR)/lib
918-
$$(STAGE1_PATH)/bin/ghc-toolchain-bin \
967+
$$(GHC_TOOLCHAIN_BIN) \
919968
--output-settings \
920969
--output $$(TARGET_DIR)/lib/settings \
921970
--triple $(1) \
922971
--cc $$(STAGE3_$(1)_CC) \
923972
$$(foreach opt,$$(STAGE3_$(1)_CC_OPTS),--cc-opt=$$(opt)) \
924973
--cxx $$(STAGE3_$(1)_CXX) \
925974
$$(foreach opt,$$(STAGE3_$(1)_CXX_OPTS),--cxx-opt=$$(opt)) \
975+
$(if $(STAGE3_$(1)_CPP),--cpp $$(STAGE3_$(1)_CPP),) \
976+
$(if $(STAGE3_$(1)_HS_CPP),--hs-cpp $$(STAGE3_$(1)_HS_CPP),) \
977+
$(if $(STAGE3_$(1)_CMM_CPP),--cmm-cpp $$(STAGE3_$(1)_CMM_CPP),) \
926978
$(if $(STAGE3_$(1)_AR),--ar $$(STAGE3_$(1)_AR),) \
927979
$(if $(STAGE3_$(1)_LD),--ld $$(STAGE3_$(1)_LD),) \
928980
$(if $(STAGE3_$(1)_ND),--nm $$(STAGE3_$(1)_NM),) \
@@ -1072,11 +1124,13 @@ $(DIST_DIR)/tests.tar.gz:
10721124
# |_| |_|\__,_|\___|_|\_\__,_|\__, |\___|
10731125
# |___/
10741126

1127+
ifndef DIST_BUILD
10751128
# .PHONY: hackage
10761129
hackage: $(BUILD_DIR)/packages/hackage.haskell.org/01-index.tar.gz
10771130

10781131
$(BUILD_DIR)/packages/hackage.haskell.org/01-index.tar.gz:
10791132
$(CABAL) --remote-repo-cache $(call NORMALIZE_FP,$(CURDIR)/$(BUILD_DIR)/packages) update
1133+
endif # DIST_BUILD (hackage)
10801134

10811135
# ____ __ _
10821136
# / ___|___ _ __ / _(_) __ _ _ _ _ __ ___

cabal.project.stage3

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -173,12 +173,17 @@ package libffi-clib
173173
ghc-options: -no-rts
174174

175175
-- WASM/WASI-specific overrides:
176-
-- - shared: True is required for GHCi/TH via dyld.mjs
177-
-- - +use-system-libffi is already set unconditionally on package rts above,
178-
-- but we keep this block for clarity and future conditional changes.
179-
if os(wasi)
180-
package *
181-
shared: True
176+
-- NOTE: shared: True is needed for GHCi/TH via dyld.mjs at runtime, but
177+
-- setting it via `if os(wasi) / package *` also applies to BUILD packages
178+
-- (e.g. happy-lib compiled by the native build compiler). This causes cabal
179+
-- to use -dynamic-too for BUILD packages, which fails because the build
180+
-- compiler's library directories don't include .dyn_hi files.
181+
-- Re-enable shared: True once cabal supports separate BUILD/HOST package
182+
-- settings, or once the dist artifact includes .dyn_hi files.
183+
--
184+
-- if os(wasi)
185+
-- package *
186+
-- shared: True
182187

183188
package ghc
184189
flags: +build-tool-depends +internal-interpreter

0 commit comments

Comments
 (0)