Make DLLs in deps available to wine TH runner (#1405)

This is a better fix to the problem of making DLLs available to the process running in wine that is used to evaluate TH code when cross compiling for Windows.

Also fixes macOS loadArchive in TH for ghc 9

Co-authored-by: Michael Peyton Jones <michael.peyton-jones@iohk.io>
This commit is contained in:
Hamish Mackenzie 2022-03-25 21:44:02 +13:00 committed by GitHub
parent 2c895a2c41
commit 4b6ee9767d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 116 additions and 40 deletions

View File

@ -382,18 +382,26 @@ let
runHook postConfigure
'';
buildPhase = if stdenv.hostPlatform.isGhcjs then ''
runHook preBuild
# https://gitlab.haskell.org/ghc/ghc/issues/9221
$SETUP_HS build ${haskellLib.componentTarget componentId} ${lib.concatStringsSep " " setupBuildFlags}
runHook postBuild
'' else ''
runHook preBuild
# https://gitlab.haskell.org/ghc/ghc/issues/9221
$SETUP_HS build ${haskellLib.componentTarget componentId} -j$(($NIX_BUILD_CORES > 4 ? 4 : $NIX_BUILD_CORES)) ${lib.concatStringsSep " " setupBuildFlags}
runHook postBuild
''
;
buildPhase =
# It seems that by the time the iserv wrapper specifiec by `--ghc-option=-pgmi` runs
# all the array environment variables are removed from the environment. To get a list
# of all the locations a DLLs might be present we need access to pkgsHostTarget.
# Adding a string version of the list array of nix store paths allows us to get that
# list when we need it.
(lib.optionalString stdenv.hostPlatform.isWindows ''
export pkgsHostTargetAsString="''${pkgsHostTarget[@]}"
'') +
(if stdenv.hostPlatform.isGhcjs then ''
runHook preBuild
# https://gitlab.haskell.org/ghc/ghc/issues/9221
$SETUP_HS build ${haskellLib.componentTarget componentId} ${lib.concatStringsSep " " setupBuildFlags}
runHook postBuild
'' else ''
runHook preBuild
# https://gitlab.haskell.org/ghc/ghc/issues/9221
$SETUP_HS build ${haskellLib.componentTarget componentId} -j$(($NIX_BUILD_CORES > 4 ? 4 : $NIX_BUILD_CORES)) ${lib.concatStringsSep " " setupBuildFlags}
runHook postBuild
'');
# Note: Cabal does *not* copy test executables during the `install` phase.
#

View File

@ -169,6 +169,8 @@ in {
++ fromUntil "8.8.2" "8.9" ./patches/ghc/ghc-8.8.2-reinstallable-lib-ghc.patch
++ final.lib.optional (version == "8.6.4") ./patches/ghc/ghc-8.6.4-better-plusSimplCountErrors.patch
++ final.lib.optional (versionAtLeast "8.6.4" && versionLessThan "9.0" && final.stdenv.isDarwin) ./patches/ghc/ghc-macOS-loadArchive-fix.patch
++ final.lib.optional (versionAtLeast "9.0.0" && versionLessThan "9.2" && final.stdenv.isDarwin) ./patches/ghc/ghc-9.0-macOS-loadArchive-fix.patch
++ final.lib.optional (versionAtLeast "9.2.0" && versionLessThan "9.3" && final.stdenv.isDarwin) ./patches/ghc/ghc-9.2-macOS-loadArchive-fix.patch
++ final.lib.optional (versionAtLeast "8.4.4" && versionLessThan "8.10" && final.stdenv.isDarwin) ./patches/ghc/ghc-darwin-gcc-version-fix.patch
++ final.lib.optional (versionAtLeast "8.10.1" && versionLessThan "9.0.2" && final.stdenv.isDarwin) ./patches/ghc/ghc-8.10-darwin-gcc-version-fix.patch
# backport of https://gitlab.haskell.org/ghc/ghc/-/merge_requests/3227

View File

@ -18,7 +18,7 @@ let
configureFlags = lib.optional hostPlatform.isWindows "--disable-split-sections";
wineIservWrapperVanilla = writeScriptBin "iserv-wrapper" ''
wineIservWrapperScript = enableProfiling: writeScriptBin ("iserv-wrapper" + lib.optionalString enableProfiling "-prof") ''
#!${stdenv.shell}
set -euo pipefail
# unset the configureFlags.
@ -28,7 +28,23 @@ let
unset configureFlags
PORT=$((5000 + $RANDOM % 5000))
(>&2 echo "---> Starting remote-iserv on port $PORT")
WINEDLLOVERRIDES="winemac.drv=d" WINEDEBUG=warn-all,fixme-all,-menubuilder,-mscoree,-ole,-secur32,-winediag WINEPREFIX=$TMP ${wine}/bin/wine64 ${remote-iserv}/bin/remote-iserv.exe tmp $PORT &
REMOTE_ISERV=$(mktemp -d)
ln -s ${if enableProfiling then remote-iserv.override { inherit enableProfiling; } else remote-iserv}/bin/* $REMOTE_ISERV
# See coment in comp-builder.nix for where this comes from and why it's here
for p in $pkgsHostTargetAsString; do
find "$p" -iname '*.dll' -exec ln -s {} $REMOTE_ISERV \;
find "$p" -iname '*.dll.a' -exec ln -s {} $REMOTE_ISERV \;
done
# Some DLLs have a `lib` prefix but we attempt to load them without the prefix.
# This was a problem for `double-conversion` package when used in TH code.
# Creating links from the `X.dll` to `libX.dll` works around this issue.
(
cd $REMOTE_ISERV
for l in lib*.dll; do
ln -s "$l" "''${l#lib}"
done
)
WINEDLLOVERRIDES="winemac.drv=d" WINEDEBUG=warn-all,fixme-all,-menubuilder,-mscoree,-ole,-secur32,-winediag WINEPREFIX=$TMP ${wine}/bin/wine64 $REMOTE_ISERV/remote-iserv.exe tmp $PORT &
(>&2 echo "---| remote-iserv should have started on $PORT")
RISERV_PID="$!"
${iserv-proxy}/bin/iserv-proxy $@ 127.0.0.1 "$PORT"
@ -36,25 +52,7 @@ let
kill $RISERV_PID
'';
wineIservWrapperProf = writeScriptBin "iserv-wrapper-prof" ''
#!${stdenv.shell}
set -euo pipefail
# unset the configureFlags.
# configure should have run already
# without restting it, wine might fail
# due to a too large environment.
unset configureFlags
PORT=$((5000 + $RANDOM % 5000))
(>&2 echo "---> Starting remote-iserv on port $PORT")
WINEDLLOVERRIDES="winemac.drv=d" WINEDEBUG=warn-all,fixme-all,-menubuilder,-mscoree,-ole,-secur32,-winediag WINEPREFIX=$TMP ${wine}/bin/wine64 ${remote-iserv.override { enableProfiling = true; }}/bin/remote-iserv.exe tmp $PORT &
(>&2 echo "---| remote-iserv should have started on $PORT")
RISERV_PID="$!"
${iserv-proxy}/bin/iserv-proxy $@ 127.0.0.1 "$PORT"
(>&2 echo "---> killing remote-iserv...")
kill $RISERV_PID
'';
wineIservWrapper = symlinkJoin { name = "iserv-wrapper"; paths = [ wineIservWrapperVanilla wineIservWrapperProf ]; };
wineIservWrapper = symlinkJoin { name = "iserv-wrapper"; paths = [ (wineIservWrapperScript false) (wineIservWrapperScript true) ]; };
################################################################################
# Build logic (TH support via remote iserv via wine)

View File

@ -0,0 +1,9 @@
--- a/compiler/GHC/Runtime/Linker.hs
+++ b/compiler/GHC/Runtime/Linker.hs
@@ -1487,7 +1487,6 @@ locateLib hsc_env is_hs lib_dirs gcc_dirs lib
dyn_obj_file = lib <.> "dyn_o"
arch_files = [ "lib" ++ lib ++ lib_tag <.> "a"
, lib <.> "a" -- native code has no lib_tag
- , "lib" ++ lib, lib
]
lib_tag = if is_hs && loading_profiled_hs_libs then "_p" else ""

View File

@ -0,0 +1,9 @@
--- a/compiler/GHC/Linker/Loader.hs
+++ b/compiler/GHC/Linker/Loader.hs
@@ -1487,7 +1487,6 @@ locateLib hsc_env is_hs lib_dirs gcc_dirs lib
dyn_obj_file = lib <.> "dyn_o"
arch_files = [ "lib" ++ lib ++ lib_tag <.> "a"
, lib <.> "a" -- native code has no lib_tag
- , "lib" ++ lib, lib
]
lib_tag = if is_hs && loading_profiled_hs_libs then "_p" else ""

View File

@ -73,17 +73,11 @@ final: prev:
# dependencies) and then placing them somewhere where wine+remote-iserv
# will find them.
remote-iserv.postInstall = pkgs.lib.optionalString pkgs.stdenv.hostPlatform.isWindows (
let extra-libs = [ pkgs.openssl.bin pkgs.libffi pkgs.gmp (pkgs.libsodium-vrf or pkgs.libsodium) pkgs.windows.mcfgthreads pkgs.buildPackages.gcc.cc pkgs.secp256k1 ]; in ''
let extra-libs = [ pkgs.libffi pkgs.gmp pkgs.windows.mcfgthreads pkgs.buildPackages.gcc.cc ]; in ''
for p in ${lib.concatStringsSep " "extra-libs}; do
find "$p" -iname '*.dll' -exec cp {} $out/bin/ \;
find "$p" -iname '*.dll.a' -exec cp {} $out/bin/ \;
done
(
cd $out/bin
for l in lib*.dll; do
ln -s "$l" "''${l#lib}"
done
)
'');
# Apply https://github.com/haskell/cabal/pull/6055

View File

@ -202,6 +202,7 @@ let
sublib-docs = callTest ./sublib-docs { inherit util compiler-nix-name; };
githash = haskell-nix.callPackage ./githash { inherit compiler-nix-name; testSrc = testSrcWithGitDir; };
c-ffi = callTest ./c-ffi { inherit util compiler-nix-name; };
th-dlls = callTest ./th-dlls { inherit util compiler-nix-name; };
unit = unitTests;
};

23
test/th-dlls/default.nix Normal file
View File

@ -0,0 +1,23 @@
# Test building TH code that needs DLLs when cross compiling for windows
{ stdenv, lib, util, project', haskellLib, recurseIntoAttrs, testSrc, compiler-nix-name }:
with lib;
let
project = project' {
inherit compiler-nix-name;
src = testSrc "th-dlls";
};
packages = project.hsPkgs;
in recurseIntoAttrs {
meta.disabled = stdenv.hostPlatform.isGhcjs;
ifdInputs = {
inherit (project) plan-nix;
};
build = packages.th-dlls.components.library;
build-profiled = packages.th-dlls.components.library.profiled;
}

14
test/th-dlls/src/Lib.hs Normal file
View File

@ -0,0 +1,14 @@
{-# LANGUAGE TemplateHaskell #-}
module Lib where
import Control.Monad.IO.Class (liftIO)
import OpenSSL (withOpenSSL)
import OpenSSL.BN (withBN)
import Libsodium (sodium_init)
import Language.Haskell.TH.Syntax (Exp(..), Lit(..))
import Data.Text as T
import Data.Double.Conversion.Text (toShortest)
x = $(liftIO (withOpenSSL (withBN 0 (\_ -> return (LitE (IntegerL 0))))))
y = $(liftIO (sodium_init >> return (LitE (IntegerL 0))))
z = $(liftIO (return (LitE (IntegerL (fromIntegral (T.length (toShortest 1.0)))))))

View File

@ -0,0 +1,18 @@
cabal-version: >=1.10
name: th-dlls
version: 0.1.0.0
license: PublicDomain
author: Hamish Mackenzie
maintainer: Hamish.K.Mackenzie@gmail.com
build-type: Simple
library
build-depends: base
, HsOpenSSL
, libsodium
, template-haskell
, text
, double-conversion
exposed-modules: Lib
hs-source-dirs: src
default-language: Haskell2010