aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cabal-helper.cabal68
-rw-r--r--lib/Distribution/Helper.hs365
-rw-r--r--src/CabalHelper/Compiletime/Compile.hs316
-rw-r--r--src/CabalHelper/Compiletime/Log.hs45
-rw-r--r--src/CabalHelper/Compiletime/Types.hs56
-rw-r--r--src/CabalHelper/Compiletime/Wrapper.hs227
-rw-r--r--src/CabalHelper/Shared/Sandbox.hs13
-rw-r--r--tests/CompileTest.hs23
8 files changed, 419 insertions, 694 deletions
diff --git a/cabal-helper.cabal b/cabal-helper.cabal
index 1d6bbf3..b7b5f73 100644
--- a/cabal-helper.cabal
+++ b/cabal-helper.cabal
@@ -88,60 +88,24 @@ library
hs-source-dirs: lib, src
exposed-modules: Distribution.Helper
other-modules:
- CabalHelper.Shared.InterfaceTypes
- CabalHelper.Shared.Sandbox
- Paths_cabal_helper
- autogen-modules: Paths_cabal_helper
-
- ghc-options: -Wall
-
- -- well actually this is a "runtime"-tool-depends :)
- build-tool-depends: cabal-helper:cabal-helper-wrapper
-
- build-depends: base < 5 && >= 4.7
- build-depends: Cabal < 2.5 && >= 2.0 || < 1.26 && >= 1.14
- , cabal-plan < 0.5 && >= 0.3.0.0
- , containers < 1 && >= 0.5.5.1
- , directory < 1.4 && >= 1.2.1.0
- , filepath < 1.5 && >= 1.3.0.0
- , transformers < 0.6 && >= 0.3.0.0
- , mtl < 2.3 && >= 2.0
- , process < 1.7 && >= 1.1.0.1
- if !os(windows)
- build-depends: unix < 2.8 && >= 2.5.1.1
- build-depends: unix-compat < 0.6 && >= 0.4.3.1
- , semigroupoids < 5.3 && >= 5.2
-
-
-
-executable cabal-helper-wrapper
- main-is: CabalHelper/Compiletime/Wrapper.hs
- if flag(dev)
- ghc-options: -Wall
- scope: private
-
- -- Common c-h-wrapper-fields -- See [Note test dependencies]
- default-language: Haskell2010
- default-extensions: NondecreasingIndentation
- other-extensions: TemplateHaskell
- hs-source-dirs: src
- other-modules:
CabalHelper.Compiletime.Compat.Environment
CabalHelper.Compiletime.Compat.ProgramDb
CabalHelper.Compiletime.Compat.Version
CabalHelper.Compiletime.Compile
CabalHelper.Compiletime.Data
- CabalHelper.Compiletime.Log
CabalHelper.Compiletime.Types
CabalHelper.Shared.Common
CabalHelper.Shared.InterfaceTypes
CabalHelper.Shared.Sandbox
Paths_cabal_helper
- build-tool-depends: cabal-install:cabal
+
+ autogen-modules:
+ Paths_cabal_helper
+
+ ghc-options: -Wall
+
build-depends: base < 5 && >= 4.7
- if os(windows)
- build-depends: base >= 4.7
- build-depends: Cabal < 2.5 && >= 2.0 || < 1.26 && >= 1.14
+ build-depends: Cabal < 2.5 && >= 2.0 || < 1.26 && >= 1.14
, cabal-plan < 0.5 && >= 0.3.0.0
, containers < 1 && >= 0.5.5.1
, bytestring < 0.11 && >= 0.9.2.1
@@ -150,6 +114,7 @@ executable cabal-helper-wrapper
, mtl < 2.3 && >= 2.0
, process < 1.7 && >= 1.1.0.1
, pretty-show < 1.9 && >= 1.8.1
+ , semigroupoids < 5.3 && >= 5.2
, text < 1.3 && >= 1.0.0.0
, template-haskell < 2.14 && >= 2.7.0.0
, temporary < 1.3 && >= 1.2.1
@@ -158,7 +123,9 @@ executable cabal-helper-wrapper
build-depends: unix < 2.8 && >= 2.5.1.1
build-depends: unix-compat < 0.6 && >= 0.4.3.1
, utf8-string < 1.1 && >= 1.0.1.1
+
build-tools: cabal
+ build-tool-depends: cabal-install:cabal
test-suite compile-test
@@ -166,9 +133,8 @@ test-suite compile-test
main-is: CompileTest.hs
hs-source-dirs: tests
ghc-options: -Wall
- build-tools: cabal
- -- Instantiate common c-h-wrapper-fields -- See [Note test dependencies]
+ -- Common c-h-wrapper-fields -- See [Note test dependencies]
default-language: Haskell2010
default-extensions: NondecreasingIndentation
other-extensions: TemplateHaskell
@@ -179,13 +145,13 @@ test-suite compile-test
CabalHelper.Compiletime.Compat.Version
CabalHelper.Compiletime.Compile
CabalHelper.Compiletime.Data
- CabalHelper.Compiletime.Log
CabalHelper.Compiletime.Types
CabalHelper.Shared.Common
CabalHelper.Shared.InterfaceTypes
CabalHelper.Shared.Sandbox
Paths_cabal_helper
build-tool-depends: cabal-install:cabal
+ build-tools: cabal
build-depends: base < 5 && >= 4.7
if os(windows)
build-depends: base >= 4.7
@@ -206,7 +172,6 @@ test-suite compile-test
build-depends: unix < 2.8 && >= 2.5.1.1
build-depends: unix-compat < 0.6 && >= 0.4.3.1
, utf8-string < 1.1 && >= 1.0.1.1
- build-tools: cabal
test-suite ghc-session
@@ -230,13 +195,13 @@ test-suite ghc-session
CabalHelper.Compiletime.Compat.Version
CabalHelper.Compiletime.Compile
CabalHelper.Compiletime.Data
- CabalHelper.Compiletime.Log
CabalHelper.Compiletime.Types
CabalHelper.Shared.Common
CabalHelper.Shared.InterfaceTypes
CabalHelper.Shared.Sandbox
Paths_cabal_helper
build-tool-depends: cabal-install:cabal
+ build-tools: cabal
build-depends: base < 5 && >= 4.7
if os(windows)
build-depends: base >= 4.7
@@ -257,7 +222,7 @@ test-suite ghc-session
build-depends: unix < 2.8 && >= 2.5.1.1
build-depends: unix-compat < 0.6 && >= 0.4.3.1
, utf8-string < 1.1 && >= 1.0.1.1
- build-tools: cabal
+
executable cabal-helper-main
@@ -269,9 +234,10 @@ executable cabal-helper-main
CabalHelper.Shared.Common
CabalHelper.Shared.InterfaceTypes
CabalHelper.Shared.Sandbox
+ CabalHelper.Shared.Common
- -- This component is usually built at runtime by cabal-helper-wrapper but
- -- during development it's convinient to build it via cabal
+ -- This component is usually built at runtime but during development it's
+ -- convinient to build it via cabal
if flag(dev)
buildable: True
else
diff --git a/lib/Distribution/Helper.hs b/lib/Distribution/Helper.hs
index 1d93b84..622972a 100644
--- a/lib/Distribution/Helper.hs
+++ b/lib/Distribution/Helper.hs
@@ -14,10 +14,9 @@
-- You should have received a copy of the GNU General Public License
-- along with this program. If not, see <http://www.gnu.org/licenses/>.
-{-# LANGUAGE CPP, RecordWildCards, FlexibleContexts, ConstraintKinds,
+{-# LANGUAGE RecordWildCards, FlexibleContexts, ConstraintKinds,
GeneralizedNewtypeDeriving, DeriveDataTypeable, DeriveGeneric, DeriveFunctor,
- NamedFieldPuns, OverloadedStrings
- #-}
+ NamedFieldPuns, OverloadedStrings, ViewPatterns #-}
{-|
Module : Distribution.Helper
@@ -80,7 +79,7 @@ module Distribution.Helper (
, NeedsBuildOutput(..)
-- * General information
- , buildPlatform
+ , Distribution.Helper.buildPlatform
-- * Stuff that cabal-install really should export
, Distribution.Helper.getSandboxPkgDb
@@ -90,10 +89,6 @@ module Distribution.Helper (
, reconfigure
, writeAutogenFiles
- -- * $libexec related error handling
- , LibexecNotFoundError(..)
- , libexecNotFoundError
-
-- * Reexports
, module Data.Functor.Apply
) where
@@ -105,46 +100,40 @@ import Control.Monad.IO.Class
import Control.Monad.State.Strict
import Control.Monad.Reader
import Control.Exception as E
-import Data.Char
import Data.List
import Data.Maybe
-import qualified Data.Map as Map
+import qualified Data.Map.Strict as Map
import Data.Version
-import Data.Typeable
+import qualified Data.Text as Text
import Data.Function
import Data.Functor.Apply
-import Distribution.System (buildOS, OS(Windows))
import System.Environment
import System.FilePath hiding ((<.>))
-import qualified System.FilePath as FP
import System.Directory
import System.Process
-import System.IO.Unsafe
import Text.Printf
-import GHC.Generics
+import Text.Show.Pretty
import Prelude
-import Paths_cabal_helper (getLibexecDir)
+
+import CabalHelper.Compiletime.Compile
+import CabalHelper.Compiletime.Types
import CabalHelper.Shared.InterfaceTypes
import CabalHelper.Shared.Sandbox
--- | Paths or names of various programs we need.
-data Programs = Programs {
- -- | The path to the @cabal@ program.
- cabalProgram :: FilePath,
- -- | The path to the @ghc@ program.
- ghcProgram :: FilePath,
+import Distribution.System (buildPlatform)
+import Distribution.Text (display)
+import Distribution.Verbosity (silent, deafening)
+import Distribution.Package (packageName, packageVersion)
+import Distribution.Simple.GHC as GHC (configure)
+
+import qualified CabalHelper.Compiletime.Compat.ProgramDb as ProgDb
+ ( defaultProgramDb, programPath, lookupProgram, ghcProgram, ghcPkgProgram)
+import CabalHelper.Compiletime.Compat.Version
+import CabalHelper.Shared.Common
- -- | The path to the @ghc-pkg@ program. If
- -- not changed it will be derived from the path to 'ghcProgram'.
- ghcPkgProgram :: FilePath
- } deriving (Eq, Ord, Show, Read, Generic, Typeable)
--- | Default all programs to their unqualified names, i.e. they will be searched
--- for on @PATH@.
-defaultPrograms :: Programs
-defaultPrograms = Programs "cabal" "ghc" "ghc-pkg"
-- | Environment for running a 'Query'. The real constructor is not exposed,
-- the field accessors are however. See below. Use the 'mkQueryEnv' smart
@@ -353,35 +342,41 @@ reconfigure readProc progs cabalOpts = do
readHelper :: (MonadIO m, MonadQuery m) => [String] -> m [Maybe ChResponse]
readHelper args = ask >>= \qe -> liftIO $ do
- out <- either error id <$> invokeHelper qe args
+ out <- invokeHelper qe args
let res = read out
liftIO $ evaluate res `E.catch` \se@(SomeException _) -> do
md <- lookupEnv' "CABAL_HELPER_DEBUG"
let msg = "readHelper: exception: '" ++ show se ++ "'"
- error $ msg ++ case md of
+ panicIO $ msg ++ case md of
Nothing -> ", for more information set the environment variable CABAL_HELPER_DEBUG"
Just _ -> ", output: '"++ out ++"'"
-invokeHelper :: QueryEnv -> [String] -> IO (Either String String)
-invokeHelper QueryEnv {..} args = do
- let progArgs = [ "--with-ghc=" ++ ghcProgram qePrograms
- , "--with-ghc-pkg=" ++ ghcPkgProgram qePrograms
- , "--with-cabal=" ++ cabalProgram qePrograms
- ]
- exe <- findLibexecExe
- let args' = progArgs ++ "v1-style":qeProjectDir:qeDistDir:args
- out <- qeReadProcess exe args' ""
- (Right <$> evaluate out) `E.catch` \(SomeException _) ->
- return $ Left $ concat
- ["invokeHelper", ": ", exe, " "
- , intercalate " " (map show args')
- , " failed"
- ]
-
-getPackageId :: MonadQuery m => m (String, Version)
-getPackageId = ask >>= \QueryEnv {..} -> do
- [ Just (ChResponseVersion pkgName pkgVer) ] <- readHelper [ "package-id" ]
- return (pkgName, pkgVer)
+invokeHelper :: QueryEnv -> [String] -> IO String
+invokeHelper QueryEnv {..} args0 = do
+ let opts = defaultCompileOptions
+ { oPrograms = qePrograms
+ , oCabalPkgDb = PackageDbDir <$> qeCabalPkgDb }
+
+ opts' <- overrideVerbosityEnvVar =<< guessProgramPaths opts
+
+ exe <- wrapperV1 opts' qeProjectDir qeDistDir
+
+ let args1 = qeProjectDir : qeDistDir : args0
+
+ out <- qeReadProcess exe args1 ""
+ evaluate out `E.catch` \(SomeException _) ->
+ panicIO $ concat
+ ["invokeHelper", ": ", exe, " "
+ , intercalate " " (map show args1)
+ , " failed!"
+ ]
+
+getPackageId :: (MonadQuery m, MonadIO m) => m (String, Version)
+getPackageId = ask >>= \QueryEnv {..} -> liftIO $ do
+ let v = silent
+ [cfile] <- filter isCabalFile <$> getDirectoryContents qeProjectDir
+ gpd <- readPackageDescription v (qeProjectDir </> cfile)
+ return $ (display (packageName gpd), toDataVersion (packageVersion gpd))
getSomeConfigState :: MonadQuery m => m SomeLocalBuildInfo
getSomeConfigState = ask >>= \QueryEnv {..} -> do
@@ -439,151 +434,129 @@ writeAutogenFiles qe =
liftIO $ void $ invokeHelper qe ["write-autogen-files"]
-- | Get the path to the sandbox package-db in a project
-getSandboxPkgDb :: (FilePath -> [String] -> String -> IO String)
- -> String
- -- ^ Cabal build platform, i.e. @buildPlatform@
- -> Version
- -- ^ GHC version (@cProjectVersion@ is your friend)
- -> IO (Maybe FilePath)
-getSandboxPkgDb readProc =
- CabalHelper.Shared.Sandbox.getSandboxPkgDb $ unsafePerformIO $ buildPlatform readProc
-
-buildPlatform :: (FilePath -> [String] -> String -> IO String) -> IO String
-buildPlatform readProc = do
- exe <- findLibexecExe
- CabalHelper.Shared.Sandbox.dropWhileEnd isSpace <$> readProc exe ["print-build-platform"] ""
-
--- | This exception is thrown by all 'runQuery' functions if the internal
--- wrapper executable cannot be found. You may catch this and present the user
--- an appropriate error message however the default is to print
--- 'libexecNotFoundError'.
-data LibexecNotFoundError = LibexecNotFoundError String FilePath
- deriving (Typeable)
-instance Exception LibexecNotFoundError
-instance Show LibexecNotFoundError where
- show (LibexecNotFoundError exe dir) =
- libexecNotFoundError exe dir "https://github.com/DanielG/cabal-helper/issues"
-
-findLibexecExe :: IO FilePath
-findLibexecExe = do
- libexecdir <- getLibexecDir
- let exeName = "cabal-helper-wrapper"
- exe = libexecdir </> exeName FP.<.> exeExtension'
-
- exists <- doesFileExist exe
-
- if exists
- then return exe
- else do
- mdir <- tryFindCabalHelperTreeDistDir
- dir <- case mdir of
- Nothing ->
- throwIO $ LibexecNotFoundError exeName libexecdir
- Just dir ->
- return dir
-
- return $ dir </> "build" </> exeName </> exeName
-
-findPlanJson :: FilePath -> IO (Maybe FilePath)
-findPlanJson base =
- findFile (map (</> "cache") $ parents base) "plan.json"
-
-parents :: FilePath -> [FilePath]
-parents path = takeWhile (not . (`elem` ["", "."]) . dropDrive) dirs
- where dirs = iterate takeDirectory path
-
-data DistDir = DistDir { ddType :: DistDirType, unDistDir :: FilePath }
- deriving (Eq, Ord, Read, Show)
-data DistDirType = NewBuildDist | OldBuildDist
- deriving (Eq, Ord, Read, Show)
-
-tryFindCabalHelperTreeDistDir :: IO (Maybe FilePath)
-tryFindCabalHelperTreeDistDir = do
- exe <- canonicalizePath =<< getExecutablePath'
- mplan <- findPlanJson exe
- let mdistdir = takeDirectory . takeDirectory <$> mplan
- cwd <- getCurrentDirectory
-
- let candidates = sortBy (compare `on` ddType) $ concat
- [ maybeToList $ DistDir NewBuildDist <$> mdistdir
- , [ DistDir OldBuildDist $ (!!3) $ iterate takeDirectory exe ]
- , if takeFileName exe == "ghc" -- we're probably in ghci; try CWD
- then [ DistDir NewBuildDist $ cwd </> "dist-newstyle"
- , DistDir NewBuildDist $ cwd </> "dist"
- , DistDir OldBuildDist $ cwd </> "dist"
- ]
- else []
- ]
-
- distdirs
- <- filterM isDistDir candidates
- >>= mapM toOldBuildDistDir
-
- return $ fmap unDistDir $ join $ listToMaybe $ distdirs
-
-isCabalHelperSourceDir :: FilePath -> IO Bool
-isCabalHelperSourceDir dir =
- doesFileExist $ dir </> "cabal-helper.cabal"
-
-isDistDir :: DistDir -> IO Bool
-isDistDir (DistDir NewBuildDist dir) =
- doesFileExist (dir </> "cache" </> "plan.json")
-isDistDir (DistDir OldBuildDist dir) =
- doesFileExist (dir </> "setup-config")
-
-toOldBuildDistDir :: DistDir -> IO (Maybe DistDir)
-toOldBuildDistDir (DistDir NewBuildDist dir) = do
- PlanJson {pjUnits} <- decodePlanJson $ dir </> "cache" </> "plan.json"
- let munit = find isCabalHelperUnit $ Map.elems pjUnits
- return $ DistDir OldBuildDist <$> join ((\Unit { uDistDir = mdistdir } -> mdistdir) <$> munit)
- where
- isCabalHelperUnit
- Unit { uPId = PkgId (PkgName n) _
- , uType = UnitTypeLocal
- , uComps
- } | n == "cabal-helper" &&
- Map.member (CompNameExe "cabal-helper-wrapper") uComps
- = True
- isCabalHelperUnit _ = False
-toOldBuildDistDir x = return $ Just x
-
-
-
-
-libexecNotFoundError :: String -- ^ Name of the executable we were trying to
- -- find
- -> FilePath -- ^ Path to @$libexecdir@
- -> String -- ^ URL the user will be directed towards to
- -- report a bug.
- -> String
-libexecNotFoundError exe dir reportBug = printf
- ( "Could not find $libexecdir/%s\n"
- ++"\n"
- ++"If you are a cabal-helper developer you can set the environment variable\n"
- ++"`cabal_helper_libexecdir' to override $libexecdir[1]. The following will\n"
- ++"work in the cabal-helper source tree:\n"
- ++"\n"
- ++" $ export cabal_helper_libexecdir=$PWD/dist/build/%s\n"
- ++"\n"
- ++"[1]: %s\n"
- ++"\n"
- ++"If you don't know what I'm talking about something went wrong with your\n"
- ++"installation. Please report this problem here:\n"
- ++"\n"
- ++" %s") exe exe dir reportBug
-
-getExecutablePath' :: IO FilePath
-getExecutablePath' =
-#if MIN_VERSION_base(4,6,0)
- getExecutablePath
-#else
- getProgName
-#endif
+getSandboxPkgDb
+ :: String
+ -- ^ Cabal build platform, i.e. @buildPlatform@
+ -> Version
+ -- ^ GHC version (@cProjectVersion@ is your friend)
+ -> FilePath
+ -- ^ Path to the project directory, i.e. a directory containing a
+ -- @cabal.sandbox.config@ file
+ -> IO (Maybe FilePath)
+getSandboxPkgDb buildPlat ghc_ver projdir =
+ CabalHelper.Shared.Sandbox.getSandboxPkgDb buildPlat ghc_ver projdir
+
+buildPlatform :: String
+buildPlatform = display Distribution.System.buildPlatform
lookupEnv' :: String -> IO (Maybe String)
lookupEnv' k = lookup k <$> getEnvironment
-exeExtension' :: FilePath
-exeExtension'
- | Windows <- buildOS = "exe"
- | otherwise = ""
+
+guessProgramPaths :: CompileOptions -> IO CompileOptions
+guessProgramPaths opts = do
+ let v | oVerbose opts = deafening
+ | otherwise = silent
+
+ mGhcPath0 | same ghcProgram progs dprogs = Nothing
+ | otherwise = Just $ ghcProgram progs
+ mGhcPkgPath0 | same ghcPkgProgram progs dprogs = Nothing
+ | otherwise = Just $ ghcPkgProgram progs
+
+ (_compiler, _mplatform, progdb)
+ <- GHC.configure
+ v
+ mGhcPath0
+ mGhcPkgPath0
+ ProgDb.defaultProgramDb
+ let getProg p = ProgDb.programPath <$> ProgDb.lookupProgram p progdb
+ mghcPath1 = getProg ProgDb.ghcProgram
+ mghcPkgPath1 = getProg ProgDb.ghcPkgProgram
+
+ progs' = progs
+ { ghcProgram = fromMaybe (ghcProgram progs) mghcPath1
+ , ghcPkgProgram = fromMaybe (ghcProgram progs) mghcPkgPath1
+ }
+ return opts { oPrograms = progs' }
+ where
+ same f o o' = f o == f o'
+ progs = oPrograms opts
+ dprogs = defaultPrograms
+
+overrideVerbosityEnvVar :: CompileOptions -> IO CompileOptions
+overrideVerbosityEnvVar opts = do
+ x <- lookup "CABAL_HELPER_DEBUG" <$> getEnvironment
+ return $ case x of
+ Just _ -> opts { oVerbose = True }
+ Nothing -> opts
+
+wrapperV1
+ :: CompileOptions
+ -> FilePath
+ -> FilePath
+ -> IO FilePath
+wrapperV1 opts projdir distdir = do
+ cfgf <- canonicalizePath (distdir </> "setup-config")
+ mhdr <- getCabalConfigHeader cfgf
+ case (mhdr, oCabalVersion opts) of
+ (Nothing, _) -> panicIO $ printf "\
+\Could not read Cabal's persistent setup configuration header\n\
+\- Check first line of: %s\n\
+\- Maybe try: $ cabal configure" cfgf
+ (Just (hdrCabalVersion, _), Just ver)
+ | hdrCabalVersion /= ver -> panicIO $ printf "\
+\Cabal version %s was requested but setup configuration was\n\
+\written by version %s" (showVersion ver) (showVersion hdrCabalVersion)
+ (Just (hdrCabalVersion, _), _) -> do
+ compileHelper' opts hdrCabalVersion projdir Nothing distdir
+
+wrapperV2
+ :: CompileOptions
+ -> FilePath
+ -> FilePath
+ -> UnitId
+ -> IO (FilePath, FilePath)
+wrapperV2 opts projdir distdir unitid@(UnitId (Text.unpack -> unitid')) = do
+ let plan_path = distdir </> "cache" </> "plan.json"
+ plan@PlanJson {pjCabalLibVersion=Ver (makeDataVersion -> pjCabalLibVersion) }
+ <- decodePlanJson plan_path
+ case oCabalVersion opts of
+ Just ver | pjCabalLibVersion /= ver -> let
+ sver = showVersion ver
+ spjVer = showVersion pjCabalLibVersion
+ in panicIO $ printf "\
+\Cabal version %s was requested but plan.json was written by version %s" sver spjVer
+ _ -> case Map.lookup unitid $ pjUnits plan of
+ Just u@Unit {uType} | uType /= UnitTypeLocal -> do
+ panicIO $ "\
+\UnitId '"++ unitid' ++"' points to non-local unit: " ++ ppShow u
+ Just Unit {uDistDir=Nothing} -> panicIO $ printf "\
+\plan.json doesn't contain 'dist-dir' for UnitId '"++ unitid' ++"'"
+ Just Unit {uType=UnitTypeLocal, uDistDir=Just distdirv1} -> do
+ exe <- compileHelper' opts pjCabalLibVersion projdir (Just (plan, distdir)) distdirv1
+ return (exe, distdirv1)
+ _ -> let
+ units = map (\(UnitId u) -> Text.unpack u)
+ $ Map.keys
+ $ Map.filter ((==UnitTypeLocal) . uType)
+ $ pjUnits plan
+ units_list = unlines $ map (" "++) units
+ in
+ panicIO $ "\
+\UnitId '"++ unitid' ++"' not found in plan.json, available local units:\n" ++ units_list
+
+
+compileHelper'
+ :: CompileOptions
+ -> Version
+ -> FilePath
+ -> Maybe (PlanJson, FilePath)
+ -> FilePath
+ -> IO FilePath
+compileHelper' opts pjCabalLibVersion projdir mnewstyle distdirv1 = do
+ eexe <- compileHelper opts pjCabalLibVersion projdir mnewstyle distdirv1
+ case eexe of
+ Left rv ->
+ panicIO $ "compileHelper': compiling helper failed! (exit code "++ show rv
+ Right exe ->
+ return exe
diff --git a/src/CabalHelper/Compiletime/Compile.hs b/src/CabalHelper/Compiletime/Compile.hs
index 8da426f..2b80b2f 100644
--- a/src/CabalHelper/Compiletime/Compile.hs
+++ b/src/CabalHelper/Compiletime/Compile.hs
@@ -14,7 +14,7 @@
-- You should have received a copy of the GNU General Public License
-- along with this program. If not, see <http://www.gnu.org/licenses/>.
{-# LANGUAGE RecordWildCards, FlexibleContexts, NamedFieldPuns, DeriveFunctor,
-GADTs #-}
+ GADTs, ImplicitParams, ConstraintKinds #-}
{-|
Module : CabalHelper.Compiletime.Compile
@@ -58,7 +58,6 @@ import Distribution.Text (display)
import Paths_cabal_helper (version)
import CabalHelper.Compiletime.Data
-import CabalHelper.Compiletime.Log
import CabalHelper.Compiletime.Types
import CabalHelper.Shared.Common
import CabalHelper.Shared.Sandbox (getSandboxPkgDb)
@@ -87,33 +86,41 @@ data CompPaths = CompPaths
-- executable.
data CompilationProductScope = CPSGlobal | CPSProject
-compileHelper :: Options -> Version -> FilePath -> Maybe (PlanJson, FilePath) -> FilePath -> IO (Either ExitCode FilePath)
+compileHelper
+ :: CompileOptions
+ -> Version
+ -> FilePath
+ -> Maybe (PlanJson, FilePath)
+ -> FilePath
+ -> IO (Either ExitCode FilePath)
compileHelper opts hdrCabalVersion projdir mnewstyle distdir = do
- ghcVer <- ghcVersion opts
- Just (prepare, comp) <- runMaybeT $ msum $
- case oCabalPkgDb opts of
- Nothing ->
- [ compileCabalSource
- , compileNewBuild ghcVer
- , compileSandbox ghcVer
- , compileGlobal
- , MaybeT $ Just <$> compileWithCabalInPrivatePkgDb
- ]
- Just db ->
- [ return $ (return (), compileWithPkg (Just db) hdrCabalVersion CPSProject)
- ]
-
- appdir <- appCacheDir
-
- let cp@CompPaths {compExePath} = compPaths appdir distdir comp
- exists <- doesFileExist compExePath
- if exists
- then do
- vLog opts $ "helper already compiled, using exe: "++compExePath
- return (Right compExePath)
- else do
- vLog opts $ "helper exe does not exist, compiling "++compExePath
- prepare >> compile comp cp opts
+ let ?opts = opts
+
+ ghcVer <- ghcVersion
+ Just (prepare, comp) <- runMaybeT $ msum $
+ case oCabalPkgDb opts of
+ Nothing ->
+ [ compileCabalSource
+ , compileNewBuild ghcVer
+ , compileSandbox ghcVer
+ , compileGlobal
+ , MaybeT $ Just <$> compileWithCabalInPrivatePkgDb
+ ]
+ Just db ->
+ [ return $ (return (), compileWithPkg (Just db) hdrCabalVersion CPSProject)
+ ]
+
+ appdir <- appCacheDir
+
+ let cp@CompPaths {compExePath} = compPaths appdir distdir comp
+ exists <- doesFileExist compExePath
+ if exists
+ then do
+ vLog $ "helper already compiled, using exe: "++compExePath
+ return (Right compExePath)
+ else do
+ vLog $ "helper exe does not exist, compiling "++compExePath
+ prepare >> compile comp cp
where
logMsg = "using helper compiled with Cabal from "
@@ -121,24 +128,24 @@ compileHelper opts hdrCabalVersion projdir mnewstyle distdir = do
-- for relaxed deps: find (sameMajorVersionAs hdrCabalVersion) . reverse . sort
-- | Check if this version is globally available
- compileGlobal :: MaybeT IO (IO (), Compile)
+ compileGlobal :: Env => MaybeT IO (IO (), Compile)
compileGlobal = do
- cabal_versions <- listCabalVersions opts
+ cabal_versions <- listCabalVersions
ver <- MaybeT $ return $ find (== hdrCabalVersion) cabal_versions
- vLog opts $ logMsg ++ "user/global package-db"
+ vLog $ logMsg ++ "user/global package-db"
return $ (return (), compileWithPkg Nothing ver CPSGlobal)
-- | Check if this version is available in the project sandbox
- compileSandbox :: Version -> MaybeT IO (IO (), Compile)
+ compileSandbox :: Env => Version -> MaybeT IO (IO (), Compile)
compileSandbox ghcVer = do
- let mdb_path = getSandboxPkgDb projdir (display buildPlatform) ghcVer
+ let mdb_path = getSandboxPkgDb (display buildPlatform) ghcVer projdir
sandbox <- PackageDbDir <$> MaybeT mdb_path
- cabal_versions <- listCabalVersions' opts (Just sandbox)
+ cabal_versions <- listCabalVersions' (Just sandbox)
ver <- MaybeT $ return $ find (== hdrCabalVersion) cabal_versions
- vLog opts $ logMsg ++ "sandbox package-db"
+ vLog $ logMsg ++ "sandbox package-db"
return $ (return (), compileWithPkg (Just sandbox) ver CPSProject)
- compileNewBuild :: Version -> MaybeT IO (IO (), Compile)
+ compileNewBuild :: Env => Version -> MaybeT IO (IO (), Compile)
compileNewBuild ghcVer = do
(PlanJson {pjUnits}, distdir_newstyle) <- maybe mzero pure mnewstyle
let cabal_pkgid =
@@ -150,28 +157,28 @@ compileHelper opts hdrCabalVersion projdir mnewstyle distdir = do
let inplace_db_path = distdir_newstyle
</> "packagedb" </> ("ghc-" ++ showVersion ghcVer)
inplace_db = PackageDbDir inplace_db_path
- cabal_versions <- listCabalVersions' opts (Just inplace_db)
+ cabal_versions <- listCabalVersions' (Just inplace_db)
ver <- MaybeT $ return $ find (== hdrCabalVersion) cabal_versions
- vLog opts $ logMsg ++ "v2-build package-db " ++ inplace_db_path
+ vLog $ logMsg ++ "v2-build package-db " ++ inplace_db_path
return $ (return (), compileWithPkg (Just inplace_db) ver CPSProject)
-- | Compile the requested Cabal version into an isolated package-db if it's
-- not there already
- compileWithCabalInPrivatePkgDb :: IO (IO (), Compile)
+ compileWithCabalInPrivatePkgDb :: Env => IO (IO (), Compile)
compileWithCabalInPrivatePkgDb = do
db@(PackageDbDir db_path)
- <- getPrivateCabalPkgDb opts (CabalVersion hdrCabalVersion)
- vLog opts $ logMsg ++ "private package-db in " ++ db_path
+ <- getPrivateCabalPkgDb (CabalVersion hdrCabalVersion)
+ vLog $ logMsg ++ "private package-db in " ++ db_path
return (prepare db, compileWithPkg (Just db) hdrCabalVersion CPSGlobal)
where
prepare db = do
- db_exists <- liftIO $ cabalVersionExistsInPkgDb opts hdrCabalVersion db
+ db_exists <- liftIO $ cabalVersionExistsInPkgDb hdrCabalVersion db
when (not db_exists) $
- void $ installCabal opts (Right hdrCabalVersion) `E.catch`
+ void $ installCabal (Right hdrCabalVersion) `E.catch`
\(SomeException _) -> errorInstallCabal hdrCabalVersion distdir
-- | See if we're in a cabal source tree
- compileCabalSource :: MaybeT IO (IO (), Compile)
+ compileCabalSource :: Env => MaybeT IO (IO (), Compile)
compileCabalSource = do
let cabalFile = projdir </> "Cabal.cabal"
cabalSrc <- liftIO $ doesFileExist cabalFile
@@ -179,17 +186,17 @@ compileHelper opts hdrCabalVersion projdir mnewstyle distdir = do
case cabalSrc of
False -> mzero
True -> do
- vLog opts $ "projdir looks like Cabal source tree (Cabal.cabal exists)"
+ vLog $ "projdir looks like Cabal source tree (Cabal.cabal exists)"
cf <- liftIO $ readFile cabalFile
let buildType = cabalFileBuildType cf
ver = cabalFileVersion cf
case buildType of
"simple" -> do
- vLog opts $ "Cabal source tree is build-type:simple, moving on"
+ vLog $ "Cabal source tree is build-type:simple, moving on"
mzero
"custom" -> do
- vLog opts $ "compiling helper with local Cabal source tree"
+ vLog $ "compiling helper with local Cabal source tree"
return $ (return (), compileWithCabalSource projdir' ver)
_ -> error $ "compileCabalSource: unknown build-type: '"++buildType++"'"
@@ -209,16 +216,16 @@ compileHelper opts hdrCabalVersion projdir mnewstyle distdir = do
cabalPkgId v = "Cabal-" ++ showVersion v
-compile :: Compile -> CompPaths -> Options -> IO (Either ExitCode FilePath)
-compile comp paths@CompPaths {..} opts@Options {..} = do
+compile :: Env => Compile -> CompPaths -> IO (Either ExitCode FilePath)
+compile comp paths@CompPaths {..} = do
createDirectoryIfMissing True compOutDir
createHelperSources compSrcDir
- vLog opts $ "compSrcDir: " ++ compSrcDir
- vLog opts $ "compOutDir: " ++ compOutDir
- vLog opts $ "compExePath: " ++ compExePath
+ vLog $ "compSrcDir: " ++ compSrcDir
+ vLog $ "compOutDir: " ++ compOutDir
+ vLog $ "compExePath: " ++ compExePath
- invokeGhc opts $ compGhcInvocation comp paths
+ invokeGhc $ compGhcInvocation comp paths
compPaths :: FilePath -> FilePath -> Compile -> CompPaths
compPaths appdir distdir c =
@@ -309,25 +316,27 @@ cabalMinVersionMacro (Version (mj1:mj2:mi:_) _) =
cabalMinVersionMacro _ =
error "cabalMinVersionMacro: Version must have at least 3 components"
-invokeGhc :: Options -> GhcInvocation -> IO (Either ExitCode FilePath)
-invokeGhc opts@Options {..} GhcInvocation {..} = do
- rv <- callProcessStderr' opts Nothing oGhcProgram $ concat
- [ [ "-outputdir", giOutDir
- , "-o", giOutput
+invokeGhc :: Env => GhcInvocation -> IO (Either ExitCode FilePath)
+invokeGhc GhcInvocation {..} = do
+ rv <- callProcessStderr' Nothing oGhcProgram $ concat
+ [ [ "-outputdir", giOutDir
+ , "-o", giOutput
+ ]
+ , map ("-optP"++) giCPPOptions
+ , map ("-package-conf="++) $ unPackageDbDir <$> giPackageDBs
+ , map ("-i"++) $ nub $ "" : giIncludeDirs
+ , if giHideAllPackages then ["-hide-all-packages"] else []
+ , concatMap (\p -> ["-package", p]) giPackages
+ , giWarningFlags
+ , ["--make"]
+ , giInputs
]
- , map ("-optP"++) giCPPOptions
- , map ("-package-conf="++) $ unPackageDbDir <$> giPackageDBs
- , map ("-i"++) $ nub $ "" : giIncludeDirs
- , if giHideAllPackages then ["-hide-all-packages"] else []
- , concatMap (\p -> ["-package", p]) giPackages
- , giWarningFlags
- , ["--make"]
- , giInputs
- ]
- return $
- case rv of
- ExitSuccess -> Right giOutput
- e@(ExitFailure _) -> Left e
+ return $
+ case rv of
+ ExitSuccess -> Right giOutput
+ e@(ExitFailure _) -> Left e
+ where
+ CompileOptions {..} = ?opts
-- | Cabal library version we're compiling the helper exe against.
@@ -347,26 +356,26 @@ exeName CabalVersion {cabalVersion} = intercalate "-"
, "Cabal" ++ showVersion cabalVersion
]
-readProcess' :: Options -> FilePath -> [String] -> String -> IO String
-readProcess' opts@Options{..} exe args inp = do
- vLog opts $ intercalate " " $ map formatProcessArg (oGhcPkgProgram:args)
+readProcess' :: Env => FilePath -> [String] -> String -> IO String
+readProcess' exe args inp = do
+ vLog $ intercalate " " $ map formatProcessArg (exe:args)
outp <- readProcess exe args inp
- vLog opts $ unlines $ map ("=> "++) $ lines outp
+ vLog $ unlines $ map ("=> "++) $ lines outp
return outp
callProcessStderr'
- :: Options -> Maybe FilePath -> FilePath -> [String] -> IO ExitCode
-callProcessStderr' opts mwd exe args = do
+ :: Env => Maybe FilePath -> FilePath -> [String] -> IO ExitCode
+callProcessStderr' mwd exe args = do
let cd = case mwd of
Nothing -> []; Just wd -> [ "cd", formatProcessArg wd++";" ]
- vLog opts $ intercalate " " $ cd ++ map formatProcessArg (exe:args)
+ vLog $ intercalate " " $ cd ++ map formatProcessArg (exe:args)
(_, _, _, h) <- createProcess (proc exe args) { std_out = UseHandle stderr
, cwd = mwd }
waitForProcess h
-callProcessStderr :: Options -> Maybe FilePath -> FilePath -> [String] -> IO ()
-callProcessStderr opts mwd exe args = do
- rv <- callProcessStderr' opts mwd exe args
+callProcessStderr :: Env => Maybe FilePath -> FilePath -> [String] -> IO ()
+callProcessStderr mwd exe args = do
+ rv <- callProcessStderr' mwd exe args
case rv of
ExitSuccess -> return ()
ExitFailure v -> processFailedException "callProcessStderr" exe args v
@@ -387,8 +396,8 @@ formatProcessArg xs
data HEAD = HEAD deriving (Eq, Show)
-installCabal :: Options -> Either HEAD Version -> IO (PackageDbDir, CabalVersion)
-installCabal opts ever = do
+installCabal :: Env => Either HEAD Version -> IO (PackageDbDir, CabalVersion)
+installCabal ever = do
appdir <- appCacheDir
let message ver = do
let sver = showVersion ver
@@ -409,16 +418,16 @@ installCabal opts ever = do
withSystemTempDirectory "cabal-helper-Cabal-source" $ \tmpdir -> do
(srcdir, cabalVer) <- case ever of
Left HEAD -> do
- second CabalHEAD <$> unpackCabalHEAD opts tmpdir
+ second CabalHEAD <$> unpackCabalHEAD tmpdir
Right ver -> do
message ver
let patch = fromMaybe nopCabalPatchDescription $
find ((ver`elem`) . cpdVersions) patchyCabalVersions
- (,) <$> unpackPatchedCabal opts ver tmpdir patch <*> pure (CabalVersion ver)
+ (,) <$> unpackPatchedCabal ver tmpdir patch <*> pure (CabalVersion ver)
- db <- createPkgDb opts cabalVer
+ db <- createPkgDb cabalVer
- runCabalInstall opts db srcdir ever
+ runCabalInstall db srcdir ever
return (db, cabalVer)
@@ -436,9 +445,9 @@ Otherwise we might be able to use the shipped Setup.hs
-}
runCabalInstall
- :: Options -> PackageDbDir -> CabalSourceDir -> Either HEAD Version-> IO ()
-runCabalInstall opts (PackageDbDir db) (CabalSourceDir srcdir) ever = do
- civ@CabalInstallVersion {..} <- cabalInstallVersion opts
+ :: Env => PackageDbDir -> CabalSourceDir -> Either HEAD Version-> IO ()
+runCabalInstall (PackageDbDir db) (CabalSourceDir srcdir) ever = do
+ civ@CabalInstallVersion {..} <- cabalInstallVersion
cabal_opts <- return $ concat
[
[ "--package-db=clear"
@@ -446,45 +455,45 @@ runCabalInstall opts (PackageDbDir db) (CabalSourceDir srcdir) ever = do
, "--package-db=" ++ db
, "--prefix=" ++ db </> "prefix"
]
- , withGHCProgramOptions opts
+ , withGHCProgramOptions
, if cabalInstallVer >= Version [1,20,0,0] []
then ["--no-require-sandbox"]
else []
, [ "install", srcdir ]
- , if oVerbose opts
+ , if oVerbose ?opts
then ["-v"]
else []
, [ "--only-dependencies" ]
]
- callProcessStderr opts (Just "/") (oCabalProgram opts) cabal_opts
+ callProcessStderr (Just "/") oCabalProgram cabal_opts
- runSetupHs opts db srcdir ever civ
+ runSetupHs db srcdir ever civ
hPutStrLn stderr "done"
-withGHCProgramOptions :: Options -> [String]
-withGHCProgramOptions opts =
- concat [ [ "--with-ghc=" ++ oGhcProgram opts ]
- , if oGhcPkgProgram opts /= oGhcPkgProgram defaultOptions
- then [ "--with-ghc-pkg=" ++ oGhcPkgProgram opts ]
+withGHCProgramOptions :: Env => [String]
+withGHCProgramOptions =
+ concat [ [ "--with-ghc=" ++ oGhcProgram ]
+ , if oGhcProgram /= ghcPkgProgram defaultPrograms
+ then [ "--with-ghc-pkg=" ++ oGhcPkgProgram ]
else []
]
runSetupHs
- :: Options
- -> FilePath
+ :: Env
+ => FilePath
-> FilePath
-> Either HEAD Version
-> CabalInstallVersion
-> IO ()
-runSetupHs opts@Options {..} db srcdir ever CabalInstallVersion {..}
+runSetupHs db srcdir ever CabalInstallVersion {..}
| cabalInstallVer >= parseVer "1.24" = do
- go $ \args -> callProcessStderr opts (Just srcdir) oCabalProgram $
+ go $ \args -> callProcessStderr (Just srcdir) oCabalProgram $
[ "act-as-setup", "--" ] ++ args
| otherwise = do
- SetupProgram {..} <- compileSetupHs opts db srcdir
- go $ callProcessStderr opts (Just srcdir) setupProgram
+ SetupProgram {..} <- compileSetupHs db srcdir
+ go $ callProcessStderr (Just srcdir) setupProgram
where
parmake_opt :: Maybe Int -> [String]
parmake_opt nproc'
@@ -497,7 +506,7 @@ runSetupHs opts@Options {..} db srcdir ever CabalInstallVersion {..}
go :: ([String] -> IO ()) -> IO ()
go run = do
run $ [ "configure", "--package-db", db, "--prefix", db </> "prefix" ]
- ++ withGHCProgramOptions opts
+ ++ withGHCProgramOptions
mnproc <- join . fmap readMaybe <$> lookupEnv "NPROC"
run $ [ "build" ] ++ parmake_opt mnproc
run [ "copy" ]
@@ -507,16 +516,16 @@ runSetupHs opts@Options {..} db srcdir ever CabalInstallVersion {..}
newtype SetupProgram = SetupProgram { setupProgram :: FilePath }
-compileSetupHs :: Options -> FilePath -> FilePath -> IO SetupProgram
-compileSetupHs opts db srcdir = do
- ver <- ghcVersion opts
+compileSetupHs :: Env => FilePath -> FilePath -> IO SetupProgram
+compileSetupHs db srcdir = do
+ ver <- ghcVersion
let no_version_macros
| ver >= Version [8] [] = [ "-fno-version-macros" ]
| otherwise = []
file = srcdir </> "Setup"
- callProcessStderr opts (Just srcdir) (oGhcProgram opts) $ concat
+ callProcessStderr (Just srcdir) oGhcProgram $ concat
[ [ "--make"
, "-package-conf", db
]
@@ -588,35 +597,35 @@ patchyCabalVersions = [
renameFile versionFileTmp versionFile
unpackPatchedCabal
- :: Options
- -> Version
+ :: Env
+ => Version
-> FilePath
-> CabalPatchDescription
-> IO CabalSourceDir
-unpackPatchedCabal opts cabalVer tmpdir (CabalPatchDescription _ variant patch) = do
- res@(CabalSourceDir dir) <- unpackCabal opts cabalVer tmpdir variant
+unpackPatchedCabal cabalVer tmpdir (CabalPatchDescription _ variant patch) = do
+ res@(CabalSourceDir dir) <- unpackCabal cabalVer tmpdir variant
patch dir
return res
data UnpackCabalVariant = Pristine | LatestRevision
newtype CabalSourceDir = CabalSourceDir { unCabalSourceDir :: FilePath }
unpackCabal
- :: Options -> Version -> FilePath -> UnpackCabalVariant -> IO CabalSourceDir
-unpackCabal opts cabalVer tmpdir variant = do
+ :: Env => 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 opts (Just tmpdir) (oCabalProgram opts) args
+ callProcessStderr (Just tmpdir) oCabalProgram args
return $ CabalSourceDir dir
-unpackCabalHEAD :: Options -> FilePath -> IO (CabalSourceDir, CommitId)
-unpackCabalHEAD opts tmpdir = do
+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' opts "git" ["rev-parse", "HEAD"] ""
+ withDirectory_ dir $ trim <$> readProcess' "git" ["rev-parse", "HEAD"] ""
return (CabalSourceDir $ dir </> "Cabal", CommitId commit)
where
withDirectory_ :: FilePath -> IO a -> IO a
@@ -661,58 +670,60 @@ errorInstallCabal cabalVer _distdir = panicIO $ printf "\
where
sver = showVersion cabalVer
-listCabalVersions :: Options -> MaybeT IO [Version]
-listCabalVersions opts = listCabalVersions' opts Nothing
+listCabalVersions :: Env => MaybeT IO [Version]
+listCabalVersions = listCabalVersions' Nothing
-listCabalVersions' :: Options -> Maybe PackageDbDir -> MaybeT IO [Version]
-listCabalVersions' opts@Options {..} mdb = do
+listCabalVersions' :: Env => Maybe PackageDbDir -> MaybeT IO [Version]
+listCabalVersions' mdb = do
case mdb of
Nothing -> mzero
Just (PackageDbDir db_path) -> do
exists <- liftIO $ doesDirectoryExist db_path
case exists of
False -> mzero
- True -> MaybeT $ logIOError opts "listCabalVersions'" $ Just <$> do
+ True -> MaybeT $ logIOError "listCabalVersions'" $ Just <$> do
let mdbopt = ("--package-conf="++) <$> unPackageDbDir <$> mdb
args = ["list", "--simple-output", "Cabal"] ++ maybeToList mdbopt
catMaybes . map (fmap snd . parsePkgId . fromString) . words
- <$> readProcess' opts oGhcPkgProgram args ""
+ <$> readProcess' oGhcPkgProgram args ""
-cabalVersionExistsInPkgDb :: Options -> Version -> PackageDbDir -> IO Bool
-cabalVersionExistsInPkgDb opts cabalVer db@(PackageDbDir db_path) = do
+cabalVersionExistsInPkgDb :: Env => Version -> PackageDbDir -> IO Bool
+cabalVersionExistsInPkgDb cabalVer db@(PackageDbDir db_path) = do
exists <- doesDirectoryExist db_path
case exists of
False -> return False
True -> fromMaybe False <$> runMaybeT (do
- vers <- listCabalVersions' opts (Just db)
+ vers <- listCabalVersions' (Just db)
return $ cabalVer `elem` vers)
-ghcVersion :: Options -> IO Version
-ghcVersion opts@Options {..} = do
- parseVer . trim <$> readProcess' opts oGhcProgram ["--numeric-version"] ""
+ghcVersion :: Env => IO Version
+ghcVersion = do
+ parseVer . trim <$> readProcess' oGhcProgram ["--numeric-version"] ""
-ghcPkgVersion :: Options -> IO Version
-ghcPkgVersion opts@Options {..} = do
- parseVer . trim . dropWhile (not . isDigit) <$> readProcess' opts oGhcPkgProgram ["--version"] ""
+ghcPkgVersion :: Env => IO Version
+ghcPkgVersion =
+ parseVer . trim . dropWhile (not . isDigit)
+ <$> readProcess' oGhcPkgProgram ["--version"] ""
newtype CabalInstallVersion = CabalInstallVersion { cabalInstallVer :: Version }
-cabalInstallVersion :: Options -> IO CabalInstallVersion
-cabalInstallVersion opts@Options {..} = do
- CabalInstallVersion . parseVer . trim
- <$> readProcess' opts oCabalProgram ["--numeric-version"] ""
-
-createPkgDb :: Options -> CabalVersion -> IO PackageDbDir
-createPkgDb opts@Options {..} cabalVer = do
- db@(PackageDbDir db_path) <- getPrivateCabalPkgDb opts cabalVer
+cabalInstallVersion :: Env => IO CabalInstallVersion
+cabalInstallVersion = do
+ CabalInstallVersion . parseVer . trim
+ <$> readProcess' oCabalProgram ["--numeric-version"] ""
+
+createPkgDb :: Env => CabalVersion -> IO PackageDbDir
+createPkgDb cabalVer = do
+ db@(PackageDbDir db_path) <- getPrivateCabalPkgDb cabalVer
exists <- doesDirectoryExist db_path
- when (not exists) $ callProcessStderr opts Nothing oGhcPkgProgram ["init", db_path]
+ when (not exists) $
+ callProcessStderr Nothing oGhcPkgProgram ["init", db_path]
return db
-getPrivateCabalPkgDb :: Options -> CabalVersion -> IO PackageDbDir
-getPrivateCabalPkgDb opts cabalVer = do
+getPrivateCabalPkgDb :: Env => CabalVersion -> IO PackageDbDir
+getPrivateCabalPkgDb cabalVer = do
appdir <- appCacheDir
- ghcVer <- ghcVersion opts
+ ghcVer <- ghcVersion
let db_path = appdir </> exeName cabalVer
++ "-ghc" ++ showVersion ghcVer
++ ".package-db"
@@ -734,3 +745,14 @@ cabalFileTopField field cabalFile = value
Just value = extract <$> find ((field++":") `isPrefixOf`) ls
ls = map (map toLower) $ lines cabalFile
extract = dropWhile (/=':') >>> drop 1 >>> dropWhile isSpace >>> takeWhile (not . isSpace)
+
+vLog :: (Env, MonadIO m) => String -> m ()
+vLog msg | CompileOptions { oVerbose = True } <- ?opts =
+ liftIO $ hPutStrLn stderr msg
+vLog _ = return ()
+
+logIOError :: Env => String -> IO (Maybe a) -> IO (Maybe a)
+logIOError label a = do
+ a `catchIOError` \ex -> do
+ vLog $ label ++ ": " ++ show ex
+ return Nothing
diff --git a/src/CabalHelper/Compiletime/Log.hs b/src/CabalHelper/Compiletime/Log.hs
deleted file mode 100644
index a329c54..0000000
--- a/src/CabalHelper/Compiletime/Log.hs
+++ /dev/null
@@ -1,45 +0,0 @@
--- cabal-helper: Simple interface to Cabal's configuration state
--- Copyright (C) 2017-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/>.
-
-{-# LANGUAGE ScopedTypeVariables #-}
-
-{-|
-Module : CabalHelper.Compiletime.Log
-Description : Basic logging facilities
-License : GPL-3
--}
-
-module CabalHelper.Compiletime.Log where
-
-import Control.Monad
-import Control.Monad.IO.Class
-import Control.Exception as E
-import Data.String
-import System.IO
-import Prelude
-
-import CabalHelper.Compiletime.Types
-
-vLog :: MonadIO m => Options -> String -> m ()
-vLog Options { oVerbose = True } msg =
- liftIO $ hPutStrLn stderr msg
-vLog _ _ = return ()
-
-logIOError :: Options -> String -> IO (Maybe a) -> IO (Maybe a)
-logIOError opts label a = do
- a `E.catch` \(ex :: IOError) -> do
- vLog opts $ label ++ ": " ++ show ex
- return Nothing
diff --git a/src/CabalHelper/Compiletime/Types.hs b/src/CabalHelper/Compiletime/Types.hs
index 77c3255..10fe916 100644
--- a/src/CabalHelper/Compiletime/Types.hs
+++ b/src/CabalHelper/Compiletime/Types.hs
@@ -14,7 +14,8 @@
-- You should have received a copy of the GNU General Public License
-- along with this program. If not, see <http://www.gnu.org/licenses/>.
-{-# LANGUAGE DeriveGeneric, DeriveDataTypeable, DefaultSignatures #-}
+{-# LANGUAGE DeriveGeneric, DeriveDataTypeable, DefaultSignatures,
+ KindSignatures, ImplicitParams, ConstraintKinds #-}
{-|
Module : CabalHelper.Compiletime.Types
@@ -25,18 +26,47 @@ License : GPL-3
module CabalHelper.Compiletime.Types where
import Data.Version
+import Data.Typeable
+import GHC.Generics
-data Options = Options {
- oHelp :: Bool
- , oVerbose :: Bool
- , oGhcProgram :: FilePath
- , oGhcPkgProgram :: FilePath
- , oCabalProgram :: FilePath
- , oCabalVersion :: Maybe Version
- , oCabalPkgDb :: Maybe PackageDbDir
-}
+type Env = (?opts :: CompileOptions)
-newtype PackageDbDir = PackageDbDir { unPackageDbDir :: FilePath }
+-- | Paths or names of various programs we need.
+data Programs = Programs {
+ -- | The path to the @cabal@ program.
+ cabalProgram :: FilePath,
+
+ -- | The path to the @ghc@ program.
+ ghcProgram :: FilePath,
+
+ -- | The path to the @ghc-pkg@ program. If
+ -- not changed it will be derived from the path to 'ghcProgram'.
+ ghcPkgProgram :: FilePath
+ } deriving (Eq, Ord, Show, Read, Generic, Typeable)
+
+-- | Default all programs to their unqualified names, i.e. they will be searched
+-- for on @PATH@.
+defaultPrograms :: Programs
+defaultPrograms = Programs "cabal" "ghc" "ghc-pkg"
+
+data CompileOptions = CompileOptions
+ { oVerbose :: Bool
+ , oCabalPkgDb :: Maybe PackageDbDir
+ , oCabalVersion :: Maybe Version
+ , oPrograms :: Programs
+ }
-defaultOptions :: Options
-defaultOptions = Options False False "ghc" "ghc-pkg" "cabal" Nothing Nothing
+oCabalProgram :: Env => FilePath
+oCabalProgram = cabalProgram $ oPrograms ?opts
+
+oGhcProgram :: Env => FilePath
+oGhcProgram = ghcProgram $ oPrograms ?opts
+
+oGhcPkgProgram :: Env => FilePath
+oGhcPkgProgram = ghcPkgProgram $ oPrograms ?opts
+
+defaultCompileOptions :: CompileOptions
+defaultCompileOptions =
+ CompileOptions False Nothing Nothing defaultPrograms
+
+newtype PackageDbDir = PackageDbDir { unPackageDbDir :: FilePath }
diff --git a/src/CabalHelper/Compiletime/Wrapper.hs b/src/CabalHelper/Compiletime/Wrapper.hs
deleted file mode 100644
index 461ef96..0000000
--- a/src/CabalHelper/Compiletime/Wrapper.hs
+++ /dev/null
@@ -1,227 +0,0 @@
--- cabal-helper: Simple interface to Cabal's configuration state
--- Copyright (C) 2015-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/>.
-{-# LANGUAGE RecordWildCards, NamedFieldPuns, FlexibleContexts, ViewPatterns #-}
-module Main where
-
-import Cabal.Plan
-import Control.Applicative
-import Control.Monad
-import Data.Char
-import Data.List
-import Data.Maybe
-import Data.String
-import Text.Printf
-import Text.Show.Pretty
-import System.Console.GetOpt
-import System.Environment
-import System.Directory
-import System.FilePath
-import System.Process
-import System.Exit
-import System.IO
-import Prelude
-
-import qualified Data.Text as Text
-import qualified Data.Map.Strict as Map
-
-import Distribution.System (buildPlatform)
-import Distribution.Text (display)
-import Distribution.Verbosity (silent, deafening)
-import Distribution.Package (packageName, packageVersion)
-import Distribution.Simple.GHC as GHC (configure)
-
-import Paths_cabal_helper (version)
-import CabalHelper.Compiletime.Compat.ProgramDb
- ( defaultProgramDb, programPath, lookupProgram, ghcProgram, ghcPkgProgram)
-import CabalHelper.Compiletime.Compat.Version
-import CabalHelper.Compiletime.Compile
-import CabalHelper.Compiletime.Types
-import CabalHelper.Shared.Common
-import CabalHelper.Shared.InterfaceTypes
-
-usage :: IO ()
-usage = do
- prog <- getProgName
- hPutStr stderr $ "Usage: " ++ prog ++ " " ++ usageMsg
- where
- usageMsg = "\
-\( print-appcachedir\n\
-\| print-build-platform\n\
-\| [--verbose]\n\
-\ [--with-ghc=GHC_PATH]\n\
-\ [--with-ghc-pkg=GHC_PKG_PATH]\n\
-\ [--with-cabal=CABAL_PATH]\n\
-\ [--with-cabal-version=VERSION]\n\
-\ [--with-cabal-pkg-db=PKG_DB]\n\
-\ v1-style PROJ_DIR DIST_DIR \n\
-\ ( print-exe | package-id | [CABAL_HELPER_ARGS...] )\n\
-\ v2-style PROJ_DIR DIST_NEWSTYLE_DIR DIST_DIR\n\
-\ ( print-exe | package-id | [CABAL_HELPER_ARGS...] )\n\
-\)\n"
-
-globalArgSpec :: [OptDescr (Options -> Options)]
-globalArgSpec =
- [ option "h" ["help"] "Display help message" $
- NoArg $ \o -> o { oHelp = True }
- , option "" ["verbose"] "Be more verbose" $
- NoArg $ \o -> o { oVerbose = True }
-
- , option "" ["with-ghc"] "GHC executable to use" $
- reqArg "PROG" $ \p o -> o { oGhcProgram = p }
-
- , option "" ["with-ghc-pkg"] "ghc-pkg executable to use (only needed when guessing from GHC path fails)" $
- reqArg "PROG" $ \p o -> o { oGhcPkgProgram = p }
-
- , option "" ["with-cabal"] "cabal-install executable to use" $
- reqArg "PROG" $ \p o -> o { oCabalProgram = p }
-
- , option "" ["with-cabal-version"] "Cabal library version to use" $
- reqArg "VERSION" $ \p o -> o { oCabalVersion = Just $ parseVer p }
-
- , option "" ["with-cabal-pkg-db"] "package database to look for Cabal library in" $
- reqArg "PKG_DB" $ \p o -> o { oCabalPkgDb = Just (PackageDbDir p) }
-
- ]
- where
- option :: [Char] -> [String] -> String -> ArgDescr a -> OptDescr a
- option s l udsc dsc = Option s l dsc udsc
-
- reqArg :: String -> (String -> a) -> ArgDescr a
- reqArg udsc dsc = ReqArg dsc udsc
-
-parseCommandArgs :: Options -> [String] -> (Options, [String])
-parseCommandArgs opts argv
- = case getOpt RequireOrder globalArgSpec argv of
- (o,r,[]) -> (foldr id opts o, r)
- (_,_,errs) ->
- panic $ "Parsing command options failed:\n" ++ concat errs
-
-guessProgramPaths :: Options -> IO Options
-guessProgramPaths opts = do
- let v | oVerbose opts = deafening
- | otherwise = silent
-
- mGhcPath0 | same oGhcProgram opts dopts = Nothing
- | otherwise = Just $ oGhcProgram opts
- mGhcPkgPath0 | same oGhcPkgProgram opts dopts = Nothing
- | otherwise = Just $ oGhcPkgProgram opts
-
- (_compiler, _mplatform, progdb)
- <- GHC.configure
- v
- mGhcPath0
- mGhcPkgPath0
- defaultProgramDb
-
- let mghcPath1 = programPath <$> lookupProgram ghcProgram progdb
- mghcPkgPath1 = programPath <$> lookupProgram ghcPkgProgram progdb
-
- return $ opts { oGhcProgram = fromMaybe (oGhcProgram opts) mghcPath1
- , oGhcPkgProgram = fromMaybe (oGhcProgram opts) mghcPkgPath1
- }
- where
- same f o o' = f o == f o'
- dopts = defaultOptions
-
-overrideVerbosityEnvVar :: Options -> IO Options
-overrideVerbosityEnvVar opts = do
- x <- lookup "CABAL_HELPER_DEBUG" <$> getEnvironment
- return $ case x of
- Just _ -> opts { oVerbose = True }
- Nothing -> opts
-
-main :: IO ()
-main = handlePanic $ do
- (opts', args) <- parseCommandArgs defaultOptions <$> getArgs
- opts <- overrideVerbosityEnvVar =<< guessProgramPaths opts'
- case args of
- _ | oHelp opts -> usage
- [] -> usage
- "help":[] -> usage
- "version":[] -> putStrLn $ showVersion version
- "print-appdatadir":[] -> putStrLn =<< appCacheDir
- "print-appcachedir":[] -> putStrLn =<< appCacheDir
- "print-build-platform":[] -> putStrLn $ display buildPlatform
-
- _:projdir:_distdir:"package-id":[] -> do
- let v | oVerbose opts = deafening
- | otherwise = silent
- -- ghc-mod will catch multiple cabal files existing before we get here
- [cfile] <- filter isCabalFile <$> getDirectoryContents projdir
- gpd <- readPackageDescription v (projdir </> cfile)
- putStrLn $ show $
- [Just $ ChResponseVersion (display (packageName gpd)) (toDataVersion $ packageVersion gpd)]
-
- "v2-style":projdir:distdir_newstyle:unitid':args' -> do
- let unitid = UnitId $ Text.pack unitid'
- let plan_path = distdir_newstyle </> "cache" </> "plan.json"
- plan@PlanJson {pjCabalLibVersion=Ver (makeDataVersion -> pjCabalLibVersion) }
- <- decodePlanJson plan_path
- case oCabalVersion opts of
- Just ver | pjCabalLibVersion /= ver -> let
- sver = showVersion ver
- spjVer = showVersion pjCabalLibVersion
- in panic $ printf "\
-\Cabal version %s was requested but plan.json was written by version %s" sver spjVer
- _ -> case Map.lookup unitid $ pjUnits plan of
- Just u@Unit {uType} | uType /= UnitTypeLocal -> do
- panic $ "\
-\UnitId '"++ unitid' ++"' points to non-local unit: " ++ ppShow u
- Just Unit {uDistDir=Nothing} -> panic $ printf "\
-\plan.json doesn't contain 'dist-dir' for UnitId '"++ unitid' ++"'"
- Just Unit {uType=UnitTypeLocal, uDistDir=Just distdir} ->
- runHelper opts projdir (Just (plan, distdir_newstyle)) distdir pjCabalLibVersion args'
- _ -> let
- units = map (\(UnitId u) -> Text.unpack u)
- $ Map.keys
- $ Map.filter ((==UnitTypeLocal) . uType)
- $ pjUnits plan
-
- units_list = unlines $ map (" "++) units
- in
- panic $ "\
-\UnitId '"++ unitid' ++"' not found in plan.json, available local units:\n" ++ units_list
-
- "v1-style":projdir:distdir:args' -> do
- cfgf <- canonicalizePath (distdir </> "setup-config")
- mhdr <- getCabalConfigHeader cfgf
- case (mhdr, oCabalVersion opts) of
- (Nothing, _) -> panic $ printf "\
-\Could not read Cabal's persistent setup configuration header\n\
-\- Check first line of: %s\n\
-\- Maybe try: $ cabal configure" cfgf
- (Just (hdrCabalVersion, _), Just ver)
- | hdrCabalVersion /= ver -> panic $ printf "\
-\Cabal version %s was requested but setup configuration was\n\
-\written by version %s" (showVersion ver) (showVersion hdrCabalVersion)
- (Just (hdrCabalVersion, _), _) ->
- runHelper opts projdir Nothing distdir hdrCabalVersion args'
- _ -> do
- hPutStrLn stderr "Invalid command line!"
- usage
- exitWith $ ExitFailure 1
-
-runHelper :: Options -> FilePath -> Maybe (PlanJson, FilePath) -> FilePath -> DataVersion -> [String] -> IO ()
-runHelper opts projdir mnewstyle distdir cabal_ver args' = do
- eexe <- compileHelper opts cabal_ver projdir mnewstyle distdir
- case eexe of
- Left e -> exitWith e
- Right exe -> do
- case args' of
- "print-exe":_ -> putStrLn exe
- _ -> do
- (_,_,_,h) <- createProcess $ proc exe $ projdir : distdir : args'
- exitWith =<< waitForProcess h
diff --git a/src/CabalHelper/Shared/Sandbox.hs b/src/CabalHelper/Shared/Sandbox.hs
index 2f3774f..f7b7470 100644
--- a/src/CabalHelper/Shared/Sandbox.hs
+++ b/src/CabalHelper/Shared/Sandbox.hs
@@ -34,16 +34,17 @@ import Prelude
import qualified Data.Traversable as T
-- | Get the path to the sandbox package-db in a project
-getSandboxPkgDb :: FilePath
- -- ^ Path to the cabal package root directory (containing the
- -- @cabal.sandbox.config@ file)
- -> String
+getSandboxPkgDb :: String
-- ^ Cabal build platform, i.e. @buildPlatform@
-> Version
-- ^ GHC version (@cProjectVersion@ is your friend)
+ -> FilePath
+ -- ^ Path to the cabal package root directory (containing the
+ -- @cabal.sandbox.config@ file)
-> IO (Maybe FilePath)
-getSandboxPkgDb d platform ghcVer = do
- mConf <- T.traverse readFile =<< mightExist (d </> "cabal.sandbox.config")
+getSandboxPkgDb platform ghcVer projdir = do
+ mConf <-
+ T.traverse readFile =<< mightExist (projdir </> "cabal.sandbox.config")
return $ fixPkgDbVer <$> (extractSandboxDbDir =<< mConf)
where
diff --git a/tests/CompileTest.hs b/tests/CompileTest.hs
index 4c1f752..9eb6175 100644
--- a/tests/CompileTest.hs
+++ b/tests/CompileTest.hs
@@ -1,4 +1,4 @@
-{-# LANGUAGE ScopedTypeVariables, GADTs #-}
+{-# LANGUAGE ScopedTypeVariables, GADTs, ImplicitParams #-}
import System.Environment (getArgs)
import System.Directory
@@ -44,15 +44,19 @@ setupHOME = do
main :: IO ()
main = do
+ let ?progs = defaultPrograms
+ let ?opts = defaultCompileOptions { oVerbose = True }
+
args <- getArgs
case args of
"list-versions":[] -> do
- mapM_ print =<< (allCabalVersions <$> ghcVersion defaultOptions)
+ mapM_ print =<< (allCabalVersions <$> ghcVersion)
"list-versions":ghc_ver_str:[] ->
mapM_ print $ allCabalVersions (parseVer ghc_ver_str)
_ ->
test args
+test :: Env => [String] -> IO ()
test args = do
let action
| null args = testAllCabalVersions
@@ -135,13 +139,13 @@ allCabalVersions ghc_ver = let
reverse $ filter (flip withinRange'CH constraint) cabal_versions
-testAllCabalVersions :: IO ()
+testAllCabalVersions :: Env => IO ()
testAllCabalVersions = do
- ghc_ver <- ghcVersion defaultOptions
+ ghc_ver <- ghcVersion
let relevant_cabal_versions = allCabalVersions ghc_ver
testCabalVersions $ map Right relevant_cabal_versions ++ [Left HEAD]
-testCabalVersions :: [Either HEAD Version] -> IO ()
+testCabalVersions :: Env => [Either HEAD Version] -> IO ()
testCabalVersions versions = do
rvs <- forM versions $ \ver -> do
let sver = either show showVersion ver
@@ -167,9 +171,10 @@ testCabalVersions versions = do
isLeft' (Left _) = True
isLeft' (Right _) = False
-compilePrivatePkgDb :: Either HEAD Version -> IO (Either ExitCode FilePath)
+compilePrivatePkgDb
+ :: Env => Either HEAD Version -> IO (Either ExitCode FilePath)
compilePrivatePkgDb eCabalVer = do
- res <- E.try $ installCabal defaultOptions { oVerbose = True } eCabalVer
+ res <- E.try $ installCabal eCabalVer
case res of
Right (db, cabalVer) ->
compileWithPkg db cabalVer
@@ -177,7 +182,8 @@ compilePrivatePkgDb eCabalVer = do
print ioe
return $ Left (ExitFailure 1)
-compileWithPkg :: PackageDbDir
+compileWithPkg :: Env
+ => PackageDbDir
-> CabalVersion
-> IO (Either ExitCode FilePath)
compileWithPkg db cabalVer = do
@@ -187,7 +193,6 @@ compileWithPkg db cabalVer = do
compile
comp
(compPaths appdir (error "compile-test: distdir not available") comp)
- defaultOptions { oVerbose = True }
cabalPkgId :: CabalVersion -> String