Add cross compilation (#401)
authorZhen Zhang <izgzhen@gmail.com>
Mon, 28 Aug 2017 10:26:27 +0000 (18:26 +0800)
committerAndrey Mokhov <andrey.mokhov@gmail.com>
Mon, 28 Aug 2017 10:26:27 +0000 (11:26 +0100)
Tested with arm-linux-gnueabihf.

12 files changed:
doc/cross-compile.md [new file with mode: 0644]
hadrian.cabal
src/Oracles/Flag.hs
src/Rules.hs
src/Settings.hs
src/Settings/Builders/Common.hs
src/Settings/Default.hs
src/Settings/Packages/Compiler.hs
src/Settings/Packages/Ghc.hs
src/Settings/Packages/GhcPkg.hs [new file with mode: 0644]
src/Settings/Packages/Haskeline.hs [new file with mode: 0644]
src/UserSettings.hs

diff --git a/doc/cross-compile.md b/doc/cross-compile.md
new file mode 100644 (file)
index 0000000..c51eaa5
--- /dev/null
@@ -0,0 +1,57 @@
+## Build a cross-compiling GHC
+
+Our host machine is "Ubuntu 16.04.2 LTS, Linux ubuntu 4.4.0-79-generic 86_64".
+
+We need to download necessary tools, including:
+
+- [LLVM-4.0 source](http://releases.llvm.org/4.0.0/llvm-4.0.0.src.tar.xz), you need to build it yourself. Remember to choose release channel and use gold linker (`cmake -DCMAKE_BUILD_TYPE=Release -DLLVM_USE_LINKER=gold ..`)
+- `sudo apt-get install gcc-arm-linux-gnueabihf` to install the GCC cross-compiler
+- Download and install [Haskell Platform 8.0.2](https://haskell.org/platform/download/8.0.2/haskell-platform-8.0.2-unknown-posix--full-x86_64.tar.gz). Install it according to [instructions here](https://www.haskell.org/platform/linux.html#linux-generic)
+
+After all the dependencies are in place:
+
+- `git clone https://github.com/ghc/ghc`
+- `cd ghc`
+- `git clone https://github.com/snowleopard/hadrian`
+- `git submodule update --init`
+- `./configure --target=arm-linux-gnueabihf`
+- `cd hadrian`
+- Modify `src/Settings.hs`, set `stage1Only` and `crossCompiling` to `True`.
+- Build the compiler by e.g. `./build.cabal.sh --flavour=quickest --integer-simple --skip-configure -V -j`
+
+After that, you should have built `inplace/bin/ghc-stage1` cross compiler. We will go to the next section to validate this.
+
+## Test run
+
+Write a simple hello world haskell program:
+
+```haskell
+module Main where
+main = putStrLn "Hello, world!"
+```
+Compile it with cross-compiling GHC: `<ghc-folder>/inplace/bin/ghc-stage1 -static Main`. Note that we created a static version of it which packs together all depending libraries.
+
+- Install QEMU: `sudo apt-get install qemu-system-arm`
+- Download `vmlinuz` (kernel) and `initrd.gz` (initial ramdisk) from mirror like https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/dists/xenial/main/installer-armhf/current/images/generic-lpae/cdrom/.
+- Add the ARM Linux executable `Main` to the initial ramdisk so we can load it directly into memory. No need for real installation
+  + `gunzip initrd.gz` to get `initrd`
+  + `mkdir tmp2; cd tmp2; sudo cpio -id < ../initrd` to get a file system
+  + `cp /PATH/TO/Main usr/bin`
+  + `find . | cpio --create --format='newc' > /tmp/newinitrd` to pack back the `initrd`
+  + `gzip /tmp/newinitrd`
+  + Move `newinitrd` to where `vmlinuz` is, rename it to `newinitrd.img`
+  + Run the following configured QEMU:
+
+```bash
+#!/bin/sh
+qemu-system-arm \
+    -kernel vmlinuz \
+    -initrd newinitrd.img \
+    -append "root=/dev/vda2 rootfstype=ext4" \
+    -no-reboot \
+    -nographic \
+    -m 1024 \
+    -M virt
+```
+
+This will lead you to a installer interface. But we don't need to do that, so we can save ourself from the hassel of setup networks etc. We just keep `Go Back`, until see a line `Execute a shell`, and select it. Now you get a shell, go find `/usr/bin/Main` and run it!
index dbe454e..66072a2 100644 (file)
@@ -89,8 +89,10 @@ executable hadrian
                        , Settings.Packages.Ghc
                        , Settings.Packages.GhcCabal
                        , Settings.Packages.Ghci
+                       , Settings.Packages.GhcPkg
                        , Settings.Packages.GhcPrim
                        , Settings.Packages.Haddock
+                       , Settings.Packages.Haskeline
                        , Settings.Packages.IntegerGmp
                        , Settings.Packages.Rts
                        , Settings.Packages.RunGhc
index fb1f91c..20aca1f 100644 (file)
@@ -1,5 +1,5 @@
 module Oracles.Flag (
-    Flag (..), flag, crossCompiling, platformSupportsSharedLibs,
+    Flag (..), flag, platformSupportsSharedLibs,
     ghcWithSMP, ghcWithNativeCodeGen, supportsSplitObjects
     ) where
 
@@ -43,9 +43,6 @@ flag f = do
         ++ quote (key ++ " = " ++ value) ++ "cannot be parsed."
     return $ value == "YES"
 
-crossCompiling :: Action Bool
-crossCompiling = flag CrossCompiling
-
 platformSupportsSharedLibs :: Action Bool
 platformSupportsSharedLibs = do
     badPlatform   <- anyTargetPlatform [ "powerpc-unknown-linux"
index 1f0437b..02404dc 100644 (file)
@@ -23,6 +23,7 @@ import qualified Rules.Perl
 import qualified Rules.Program
 import qualified Rules.Register
 import Settings
+import UserSettings (stage1Only)
 import Target
 import Utilities
 
index 291af54..d903255 100644 (file)
@@ -1,7 +1,7 @@
 module Settings (
     getArgs, getLibraryWays, getRtsWays, flavour, knownPackages,
     findPackageByName, getPkgData, getPkgDataList, isLibrary, stagePackages,
-    latestBuildStage, programContext, integerLibraryName, getDestDir, stage1Only
+    latestBuildStage, programContext, integerLibraryName, getDestDir
     ) where
 
 import Context
@@ -70,11 +70,6 @@ latestBuildStage pkg = do
     stages <- filterM (fmap (pkg `elem`) . stagePackages) [Stage0 ..]
     return $ if null stages then Nothing else Just $ maximum stages
 
--- TODO: Set this from command line
--- | Stage1Only flag.
-stage1Only :: Bool
-stage1Only = defaultStage1Only
-
 -- | Install's DESTDIR setting.
 getDestDir :: Action FilePath
 getDestDir = fromMaybe "" <$> cmdInstallDestDir
index 39d27b5..707427c 100644 (file)
@@ -28,7 +28,8 @@ cIncludeArgs = do
     path    <- getBuildPath
     incDirs <- getPkgDataList IncludeDirs
     depDirs <- getPkgDataList DepIncludeDirs
-    mconcat [ arg "-Iincludes"
+    compilerOrGhc <- package compiler ||^ package ghc
+    mconcat [ not (crossCompiling && compilerOrGhc) ? arg "-Iincludes"
             , arg $ "-I" ++ root -/- generatedDir
             , arg $ "-I" ++ path
             , pure [ "-I" ++ pkgPath pkg -/- dir | dir <- incDirs ]
index 92a6cbf..5ab1e44 100644 (file)
@@ -34,8 +34,10 @@ import Settings.Packages.Compiler
 import Settings.Packages.Ghc
 import Settings.Packages.GhcCabal
 import Settings.Packages.Ghci
+import Settings.Packages.GhcPkg
 import Settings.Packages.GhcPrim
 import Settings.Packages.Haddock
+import Settings.Packages.Haskeline (haskelinePackageArgs)
 import Settings.Packages.IntegerGmp
 import Settings.Packages.Rts
 import Settings.Packages.RunGhc
@@ -207,4 +209,6 @@ defaultPackageArgs = mconcat
     , integerGmpPackageArgs
     , rtsPackageArgs
     , runGhcPackageArgs
-    , disableWarningArgs ]
+    , disableWarningArgs
+    , ghcPkgPackageArgs
+    , haskelinePackageArgs ]
index 4e84ee7..0100f96 100644 (file)
@@ -7,6 +7,7 @@ import GHC
 import Oracles.Flag
 import Oracles.Setting
 import Settings
+import UserSettings (crossCompiling)
 
 compilerPackageArgs :: Args
 compilerPackageArgs = package compiler ? do
@@ -32,6 +33,7 @@ compilerPackageArgs = package compiler ? do
               , ghcWithNativeCodeGen ? arg "--flags=ncg"
               , ghcWithInterpreter ?
                 notStage0 ? arg "--flags=ghci"
+              , crossCompiling ? arg "-f-terminfo"
               , ghcWithInterpreter ?
                 ghcEnableTablesNextToCode ?
                 notM (flag GhcUnregisterised) ?
index fd6b485..ee5ed40 100644 (file)
@@ -4,10 +4,12 @@ import Context (buildPath)
 import GHC
 import Expression
 import Oracles.Setting
+import UserSettings (crossCompiling)
 
 ghcPackageArgs :: Args
 ghcPackageArgs = package ghc ? do
     stage <- getStage
     path  <- expr $ buildPath (vanillaContext stage compiler)
     mconcat [ builder Ghc      ? arg ("-I" ++ path)
-            , builder GhcCabal ? ghcWithInterpreter ? notStage0 ? arg "--flags=ghci" ]
+            , builder GhcCabal ? ghcWithInterpreter ? notStage0 ? arg "--flags=ghci"
+            , builder GhcCabal ? crossCompiling ? arg "-f-terminfo" ]
diff --git a/src/Settings/Packages/GhcPkg.hs b/src/Settings/Packages/GhcPkg.hs
new file mode 100644 (file)
index 0000000..5286c68
--- /dev/null
@@ -0,0 +1,8 @@
+module Settings.Packages.GhcPkg (ghcPkgPackageArgs) where
+
+import GHC
+import Expression
+import UserSettings (crossCompiling)
+
+ghcPkgPackageArgs :: Args
+ghcPkgPackageArgs = crossCompiling ? package ghcPkg ? builder GhcCabal ? arg "-f-terminfo"
diff --git a/src/Settings/Packages/Haskeline.hs b/src/Settings/Packages/Haskeline.hs
new file mode 100644 (file)
index 0000000..31694fe
--- /dev/null
@@ -0,0 +1,10 @@
+module Settings.Packages.Haskeline (haskelinePackageArgs) where
+
+import Base
+import Expression
+import GHC
+import UserSettings (crossCompiling)
+
+haskelinePackageArgs :: Args
+haskelinePackageArgs =
+    package haskeline ? builder GhcCabal ? crossCompiling ? arg "-f-terminfo"
index 17d13df..981e704 100644 (file)
@@ -4,7 +4,7 @@
 -- accidentally commit them.
 module UserSettings (
     userBuildRoot, userFlavours, userPackages, verboseCommands,
-    buildProgressColour, successColour, defaultStage1Only
+    buildProgressColour, successColour, stage1Only, crossCompiling
     ) where
 
 import Hadrian.Utilities
@@ -45,6 +45,11 @@ buildProgressColour = BuildProgressColour (Dull, Magenta)
 successColour :: SuccessColour
 successColour = SuccessColour (Dull, Green)
 
+-- | Build a cross compiling GHC
+-- TODO: Use @Action Bool@ version in @Oracles.Flag@
+crossCompiling :: Bool
+crossCompiling = False
+
 {-
   Stage1Only=YES means:
    - don't build ghc-stage2 (the executable)
@@ -57,5 +62,6 @@ successColour = SuccessColour (Dull, Green)
    - (*do* still build all other libraries)
 -}
 -- | Stage1Only flag, default off
-defaultStage1Only :: Bool
-defaultStage1Only = False
+-- | TODO: Set this dynamically
+stage1Only :: Bool
+stage1Only = False