diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index dd9f92f2dbe7ba84bf193e8115ea2298ae2ddde5..cc40748c4bdcc65ad0250e80146248e46191ac1e 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -281,11 +281,31 @@ test:linux:cross-armv7:
     CROSS: "arm-linux-gnueabihf"
   needs: []
   when: manual
+  allow_failure: true
   before_script:
     - ./.gitlab/before_script/linux/install_deps.sh
   script:
     - ./.gitlab/script/ghcup_cross.sh
 
+test:linux:git:hadrian:
+  stage: test
+  extends:
+    - .test_ghcup_version
+    - .debian
+  variables:
+    GHC_VERSION: "8.10.5"
+    GHC_GIT_TAG: "ghc-9.0.1-release"
+    GHC_GIT_VERSION: "9.0.1"
+    CABAL_VERSION: "3.4.0.0"
+    CROSS: ""
+  needs: []
+  when: manual
+  allow_failure: true
+  before_script:
+    - ./.gitlab/before_script/linux/install_deps.sh
+  script:
+    - ./.gitlab/script/ghcup_git.sh
+
 
 ######## linux 32bit test ########
 
diff --git a/.gitlab/script/ghcup_git.sh b/.gitlab/script/ghcup_git.sh
new file mode 100755
index 0000000000000000000000000000000000000000..192e70e85f1a7a60e9aa862354f4d01159075161
--- /dev/null
+++ b/.gitlab/script/ghcup_git.sh
@@ -0,0 +1,52 @@
+#!/bin/sh
+
+set -eux
+
+. "$( cd "$(dirname "$0")" ; pwd -P )/../ghcup_env"
+
+mkdir -p "$CI_PROJECT_DIR"/.local/bin
+
+CI_PROJECT_DIR=$(pwd)
+
+ecabal() {
+	cabal "$@"
+}
+
+eghcup() {
+	ghcup -v -c -s file://$CI_PROJECT_DIR/ghcup-${JSON_VERSION}.yaml "$@"
+}
+
+git describe --always
+
+### build
+
+ecabal update
+
+ecabal build -w ghc-${GHC_VERSION}
+cp "$(ecabal new-exec -w ghc-${GHC_VERSION} --verbose=0 --offline sh -- -c 'command -v ghcup')" "$CI_PROJECT_DIR"/.local/bin/ghcup
+
+### cleanup
+
+rm -rf "${GHCUP_INSTALL_BASE_PREFIX}"/.ghcup
+
+### manual cli based testing
+
+eghcup --numeric-version
+
+eghcup install ghc ${GHC_VERSION}
+eghcup set ghc ${GHC_VERSION}
+eghcup install cabal ${CABAL_VERSION}
+
+cabal --version
+
+eghcup debug-info
+
+eghcup compile ghc -j $(nproc) -g ${GHC_GIT_TAG} -b ${GHC_VERSION} -- --enable-unregisterised
+eghcup set ghc ${GHC_GIT_VERSION}
+
+[ `$(eghcup whereis ghc ${GHC_GIT_VERSION}) --numeric-version` = "${GHC_GIT_VERSION}" ]
+
+# nuke
+eghcup nuke
+[ ! -e "${GHCUP_INSTALL_BASE_PREFIX}/.ghcup" ]
+
diff --git a/app/ghcup/Main.hs b/app/ghcup/Main.hs
index fdfc6ed0297f1722ae96fa7ac048e01be04a3890..f5aefcd6424f1d0a7ddda3b9c9c0a031eded0e68 100644
--- a/app/ghcup/Main.hs
+++ b/app/ghcup/Main.hs
@@ -183,6 +183,7 @@ data GHCCompileOptions = GHCCompileOptions
   , setCompile   :: Bool
   , ovewrwiteVer :: Maybe Version
   , buildFlavour :: Maybe String
+  , hadrian      :: Bool
   }
 
 data UpgradeOpts = UpgradeInplace
@@ -995,6 +996,9 @@ ghcCompileOpts =
               "Set the compile build flavour (this value depends on the build system type: 'make' vs 'hadrian')"
             )
           )
+    <*> switch
+          (long "hadrian" <> help "Use the hadrian build system instead of make (only git versions seem to be properly supported atm)"
+          )
 
 
 toolVersionParser :: Parser ToolVersion
@@ -1914,6 +1918,9 @@ Report bugs at <https://gitlab.haskell.org/haskell/ghcup-hs/issues>|]
                         runLogger $ $(logError) $ T.pack $ prettyShow e
                         pure $ ExitFailure 8
 
+            Compile (CompileGHC GHCCompileOptions { hadrian = True, crossTarget = Just _ }) -> do
+              runLogger $ $(logError) "Hadrian cross compile support is not yet implemented!"
+              pure $ ExitFailure 9
             Compile (CompileGHC GHCCompileOptions {..}) ->
               runCompileGHC (do
                 case targetGhc of
@@ -1935,6 +1942,7 @@ Report bugs at <https://gitlab.haskell.org/haskell/ghcup-hs/issues>|]
                             patchDir
                             addConfArgs
                             buildFlavour
+                            hadrian
                 GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo
                 let vi = getVersionInfo (_tvVersion targetVer) GHC dls
                 when setCompile $ void $ liftE $
diff --git a/lib/GHCup.hs b/lib/GHCup.hs
index 09e18a036be7f4cb4f45dd5aff8fae7fdc5a605a..f348f10db91ea4e66bf3286ea134a57797a3650c 100644
--- a/lib/GHCup.hs
+++ b/lib/GHCup.hs
@@ -1672,6 +1672,7 @@ compileGHC :: ( MonadMask m
            -> Maybe FilePath           -- ^ patch directory
            -> [Text]                   -- ^ additional args to ./configure
            -> Maybe String             -- ^ build flavour
+           -> Bool
            -> Excepts
                 '[ AlreadyInstalled
                  , BuildFailed
@@ -1690,7 +1691,7 @@ compileGHC :: ( MonadMask m
                  ]
                 m
                 GHCTargetVersion
-compileGHC targetGhc ov bstrap jobs mbuildConfig patchdir aargs buildFlavour
+compileGHC targetGhc ov bstrap jobs mbuildConfig patchdir aargs buildFlavour hadrian
   = do
     PlatformRequest { .. } <- lift getPlatformReq
     GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo
@@ -1775,8 +1776,10 @@ compileGHC targetGhc ov bstrap jobs mbuildConfig patchdir aargs buildFlavour
       tmpUnpack
       Nothing
       (do
-        b <- compileBindist bghc tver workdir ghcdir
-        bmk <- liftIO $ B.readFile (build_mk workdir)
+        b <- if hadrian
+             then compileHadrianBindist bghc tver workdir ghcdir
+             else compileMakeBindist bghc tver workdir ghcdir
+        bmk <- liftIO $ handleIO (\_ -> pure "") $ B.readFile (build_mk workdir)
         pure (b, bmk)
       )
 
@@ -1821,71 +1824,94 @@ ifneq "$(BuildFlavour)" ""
 include mk/flavours/$(BuildFlavour).mk
 endif|]
 
-  compileBindist :: ( MonadReader env m
-                    , HasDirs env
-                    , HasSettings env
-                    , HasPlatformReq env
-                    , MonadThrow m
-                    , MonadCatch m
-                    , MonadLogger m
-                    , MonadIO m
-                    , MonadFail m
-                    )
-                 => Either FilePath FilePath
-                 -> GHCTargetVersion
-                 -> FilePath
-                 -> FilePath
-                 -> Excepts
-                      '[FileDoesNotExistError, InvalidBuildConfig, PatchFailed, ProcessError, NotFoundInPATH, CopyError]
-                      m
-                      (Maybe FilePath)  -- ^ output path of bindist, None for cross
-  compileBindist bghc tver workdir ghcdir = do
-    lift $ $(logInfo) [i|configuring build|]
-    
-    Dirs {..} <- lift getDirs
-    pfreq <- lift getPlatformReq
-
-    forM_ patchdir $ \dir -> liftE $ applyPatches dir workdir
+  compileHadrianBindist :: ( MonadReader env m
+                           , HasDirs env
+                           , HasSettings env
+                           , HasPlatformReq env
+                           , MonadThrow m
+                           , MonadCatch m
+                           , MonadLogger m
+                           , MonadIO m
+                           , MonadFail m
+                           )
+                        => Either FilePath FilePath
+                        -> GHCTargetVersion
+                        -> FilePath
+                        -> FilePath
+                        -> Excepts
+                             '[ FileDoesNotExistError
+                              , HadrianNotFound
+                              , InvalidBuildConfig
+                              , PatchFailed
+                              , ProcessError
+                              , NotFoundInPATH
+                              , CopyError]
+                             m
+                             (Maybe FilePath)  -- ^ output path of bindist, None for cross
+  compileHadrianBindist bghc tver workdir ghcdir = do
+    lEM $ execLogged "python3" ["./boot"] (Just workdir) "ghc-bootstrap" Nothing
+
+    liftE $ configureBindist bghc tver workdir ghcdir
 
-    cEnv <- liftIO getEnvironment
+    lift $ $(logInfo) [i|Building (this may take a while)...|]
+    hadrian_build <- liftE $ findHadrianFile workdir
+    lEM $ execLogged hadrian_build
+                          ( maybe [] (\j  -> [[i|-j#{j}|]]         ) jobs
+                         ++ maybe [] (\bf -> [[i|--flavour=#{bf}|]]) buildFlavour
+                         ++ ["binary-dist"]
+                          )
+                          (Just workdir) "ghc-make" Nothing
+    [tar] <- liftIO $ findFiles
+      (workdir </> "_build" </> "bindist")
+      (makeRegexOpts compExtended
+                     execBlank
+                     ([s|^ghc-.*\.tar\..*$|] :: ByteString)
+      )
+    liftE $ fmap Just $ copyBindist tver tar (workdir </> "_build" </> "bindist")
 
-    if | _tvVersion tver >= [vver|8.8.0|] -> do
-          bghcPath <- case bghc of
-            Right ghc' -> pure ghc'
-            Left  bver -> do
-              spaths <- liftIO getSearchPath
-              liftIO (searchPath spaths bver) !? NotFoundInPATH bver
-          lEM $ execLogged
-            "sh"
-            ("./configure" :  maybe mempty
-                      (\x -> ["--target=" <> T.unpack x])
-                      (_tvTarget tver)
-            ++ ["--prefix=" <> ghcdir]
-#if defined(IS_WINDOWS)
-            ++ ["--enable-tarballs-autodownload"]
-#endif
-            ++ fmap T.unpack aargs
-            )
-            (Just workdir)
-            "ghc-conf"
-            (Just (("GHC", bghcPath) : cEnv))
-       | otherwise -> do
-        lEM $ execLogged
-          "sh"
-          (  [ "./configure", "--with-ghc=" <> either id id bghc
-             ]
-          ++ maybe mempty
-                   (\x -> ["--target=" <> T.unpack x])
-                   (_tvTarget tver)
-          ++ ["--prefix=" <> ghcdir]
+  findHadrianFile :: (MonadIO m)
+                  => FilePath
+                  -> Excepts
+                       '[HadrianNotFound]
+                       m
+                       FilePath
+  findHadrianFile workdir = do
 #if defined(IS_WINDOWS)
-          ++ ["--enable-tarballs-autodownload"]
+    let possible_files = ((workdir </> "hadrian") </>) <$> ["build.bat"]
+#else   
+    let possible_files = ((workdir </> "hadrian") </>) <$> ["build", "build.sh"]
 #endif
-          ++ fmap T.unpack aargs
-          )
-          (Just workdir)
-          "ghc-conf"
-          (Just cEnv)
+    exsists <- forM possible_files (\f -> liftIO (doesFileExist f) <&> (,f))
+    case filter fst exsists of
+      [] -> throwE HadrianNotFound
+      ((_, x):_) -> pure x
+
+  compileMakeBindist :: ( MonadReader env m
+                        , HasDirs env
+                        , HasSettings env
+                        , HasPlatformReq env
+                        , MonadThrow m
+                        , MonadCatch m
+                        , MonadLogger m
+                        , MonadIO m
+                        , MonadFail m
+                        )
+                     => Either FilePath FilePath
+                     -> GHCTargetVersion
+                     -> FilePath
+                     -> FilePath
+                     -> Excepts
+                          '[ FileDoesNotExistError
+                           , HadrianNotFound
+                           , InvalidBuildConfig
+                           , PatchFailed
+                           , ProcessError
+                           , NotFoundInPATH
+                           , CopyError]
+                          m
+                       (Maybe FilePath)  -- ^ output path of bindist, None for cross
+  compileMakeBindist bghc tver workdir ghcdir = do
+    liftE $ configureBindist bghc tver workdir ghcdir
 
     case mbuildConfig of
       Just bc -> liftIOException
@@ -1913,25 +1939,46 @@ endif|]
                            execBlank
                            ([s|^ghc-.*\.tar\..*$|] :: ByteString)
             )
-          c       <- liftIO $ BL.readFile (workdir </> tar)
-          cDigest <-
-            fmap (T.take 8)
-            . lift
-            . throwEither
-            . E.decodeUtf8'
-            . B16.encode
-            . SHA256.hashlazy
-            $ c
-          cTime <- liftIO getCurrentTime
-          let tarName = makeValid [i|ghc-#{tVerToText tver}-#{pfReqToString pfreq}-#{iso8601Show cTime}-#{cDigest}.tar#{takeExtension tar}|]
-          let tarPath = cacheDir </> tarName
-          handleIO (throwE . CopyError . show) $ liftIO $ copyFile (workdir </> tar)
-                                                                   tarPath
-          lift $ $(logInfo) [i|Copied bindist to #{tarPath}|]
-          pure $ Just tarPath
+          liftE $ fmap Just $ copyBindist tver tar workdir
 
   build_mk workdir = workdir </> "mk" </> "build.mk"
 
+  copyBindist :: ( MonadReader env m
+                 , HasDirs env
+                 , HasSettings env
+                 , HasPlatformReq env
+                 , MonadIO m
+                 , MonadThrow m
+                 , MonadCatch m
+                 , MonadLogger m
+                 )
+              => GHCTargetVersion
+              -> FilePath           -- ^ tar file
+              -> FilePath           -- ^ workdir
+              -> Excepts
+                   '[CopyError]
+                   m
+                   FilePath
+  copyBindist tver tar workdir = do
+    Dirs {..} <- lift getDirs
+    pfreq <- lift getPlatformReq
+    c       <- liftIO $ BL.readFile (workdir </> tar)
+    cDigest <-
+      fmap (T.take 8)
+      . lift
+      . throwEither
+      . E.decodeUtf8'
+      . B16.encode
+      . SHA256.hashlazy
+      $ c
+    cTime <- liftIO getCurrentTime
+    let tarName = makeValid [i|ghc-#{tVerToText tver}-#{pfReqToString pfreq}-#{iso8601Show cTime}-#{cDigest}.tar#{takeExtension tar}|]
+    let tarPath = cacheDir </> tarName
+    handleIO (throwE . CopyError . show) $ liftIO $ copyFile (workdir </> tar)
+                                                             tarPath
+    lift $ $(logInfo) [i|Copied bindist to #{tarPath}|]
+    pure tarPath
+
   checkBuildConfig :: (MonadCatch m, MonadIO m, MonadLogger m)
                    => FilePath
                    -> Excepts
@@ -1959,14 +2006,87 @@ endif|]
         liftIO $ threadDelay 5000000
 
   addBuildFlavourToConf bc = case buildFlavour of
-    Just bf -> [i|BuildFlavour = #{bf}
-#{bc}|]
+    Just bf -> [i|BuildFlavour = #{bf}|] <> [s|
+|] <> [i|#{bc}|]
     Nothing -> bc
 
   isCross :: GHCTargetVersion -> Bool
   isCross = isJust . _tvTarget
 
 
+  configureBindist :: ( MonadReader env m
+                      , HasDirs env
+                      , HasSettings env
+                      , HasPlatformReq env
+                      , MonadThrow m
+                      , MonadCatch m
+                      , MonadLogger m
+                      , MonadIO m
+                      , MonadFail m
+                      )
+                   => Either FilePath FilePath
+                   -> GHCTargetVersion
+                   -> FilePath
+                   -> FilePath
+                   -> Excepts
+                        '[ FileDoesNotExistError
+                         , InvalidBuildConfig
+                         , PatchFailed
+                         , ProcessError
+                         , NotFoundInPATH
+                         , CopyError
+                         ]
+                        m
+                        ()
+  configureBindist bghc tver workdir ghcdir = do
+    lift $ $(logInfo) [s|configuring build|]
+    
+    forM_ patchdir (\dir -> liftE $ applyPatches dir workdir)
+
+    cEnv <- liftIO getEnvironment
+
+    if | _tvVersion tver >= [vver|8.8.0|] -> do
+          bghcPath <- case bghc of
+            Right ghc' -> pure ghc'
+            Left  bver -> do
+              spaths <- liftIO getSearchPath
+              liftIO (searchPath spaths bver) !? NotFoundInPATH bver
+          lEM $ execLogged
+            "sh"
+            ("./configure" :  maybe mempty
+                      (\x -> ["--target=" <> T.unpack x])
+                      (_tvTarget tver)
+            ++ ["--prefix=" <> ghcdir]
+#if defined(IS_WINDOWS)
+            ++ ["--enable-tarballs-autodownload"]
+#endif
+            ++ fmap T.unpack aargs
+            )
+            (Just workdir)
+            "ghc-conf"
+            (Just (("GHC", bghcPath) : cEnv))
+       | otherwise -> do
+        lEM $ execLogged
+          "sh"
+          (  [ "./configure", "--with-ghc=" <> either id id bghc
+             ]
+          ++ maybe mempty
+                   (\x -> ["--target=" <> T.unpack x])
+                   (_tvTarget tver)
+          ++ ["--prefix=" <> ghcdir]
+#if defined(IS_WINDOWS)
+          ++ ["--enable-tarballs-autodownload"]
+#endif
+          ++ fmap T.unpack aargs
+          )
+          (Just workdir)
+          "ghc-conf"
+          (Just cEnv)
+    pure ()
+
+
+
+
 
 
     ---------------------
diff --git a/lib/GHCup/Errors.hs b/lib/GHCup/Errors.hs
index a1366d7be7c4acbdee64a6d71e61e04e7105ddc8..bd803b258194efac3a1354eb8fb87e967050fe61 100644
--- a/lib/GHCup/Errors.hs
+++ b/lib/GHCup/Errors.hs
@@ -31,8 +31,8 @@ import           Data.String.Interpolate
 import           Data.Text                      ( Text )
 import           Data.Versions
 import           Haskus.Utils.Variant
-import           Text.PrettyPrint
-import           Text.PrettyPrint.HughesPJClass
+import           Text.PrettyPrint               hiding ( (<>) )
+import           Text.PrettyPrint.HughesPJClass hiding ( (<>) )
 import           URI.ByteString
 
 
@@ -240,6 +240,13 @@ instance Pretty NoNetwork where
   pPrint NoNetwork =
     text [i|A download was required or requested, but '--offline' was specified.|]
 
+data HadrianNotFound = HadrianNotFound
+  deriving Show
+
+instance Pretty HadrianNotFound where
+  pPrint HadrianNotFound =
+    text [i|Could not find Hadrian build files. Does this GHC version support Hadrian builds?|]
+
 
     -------------------------
     --[ High-level errors ]--
@@ -256,11 +263,11 @@ deriving instance Show DownloadFailed
 
 
 -- | A build failed.
-data BuildFailed = forall es . Show (V es) => BuildFailed FilePath (V es)
+data BuildFailed = forall es . (Pretty (V es), Show (V es)) => BuildFailed FilePath (V es)
 
 instance Pretty BuildFailed where
   pPrint (BuildFailed path reason) =
-    text [i|BuildFailed failed in dir "#{path}": #{reason}|]
+    text [i|BuildFailed failed in dir "#{path}": |] <> pPrint reason
 
 deriving instance Show BuildFailed
 
diff --git a/lib/GHCup/Utils.hs b/lib/GHCup/Utils.hs
index b94b7e331a874f4a226dc12f33ce3d2c70e696df..09ab375ea0d717fea8ac2f84ad52e03021bb7959 100644
--- a/lib/GHCup/Utils.hs
+++ b/lib/GHCup/Utils.hs
@@ -78,6 +78,7 @@ import           System.Win32.Console
 import           System.Win32.File     hiding ( copyFile )
 import           System.Win32.Types
 #endif
+import           Text.PrettyPrint.HughesPJClass hiding ( (<>) )
 import           Text.Regex.Posix
 import           URI.ByteString
 
@@ -882,7 +883,7 @@ getChangeLog dls tool (Right tag) =
 --
 --   1. the build directory, depending on the KeepDirs setting
 --   2. the install destination, depending on whether the build failed
-runBuildAction :: (Show (V e), MonadReader env m, HasDirs env, HasSettings env, MonadIO m, MonadMask m)
+runBuildAction :: (Pretty (V e), Show (V e), MonadReader env m, HasDirs env, HasSettings env, MonadIO m, MonadMask m)
                => FilePath          -- ^ build directory (cleaned up depending on Settings)
                -> Maybe FilePath  -- ^ dir to *always* clean up on exception
                -> Excepts e m a