diff --git a/Makefile b/Makefile index a07d7bdd9e2f1d89fcd46dd41022036045906d94..cb89c23fa8cf11c23a2492380fcea74bfb1de94e 100644 --- a/Makefile +++ b/Makefile @@ -9,3 +9,6 @@ dockerfiles: dockerfiles.dhall mkdir -p dockerfiles ${DHALL} to-directory-tree --file=$< --output=$@ +clean: + rm gitlab-pipeline.dhall || echo "Skipping" + rm -r dockerfiles || echo "Skipping" diff --git a/components/FEX.dhall b/components/FEX.dhall new file mode 100644 index 0000000000000000000000000000000000000000..6d392dc410079efb1f01a8e70369d0153e22e9c3 --- /dev/null +++ b/components/FEX.dhall @@ -0,0 +1,35 @@ +let + CF = ../deps/Containerfile.dhall + +let + Llvm = ./Llvm.dhall + +let + BinariesSpec: Type = + { version : Text + } + +let + installArm64ECToWine: Text -> BinariesSpec -> CF.Type = + \(wineDest: Text) -> \(binSpec: BinariesSpec) -> + let + package: Text = "fex-emu-wine_${binSpec.version}_arm64.deb" + let + url: Text = "https://launchpad.net/~fex-emu/+archive/ubuntu/fex/+files/${package}" + + in + CF.run "install FEX" + [ "mkdir /tmp/fex" + , "pushd /tmp/fex" + , "wget ${url}" + , "ar xv ./${package}" + , "tar --zstd -xvf ./data.tar.zst" + , "cp ./usr/lib/wine/aarch64-windows/* ${wineDest}/lib/wine/aarch64-windows/" + , "popd" + , "rm -Rf /tmp/fex" + ] + +in +{ BinariesSpec = BinariesSpec +, install = installArm64ECToWine +} diff --git a/components/Llvm.dhall b/components/Llvm.dhall index ec083bb038d62e606ae40e3d596dae9276769727..d68ec8e4cd67f90387e456255fd43b3bf1c65cf0 100644 --- a/components/Llvm.dhall +++ b/components/Llvm.dhall @@ -14,6 +14,7 @@ let , out_llc : Text , out_opt : Text , out_clang : Text + , out_clang_cpp : Text } , default = {} } @@ -36,7 +37,8 @@ let ] , out_llc = "${destDir}/bin/llc" , out_opt = "${destDir}/bin/opt" - , out_clang="${destDir}/bin/clang" } + , out_clang="${destDir}/bin/clang" + , out_clang_cpp="${destDir}/bin/clang++" } let llvmInstall: Optional Output.Type -> CF.Type = diff --git a/components/LlvmMingw.dhall b/components/LlvmMingw.dhall new file mode 100644 index 0000000000000000000000000000000000000000..d52d4420aaaa730834604009a7ee4cb7ced48467 --- /dev/null +++ b/components/LlvmMingw.dhall @@ -0,0 +1,49 @@ +let + CF = ../deps/Containerfile.dhall + +let + Archive: Type = <TAR_XZ | ZIP> + +let + BindistSpec: Type = + { -- there are two sources of such toolchain: + -- https://github.com/mstorsjo/llvm-mingw (it has msvcrt but no arm64ec) + -- https://github.com/bylaws/llvm-mingw (it has arm64ec but no msvcrt) + -- for all our purposes we need ucrt (not msvscrt) and arm64ec, so, `bylaws` is more suitable + source : Text + , version : Text + , crt : Text + , os : Text + , archive : Archive + } + +let + installFromBindistTo: Text -> BindistSpec -> CF.Type = + \(destDir: Text) -> \(bindist: BindistSpec) -> + let + url: Text = "https://github.com/${bindist.source}/llvm-mingw/releases/download/${bindist.version}/llvm-mingw-${bindist.version}-${bindist.crt}-${bindist.os}." + ++ merge { TAR_XZ = "tar.xz", ZIP = "zip" } bindist.archive + + let + unpack : List Text = merge + { TAR_XZ = [ "curl -L ${url} | tar -xJC ." + ] + + , ZIP = [ "wget -q -O llvm-mingw-tmp.zip '${url}'" + , "unzip llvm-mingw-tmp.zip -d ." + , "rm llvm-mingw-tmp.zip" + ] + } bindist.archive + + in + CF.run "install LLVM MinGW for ${bindist.os}" + (unpack # [ "mkdir ${destDir}" + , "cp -R llvm-mingw-*/* ${destDir}" + , "rm -R llvm-mingw-*" + ]) + +in +{ BindistSpec = BindistSpec +, Archive = Archive +, installFromBindistTo = installFromBindistTo +} diff --git a/components/MSYS2.dhall b/components/MSYS2.dhall new file mode 100644 index 0000000000000000000000000000000000000000..a4124af53928672796dc474f67fa322419de6e9a --- /dev/null +++ b/components/MSYS2.dhall @@ -0,0 +1,39 @@ +let + Prelude = ../deps/Prelude.dhall + +let + CF = ../deps/Containerfile.dhall + +let + BindistSpec: Type = + { version_year : Text + , version_month : Text + , version_day : Text + , wine_version : Text + , build_date : Text + , build_time : Text + } + +let + installFromBindistTo: Text -> BindistSpec -> Text -> CF.Type = + \(userHome: Text) -> \(bindist: BindistSpec) -> \(winePrefix: Text) -> + let + url: Text = "https://gitlab.haskell.org/api/v4/projects/3815/packages/generic/wine-msys2-env/${bindist.wine_version}-${bindist.version_year}_${bindist.version_month}_${bindist.version_day}-${bindist.build_date}_${bindist.build_time}/wine-msys2-env-deb12.tar.xz" + + let + drive_c: Text = "${userHome}/.wine/drive_c" + let + runMSYS: Text = "${winePrefix}/bin/wine c:/msys64/usr/bin/bash.exe -l -c" -- option `-l` is strictly important + + in + CF.env (toMap { MSYSTEM = "CLANGARM64" }) + # CF.run "download & install MSYS2" + [ "curl -L ${url} | tar -Jx -C ${drive_c}" + -- Test MSYS2 works + , "${runMSYS} ' '" + ] + +in +{ BindistSpec = BindistSpec +, install = installFromBindistTo +} diff --git a/components/Wine.dhall b/components/Wine.dhall new file mode 100644 index 0000000000000000000000000000000000000000..5259ee82e99538531b7653c7e3be26ab829ddf31 --- /dev/null +++ b/components/Wine.dhall @@ -0,0 +1,24 @@ +let + CF = ../deps/Containerfile.dhall + +let + BindistSpec: Type = + { version : Text + } + +let + installForArm64EC: BindistSpec -> CF.Type = + \(spec: BindistSpec) -> + let + url: Text = "https://gitlab.haskell.org/api/v4/projects/3815/packages/generic/wine-arm64ec-msys2/${spec.version}/wine-arm64ec-msys2-deb12.tar.xz" + + in + CF.run "install Wine" + [ "curl -L ${url} | tar -xJC /" + ] + +in +{ BindistSpec = BindistSpec +, installForArm64EC = installForArm64EC +, installedPath = "/opt/wine-arm64ec-msys2-deb12" +} diff --git a/images/debian.dhall b/images/debian.dhall index 0effc067adbf1561a04a692973f0c9d4a08b8b18..af0b1d21e8a1ac10b8bffccc97c827a48d13cfb0 100644 --- a/images/debian.dhall +++ b/images/debian.dhall @@ -14,6 +14,14 @@ let Cabal = ../components/Cabal.dhall let Image = ../Image.dhall +let + LlvmMinGW = ../components/LlvmMingw.dhall +let + Wine = ../components/Wine.dhall +let + FEX = ../components/FEX.dhall +let + MSYS2 = ../components/MSYS2.dhall let docker_base_url: Text = "registry.gitlab.haskell.org/ghc/ci-images" @@ -143,8 +151,8 @@ let ] let - installLlvmFromApt : Text -> Optional Llvm.Config = - \(ver : Text) -> Some + installLlvmFromAptStrict : Text -> Llvm.Config = + \(ver : Text) -> { action = CF.run "install llvm" [ "apt-get install -qy wget software-properties-common gnupg lsb-release" , "wget https://apt.llvm.org/llvm.sh" , "chmod +x llvm.sh" @@ -153,8 +161,13 @@ let , out_llc = "/usr/bin/llc-${ver}" , out_opt = "/usr/bin/opt-${ver}" , out_clang = "/usr/bin/clang-${ver}" + , out_clang_cpp = "/usr/bin/clang++-${ver}" } +let + installLlvmFromApt : Text -> Optional Llvm.Config = + \(ver : Text) -> Some (installLlvmFromAptStrict ver) + -- Workaround for https://github.com/llvm/llvm-project/issues/62475 let installLlvmFromAptBookworm : Text -> Optional Llvm.Config = @@ -168,6 +181,7 @@ let , out_llc = "/usr/bin/llc-${ver}" , out_opt = "/usr/bin/opt-${ver}" , out_clang = "/usr/bin/clang-${ver}" + , out_clang_cpp = "/usr/bin/clang++-${ver}" } @@ -434,6 +448,103 @@ let debian12Images: List Image.Type = , extraPackages = [ ] : List Text } +, ( let + home_dir = "/home/ghc" + + let + cabal_index_state = "2025-03-20T17:30:02Z" + + let + msys_cabal_version = "3.14.1.1" + + let deb = DebianImage.toDocker DebianImage:: + { name = "aarch64-linux-deb12-wine" + , fromImage = "arm64v8/debian:bookworm" + , runnerTags = [ "aarch64-linux" ] + , bootstrapGhc = { version = "9.12.2", triple = "aarch64-deb12-linux" } + , llvm = installLlvmFromApt "19" + , cabalSource = Cabal.fromUpstreamBindist { version = "3.14.1.1", triple = "aarch64-linux-deb12" } + , extraPackages = [ + , "zstd" -- uncompress debs for FEX dlls + ] : List Text + } + in deb with image = deb.image + # [CF.Statement.User "root"] + # CF.workdir "/root/" + + -- Its purpose is to test the building of cross-compiled GHC for Aarch64 Windows from Linux + -- At the moment GHC supports LLVM =< 19, where 20250114@mstorsjo is 19.1.7 + # LlvmMinGW.installFromBindistTo "/opt/llvm-mingw-linux" + { source = "mstorsjo", version = "20250114", crt = "ucrt" + , os = "ubuntu-20.04-aarch64", archive = LlvmMinGW.Archive.TAR_XZ + } + + # Wine.installForArm64EC { version = "10.4" } + # FEX.install Wine.installedPath { version = "2503~j" } + + # [CF.Statement.User "ghc"] + # CF.workdir "${home_dir}/" + # CF.env (toMap { WINEPREFIX = "${home_dir}/.wine", WINEDEBUG = "-all" }) + + # CF.run "init Wine drives for selected user with FEX registry preinstall" + [ "${Wine.installedPath}/bin/wine reg.exe ADD 'HKLM\\Software\\Microsoft\\Wow64\\amd64' /ve /t REG_SZ /d 'libarm64ecfex.dll' /f" + -- Forcely keep all registry changes are persistent + , "${Wine.installedPath}/bin/wineserver -k 15" + ] + + # MSYS2.install home_dir { wine_version = "10.4", version_year = "2025", version_month = "02", version_day = "21", build_date = "20250326", build_time = "0916" } Wine.installedPath + + # CF.run "install GHC" + [ "mkdir ${home_dir}/.wine/drive_c/msys64/opt/ghc-bootstrap" + , "curl -L https://downloads.haskell.org/~ghc/9.12.2/ghc-9.12.2-x86_64-unknown-mingw32.tar.xz | tar -Jx -C ${home_dir}/.wine/drive_c/msys64/opt/ghc-bootstrap --strip-components=1" + , "echo 'export PATH=/opt/ghc-bootstrap/bin:$PATH' >> ${home_dir}/.wine/drive_c/msys64/home/ghc/.bash_profile" + , "echo 'export GHC=/opt/ghc-bootstrap/bin/ghc' >> ${home_dir}/.wine/drive_c/msys64/home/ghc/.bash_profile" + , "${Wine.installedPath}/bin/wine c:/msys64/usr/bin/bash.exe -l -c 'ghc --version'" + ] + # CF.run "install Cabal" + [ "mkdir -p ${home_dir}/.wine/drive_c/msys64/opt/cabal/{bin,dir}" + , "wget -q -O tmp.zip https://downloads.haskell.org/cabal/cabal-install-${msys_cabal_version}/cabal-install-${msys_cabal_version}-x86_64-windows.zip" + , "unzip tmp.zip -d Cabal-msys2" + , "cp ./Cabal-msys2/cabal.exe ${home_dir}/.wine/drive_c/msys64/opt/cabal/bin/" + , "rm tmp.zip" + , "rm -r ./Cabal-msys2" + , "echo 'export PATH=/opt/cabal/dir/bin:/opt/cabal/bin:$PATH' >> ${home_dir}/.wine/drive_c/msys64/home/ghc/.bash_profile" + , "echo 'export CABAL=/opt/cabal/bin/cabal' >> ${home_dir}/.wine/drive_c/msys64/home/ghc/.bash_profile" + -- Double escaping: one for Dhall, second for Bash + , "echo 'export CABAL_CONFIG=C:\\\\msys64\\\\opt\\\\cabal\\\\cabal.conf' >> ${home_dir}/.wine/drive_c/msys64/home/ghc/.bash_profile" + , "echo 'export CABAL_DIR=C:\\\\msys64\\\\opt\\\\cabal\\\\dir' >> ${home_dir}/.wine/drive_c/msys64/home/ghc/.bash_profile" + , "${Wine.installedPath}/bin/wine c:/msys64/usr/bin/bash.exe -l -c 'cabal user-config init'" + -- Wine/FEX has a bug: it seemed that cabal install is terminated prematurely when it stops writing into stdout/stderr for long time. + -- To prevent showing much garbage at the terminal use `2>/dev/null`, i.e.: + -- # hadrian/build clean 2>/dev/null + -- (MSYS2's bash prompt begins with `#`) + , "sed -i 's/ -- ghc-options:/ ghc-options: -v/g' ${home_dir}/.wine/drive_c/msys64/opt/cabal/cabal.conf" + -- The install method should be COPY because MSYS2 does not support symlinking under Wine + , "sed -i 's/-- install-method:/install-method: copy/g' ${home_dir}/.wine/drive_c/msys64/opt/cabal/cabal.conf" + -- Set Cabal registry to specific revision, yep, we need pass same index state reference twice. + , "sed -i 's/-- index-state:/index-state: ${cabal_index_state}/g' ${home_dir}/.wine/drive_c/msys64/opt/cabal/cabal.conf" + , "${Wine.installedPath}/bin/wine c:/msys64/usr/bin/bash.exe -l -c 'cabal update \"hackage.haskell.org,${cabal_index_state}\"'" + ] + + # CF.run "install GHC build deps" + [ '' + ${Wine.installedPath}/bin/wine c:/msys64/usr/bin/bash.exe -l -c 'cabal v2-install \ + --constraint="alex ^>= 3.5.2.0" \ + --constraint="happy ^>= 2.1.5" \ + --constraint="hscolour ^>= 1.25" \ + '' ++ Prelude.Text.concatSep " " [ "hscolour", "happy", "alex" ] ++ "'" + , "echo 'export ALEX=/opt/cabal/dir/bin/alex' >> ${home_dir}/.wine/drive_c/msys64/home/ghc/.bash_profile" + , "echo 'export HAPPY=/opt/cabal/dir/bin/happy' >> ${home_dir}/.wine/drive_c/msys64/home/ghc/.bash_profile" + , "echo 'export HSCOLOUR=/opt/cabal/dir/bin/HsColour' >> ${home_dir}/.wine/drive_c/msys64/home/ghc/.bash_profile" + ] + + -- At the moment GHC supports LLVM =< 19, where 20250114@mstorsjo is 19.1.7 + # LlvmMinGW.installFromBindistTo "${home_dir}/.wine/drive_c/msys64/opt/llvm-mingw-windows" + { source = "mstorsjo", version = "20250114", crt = "ucrt" + , os = "aarch64", archive = LlvmMinGW.Archive.ZIP + } + ) + , DebianImage.toDocker DebianImage:: { name = "i386-linux-deb12" , fromImage = "i386/debian:bookworm"