├── LICENSE ├── Main.hs ├── hs-hello.png ├── hs-hello.cabal ├── hello-world.nix ├── default.nix ├── cabal-head.nix ├── config.nix ├── fetchNixpkgs.nix └── README.org /LICENSE: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Main.hs: -------------------------------------------------------------------------------- 1 | module Main where 2 | 3 | main :: IO () 4 | main = putStrLn "Hello, Windows!" 5 | -------------------------------------------------------------------------------- /hs-hello.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/input-output-hk/nix-hs-hello-windows/HEAD/hs-hello.png -------------------------------------------------------------------------------- /hs-hello.cabal: -------------------------------------------------------------------------------- 1 | name: hs-hello 2 | version: 1.0.0 3 | license: BSD3 4 | license-file: LICENSE 5 | cabal-version: >= 1.18 6 | build-type: Simple 7 | 8 | executable hs-hello 9 | build-depends: base < 5 10 | main-is: Main.hs 11 | default-language: Haskell2010 12 | -------------------------------------------------------------------------------- /hello-world.nix: -------------------------------------------------------------------------------- 1 | { mkDerivation, base, stdenv, Cabal_HEAD }: 2 | mkDerivation { 3 | pname = "hs-hello"; 4 | version = "1.0.0"; 5 | src = ./.; 6 | isLibrary = false; 7 | isExecutable = true; 8 | enableSharedExecutables = false; 9 | setupHaskellDepends = [ Cabal_HEAD ]; 10 | executableHaskellDepends = [ base ]; 11 | license = stdenv.lib.licenses.bsd3; 12 | 13 | hardeningDisable = [ "stackprotector" ]; 14 | } 15 | -------------------------------------------------------------------------------- /default.nix: -------------------------------------------------------------------------------- 1 | let 2 | pkgsPath = import ./fetchNixpkgs.nix { 3 | rev = "ff9275c2886a1d984bf9a20ef7871b65d85aa08f"; 4 | sha256 = "0c8nwx1ghl6p1z0wv8rf02f1zgvry66q3jn3xk74dw578avnsfvy"; 5 | sha256unpacked = "1mz15byiv0fb0iyjb245c0q613ipmklmbzx4wb74i8lvmfwa3cwr"; 6 | owner = "angerman"; 7 | }; 8 | in 9 | { pkgsPath' ? pkgsPath }: 10 | 11 | with import pkgsPath' { 12 | crossSystem = (import ).systems.examples.mingwW64; 13 | config = import ./config.nix; 14 | }; 15 | let Cabal_HEAD = buildPackages.haskell.packages.ghcHEAD.callPackage ./cabal-head.nix { }; 16 | in { 17 | hello-world = pkgs.haskell.packages.ghcHEAD.callPackage ./hello-world.nix { inherit Cabal_HEAD; }; 18 | cross-ghc = haskell.packages.ghcHEAD.ghc; 19 | inherit Cabal_HEAD; 20 | } 21 | -------------------------------------------------------------------------------- /cabal-head.nix: -------------------------------------------------------------------------------- 1 | { mkDerivation, pkgs, array, base, binary, bytestring, containers 2 | , deepseq, directory, filepath, pretty, process, QuickCheck, tagged 3 | , tasty, tasty-hunit, tasty-quickcheck, time, unix, stdenv }: 4 | mkDerivation { 5 | pname = "Cabal"; 6 | version = "2.3.0.0"; 7 | src = pkgs.fetchgit { 8 | url = "https://github.com/zw3rk/cabal.git"; 9 | rev = "7016dfa4b7da82a5b6b920d6af44c7add27d8c06"; 10 | sha256 = "140v0zxyrl7apw2glc79qdzxq03cbgal2f412sivm3758xm1r2gm"; 11 | # extracting the "Cabal" subfolder from the checkout. 12 | # this is terrible :( 13 | postFetch = '' 14 | mv "$out" "$out.bck" 15 | mv "$out.bck/Cabal" "$out" 16 | rm -fR "$out.bck" 17 | ''; 18 | }; 19 | # The program 'haddock' version >=2.0 is required 20 | doHaddock = false; 21 | #sha256 = "06rx6jxikqrdf7k6pmam5cvhwnagq6njmb9qm5777nrz278ccaw0"; 22 | libraryHaskellDepends = [ 23 | array base binary bytestring containers deepseq directory filepath 24 | pretty process time unix 25 | ]; 26 | testHaskellDepends = [ 27 | array base containers directory filepath pretty QuickCheck tagged 28 | tasty tasty-hunit tasty-quickcheck 29 | ]; 30 | doCheck = false; 31 | homepage = "http://www.haskell.org/cabal/"; 32 | description = "A framework for packaging Haskell software"; 33 | license = stdenv.lib.licenses.bsd3; 34 | hydraPlatforms = stdenv.lib.platforms.none; 35 | } 36 | -------------------------------------------------------------------------------- /config.nix: -------------------------------------------------------------------------------- 1 | let customizeGhc = oldGhc: 2 | let ghc = oldGhc.override { 3 | # override the version, revision and flavour to get a custom ghc. 4 | version = "8.5.20180306"; 5 | ghcRevision = "1dfd7aa2cb06adccc9180463807e62260d66c90e"; 6 | ghcSha256 = "13iygvpdsbwxphx89lcvp358z63rnah8gzyxzv20qik6vhh7nf2j"; 7 | ghcCrossFlavour = "quick-cross-ncg"; 8 | ghcFlavour = "quick"; 9 | }; in ghc.overrideAttrs (drv: { 10 | # override the derivation attributes. 11 | # Specifically to set dontStrip. 12 | # name = "ghc-8.5.angerman"; 13 | dontStrip = true; 14 | # patches = map fetchDiff ghcDiffs; 15 | hardeningDisable = [ "stackprotector" ]; 16 | }); 17 | in 18 | { 19 | packageOverrides = ps: rec { 20 | haskell.compiler = ps.haskell.compiler // { 21 | ghcHEAD = customizeGhc ps.haskell.compiler.ghcHEAD; 22 | }; 23 | haskell.packages = ps.haskell.packages // { 24 | ghcHEAD = ps.haskell.packages.ghcHEAD.override { 25 | ghc = customizeGhc ps.buildPackages.haskell.compiler.ghcHEAD; 26 | }; 27 | # Cabal_HEAD = ps.buildPackages.haskell.packages.ghcHEAD.callPackage ./cabal-head.nix { }; 28 | 29 | }; 30 | # TODO: Inject Cabal_HAED into setupHaskellDepends by default. 31 | # 32 | # // ps.lib.optionalAttrs (ps.hostPlatform != ps.buildPlatform) { 33 | # mkDerivation = drv: super.mkDerivation (drv // { 34 | # setupHaskellDepends = (drv.setupHaskellDepends or []) ++ [ 35 | # self.ghc.bootPkgs.Cabal 36 | # ]; 37 | # }); 38 | # }; 39 | }; 40 | } 41 | -------------------------------------------------------------------------------- /fetchNixpkgs.nix: -------------------------------------------------------------------------------- 1 | { rev # The Git revision of nixpkgs to fetch 2 | , sha256 # The SHA256 of the downloaded data 3 | , sha256unpacked 4 | , owner ? "NixOS" 5 | , system ? builtins.currentSystem # This is overridable if necessary 6 | }: 7 | 8 | with { 9 | ifThenElse = { bool, thenValue, elseValue }: ( 10 | if bool then thenValue else elseValue); 11 | }; 12 | 13 | ifThenElse { 14 | bool = (0 <= builtins.compareVersions builtins.nixVersion "1.12"); 15 | 16 | # In Nix 1.12, we can just give a `sha256` to `builtins.fetchTarball`. 17 | thenValue = ( 18 | builtins.fetchTarball { 19 | url = "https://github.com/${owner}/nixpkgs/archive/${rev}.tar.gz"; 20 | sha256 = sha256unpacked; 21 | }); 22 | 23 | # This hack should at least work for Nix 1.11 24 | elseValue = ( 25 | (rec { 26 | tarball = import { 27 | url = "https://github.com/${owner}/nixpkgs/archive/${rev}.tar.gz"; 28 | inherit sha256; 29 | }; 30 | 31 | builtin-paths = import ; 32 | 33 | script = builtins.toFile "nixpkgs-unpacker" '' 34 | "$coreutils/mkdir" "$out" 35 | cd "$out" 36 | "$gzip" --decompress < "$tarball" | "$tar" -x --strip-components=1 37 | ''; 38 | 39 | nixpkgs = builtins.derivation { 40 | name = "nixpkgs-${builtins.substring 0 6 rev}"; 41 | 42 | builder = builtins.storePath builtin-paths.shell; 43 | 44 | args = [ script ]; 45 | 46 | inherit tarball system; 47 | 48 | tar = builtins.storePath builtin-paths.tar; 49 | gzip = builtins.storePath builtin-paths.gzip; 50 | coreutils = builtins.storePath builtin-paths.coreutils; 51 | }; 52 | }).nixpkgs); 53 | } 54 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | * Hello Windows 2 | 3 | This repository contains the necessary nix-expressions and a ~nixpkgs~ 4 | submodule that should allow us to cross compile a small haskell application 5 | into a working windows executable. 6 | 7 | [[file:hs-hello.png]] 8 | 9 | ** Building 10 | To build the cross compiled ~hs-hello.exe~ with nix, all we need is 11 | 12 | #+BEGIN_SRC bash 13 | $ nix-build -A hello-world --cores 0 14 | #+END_SRC 15 | 16 | This will build the necessary dependencies, and finally build the 17 | ~hs-hello.exe~, which we can find in ~result/bin/hs-hello.exe~. 18 | 19 | ** ~config.nix~ 20 | We will need a customized GHC. Due to the introduction of the 21 | ~buildPackages~ for the cross compilation capabilities in ~nixpkgs~ this 22 | turns out to be a bit more complicated than we like. The core issue here is 23 | that the stage logic and the ~packageOverrides~ do not play well together. 24 | Specifically changes made via ~packageOverrides~ do not show up in the 25 | ~buildPackages~. 26 | 27 | ** ~hello-world.nix~ 28 | This is our usual haskell package expression. Just note the additional 29 | 30 | #+BEGIN_SRC nix 31 | enableSharedExecutables = false; 32 | setupHaskellDepends = [ Cabal_HEAD ]; 33 | #+END_SRC 34 | 35 | as we don't have dynamic libraries with our cross compiler (yet). And need 36 | to link against a custom ~Cabal~ library. 37 | 38 | ** ~default.nix~ 39 | In the ~default.nix~ we'll pull everything together. Set the ~crossSystem~ 40 | to ~mingwW64~ and the ~packageOverlays~ via ~config~. 41 | 42 | In addition we setup a custom ~Cabal~ library, which we want to link against; 43 | the one that ships with GHC has some defects when cross compiling to windows. 44 | 45 | We use ~callPackage~ on the ~buildPackages~ to ensure that the ~Cabal~ 46 | library is built for the *build* machine. For the ~hello-world~ package 47 | (~hello-world.nix~) we use ~pkgs~ which will result in the package to be built 48 | for the *host* machine. 49 | 50 | --------------------------------------------------------------------------------