aboutsummaryrefslogtreecommitdiff
path: root/src/CabalHelper/Compiletime/Cabal.hs
diff options
context:
space:
mode:
Diffstat (limited to 'src/CabalHelper/Compiletime/Cabal.hs')
-rw-r--r--src/CabalHelper/Compiletime/Cabal.hs180
1 files changed, 180 insertions, 0 deletions
diff --git a/src/CabalHelper/Compiletime/Cabal.hs b/src/CabalHelper/Compiletime/Cabal.hs
new file mode 100644
index 0000000..9e6fbeb
--- /dev/null
+++ b/src/CabalHelper/Compiletime/Cabal.hs
@@ -0,0 +1,180 @@
+-- cabal-helper: Simple interface to Cabal's configuration state
+-- Copyright (C) 2018 Daniel Gröber <cabal-helper@dxld.at>
+--
+-- This program is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+{-|
+Module : CabalHelper.Compiletime.Program.Cabal
+Description : cabal-install program interface
+License : GPL-3
+-}
+
+module CabalHelper.Compiletime.Cabal where
+
+import Control.Exception (bracket)
+import Control.Monad.IO.Class
+import Data.List
+import Data.Maybe
+import Data.Version
+import System.Exit
+import System.Directory
+import System.FilePath
+
+import CabalHelper.Compiletime.Types
+import CabalHelper.Compiletime.Process
+import CabalHelper.Shared.Common (trim, replace)
+import Paths_cabal_helper (version)
+
+-- | Cabal library version we're compiling the helper exe against.
+data CabalVersion
+ = CabalHEAD { cvCommitId :: CommitId }
+ | CabalVersion { cabalVersion :: Version }
+
+newtype CommitId = CommitId { unCommitId :: String }
+
+showCabalVersion :: CabalVersion -> String
+showCabalVersion (CabalHEAD commitid) =
+ "HEAD-" ++ unCommitId commitid
+showCabalVersion CabalVersion {cabalVersion} =
+ showVersion cabalVersion
+
+exeName :: CabalVersion -> String
+exeName (CabalHEAD commitid) = intercalate "--"
+ [ "cabal-helper-" ++ showVersion version
+ , "Cabal-HEAD" ++ unCommitId commitid
+ ]
+exeName CabalVersion {cabalVersion} = intercalate "--"
+ [ "cabal-helper-" ++ showVersion version
+ , "Cabal-" ++ showVersion cabalVersion
+ ]
+
+data CabalPatchDescription = CabalPatchDescription {
+ cpdVersions :: [Version],
+ cpdUnpackVariant :: UnpackCabalVariant,
+ cpdPatchFn :: FilePath -> IO ()
+ }
+nopCabalPatchDescription :: CabalPatchDescription
+nopCabalPatchDescription = CabalPatchDescription [] LatestRevision (const (return ()))
+
+patchyCabalVersions :: [CabalPatchDescription]
+patchyCabalVersions = [
+ let versions = [ Version [1,18,1] [] ]
+ variant = Pristine
+ patch = fixArrayConstraint
+ in CabalPatchDescription versions variant patch,
+
+ let versions = [ Version [1,18,0] [] ]
+ variant = Pristine
+ patch dir = do
+ fixArrayConstraint dir
+ fixOrphanInstance dir
+ in CabalPatchDescription versions variant patch,
+
+ let versions = [ Version [1,24,1,0] [] ]
+ variant = Pristine
+ patch _ = return ()
+ in CabalPatchDescription versions variant patch
+ ]
+ where
+ fixArrayConstraint dir = do
+ let cabalFile = dir </> "Cabal.cabal"
+ cabalFileTmp = cabalFile ++ ".tmp"
+
+ cf <- readFile cabalFile
+ writeFile cabalFileTmp $ replace "&& < 0.5" "&& < 0.6" cf
+ renameFile cabalFileTmp cabalFile
+
+ fixOrphanInstance dir = do
+ let versionFile = dir </> "Distribution/Version.hs"
+ versionFileTmp = versionFile ++ ".tmp"
+
+ let languagePragma =
+ "{-# LANGUAGE DeriveDataTypeable, StandaloneDeriving #-}"
+ languagePragmaCPP =
+ "{-# LANGUAGE CPP, DeriveDataTypeable, StandaloneDeriving #-}"
+
+ derivingDataVersion =
+ "deriving instance Data Version"
+ derivingDataVersionCPP = unlines [
+ "#if __GLASGOW_HASKELL__ < 707",
+ derivingDataVersion,
+ "#endif"
+ ]
+
+ vf <- readFile versionFile
+ writeFile versionFileTmp
+ $ replace derivingDataVersion derivingDataVersionCPP
+ $ replace languagePragma languagePragmaCPP vf
+
+ renameFile versionFileTmp versionFile
+
+unpackPatchedCabal
+ :: Env
+ => Version
+ -> FilePath
+ -> IO CabalSourceDir
+unpackPatchedCabal cabalVer tmpdir = do
+ res@(CabalSourceDir dir) <- unpackCabal cabalVer tmpdir variant
+ patch dir
+ return res
+ where
+ CabalPatchDescription _ variant patch = fromMaybe nopCabalPatchDescription $
+ find ((cabalVer `elem`) . cpdVersions) patchyCabalVersions
+
+
+data UnpackCabalVariant = Pristine | LatestRevision
+newtype CabalSourceDir = CabalSourceDir { unCabalSourceDir :: FilePath }
+unpackCabal
+ :: (Verbose, Progs)
+ => Version
+ -> FilePath
+ -> UnpackCabalVariant
+ -> IO CabalSourceDir
+unpackCabal cabalVer tmpdir variant = do
+ let cabal = "Cabal-" ++ showVersion cabalVer
+ dir = tmpdir </> cabal
+ variant_opts = case variant of Pristine -> [ "--pristine" ]; _ -> []
+ args = [ "get", cabal ] ++ variant_opts
+ callProcessStderr (Just tmpdir) (cabalProgram ?progs) args
+ return $ CabalSourceDir dir
+
+unpackCabalHEAD :: Env => FilePath -> IO (CabalSourceDir, CommitId)
+unpackCabalHEAD tmpdir = do
+ let dir = tmpdir </> "cabal-head.git"
+ url = "https://github.com/haskell/cabal.git"
+ ExitSuccess <- rawSystem "git" [ "clone", "--depth=1", url, dir]
+ commit <-
+ withDirectory_ dir $ trim <$> readProcess' "git" ["rev-parse", "HEAD"] ""
+ return (CabalSourceDir $ dir </> "Cabal", CommitId commit)
+ where
+ withDirectory_ :: FilePath -> IO a -> IO a
+ withDirectory_ dir action =
+ bracket
+ (liftIO getCurrentDirectory)
+ (liftIO . setCurrentDirectory)
+ (\_ -> liftIO (setCurrentDirectory dir) >> action)
+
+findCabalFile :: FilePath -> IO FilePath
+findCabalFile pkgdir = do
+ [cfile] <- filter isCabalFile <$> getDirectoryContents pkgdir
+ return cfile
+ where
+ isCabalFile :: FilePath -> Bool
+ isCabalFile f = takeExtension' f == ".cabal"
+
+ takeExtension' :: FilePath -> String
+ takeExtension' p =
+ if takeFileName p == takeExtension p
+ then "" -- just ".cabal" is not a valid cabal file
+ else takeExtension p