Sven Tennie pushed to branch wip/supersven/hadrian-cross-stage3 at Glasgow Haskell Compiler / GHC Commits: 936a89cc by GHC GitLab CI at 2026-07-14T20:50:46+02:00 Final cleanup - - - - - 8579bf77 by GHC GitLab CI at 2026-07-14T21:15:56+02:00 Simplify ci.sh - - - - - 5868b371 by GHC GitLab CI at 2026-07-14T21:43:07+02:00 Add stage3 check to ci.sh - - - - - 7adbf51f by GHC GitLab CI at 2026-07-14T21:46:42+02:00 Revert unnecessary change - - - - - 0defb0c6 by GHC GitLab CI at 2026-07-14T21:47:30+02:00 Update jobs.yaml - - - - - dd57c677 by GHC GitLab CI at 2026-07-14T21:47:37+02:00 Typo - - - - - 73859934 by GHC GitLab CI at 2026-07-14T21:48:12+02:00 Cleanup diff - - - - - 6 changed files: - .gitlab/ci.sh - .gitlab/generate-ci/gen_ci.hs - .gitlab/jobs.yaml - hadrian/src/BindistConfig.hs - hadrian/src/Rules/CabalReinstall.hs - hadrian/src/Rules/Generate.hs Changes: ===================================== .gitlab/ci.sh ===================================== @@ -575,8 +575,14 @@ function build_hadrian() { case "${CROSS_STAGE:-2}" in 2) BINDIST_TARGET="binary-dist";; # Stage2 cross-compiler bindists are (almost) a byproduct of Stage3 - # cross-compiled bindists. So, we bundle both of them - 3) BINDIST_TARGET="binary-dist binary-dist-stage3";; + # cross-compiled bindists. So, we bundle both of them when the Stage3 + # bindist is built. + 3) + BINDIST_TARGET="binary-dist binary-dist-stage3" + if [[ -z "${BIN_DIST_NAME_STAGE3:-}" ]]; then + fail "CROSS_STAGE=3 requires BIN_DIST_NAME_STAGE3 to be set" + fi + ;; *) fail "Unknown CROSS_STAGE, must be 2 or 3";; esac @@ -592,9 +598,6 @@ function build_hadrian() { run_hadrian test:all_deps $BINDIST_TARGET mv _build/bindist/ghc*.tar.xz "$BIN_DIST_NAME.tar.xz" if [[ "${CROSS_STAGE:-2}" == "3" ]]; then - if [[ -z "${BIN_DIST_NAME_STAGE3:-}" ]]; then - fail "CROSS_STAGE=3 requires BIN_DIST_NAME_STAGE3 to be set" - fi mv _build/bindist-stage3/ghc*.tar.xz "$BIN_DIST_NAME_STAGE3.tar.xz" fi ;; @@ -682,11 +685,6 @@ function test_hadrian() { # If we have set CROSS_EMULATOR, then can't test using normal testsuite. elif [ -n "${CROSS_EMULATOR:-}" ] && [[ "${CROSS_TARGET:-}" != *"wasm"* ]]; then local instdir="$TOP/_build/install" - # The stage-2 cross bindist has target-triple-prefixed binaries and runs - # natively on the host. Override the global cross_prefix (which is empty - # for CROSS_STAGE=3 because the stage-3 bindist has unprefixed binaries) - # so that we test the stage-2 cross compiler consistently. - local cross_prefix="$target_triple-" local test_compiler="$instdir/bin/${cross_prefix}ghc$exe" install_bindist _build/bindist/ghc-*/ "$instdir" echo 'main = putStrLn "hello world"' > expected @@ -708,6 +706,26 @@ function test_hadrian() { # --- # > main = putStrLn "hello world" run diff -w expected actual + + if [[ "${CROSS_STAGE:-2}" == "3" ]]; then + local stage3_dir + stage3_dir="$(echo _build/bindist-stage3/ghc-*/)" + local stage3_ghc="$stage3_dir/bin/ghc$exe" + + info "Smoke-testing stage3 compiler..." + file "$stage3_ghc" + run ${CROSS_EMULATOR} "$stage3_ghc" --info + + run ${CROSS_EMULATOR} "$stage3_ghc" -package ghc "$TOP/.gitlab/hello.hs" -o hello-stage3 + + if [[ "${CROSS_TARGET:-no_cross_target}" =~ "mingw" ]]; then + ${CROSS_EMULATOR:-} ./hello-stage3.exe > actual-stage3 + else + ${CROSS_EMULATOR:-} ./hello-stage3 > actual-stage3 + fi + + run diff -w expected actual-stage3 + fi elif [[ -n "${REINSTALL_GHC:-}" ]]; then run_hadrian \ test \ @@ -935,8 +953,7 @@ function clean() { # # The exclude list are the artifacts that we do expect to be # uploaded. Keep in sync with `jobArtifacts` in - # `.gitlab/generate-ci/gen_ci.hs`! The `ghc-*.tar.xz` pattern covers both - # stage2/cross and stage3/target bindists. + # `.gitlab/generate-ci/gen_ci.hs`! if [[ "${CI_DISPOSABLE_ENVIRONMENT:-}" != true ]]; then git submodule --quiet foreach --recursive git clean -xdfq git clean -xdfq \ @@ -1046,15 +1063,12 @@ case "$(uname)" in *) fail "uname $(uname) is not supported" ;; esac -cross_prefix="" if [ -n "${CROSS_TARGET:-}" ]; then - info "Cross-compiling for $CROSS_TARGET... (stage: $CROSS_STAGE)" + info "Cross-compiling for $CROSS_TARGET..." target_triple="$CROSS_TARGET" - # Stage3 native GHC runs on the target itself, so no cross prefix. - # CROSS_STAGE is either 2 (host != target) or 3 (host == target) - if [ "${CROSS_STAGE:-2}" = "2" ]; then - cross_prefix="$target_triple-" - fi + cross_prefix="$target_triple-" +else + cross_prefix="" fi echo "Branch name ${CI_MERGE_REQUEST_SOURCE_BRANCH_NAME:-}" ===================================== .gitlab/generate-ci/gen_ci.hs ===================================== @@ -159,7 +159,7 @@ data BuildConfig , withNuma :: Bool , withZstd :: Bool , crossTarget :: Maybe String - , crossStage :: Maybe Int + , crossStage :: Maybe FinalCrossStage , crossEmulator :: CrossEmulator , configureWrapper :: Maybe String , fullyStatic :: Bool @@ -275,14 +275,26 @@ static = vanilla { fullyStatic = True } staticNativeInt :: BuildConfig staticNativeInt = static { bignumBackend = Native } --- | cross-compiler (build == host, host /= target) -stage2CrossConfig :: String -- ^ target triple +-- | The final stage for which binary distrubutions should be built +-- +-- `Stage2` builds a cross-compiler (build == host, host /= target). `Stage3` +-- implies `Stage2` and additionally builds a cross-compiled compiler (build /= +-- host, host == target). +data FinalCrossStage = Stage2 | Stage3 + deriving (Eq, Ord) + +crossStageToInt :: FinalCrossStage -> Int +crossStageToInt Stage2 = 2 +crossStageToInt Stage3 = 3 + +crossConfig :: String -- ^ target triple -> CrossEmulator -- ^ emulator for testing -> Maybe String -- ^ Configure wrapper + -> FinalCrossStage -- ^ final stage to build -> BuildConfig -stage2CrossConfig triple emulator configure_wrapper = +crossConfig triple emulator configure_wrapper crossStage = vanilla { crossTarget = Just triple - , crossStage = Just 2 + , crossStage = Just crossStage , crossEmulator = emulator , configureWrapper = configure_wrapper } @@ -895,7 +907,7 @@ job arch opsys buildConfig = NamedJob { name = jobName, jobInfo = Job {..} } [ opsysVariables arch opsys , "TEST_ENV" =: testEnv arch opsys buildConfig , "BIN_DIST_NAME" =: binDistName arch opsys buildConfig - , if crossStage buildConfig == Just 3 + , if crossStage buildConfig == Just Stage3 then "BIN_DIST_NAME_STAGE3" =: binDistNameStage3 arch opsys buildConfig else mempty , "BUILD_FLAVOUR" =: flavourString jobFlavour @@ -904,7 +916,7 @@ job arch opsys buildConfig = NamedJob { name = jobName, jobInfo = Job {..} } , "INSTALL_CONFIGURE_ARGS" =: "--enable-strict-ghc-toolchain-check" , maybe mempty ("CONFIGURE_WRAPPER" =:) (configureWrapper buildConfig) , maybe mempty ("CROSS_TARGET" =:) (crossTarget buildConfig) - , maybe mempty (("CROSS_STAGE" =:) . show) (crossStage buildConfig) + , maybe mempty (("CROSS_STAGE" =:) . show . crossStageToInt) (crossStage buildConfig) , case crossEmulator buildConfig of NoEmulator -- we need an emulator but it isn't set. Won't run the testsuite @@ -938,8 +950,8 @@ job arch opsys buildConfig = NamedJob { name = jobName, jobInfo = Job {..} } trim = dropWhileEnd isSpace . dropWhile isSpace stage3Artifacts - | crossStage buildConfig == Just 3 - = [binDistNameStage3 arch opsys buildConfig ++ ".tar.xz"] + | crossStage buildConfig == Just Stage3 = + [binDistNameStage3 arch opsys buildConfig ++ ".tar.xz"] | otherwise = [] -- Keep in sync with the exclude list in `function clean()` in @@ -947,9 +959,10 @@ job arch opsys buildConfig = NamedJob { name = jobName, jobInfo = Job {..} } jobArtifacts = Artifacts { junitReport = "junit.xml" , expireIn = "2 weeks" - , artifactPaths = stage3Artifacts ++ [binDistName arch opsys buildConfig ++ ".tar.xz" + , artifactPaths = [binDistName arch opsys buildConfig ++ ".tar.xz" ,"junit.xml" ,"unexpected-test-output.tar.gz"] + ++ stage3Artifacts , artifactsWhen = ArtifactsAlways } @@ -1308,13 +1321,13 @@ alpine_aarch64 = [ cross_jobs :: [JobGroup Job] cross_jobs = [ -- x86 -> aarch64 - validateBuilds Amd64 (Linux Debian13) (stage2CrossConfig "aarch64-linux-gnu" (Emulator "qemu-aarch64 -L /usr/aarch64-linux-gnu") Nothing) + validateBuilds Amd64 (Linux Debian13) (crossConfig "aarch64-linux-gnu" (Emulator "qemu-aarch64 -L /usr/aarch64-linux-gnu") Nothing Stage2) -- x86_64 (build) -> riscv64 (host/target) - , addValidateRule RiscV (validateBuilds Amd64 (Linux Debian13Riscv) (stage2CrossConfig "riscv64-linux-gnu" (Emulator "qemu-riscv64 -L /usr/riscv64-linux-gnu") Nothing) { crossStage = Just 3 }) + , addValidateRule RiscV (validateBuilds Amd64 (Linux Debian13Riscv) (crossConfig "riscv64-linux-gnu" (Emulator "qemu-riscv64 -L /usr/riscv64-linux-gnu") Nothing Stage3)) -- x86_64 -> loongarch64 - , addValidateRule LoongArch64 (validateBuilds Amd64 (Linux Ubuntu2404LoongArch64) (stage2CrossConfig "loongarch64-linux-gnu" (Emulator "qemu-loongarch64 -L /usr/loongarch64-linux-gnu") Nothing)) + , addValidateRule LoongArch64 (validateBuilds Amd64 (Linux Ubuntu2404LoongArch64) (crossConfig "loongarch64-linux-gnu" (Emulator "qemu-loongarch64 -L /usr/loongarch64-linux-gnu") Nothing Stage2)) -- Javascript , addValidateRule JSBackend (validateBuilds Amd64 (Linux Debian11Js) javascriptConfig) @@ -1335,7 +1348,7 @@ cross_jobs = [ (validateBuilds AArch64 (Linux Debian12Wine) (winAarch64Config {llvmBootstrap = True})) ] where - javascriptConfig = (stage2CrossConfig "javascript-unknown-ghcjs" (NoEmulatorNeeded TimeoutIncrease) (Just "emconfigure")) + javascriptConfig = (crossConfig "javascript-unknown-ghcjs" (NoEmulatorNeeded TimeoutIncrease) (Just "emconfigure") Stage2) { bignumBackend = Native } makeWinArmJobs = modifyJobs @@ -1374,7 +1387,7 @@ cross_jobs = [ llvm_prefix = "/opt/llvm-mingw-linux/bin/aarch64-w64-mingw32-" cflags = "-fuse-ld=" ++ llvm_prefix ++ "ld --rtlib=compiler-rt" - winAarch64Config = (stage2CrossConfig "aarch64-unknown-mingw32" (Emulator "/opt/wine-arm64ec-msys2-deb12/bin/wine") Nothing) + winAarch64Config = (crossConfig "aarch64-unknown-mingw32" (Emulator "/opt/wine-arm64ec-msys2-deb12/bin/wine") Nothing Stage2) { bignumBackend = Native } make_wasm_jobs cfg = @@ -1387,7 +1400,7 @@ cross_jobs = [ $ addValidateRule WasmBackend $ validateBuilds Amd64 (Linux AlpineWasm) cfg wasm_build_config = - (stage2CrossConfig "wasm32-wasi" (NoEmulatorNeeded NoTimeoutIncrease) Nothing) + (crossConfig "wasm32-wasi" (NoEmulatorNeeded NoTimeoutIncrease) Nothing Stage2) { hostFullyStatic = True , buildFlavour = Release -- TODO: This needs to be validate but wasm backend doesn't pass yet , textWithSIMDUTF = True @@ -1458,9 +1471,8 @@ platform_mapping = Map.map go combined_result process sel = Map.fromListWith combine - [ (mkPlatform a o, j) + [ (uncurry mkPlatform (jobPlatform (jobInfo j)), j) | (sel -> Just j) <- job_groups - , let (a, o) = jobPlatform (jobInfo j) ] vs = process v ===================================== .gitlab/jobs.yaml ===================================== @@ -2748,10 +2748,10 @@ "artifacts": { "expire_in": "8 weeks", "paths": [ - "ghc-x86_64-linux-deb13-riscv-cross_riscv64-linux-gnu-stage3-validate.tar.xz", "ghc-x86_64-linux-deb13-riscv-cross_riscv64-linux-gnu-validate.tar.xz", "junit.xml", - "unexpected-test-output.tar.gz" + "unexpected-test-output.tar.gz", + "ghc-x86_64-linux-deb13-riscv-cross_riscv64-linux-gnu-stage3-validate.tar.xz" ], "reports": { "junit": "junit.xml" @@ -6726,10 +6726,10 @@ "artifacts": { "expire_in": "2 weeks", "paths": [ - "ghc-x86_64-linux-deb13-riscv-cross_riscv64-linux-gnu-stage3-validate.tar.xz", "ghc-x86_64-linux-deb13-riscv-cross_riscv64-linux-gnu-validate.tar.xz", "junit.xml", - "unexpected-test-output.tar.gz" + "unexpected-test-output.tar.gz", + "ghc-x86_64-linux-deb13-riscv-cross_riscv64-linux-gnu-stage3-validate.tar.xz" ], "reports": { "junit": "junit.xml" ===================================== hadrian/src/BindistConfig.hs ===================================== @@ -3,11 +3,11 @@ module BindistConfig where import Stage import Oracles.Flag import Expression + data BindistConfig = BindistConfig { library_stage :: Stage -- ^ The stage compiler which builds the libraries , executable_stage :: Stage -- ^ The stage compiler which builds the executables } - -- | A bindist for when the host = target, non cross-compilation setting. -- Both the libraries and final executables are built with stage1 compiler. normalBindist :: BindistConfig ===================================== hadrian/src/Rules/CabalReinstall.hs ===================================== @@ -67,14 +67,13 @@ cabalBuildRules = do let cabal_package_db = cwd -/- root -/- "stage-cabal" -/- "dist-newstyle" -/- "packagedb" -/- "ghc-" ++ version - executableStage <- executable_stage <$> implicitBindistConfig forM_ bin_targets $ \(bin_pkg,_bin_path) -> do let pgmName pkg | pkg == ghc = "ghc" | pkg == hpcBin = "hpc" | otherwise = pkgName pkg let cabal_bin_out = work_dir -/- "cabal-bin" -/- (pgmName bin_pkg) - needed_wrappers <- pkgToWrappers executableStage bin_pkg + needed_wrappers <- pkgToWrappers Stage2 bin_pkg forM_ needed_wrappers $ \wrapper_name -> do let wrapper_prefix = unlines ["#!/usr/bin/env sh" @@ -86,7 +85,7 @@ cabalBuildRules = do ,"export GHC_PACKAGE_PATH="++show cabal_package_db++":" ] output_file = outputDir -/- wrapper_name - wrapper_content <- wrapper executableStage wrapper_name + wrapper_content <- wrapper Stage2 wrapper_name writeFile' output_file (wrapper_prefix ++ wrapper_content) makeExecutable output_file pure () ===================================== hadrian/src/Rules/Generate.hs ===================================== @@ -423,7 +423,7 @@ bindistRules = do , interpolateVar "HostOS_CPP" $ fmap cppify $ interp $ queryHost queryOS - -- Stage2 always targets the final architecture. Thus, we can use a + -- Stage2 always targets the final architecture. Thus, we can use a -- constant stage here. , interpolateVar "TargetPlatform" $ getTarget Stage2 targetPlatformTriple , interpolateVar "TargetPlatform_CPP" $ cppify <$> getTarget Stage2 targetPlatformTriple View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/4713cc0b013178d84d544ad6b985f97... -- View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/4713cc0b013178d84d544ad6b985f97... You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
participants (1)
-
Sven Tennie (@supersven)