aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Gröber <dxld@darkboxed.org>2019-01-22 00:34:05 +0100
committerDaniel Gröber <dxld@darkboxed.org>2019-01-26 02:59:23 +0100
commit541d219dbcf097c0c50b4ee0216f270c9c8c1342 (patch)
treed4c15bf12e74d3bc4be880c20b176045e1d961f1
parenta6a20f17279e31e35861d52a16232897915918fc (diff)
Add support and test coverage for mulit-pkg projects
-rw-r--r--cabal-helper.cabal23
-rw-r--r--lib/Distribution/Helper.hs106
-rw-r--r--src/CabalHelper/Compiletime/Program/Stack.hs63
-rw-r--r--src/CabalHelper/Compiletime/Types.hs90
-rw-r--r--src/CabalHelper/Compiletime/Types/RelativePath.hs1
-rw-r--r--tests/GhcSession.hs452
-rw-r--r--tests/bkpregex/packages.list1
-rw-r--r--tests/bkpregex/stack.yaml3
-rw-r--r--tests/exeintlib/exeintlib.cabal1
-rw-r--r--tests/exeintlib/packages.list1
-rw-r--r--tests/exeintlib/stack.yaml3
-rw-r--r--tests/exelib/exelib.cabal1
-rw-r--r--tests/exelib/packages.list1
-rw-r--r--tests/exelib/stack.yaml3
-rw-r--r--tests/fliblib/fliblib.cabal1
-rw-r--r--tests/fliblib/packages.list1
-rw-r--r--tests/fliblib/stack.yaml3
-rw-r--r--tests/multipkg/.gitignore1
-rw-r--r--tests/multipkg/gen.sh39
-rw-r--r--tests/multipkg/packages.list4
-rw-r--r--tests/multipkg/pkg-oot/Exe.hs1
-rw-r--r--tests/multipkg/pkg-oot/Lib.hs2
-rw-r--r--tests/multipkg/pkg-oot/pkg-oot.cabal24
-rw-r--r--tests/multipkg/proj/Exe.hs1
-rw-r--r--tests/multipkg/proj/Lib.hs2
-rw-r--r--tests/multipkg/proj/cabal.project1
-rw-r--r--tests/multipkg/proj/pkg-a/Exe.hs1
-rw-r--r--tests/multipkg/proj/pkg-a/Lib.hs2
-rw-r--r--tests/multipkg/proj/pkg-a/pkg-a.cabal24
-rw-r--r--tests/multipkg/proj/pkg-b/Exe.hs1
-rw-r--r--tests/multipkg/proj/pkg-b/Lib.hs2
-rw-r--r--tests/multipkg/proj/pkg-b/pkg-b.cabal24
-rw-r--r--tests/multipkg/proj/proj.cabal25
-rw-r--r--tests/multipkg/proj/stack.yaml6
34 files changed, 697 insertions, 217 deletions
diff --git a/cabal-helper.cabal b/cabal-helper.cabal
index ddfe77d..a9d9715 100644
--- a/cabal-helper.cabal
+++ b/cabal-helper.cabal
@@ -38,25 +38,46 @@ extra-source-files: README.md
tests/exelib/*.hs
tests/exelib/*.cabal
+ tests/exelib/packages.list
+ tests/exelib/stack.yaml
tests/exelib/lib/*.hs
tests/exeintlib/*.hs
tests/exeintlib/*.cabal
+ tests/exeintlib/packages.list
+ tests/exeintlib/stack.yaml
tests/exeintlib/lib/*.hs
tests/exeintlib/intlib/*.hs
tests/fliblib/*.hs
tests/fliblib/*.cabal
+ tests/fliblib/packages.list
+ tests/fliblib/stack.yaml
tests/fliblib/lib/*.hs
- tests/bkpregex/*.cabal
tests/bkpregex/*.hs
+ tests/bkpregex/*.cabal
+ tests/bkpregex/packages.list
+ tests/bkpregex/stack.yaml
tests/bkpregex/regex-example/*.hs
tests/bkpregex/regex-indef/*.hs
tests/bkpregex/regex-indef/*.hsig
tests/bkpregex/regex-types/Regex/*.hs
tests/bkpregex/str-impls/Str/*.hs
+ tests/multipkg/packages.list
+ tests/multipkg/pkg-oot/*.cabal
+ tests/multipkg/pkg-oot/*.hs
+ tests/multipkg/proj/*.cabal
+ tests/multipkg/proj/*.hs
+ tests/multipkg/proj/cabal.project
+ tests/multipkg/proj/pkg-a/*.cabal
+ tests/multipkg/proj/pkg-a/*.hs
+ tests/multipkg/proj/pkg-b/*.cabal
+ tests/multipkg/proj/pkg-b/*.hs
+ tests/multipkg/proj/stack.yaml
+
+
source-repository head
type: git
location: https://github.com/DanielG/cabal-helper.git
diff --git a/lib/Distribution/Helper.hs b/lib/Distribution/Helper.hs
index d8fcdf7..4952b2e 100644
--- a/lib/Distribution/Helper.hs
+++ b/lib/Distribution/Helper.hs
@@ -65,6 +65,7 @@ module Distribution.Helper (
-- * GADTs
, DistDir(..)
, ProjType(..)
+ , SProjType(..)
, ProjLoc(..)
, Programs(..)
@@ -88,9 +89,6 @@ module Distribution.Helper (
-- * Managing @dist/@
, prepare
, writeAutogenFiles
-
- -- * Reexports
- , module Data.Functor.Apply
) where
import Cabal.Plan hiding (Unit, UnitId, uDistDir)
@@ -113,10 +111,9 @@ import Data.List.NonEmpty (NonEmpty(..))
import qualified Data.List.NonEmpty as NonEmpty
import Data.Version
import Data.Function
-import Data.Functor.Apply
import System.Clock as Clock
import System.Environment
-import System.FilePath hiding ((<.>))
+import System.FilePath
import System.Directory
import System.Process
import System.Posix.Types
@@ -130,6 +127,7 @@ import qualified CabalHelper.Compiletime.Program.GHC as GHC
import qualified CabalHelper.Compiletime.Program.CabalInstall as CabalInstall
import CabalHelper.Compiletime.Cabal
import CabalHelper.Compiletime.Log
+import CabalHelper.Compiletime.Process
import CabalHelper.Compiletime.Sandbox
import CabalHelper.Compiletime.Types
import CabalHelper.Compiletime.Types.RelativePath
@@ -188,8 +186,11 @@ mkQueryEnv
mkQueryEnv projloc distdir = do
cr <- newIORef $ QueryCache Nothing Map.empty
return $ QueryEnv
- { qeReadProcess = \mcwd exe args stdin ->
+ { qeReadProcess = \stdin mcwd exe args ->
readCreateProcess (proc exe args){ cwd = mcwd } stdin
+ , qeCallProcess = \mcwd exe args -> do
+ let ?verbose = False -- TODO: we should get this from env or something
+ callProcessStderr mcwd exe args
, qePrograms = defaultPrograms
, qeCompPrograms = defaultCompPrograms
, qeProjLoc = projloc
@@ -202,14 +203,16 @@ projConf :: ProjLoc pt -> ProjConf pt
projConf (ProjLocCabalFile cabal_file) =
ProjConfV1 cabal_file
projConf (ProjLocV2Dir projdir_path) =
+ projConf $ ProjLocV2File $ projdir_path </> "cabal.project"
+projConf (ProjLocV2File proj_file) =
ProjConfV2
- { pcV2CabalProjFile = projdir_path </> "cabal.project"
- , pcV2CabalProjLocalFile = projdir_path </> "cabal.project.local"
- , pcV2CabalProjFreezeFile = projdir_path </> "cabal.project.freeze"
+ { pcV2CabalProjFile = proj_file
+ , pcV2CabalProjLocalFile = proj_file <.> "local"
+ , pcV2CabalProjFreezeFile = proj_file <.> "freeze"
}
-projConf (ProjLocStackDir projdir_path) =
+projConf (ProjLocStackYaml stack_yaml) =
ProjConfStack
- { pcStackYaml = projdir_path </> "stack.yml" }
+ { pcStackYaml = stack_yaml }
getProjConfModTime :: ProjConf pt -> IO ProjConfModTimes
getProjConfModTime ProjConfV1{pcV1CabalFile} =
@@ -356,28 +359,50 @@ shallowReconfigureProject QueryEnv
, qeDistDir = DistDirV1 _distdirv1 } =
return ()
shallowReconfigureProject QueryEnv
+ { qeProjLoc = ProjLocV2File projfile
+ , qeDistDir = DistDirV2 _distdirv2, .. } = do
+ let projdir = takeDirectory projfile
+ _ <- qeCallProcess (Just projdir) (cabalProgram qePrograms)
+ ["new-build", "--dry-run", "--project-file="++projfile, "all"]
+ return ()
+shallowReconfigureProject QueryEnv
{ qeProjLoc = ProjLocV2Dir projdir
, qeDistDir = DistDirV2 _distdirv2, .. } = do
- _ <- liftIO $ qeReadProcess (Just projdir) (cabalProgram qePrograms)
- ["new-build", "--dry-run", "all"] ""
+ _ <- qeCallProcess (Just projdir) (cabalProgram qePrograms)
+ ["new-build", "--dry-run", "all"]
return ()
shallowReconfigureProject QueryEnv
- { qeProjLoc = ProjLocStackDir _projdir, .. } = do
+ { qeProjLoc = ProjLocStackYaml _stack_yaml, .. } = do
-- -- In case we ever need to read the cabal files before the Unit stage, this command regenerates them from package.yaml
- -- _ <- liftIO $ qeReadProcess (Just projdir) (stackProgram qePrograms)
+ -- _ <- liftIO $ qeCallProcess (Just projdir) (stackProgram qePrograms)
-- ["build", "--dry-run"] ""
return ()
reconfigureUnit :: QueryEnvI c pt -> Unit pt -> IO ()
reconfigureUnit QueryEnv{qeDistDir=DistDirV1{}, ..} Unit{uPackageDir=_} = do
return ()
-reconfigureUnit QueryEnv{qeDistDir=DistDirV2{}, ..} Unit{uPackageDir, uImpl} = do
- _ <- liftIO $ qeReadProcess (Just uPackageDir) (cabalProgram qePrograms)
- (["new-build"] ++ uiV2Components uImpl) ""
+reconfigureUnit
+ QueryEnv{qeProjLoc=ProjLocV2File projfile, ..}
+ Unit{uPackageDir, uImpl}
+ = do
+ _ <- qeCallProcess (Just uPackageDir) (cabalProgram qePrograms)
+ (["new-build", "--project-file="++projfile]
+ ++ uiV2Components uImpl)
return ()
-reconfigureUnit QueryEnv{qeDistDir=DistDirStack{}, ..} Unit{uPackageDir} = do
- _ <- liftIO $ qeReadProcess (Just uPackageDir) (stackProgram qePrograms)
- ["stack", "build", "--only-configure", "."] ""
+reconfigureUnit
+ QueryEnv{qeProjLoc=ProjLocV2Dir{}, ..}
+ Unit{uPackageDir, uImpl}
+ = do
+ _ <- qeCallProcess (Just uPackageDir) (cabalProgram qePrograms)
+ (["new-build"] ++ uiV2Components uImpl)
+ -- TODO: version check for --only-configure
+ return ()
+reconfigureUnit
+ qe@QueryEnv{qeProjLoc=ProjLocStackYaml stack_yaml, ..}
+ Unit{uPackageDir}
+ = do
+ _ <- Stack.callStackCmd qe (Just uPackageDir)
+ ["--stack-yaml="++stack_yaml, "build", "--only-configure", "."]
return ()
getFileModTime :: FilePath -> IO (FilePath, EpochTime)
@@ -388,11 +413,9 @@ getFileModTime f = do
readProjInfo
:: QueryEnvI c pt -> ProjConf pt -> ProjConfModTimes -> IO (ProjInfo pt)
readProjInfo qe pc pcm = withVerbosity $ do
- case (qeProjLoc qe, qeDistDir qe, pc) of
- ((,,)
- projloc
- (DistDirV1 distdir)
- ProjConfV1{pcV1CabalFile}) -> do
+ let projloc = qeProjLoc qe
+ case (qeDistDir qe, pc) of
+ (DistDirV1 distdir, ProjConfV1{pcV1CabalFile}) -> do
let projdir = plV1Dir projloc
setup_config_path <- canonicalizePath (distdir </> "setup-config")
mhdr <- getCabalConfigHeader setup_config_path
@@ -412,7 +435,7 @@ readProjInfo qe pc pcm = withVerbosity $ do
}
, piImpl = ProjInfoV1
}
- (ProjLocV2Dir _projdir, DistDirV2 distdirv2, _) -> do
+ (DistDirV2 distdirv2, _) -> do
let plan_path = distdirv2 </> "cache" </> "plan.json"
plan_mtime <- modificationTime <$> getFileStatus plan_path
plan@PlanJson { pjCabalLibVersion=Ver pjCabalLibVersion
@@ -430,7 +453,7 @@ readProjInfo qe pc pcm = withVerbosity $ do
, piV2CompilerId = (Text.unpack compName, makeDataVersion compVer)
}
}
- (ProjLocStackDir{} , DistDirStack{}, _) -> do
+ (DistDirStack{}, _) -> do
Just cabal_files <- NonEmpty.nonEmpty <$> Stack.listPackageCabalFiles qe
units <- mapM (Stack.getUnit qe) cabal_files
proj_paths <- Stack.projPaths qe
@@ -508,7 +531,7 @@ invokeHelper
args0
= do
let args1 = cabal_file_path : distdir : args0
- evaluate =<< qeReadProcess Nothing exe args1 "" `E.catch`
+ evaluate =<< qeReadProcess "" Nothing exe args1 `E.catch`
\(_ :: E.IOException) ->
panicIO $ concat
["invokeHelper", ": ", exe, " "
@@ -624,21 +647,25 @@ wrapper'
, cheDistV2 = Nothing
}
wrapper'
- (ProjLocV2Dir projdir)
+ projloc
(DistDirV2 distdir)
ProjInfo{piImpl=ProjInfoV2{piV2Plan=plan}}
- = CompHelperEnv
- { cheCabalVer = CabalVersion $ makeDataVersion pjCabalLibVersion
- , cheProjDir = projdir
- , cheProjLocalCacheDir = distdir </> "cache"
- , chePkgDb = Nothing
- , chePlanJson = Just plan
- , cheDistV2 = Just distdir
- }
+ = case projloc of
+ ProjLocV2Dir projdir ->
+ let cheProjDir = projdir in
+ CompHelperEnv {..}
+ ProjLocV2File proj_file ->
+ let cheProjDir = takeDirectory proj_file in
+ CompHelperEnv {..}
where
+ cheCabalVer = CabalVersion $ makeDataVersion pjCabalLibVersion
+ cheProjLocalCacheDir = distdir </> "cache"
+ chePkgDb = Nothing
+ chePlanJson = Just plan
+ cheDistV2 = Just distdir
PlanJson {pjCabalLibVersion=Ver pjCabalLibVersion } = plan
wrapper'
- (ProjLocStackDir projdir)
+ (ProjLocStackYaml stack_yaml)
(DistDirStack mworkdir)
ProjInfo
{ piCabalVersion
@@ -648,6 +675,7 @@ wrapper'
}
}
= let workdir = fromMaybe ".stack-work" $ unRelativePath <$> mworkdir in
+ let projdir = takeDirectory stack_yaml in
CompHelperEnv
{ cheCabalVer = CabalVersion $ piCabalVersion
, cheProjDir = projdir
diff --git a/src/CabalHelper/Compiletime/Program/Stack.hs b/src/CabalHelper/Compiletime/Program/Stack.hs
index f4ada8f..33ba031 100644
--- a/src/CabalHelper/Compiletime/Program/Stack.hs
+++ b/src/CabalHelper/Compiletime/Program/Stack.hs
@@ -24,43 +24,48 @@ License : GPL-3
module CabalHelper.Compiletime.Program.Stack where
+import Control.Exception (handle, throwIO)
import Control.Monad
+import Control.Monad.Trans.Maybe
+import Control.Monad.IO.Class
import Data.Char
import Data.List hiding (filter)
import Data.String
import Data.Maybe
import Data.Function
+import Data.Version
+import System.Directory (findExecutable)
import System.FilePath hiding ((<.>))
+import System.IO (hPutStrLn, stderr)
+import Text.Printf (printf)
+import Text.Show.Pretty
import Prelude
import CabalHelper.Compiletime.Types
import CabalHelper.Compiletime.Types.RelativePath
+import CabalHelper.Shared.Common
getUnit :: QueryEnvI c 'Stack -> CabalFile -> IO (Unit 'Stack)
-getUnit qe cabal_file@(CabalFile cabal_file_path) = do
+getUnit
+ qe@QueryEnv{qeProjLoc=ProjLocStackYaml stack_yaml}
+ cabal_file@(CabalFile cabal_file_path)
+ = do
+ let projdir = takeDirectory stack_yaml
let pkgdir = takeDirectory cabal_file_path
let pkg_name = dropExtension $ takeFileName cabal_file_path
look <- paths qe pkgdir
- let distdirv1 = look "dist-dir:"
+ let distdirv1_rel = look "dist-dir:"
return $ Unit
{ uUnitId = UnitId pkg_name
, uPackageDir = pkgdir
, uCabalFile = cabal_file
- , uDistDir = DistDirLib distdirv1
+ , uDistDir = DistDirLib $ pkgdir </> distdirv1_rel
, uImpl = UnitImplStack
}
--- TODO: patch ghc/ghc-pkg program paths like in ghc-mod when using stack so
--- compilation logic works even if no system compiler is installed
-
-packageDistDir :: QueryEnvI c 'Stack -> FilePath -> IO FilePath
-packageDistDir qe pkgdir = do
- look <- paths qe pkgdir
- return $ look "dist-dir:"
-
projPaths :: QueryEnvI c 'Stack -> IO StackProjPaths
-projPaths qe@QueryEnv {qeProjLoc=ProjLocStackDir projdir} = do
- look <- paths qe projdir
+projPaths qe@QueryEnv {qeProjLoc=ProjLocStackYaml stack_yaml} = do
+ look <- paths qe $ takeDirectory stack_yaml
return StackProjPaths
{ sppGlobalPkgDb = PackageDbDir $ look "global-pkg-db:"
, sppSnapPkgDb = PackageDbDir $ look "snapshot-pkg-db:"
@@ -68,20 +73,20 @@ projPaths qe@QueryEnv {qeProjLoc=ProjLocStackDir projdir} = do
, sppCompExe = look "compiler-exe:"
}
-paths :: QueryEnvI c 'Stack
- -> FilePath
- -> IO (String -> FilePath)
-paths qe dir = do
- out <- qeReadProcess qe (Just dir) (stackProgram $ qePrograms qe)
- (workdirArg qe ++ [ "path" ]) ""
- return $ \k -> let Just x = lookup k $ map split $ lines out in x
+paths :: QueryEnvI c 'Stack -> FilePath -> IO (String -> FilePath)
+paths qe@QueryEnv{qeProjLoc=ProjLocStackYaml stack_yaml} cwd
+ = do
+ out <- readStackCmd qe (Just cwd) $
+ workdirArg qe ++ [ "path", "--stack-yaml="++stack_yaml ]
+ return $ \k -> let Just x = lookup k $ map split $ lines out in x
where
split l = let (key, ' ' : val) = span (not . isSpace) l in (key, val)
listPackageCabalFiles :: QueryEnvI c 'Stack -> IO [CabalFile]
-listPackageCabalFiles qe@QueryEnv{qeProjLoc=ProjLocStackDir projdir} = do
- out <- qeReadProcess qe (Just projdir) (stackProgram $ qePrograms qe)
- [ "ide", "packages", "--cabal-files" ] ""
+listPackageCabalFiles qe@QueryEnv{qeProjLoc=ProjLocStackYaml stack_yaml} = do
+ let projdir = takeDirectory stack_yaml
+ out <- readStackCmd qe (Just projdir)
+ [ "ide", "packages", "--cabal-files", "--stdout" ]
return $ map CabalFile $ lines out
workdirArg :: QueryEnvI c 'Stack -> [String]
@@ -91,3 +96,15 @@ workdirArg QueryEnv{qeDistDir=DistDirStack mworkdir} =
patchCompPrograms :: StackProjPaths -> CompPrograms -> CompPrograms
patchCompPrograms StackProjPaths{sppCompExe} cprogs =
cprogs { ghcProgram = sppCompExe }
+
+doStackCmd :: (QueryEnvI c 'Stack -> CallProcessWithCwd a)
+ -> QueryEnvI c 'Stack -> Maybe FilePath -> [String] -> IO a
+doStackCmd procfn qe mcwd args =
+ let Programs{..} = qePrograms qe in
+ procfn qe mcwd stackProgram $ stackArgsBefore ++ args ++ stackArgsAfter
+
+readStackCmd :: QueryEnvI c 'Stack -> Maybe FilePath -> [String] -> IO String
+callStackCmd :: QueryEnvI c 'Stack -> Maybe FilePath -> [String] -> IO ()
+
+readStackCmd = doStackCmd (\qe -> qeReadProcess qe "")
+callStackCmd = doStackCmd qeCallProcess
diff --git a/src/CabalHelper/Compiletime/Types.hs b/src/CabalHelper/Compiletime/Types.hs
index 56f2468..185725d 100644
--- a/src/CabalHelper/Compiletime/Types.hs
+++ b/src/CabalHelper/Compiletime/Types.hs
@@ -30,7 +30,6 @@ import Cabal.Plan
import Data.IORef
import Data.Version
import Data.Typeable
-import Data.Map.Strict (Map)
import GHC.Generics
import System.FilePath
import System.Posix.Types
@@ -39,32 +38,41 @@ import CabalHelper.Shared.InterfaceTypes
import Data.List.NonEmpty (NonEmpty)
--import qualified Data.List.NonEmpty as NonEmpty
-
+import Data.Map.Strict (Map)
+--import qualified Data.Map.Strict as Strict
-- | The kind of project being managed by a 'QueryEnv' (pun intended).
data ProjType
= V1 -- ^ @cabal v1-build@ project, see 'DistDirV1'
| V2 -- ^ @cabal v2-build@ project, see 'DistDirV2'
| Stack -- ^ @stack@ project.
+ deriving (Eq, Ord, Show, Read)
+
+data SProjType pt where
+ SV1 :: SProjType 'V1
+ SV2 :: SProjType 'V2
+ SStack :: SProjType 'Stack
--- | The location of a project. The kind of location marker given determines the
--- 'ProjType'. The project type of a given directory can be determined by trying
--- to access a set of marker files. See below.
+-- | Location of project sources. The project type of a given directory can be
+-- determined by trying to access a set of marker files. See below.
data ProjLoc (pt :: ProjType) where
-- | A @cabal v1-build@ project directory can be identified by one file
-- ending in @.cabal@ existing in the directory. More than one such files
-- existing is a user error. Note: For this project type the concepts of
-- project and package coincide.
- ProjLocCabalFile :: { plCabalFile :: FilePath } -> ProjLoc 'V1
+ ProjLocCabalFile :: { plCabalFile :: !FilePath } -> ProjLoc 'V1
-- | A @cabal v2-build@ project\'s marker file is called
-- @cabal.project@. This configuration file points to the packages that make
-- up this project.
- ProjLocV2Dir :: { plV2Dir :: FilePath } -> ProjLoc 'V2
+ ProjLocV2File :: { plCabalProjectFile :: !FilePath } -> ProjLoc 'V2
+ ProjLocV2Dir :: { plV2Dir :: !FilePath } -> ProjLoc 'V2
-- | A @stack@ project\'s marker file is called @stack.yaml@. This
-- configuration file points to the packages that make up this project.
- ProjLocStackDir :: { plStackDir :: FilePath } -> ProjLoc 'Stack
+ ProjLocStackYaml :: { plStackYaml :: !FilePath } -> ProjLoc 'Stack
+
+deriving instance Show (ProjLoc pt)
plV1Dir :: ProjLoc 'V1 -> FilePath
plV1Dir (ProjLocCabalFile cabal_file) = takeDirectory cabal_file
@@ -76,7 +84,7 @@ data DistDir (pt :: ProjType) where
--
-- You can tell a builddir is a /v1/ builddir by looking for a file
-- called @setup-config@ directly underneath it.
- DistDirV1 :: FilePath -> DistDir 'V1
+ DistDirV1 :: !FilePath -> DistDir 'V1
-- | Build directory for cabal /new-build/ aka. /v2-build/, as of the time
-- of this writing it is usually called @dist-newstyle/@ but this will
@@ -84,11 +92,13 @@ data DistDir (pt :: ProjType) where
--
-- You can tell a builddir is a /v2/ builddir by trying to access the path
-- @cache/plan.json@ directly underneath it.
- DistDirV2 :: FilePath -> DistDir 'V2
+ DistDirV2 :: !FilePath -> DistDir 'V2
-- | Build directory for stack, aka. /work-dir/. Optionally override Stack's
-- /work-dir/. If you just want to use Stack's default set to @Nothing@
- DistDirStack :: Maybe RelativePath -> DistDir 'Stack
+ DistDirStack :: !(Maybe RelativePath) -> DistDir 'Stack
+
+deriving instance Show (DistDir pt)
-- | Environment for running a 'Query' value. The constructor is not exposed in
-- the API to allow extending the environment without breaking user code.
@@ -100,12 +110,13 @@ type QueryEnv (pt :: ProjType)
= QueryEnvI QueryCache pt
data QueryEnvI c (pt :: ProjType) = QueryEnv
- { qeReadProcess
- :: !(Maybe FilePath -> FilePath -> [String] -> String -> IO String)
+ { qeReadProcess :: !ReadProcessWithCwd
-- ^ Field accessor for 'QueryEnv'. Function used to to start
-- processes. Useful if you need to, for example, redirect standard error
-- output of programs started by cabal-helper.
+ , qeCallProcess :: !(CallProcessWithCwd ())
+
, qePrograms :: !Programs
-- ^ Field accessor for 'QueryEnv'.
@@ -126,6 +137,9 @@ data QueryEnvI c (pt :: ProjType) = QueryEnv
-- 'QueryEnv' is used.
}
+type ReadProcessWithCwd = String -> CallProcessWithCwd String
+type CallProcessWithCwd a = Maybe FilePath -> FilePath -> [String] -> IO a
+
data QueryCache pt = QueryCache
{ qcProjInfo :: !(Maybe (ProjInfo pt))
, qcUnitInfos :: !(Map DistDirLib UnitInfo)
@@ -144,7 +158,7 @@ data Unit pt = Unit
, uCabalFile :: !CabalFile
, uDistDir :: !DistDirLib
, uImpl :: !(UnitImpl pt)
- }
+ } deriving (Show)
data UnitImpl pt where
UnitImplV1 :: UnitImpl 'V1
@@ -156,6 +170,8 @@ data UnitImpl pt where
UnitImplStack :: UnitImpl 'Stack
+deriving instance Show (UnitImpl pt)
+
-- | This returns the component a 'Unit' corresponds to. This information is
-- only available if the correspondence happens to be unique and known before
-- querying setup-config for the respective project type. Currently this only
@@ -228,7 +244,7 @@ data ProjConf pt where
-- these are supposed to be opaque, as they are meant to be used only for cache
-- invalidation
newtype ProjConfModTimes = ProjConfModTimes [(FilePath, EpochTime)]
- deriving (Eq)
+ deriving (Eq, Show)
-- | Project-scope information cache.
data ProjInfo pt = ProjInfo
@@ -238,7 +254,7 @@ data ProjInfo pt = ProjInfo
, piProjConfModTimes :: !ProjConfModTimes
-- ^ Key for cache invalidation. When this is not equal to the return
-- value of 'getProjConfModTime' this 'ProjInfo' is considered invalid.
- }
+ } deriving (Show)
data ProjInfoImpl pt where
ProjInfoV1 :: ProjInfoImpl 'V1
@@ -253,6 +269,21 @@ data ProjInfoImpl pt where
{ piStackProjPaths :: !StackProjPaths
} -> ProjInfoImpl 'Stack
+instance Show (ProjInfoImpl pt) where
+ show ProjInfoV1 = "ProjInfoV1"
+ show ProjInfoV2 {..} = concat
+ [ "ProjInfoV2 {"
+ , "piV2Plan = ", show piV2Plan, ", " --
+ , "piV2PlanModTime = ", show piV2PlanModTime, ", "
+ , "piV2CompilerId = ", show piV2CompilerId
+ , "}"
+ ]
+ show ProjInfoStack {..} = concat
+ [ "ProjInfoStack {"
+ , "piStackProjPaths = ", show piStackProjPaths
+ , "}"
+ ]
+
data UnitModTimes = UnitModTimes
{ umtPkgYaml :: !(Maybe (FilePath, EpochTime))
, umtCabalFile :: !(FilePath, EpochTime)
@@ -260,13 +291,14 @@ data UnitModTimes = UnitModTimes
} deriving (Eq, Ord, Read, Show)
newtype CabalFile = CabalFile FilePath
+ deriving (Show)
data StackProjPaths = StackProjPaths
{ sppGlobalPkgDb :: !PackageDbDir
, sppSnapPkgDb :: !PackageDbDir
, sppLocalPkgDb :: !PackageDbDir
, sppCompExe :: !FilePath
- }
+ } deriving (Show)
-- Beware: GHC 8.0.2 doesn't like these being recursively defined for some
@@ -277,19 +309,23 @@ type Progs = (?cprogs :: CompPrograms, ?progs :: Programs)
type CProgs = (?cprogs :: CompPrograms)
-- | Configurable paths to various programs we use.
-data Programs = Programs {
- -- | The path to the @cabal@ program.
- cabalProgram :: FilePath,
-
- -- | The path to the @stack@ program.
- stackProgram :: FilePath
+data Programs = Programs
+ { cabalProgram :: !FilePath
+ -- ^ The path to the @cabal@ program.
+ , cabalArgsBefore :: ![String]
+ , cabalArgsAfter :: ![String]
+
+ , stackProgram :: !FilePath
+ -- ^ The path to the @stack@ program.
+ , stackArgsBefore :: ![String]
+ , stackArgsAfter :: ![String]
} deriving (Eq, Ord, Show, Read, Generic, Typeable)
data CompPrograms = CompPrograms
- { ghcProgram :: FilePath
+ { ghcProgram :: !FilePath
-- ^ The path to the @ghc@ program.
- , ghcPkgProgram :: FilePath
+ , ghcPkgProgram :: !FilePath
-- ^ The path to the @ghc-pkg@ program. If not changed it will be derived
-- from the path to 'ghcProgram'.
} deriving (Eq, Ord, Show, Read, Generic, Typeable)
@@ -297,7 +333,7 @@ data CompPrograms = CompPrograms
-- | By default all programs use their unqualified names, i.e. they will be
-- searched for on @PATH@.
defaultPrograms :: Programs
-defaultPrograms = Programs "cabal" "stack"
+defaultPrograms = Programs "cabal" [] [] "stack" [] []
defaultCompPrograms :: CompPrograms
defaultCompPrograms = CompPrograms "ghc" "ghc-pkg"
@@ -317,4 +353,6 @@ defaultCompileOptions =
CompileOptions False Nothing Nothing defaultPrograms
newtype PackageDbDir = PackageDbDir { unPackageDbDir :: FilePath }
+ deriving (Show)
newtype PackageEnvFile = PackageEnvFile { unPackageEnvFile :: FilePath }
+ deriving (Show)
diff --git a/src/CabalHelper/Compiletime/Types/RelativePath.hs b/src/CabalHelper/Compiletime/Types/RelativePath.hs
index bfc29bf..107a8ce 100644
--- a/src/CabalHelper/Compiletime/Types/RelativePath.hs
+++ b/src/CabalHelper/Compiletime/Types/RelativePath.hs
@@ -30,6 +30,7 @@ import System.FilePath
-- | A path guaranteed to be relative. The constructor is not exposed, use the
-- 'mkRelativePath' smart constructor.
newtype RelativePath = RelativePath { unRelativePath :: FilePath }
+ deriving (Show)
-- | Smart constructor for 'RelativePath'. Checks if the given path is absolute
-- and throws 'UserError' if not.
diff --git a/tests/GhcSession.hs b/tests/GhcSession.hs
index 3e67ae2..0d20a5f 100644
--- a/tests/GhcSession.hs
+++ b/tests/GhcSession.hs
@@ -1,4 +1,6 @@
-{-# LANGUAGE TupleSections, ScopedTypeVariables, RecordWildCards, RankNTypes, DataKinds #-}
+{-# LANGUAGE TupleSections, ScopedTypeVariables, RecordWildCards, RankNTypes,
+ DataKinds, ExistentialQuantification, PolyKinds, ViewPatterns,
+ DeriveFunctor, MonoLocalBinds, GADTs, MultiWayIf #-}
{-| This test ensures we can get a GHC API session up and running in a variety of
project environments.
@@ -8,8 +10,10 @@ module Main where
import GHC
import GHC.Paths (libdir)
+import Outputable
import DynFlags
+import Control.Arrow ((***))
import qualified Control.Exception as E
import Control.Monad
import Control.Monad.IO.Class
@@ -18,121 +22,174 @@ import Data.Version
import qualified Data.Map as Map
import System.Environment (getArgs)
import System.Exit
-import System.FilePath ((</>), takeFileName, takeDirectory)
+import System.FilePath ((</>), (<.>), makeRelative, takeDirectory)
import System.Directory
import System.IO
import System.IO.Temp
import System.Process (readProcess)
+import Text.Printf (printf)
+import Text.Show.Pretty
import Distribution.Helper
import CabalHelper.Shared.Common
import CabalHelper.Compiletime.Process
+data TestConfig = TC
+ { location :: TestLocation
+ , cabalLowerBound :: Version
+ , ghcLowerBound :: Version
+ , projTypes :: [ProjType]
+ } deriving (Show)
+
+data TestLocation
+ = TN String
+ | TF FilePath FilePath FilePath
+ deriving (Show)
main :: IO ()
main = do
args <- getArgs
- topdir <- getCurrentDirectory
- res <- mapM (setup topdir test) $ case args of
- [] -> [ ("tests/exelib/exelib.cabal", parseVer "1.10", parseVer "0")
- , ("tests/exeintlib/exeintlib.cabal", parseVer "2.0", parseVer "0")
- , ("tests/fliblib/fliblib.cabal", parseVer "2.0", parseVer "0")
- , ("tests/bkpregex/bkpregex.cabal", parseVer "2.0", parseVer "8.1")
- -- min Cabal lib ver -^ min GHC ver -^
- ]
- xs -> map (, parseVer "0", parseVer "0") xs
+-- topdir <- getCurrentDirectory
+
+ ci_ver <- cabalInstallVersion
+ c_ver <- cabalInstallBuiltinCabalVersion
+ g_ver <- ghcVersion
+ s_ver <- stackVersion
+ `E.catch` \(_ :: IOError) -> return (makeVersion [0])
+
+ putStrLn $ "cabal-install version: " ++ showVersion ci_ver
+ putStrLn $ "Cabal version: " ++ showVersion c_ver
+ putStrLn $ "GHC version: " ++ showVersion g_ver
+ putStrLn $ "Stack version: " ++ showVersion s_ver
+
+ let proj_impls :: [(ProjType, ProjSetup0)]
+ proj_impls =
+ [ (V1, oldBuildProjSetup)
+ , (V2, newBuildProjSetup)
+ , (Stack, stackProjSetup g_ver)
+ ]
+
+ tests <- return $ case args of
+ xs@(_:_) -> flip map xs $ \loc ->
+ let (topdir, ':':x0) = span (/=':') loc
+ (projdir0, ':':x1) = span (/=':') x0
+ (cabal_file0, ':':pt) = span (/=':') x1
+ projdir = makeRelative topdir projdir0
+ cabal_file = makeRelative topdir cabal_file0 in
+ TC (TF topdir projdir cabal_file) (parseVer "0") (parseVer "0") [read pt]
+ [] ->
+ [ TC (TN "exelib") (parseVer "1.10") (parseVer "0") []
+ , TC (TN "exeintlib") (parseVer "2.0") (parseVer "0") []
+ , TC (TN "fliblib") (parseVer "2.0") (parseVer "0") []
+ , TC (TN "bkpregex") (parseVer "2.0") (parseVer "8.1") [V1, V2]
+ , let multipkg_loc = TF "tests/multipkg/" "proj/" "proj/proj.cabal" in
+ TC multipkg_loc (parseVer "1.10") (parseVer "0") [V2, Stack]
+ -- min Cabal lib ver -^ min GHC ver -^
+ ]
+
+ -- pPrint tests
+ -- mapM_ (\(TC loc _ _ _) -> pPrint $ testLocPath loc) tests
+
+ res :: [[Bool]] <- sequence $ do
+ tc@TC {..} <- tests
+ (pt, ps0 :: ProjSetup0) <- proj_impls
+ guard (null projTypes || pt `elem` projTypes)
+
+ let skip (SkipReason reason) = do
+ hPutStrLn stderr $ intercalate " "
+ [ "Skipping test"
+ , psdHeading ps0
+ , "'" ++ projdir_rel ++ "'"
+ , "because"
+ , reason
+ ]
+ where
+ (_, projdir_rel, _) = testLocPath location
+
+ case psdImpl ps0 of
+ Left reason -> return $ skip reason >> return []
+ Right eximpl -> do
+ let ps1 = ps0 { psdImpl = eximpl }
+ case checkAndRunTestConfig VerEnv{..} ps1 tc of
+ Left reason -> return $ skip reason >> return []
+ Right (Message msg, act) -> return $ hPutStrLn stderr msg >> act
if any (==False) $ concat res
then exitFailure
else exitSuccess
-cabalInstallVersion :: IO Version
-cabalInstallVersion =
- parseVer . trim <$> readProcess "cabal" ["--numeric-version"] ""
-
-ghcVersion :: IO Version
-ghcVersion =
- parseVer . trim <$> readProcess "ghc" ["--numeric-version"] ""
-
-cabalInstallBuiltinCabalVersion :: IO Version
-cabalInstallBuiltinCabalVersion =
- parseVer . trim <$> readProcess "cabal"
- ["act-as-setup", "--", "--numeric-version"] ""
+data VerEnv = VerEnv
+ { ci_ver :: Version
+ , c_ver :: Version
+ , g_ver :: Version
+ , s_ver :: Version
+ }
-data ProjSetup pt =
- ProjSetup
- { psDistDir :: FilePath -> DistDir pt
- , psProjDir :: FilePath -> ProjLoc pt
- , psConfigure :: FilePath -> IO ()
- , psBuild :: FilePath -> IO ()
- , psSdist :: FilePath -> FilePath -> IO ()
- }
+data Message = Message String
+data SkipReason = SkipReason String
-oldBuild :: ProjSetup 'V1
-oldBuild = ProjSetup
- { psDistDir = \dir -> DistDirV1 (dir </> "dist")
- , psProjDir = \cabal_file -> ProjLocCabalFile cabal_file
- , psConfigure = \dir ->
- runWithCwd dir "cabal" [ "configure" ]
- , psBuild = \dir ->
- runWithCwd dir "cabal" [ "build" ]
- , psSdist = \srcdir destdir ->
- runWithCwd srcdir "cabal" [ "sdist", "-v0", "--output-dir", destdir ]
- }
-
-newBuild :: ProjSetup 'V2
-newBuild = ProjSetup
- { psDistDir = \dir -> DistDirV2 (dir </> "dist-newstyle")
- , psProjDir = \cabal_file -> ProjLocV2Dir (takeDirectory cabal_file)
- , psConfigure = \dir ->
- runWithCwd dir "cabal" [ "new-configure" ]
- , psBuild = \dir ->
- runWithCwd dir "cabal" [ "new-build" ]
- , psSdist = \srcdir destdir ->
- runWithCwd srcdir "cabal" [ "sdist", "-v0", "--output-dir", destdir ]
- }
-
-setup :: FilePath -> (forall pt . ProjSetup pt -> FilePath -> IO [Bool]) -> (FilePath, Version, Version) -> IO [Bool]
-setup topdir act (cabal_file, min_cabal_ver, min_ghc_ver) = do
- let projdir = takeDirectory cabal_file
- ci_ver <- cabalInstallVersion
- c_ver <- cabalInstallBuiltinCabalVersion
- g_ver <- ghcVersion
- let mreason
- | (ci_ver < parseVer "1.24") =
- Just $ "cabal-install-" ++ showVersion ci_ver ++ " is too old"
- | c_ver < min_cabal_ver =
- Just $ "Cabal-" ++ showVersion c_ver
- ++ " < " ++ showVersion min_cabal_ver
- | g_ver < min_ghc_ver =
- Just $ "ghc-" ++ showVersion g_ver
- ++ " < " ++ showVersion min_ghc_ver
- | otherwise =
- Nothing
-
- case mreason of
- Just reason -> do
- putStrLn $ "Skipping test '" ++ projdir ++ "' because " ++ reason ++ "."
- return []
- Nothing -> do
- putStrLn $ "Running test '" ++ projdir ++ "' with " ++ showVersion ci_ver ++ "."
- putStrLn "Old build -------------------------------------"
- rold <- runTest oldBuild topdir projdir cabal_file act
- putStrLn "New build -------------------------------------"
- rnew <- runTest newBuild topdir projdir cabal_file act
- return (rold ++ rnew)
-
-runTest :: ProjSetup pt -> FilePath -> String -> FilePath
- -> (ProjSetup pt -> FilePath -> IO [Bool]) -> IO [Bool]
-runTest ps@ProjSetup{..} topdir projdir cabal_file act = do
- putStrLn $ "Running test '" ++ projdir ++ "'-------------------------"
+testLocPath :: TestLocation -> (FilePath, FilePath, FilePath)
+testLocPath (TN test_name) = (projdir, ".", cabal_file)
+ where
+ projdir :: FilePath
+ projdir = "tests" </> test_name
+ cabal_file :: FilePath
+ cabal_file = test_name <.> "cabal"
+testLocPath (TF topdir projdir cabal_file) =
+ (topdir, projdir, cabal_file)
+
+data Ex a = forall x. Ex (a x)
+
+checkAndRunTestConfig
+ :: VerEnv
+ -> ProjSetup1
+ -> TestConfig
+ -> Either SkipReason (Message, IO [Bool])
+checkAndRunTestConfig
+ VerEnv { ci_ver, c_ver, g_ver, s_ver }
+ ps1@(psdImpl -> Ex psdImpl2)
+ (TC test_loc min_cabal_ver min_ghc_ver _proj_types)
+ = let
+ (topdir, projdir_rel, cabal_file) = testLocPath test_loc
+ mreason
+ | SStack <- psiProjType psdImpl2
+ , s_ver < parseVer "1.9.4" =
+ if| g_ver >= parseVer "8.2.2" ->
+ error $ printf
+ "stack-%s is too old, but GHC %s is recent enough to build it.\n\
+ \The CI scripts should have installed it! See 25-deps.sh\n"
+ (showVersion s_ver) (showVersion g_ver)
+ | otherwise ->
+ Just $ "stack-" ++ showVersion s_ver ++ " is too old"
+ | (ci_ver < parseVer "1.24") =
+ Just $ "cabal-install-" ++ showVersion ci_ver ++ " is too old"
+ | c_ver < min_cabal_ver =
+ Just $ "Cabal-" ++ showVersion c_ver
+ ++ " < " ++ showVersion min_cabal_ver
+ | g_ver < min_ghc_ver =
+ Just $ "ghc-" ++ showVersion g_ver
+ ++ " < " ++ showVersion min_ghc_ver
+ | otherwise =
+ Nothing
+ in case mreason of
+ Just reason -> do
+ Left $ SkipReason reason
+ Nothing -> do
+ Right $ (,)
+ (Message $ intercalate " "
+ [ "\n\n\nRunning test"
+ , psdHeading ps1
+ , "'" ++ topdir ++ "'"
+ ])
+ (runTest ps1{ psdImpl = psdImpl2 } topdir projdir_rel cabal_file)
+
+runTest :: ProjSetup2 pt -> FilePath -> FilePath -> FilePath -> IO [Bool]
+runTest ps2@(psdImpl -> ProjSetupImpl{..}) topdir projdir cabal_file = do
withSystemTempDirectory' "cabal-helper.ghc-session.test" $ \tmpdir -> do
-
- psSdist (topdir </> projdir) tmpdir
- psConfigure tmpdir
-
- act ps $ tmpdir </> takeFileName cabal_file
+ psiSdist topdir tmpdir
+ psiConfigure (tmpdir </> projdir)
+ test ps2 (tmpdir </> projdir) (tmpdir </> cabal_file)
runWithCwd :: FilePath -> String -> [String] -> IO ()
runWithCwd cwd x xs = do
@@ -144,25 +201,27 @@ run x xs = do
let ?verbose = True
callProcessStderr Nothing x xs
-test :: ProjSetup pt -> FilePath -> IO [Bool]
-test ProjSetup{..} cabal_file = do
- let projdir = takeDirectory cabal_file
- qe <- mkQueryEnv
- (psProjDir cabal_file)
- (psDistDir projdir)
+test :: ProjSetup2 pt -> FilePath -> FilePath -> IO [Bool]
+test (psdImpl -> ProjSetupImpl{..}) projdir cabal_file = do
+ qe <- psiQEmod <$> mkQueryEnv
+ (psiProjLoc (CabalFile cabal_file) projdir)
+ (psiDistDir projdir)
+
cs <- concat <$> runQuery (allUnits (Map.elems . uiComponents)) qe
- forM cs $ \ChComponentInfo{..} -> do
- putStrLn $ "\n" ++ show ciComponentName ++ ":::: " ++ show ciNeedsBuildOutput
- when (ciNeedsBuildOutput == ProduceBuildOutput) $ do
- psBuild projdir
+ when (any ((==ProduceBuildOutput) . ciNeedsBuildOutput) cs) $
+ psiBuild projdir
- let opts' = "-Werror" : ciGhcOptions
+ let pkgdir = takeDirectory cabal_file
+ forM cs $ \ChComponentInfo{..} -> do
+ putStrLn $ "\n" ++ show ciComponentName
+ ++ ":::: " ++ show ciNeedsBuildOutput
- let sopts = intercalate " " $ map formatArg $ "\nghc" : opts'
- putStrLn $ "\n" ++ show ciComponentName ++ ": " ++ sopts
+ let opts' = "-Werror" : ciGhcOptions
+ let sopts = intercalate " " $ map formatArg $ "ghc" : opts'
+ putStrLn $ "\n" ++ show ciComponentName ++ ":\n" ++ "cd " ++ pkgdir ++ "\n" ++ sopts
hFlush stdout
- compileModule projdir ciNeedsBuildOutput ciEntrypoints opts'
+ compileModule pkgdir ciNeedsBuildOutput ciEntrypoints ciSourceDirs opts'
where
formatArg x
| "-" `isPrefixOf` x = "\n "++x
@@ -173,11 +232,13 @@ addCabalProject dir = do
writeFile (dir </> "cabal.project") "packages: .\n"
compileModule
- :: FilePath -> NeedsBuildOutput -> ChEntrypoint -> [String] -> IO Bool
-compileModule projdir nb ep opts = do
- setCurrentDirectory projdir
+ :: FilePath -> NeedsBuildOutput -> ChEntrypoint -> [FilePath] -> [String] -> IO Bool
+compileModule pkgdir nb ep srcdirs opts = do
+ cwd_before <- getCurrentDirectory
+ setCurrentDirectory pkgdir
+ flip E.finally (setCurrentDirectory cwd_before) $ do
- putStrLn $ "compiling:" ++ show ep ++ " (" ++ show nb ++ ")"
+ putStrLn $ "compiling: " ++ show ep ++ " (" ++ show nb ++ ")"
E.handle (\(ec :: ExitCode) -> print ec >> return False) $ do
@@ -202,28 +263,29 @@ compileModule projdir nb ep opts = do
(dflags2, _, _) <- parseDynamicFlags dflags1 (map noLoc opts)
_ <- setSessionDynFlags dflags2
- ts <- mapM (\t -> guessTarget t Nothing) $
+ ts <- mapM (\t -> guessTarget t Nothing) =<<
case ep of
- ChLibEntrypoint ms ms' ss -> map unChModuleName $ ms ++ ms' ++ ss
- ChExeEntrypoint m' ms ->
- let
-
- -- The options first clear out includes, then put in the build
- -- dir. We want the first one after that, so "regex-example" in
- -- the following case
- --
- -- ,"-i"
- -- ,"-idist/build/regex-example"
- -- ,"-iregex-example"
- firstInclude = drop 2 $ head $ drop 2 $ filter (isPrefixOf "-i") opts
- m = firstInclude </> m'
- in [m] ++ map unChModuleName ms
- ChSetupEntrypoint -> ["Setup.hs"]
+ ChLibEntrypoint ms ms' ss -> return $
+ map unChModuleName $ ms ++ ms' ++ ss
+ ChExeEntrypoint m ms -> do
+ -- TODO: this doesn't take preprocessor outputs in
+ -- dist/build/$pkg/$pkg-tmp/ into account.
+ m1 <- liftIO $ findFile srcdirs m
+ case m1 of
+ Just m2 -> return $ [m2] ++ map unChModuleName ms
+ Nothing -> error $ printf
+ "Couldn't find source file for Main module (%s), search path:\n\
+ \%s\n" m (show srcdirs)
+ ChSetupEntrypoint -> return $
+ -- TODO: this doesn't support Setup.lhs
+ ["Setup.hs"]
let ts' = case nb of
NoBuildOutput -> map (\t -> t { targetAllowObjCode = False }) ts
ProduceBuildOutput -> ts
+ liftIO $ putStrLn $ "targets: " ++ showPpr dflags2 ts'
+
setTargets ts'
_ <- load LoadAllTargets
@@ -239,9 +301,143 @@ compileModule projdir nb ep opts = do
liftIO $ print ExitSuccess
return True
+
+data CabalFile = CabalFile FilePath
+
+type ProjSetup0 = ProjSetupDescr (Either SkipReason (Ex ProjSetupImpl))
+type ProjSetup1 = ProjSetupDescr (Ex ProjSetupImpl)
+type ProjSetup2 pt = ProjSetupDescr (ProjSetupImpl pt)
+
+data ProjSetupDescr a =
+ ProjSetupDescr
+ { psdHeading :: !String
+ , psdImpl :: !a
+ } deriving (Functor)
+
+data ProjSetupImpl pt =
+ ProjSetupImpl
+ { psiProjType :: !(SProjType pt)
+ , psiDistDir :: !(FilePath -> DistDir pt)
+ , psiProjLoc :: !(CabalFile -> FilePath -> ProjLoc pt)
+ , psiConfigure :: !(FilePath -> IO ())
+ , psiBuild :: !(FilePath -> IO ())
+ , psiSdist :: !(FilePath -> FilePath -> IO ())
+ , psiQEmod :: !(QueryEnv pt -> QueryEnv pt)
+ }
+
+oldBuildProjSetup :: ProjSetup0
+oldBuildProjSetup = ProjSetupDescr "cabal-v1" $ Right $ Ex $ ProjSetupImpl
+ { psiProjType = SV1
+ , psiDistDir = \dir -> DistDirV1 (dir </> "dist")
+ , psiProjLoc = \(CabalFile cf) _projdir -> ProjLocCabalFile cf
+ , psiConfigure = \dir ->
+ runWithCwd dir "cabal" [ "configure" ]
+ , psiBuild = \dir ->
+ runWithCwd dir "cabal" [ "build" ]
+ , psiSdist = \srcdir destdir ->
+ copyMuliPackageProject srcdir destdir (\_ _ -> return ())
+ , psiQEmod = id
+ }
+
+newBuildProjSetup :: ProjSetup0
+newBuildProjSetup = ProjSetupDescr "cabal-v2" $ Right $ Ex $ ProjSetupImpl
+ { psiProjType = SV2
+ , psiDistDir = \dir -> DistDirV2 (dir </> "dist-newstyle")
+ , psiProjLoc = \_cabal_file projdir -> ProjLocV2File $ projdir </> "cabal.project"
+ -- TODO: check if cabal.project is there and only use
+ -- V2File then, also remove addCabalProject below so we
+ -- cover both cases.
+ , psiConfigure = \dir ->
+ runWithCwd dir "cabal" [ "new-configure" ]
+ , psiBuild = \dir ->
+ runWithCwd dir "cabal" [ "new-build" ]
+ , psiSdist = \srcdir destdir -> do
+ copyMuliPackageProject srcdir destdir $ \pkgsrc pkgdest -> do
+ exists <- doesFileExist (pkgsrc </> "cabal.project")
+ if exists then
+ copyFile (pkgsrc </> "cabal.project") (pkgdest </> "cabal.project")
+ else
+ addCabalProject pkgdest
+ , psiQEmod = id
+ }
+
+stackProjSetup :: Version -> ProjSetup0
+stackProjSetup ghcVer =
+ ProjSetupDescr "stack" $
+ let msg = SkipReason $ "missing stack_resolver_table entry for "++
+ showVersion ghcVer in
+ maybe (Left msg) Right $ do
+ res <- lookup ghcVer stack_resolver_table
+ let argsBefore = [ "--resolver="++res, "--system-ghc" ]
+ return $ Ex $ ProjSetupImpl
+ { psiProjType = SStack
+ , psiDistDir = \_dir -> DistDirStack Nothing
+ , psiProjLoc = \_cabal_file projdir ->
+ ProjLocStackYaml $ projdir </> "stack.yaml"
+ , psiConfigure = \dir ->
+ runWithCwd dir "stack" $ argsBefore ++ [ "build", "--dry-run" ]
+ , psiBuild = \dir ->
+ runWithCwd dir "stack" $ argsBefore ++ [ "build" ]
+ , psiSdist = \srcdir destdir -> do
+ copyMuliPackageProject srcdir destdir copyStackYamls
+ , psiQEmod = \qe ->
+ qe { qePrograms = (qePrograms qe)
+ { stackArgsBefore = argsBefore
+ }
+ }
+ }
+
+stack_resolver_table :: [(Version, String)]
+stack_resolver_table = map (parseVer *** ("lts-"++))
+ [ ("7.10.3", "6.35")
+ , ("8.0.1", "7.24")
+ , ("8.0.2", "9.21")
+ , ("8.2.2", "11.22")
+ , ("8.4.3", "12.14")
+ , ("8.4.4", "12.19")
+ ]
+
+copyStackYamls :: FilePath -> FilePath -> IO ()
+copyStackYamls srcdir destdir = do
+ files <- (\\ [".", ".."]) <$> getDirectoryContents srcdir
+ let ymls = filter (".yaml" `isSuffixOf`) $
+ filter ("stack-" `isPrefixOf`) $ files
+ forM_ ymls $ \filename -> copyFile (srcdir </> filename) (destdir </> filename)
+
+-- | For each Cabal package listed in a @packages.list@ file, copy the package
+-- to another directory while only including source files referenced in the
+-- cabal file.
+copyMuliPackageProject
+ :: FilePath -> FilePath -> (FilePath -> FilePath -> IO ()) -> IO ()
+copyMuliPackageProject srcdir destdir copyPkgExtra = do
+ let packages_file = srcdir </> "packages.list"
+ pkgdirs <- lines <$> readFile packages_file
+ forM_ pkgdirs $ \pkgdir -> do
+ runWithCwd (srcdir </> pkgdir) "cabal"
+ [ "act-as-setup", "--", "sdist"
+ , "--output-directory="++destdir </> pkgdir ]
+ copyPkgExtra (srcdir </> pkgdir) (destdir </> pkgdir)
+
unChModuleName :: ChModuleName -> String
unChModuleName (ChModuleName mn) = mn
+cabalInstallVersion :: IO Version
+cabalInstallVersion =
+ parseVer . trim <$> readProcess "cabal" ["--numeric-version"] ""
+
+ghcVersion :: IO Version
+ghcVersion =
+ parseVer . trim <$> readProcess "ghc" ["--numeric-version"] ""
+
+stackVersion :: IO Version
+stackVersion =
+ parseVer . trim <$> readProcess "stack" [ "--numeric-version" ] ""
+
+cabalInstallBuiltinCabalVersion :: IO Version
+cabalInstallBuiltinCabalVersion =
+ parseVer . trim <$> readProcess "cabal"
+ ["act-as-setup", "--", "--numeric-version"] ""
+
-- ---------------------------------------------------------------------
-- | Create and use a temporary directory in the system standard temporary directory.
--
diff --git a/tests/bkpregex/packages.list b/tests/bkpregex/packages.list
new file mode 100644
index 0000000..80e52ce
--- /dev/null
+++ b/tests/bkpregex/packages.list
@@ -0,0 +1 @@
+./
diff --git a/tests/bkpregex/stack.yaml b/tests/bkpregex/stack.yaml
new file mode 100644
index 0000000..27cc995
--- /dev/null
+++ b/tests/bkpregex/stack.yaml
@@ -0,0 +1,3 @@
+resolver: lts-0.0 # will be overridden on the commandline
+packages:
+- ./
diff --git a/tests/exeintlib/exeintlib.cabal b/tests/exeintlib/exeintlib.cabal
index 7507152..0d5bb7d 100644
--- a/tests/exeintlib/exeintlib.cabal
+++ b/tests/exeintlib/exeintlib.cabal
@@ -2,6 +2,7 @@ name: exeintlib
version: 0
build-type: Simple
cabal-version: >=2.0
+extra-source-files: stack.yaml
library
exposed-modules: Lib
diff --git a/tests/exeintlib/packages.list b/tests/exeintlib/packages.list
new file mode 100644
index 0000000..80e52ce
--- /dev/null
+++ b/tests/exeintlib/packages.list
@@ -0,0 +1 @@
+./
diff --git a/tests/exeintlib/stack.yaml b/tests/exeintlib/stack.yaml
new file mode 100644
index 0000000..27cc995
--- /dev/null
+++ b/tests/exeintlib/stack.yaml
@@ -0,0 +1,3 @@
+resolver: lts-0.0 # will be overridden on the commandline
+packages:
+- ./
diff --git a/tests/exelib/exelib.cabal b/tests/exelib/exelib.cabal
index 2422998..bd76dd4 100644
--- a/tests/exelib/exelib.cabal
+++ b/tests/exelib/exelib.cabal
@@ -2,6 +2,7 @@ name: exelib
version: 0
build-type: Simple
cabal-version: >=1.10
+extra-source-files: stack.yaml
library
exposed-modules: Lib
diff --git a/tests/exelib/packages.list b/tests/exelib/packages.list
new file mode 100644
index 0000000..80e52ce
--- /dev/null
+++ b/tests/exelib/packages.list
@@ -0,0 +1 @@
+./
diff --git a/tests/exelib/stack.yaml b/tests/exelib/stack.yaml
new file mode 100644
index 0000000..27cc995
--- /dev/null
+++ b/tests/exelib/stack.yaml
@@ -0,0 +1,3 @@
+resolver: lts-0.0 # will be overridden on the commandline
+packages:
+- ./
diff --git a/tests/fliblib/fliblib.cabal b/tests/fliblib/fliblib.cabal
index 4610605..21c0d61 100644
--- a/tests/fliblib/fliblib.cabal
+++ b/tests/fliblib/fliblib.cabal
@@ -2,6 +2,7 @@ name: fliblib
version: 0
build-type: Simple
cabal-version: >=1.10
+extra-source-files: stack.yaml
library
exposed-modules: Lib
diff --git a/tests/fliblib/packages.list b/tests/fliblib/packages.list
new file mode 100644
index 0000000..80e52ce
--- /dev/null
+++ b/tests/fliblib/packages.list
@@ -0,0 +1 @@
+./
diff --git a/tests/fliblib/stack.yaml b/tests/fliblib/stack.yaml
new file mode 100644
index 0000000..27cc995
--- /dev/null
+++ b/tests/fliblib/stack.yaml
@@ -0,0 +1,3 @@
+resolver: lts-0.0 # will be overridden on the commandline
+packages:
+- ./
diff --git a/tests/multipkg/.gitignore b/tests/multipkg/.gitignore
new file mode 100644
index 0000000..18add1c
--- /dev/null
+++ b/tests/multipkg/.gitignore
@@ -0,0 +1 @@
+/package-paths.list \ No newline at end of file
diff --git a/tests/multipkg/gen.sh b/tests/multipkg/gen.sh
new file mode 100644
index 0000000..670b94e
--- /dev/null
+++ b/tests/multipkg/gen.sh
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+printf '' > package-paths.list
+
+while read -r path name deps; do
+ mkdir -p "$path"
+ printf '%s\n' "$path" >> package-paths.list
+ cat > "$path/$name.cabal" <<EOF
+name: ${name}
+version: 0
+build-type: Simple
+cabal-version: >=1.10
+
+library
+ exposed-modules: Lib
+ build-depends: base, filepath, directory ${deps}
+ default-language: Haskell2010
+
+executable ${name}-exe
+ main-is: Exe.hs
+ build-depends: base, ${name} ${deps}
+ default-language: Haskell2010
+
+test-suite ${name}-test
+ type: exitcode-stdio-1.0
+ main-is: Exe.hs
+ build-depends: base, ${name} ${deps}
+
+benchmark ${name}-bench
+ type: exitcode-stdio-1.0
+ main-is: Exe.hs
+ build-depends: base, ${name} ${deps}
+EOF
+done <<EOF
+proj/ proj ,pkg-a,pkg-b,pkg-oot
+proj/pkg-a pkg-a
+proj/pkg-b pkg-b
+pkg-oot/ pkg-oot
+EOF
diff --git a/tests/multipkg/packages.list b/tests/multipkg/packages.list
new file mode 100644
index 0000000..e334777
--- /dev/null
+++ b/tests/multipkg/packages.list
@@ -0,0 +1,4 @@
+proj/
+proj/pkg-a
+proj/pkg-b
+pkg-oot/
diff --git a/tests/multipkg/pkg-oot/Exe.hs b/tests/multipkg/pkg-oot/Exe.hs
new file mode 100644
index 0000000..d5e55cc
--- /dev/null
+++ b/tests/multipkg/pkg-oot/Exe.hs
@@ -0,0 +1 @@
+main = putStrLn "Hello World!"
diff --git a/tests/multipkg/pkg-oot/Lib.hs b/tests/multipkg/pkg-oot/Lib.hs
new file mode 100644
index 0000000..b851fd6
--- /dev/null
+++ b/tests/multipkg/pkg-oot/Lib.hs
@@ -0,0 +1,2 @@
+module Lib where
+lib = ()
diff --git a/tests/multipkg/pkg-oot/pkg-oot.cabal b/tests/multipkg/pkg-oot/pkg-oot.cabal
new file mode 100644
index 0000000..8d61599
--- /dev/null
+++ b/tests/multipkg/pkg-oot/pkg-oot.cabal
@@ -0,0 +1,24 @@
+name: pkg-oot
+version: 0
+build-type: Simple
+cabal-version: >=1.10
+
+library
+ exposed-modules: Lib
+ build-depends: base, filepath, directory
+ default-language: Haskell2010
+
+executable pkg-oot-exe
+ main-is: Exe.hs
+ build-depends: base, pkg-oot
+ default-language: Haskell2010
+
+test-suite pkg-oot-test
+ type: exitcode-stdio-1.0
+ main-is: Exe.hs
+ build-depends: base, pkg-oot
+
+benchmark pkg-oot-bench
+ type: exitcode-stdio-1.0
+ main-is: Exe.hs
+ build-depends: base, pkg-oot
diff --git a/tests/multipkg/proj/Exe.hs b/tests/multipkg/proj/Exe.hs
new file mode 100644
index 0000000..d5e55cc
--- /dev/null
+++ b/tests/multipkg/proj/Exe.hs
@@ -0,0 +1 @@
+main = putStrLn "Hello World!"
diff --git a/tests/multipkg/proj/Lib.hs b/tests/multipkg/proj/Lib.hs
new file mode 100644
index 0000000..b851fd6
--- /dev/null
+++ b/tests/multipkg/proj/Lib.hs
@@ -0,0 +1,2 @@
+module Lib where
+lib = ()
diff --git a/tests/multipkg/proj/cabal.project b/tests/multipkg/proj/cabal.project
new file mode 100644
index 0000000..ecd97d5
--- /dev/null
+++ b/tests/multipkg/proj/cabal.project
@@ -0,0 +1 @@
+packages: ./ ./pkg-a ./pkg-b ../pkg-oot \ No newline at end of file
diff --git a/tests/multipkg/proj/pkg-a/Exe.hs b/tests/multipkg/proj/pkg-a/Exe.hs
new file mode 100644
index 0000000..d5e55cc
--- /dev/null
+++ b/tests/multipkg/proj/pkg-a/Exe.hs
@@ -0,0 +1 @@
+main = putStrLn "Hello World!"
diff --git a/tests/multipkg/proj/pkg-a/Lib.hs b/tests/multipkg/proj/pkg-a/Lib.hs
new file mode 100644
index 0000000..b851fd6
--- /dev/null
+++ b/tests/multipkg/proj/pkg-a/Lib.hs
@@ -0,0 +1,2 @@
+module Lib where
+lib = ()
diff --git a/tests/multipkg/proj/pkg-a/pkg-a.cabal b/tests/multipkg/proj/pkg-a/pkg-a.cabal
new file mode 100644
index 0000000..3fd83f6
--- /dev/null
+++ b/tests/multipkg/proj/pkg-a/pkg-a.cabal
@@ -0,0 +1,24 @@
+name: pkg-a
+version: 0
+build-type: Simple
+cabal-version: >=1.10
+
+library
+ exposed-modules: Lib
+ build-depends: base, filepath, directory
+ default-language: Haskell2010
+
+executable pkg-a-exe
+ main-is: Exe.hs
+ build-depends: base, pkg-a
+ default-language: Haskell2010
+
+test-suite pkg-a-test
+ type: exitcode-stdio-1.0
+ main-is: Exe.hs
+ build-depends: base, pkg-a
+
+benchmark pkg-a-bench
+ type: exitcode-stdio-1.0
+ main-is: Exe.hs
+ build-depends: base, pkg-a
diff --git a/tests/multipkg/proj/pkg-b/Exe.hs b/tests/multipkg/proj/pkg-b/Exe.hs
new file mode 100644
index 0000000..d5e55cc
--- /dev/null
+++ b/tests/multipkg/proj/pkg-b/Exe.hs
@@ -0,0 +1 @@
+main = putStrLn "Hello World!"
diff --git a/tests/multipkg/proj/pkg-b/Lib.hs b/tests/multipkg/proj/pkg-b/Lib.hs
new file mode 100644
index 0000000..b851fd6
--- /dev/null
+++ b/tests/multipkg/proj/pkg-b/Lib.hs
@@ -0,0 +1,2 @@
+module Lib where
+lib = ()
diff --git a/tests/multipkg/proj/pkg-b/pkg-b.cabal b/tests/multipkg/proj/pkg-b/pkg-b.cabal
new file mode 100644
index 0000000..b8d39e6
--- /dev/null
+++ b/tests/multipkg/proj/pkg-b/pkg-b.cabal
@@ -0,0 +1,24 @@
+name: pkg-b
+version: 0
+build-type: Simple
+cabal-version: >=1.10
+
+library
+ exposed-modules: Lib
+ build-depends: base, filepath, directory
+ default-language: Haskell2010
+
+executable pkg-b-exe
+ main-is: Exe.hs
+ build-depends: base, pkg-b
+ default-language: Haskell2010
+
+test-suite pkg-b-test
+ type: exitcode-stdio-1.0
+ main-is: Exe.hs
+ build-depends: base, pkg-b
+
+benchmark pkg-b-bench
+ type: exitcode-stdio-1.0
+ main-is: Exe.hs
+ build-depends: base, pkg-b
diff --git a/tests/multipkg/proj/proj.cabal b/tests/multipkg/proj/proj.cabal
new file mode 100644
index 0000000..80fd682
--- /dev/null
+++ b/tests/multipkg/proj/proj.cabal
@@ -0,0 +1,25 @@
+name: proj
+version: 0
+build-type: Simple
+cabal-version: >=1.10
+extra-source-files: stack.yaml
+
+library
+ exposed-modules: Lib
+ build-depends: base, filepath, directory ,pkg-a,pkg-b,pkg-oot
+ default-language: Haskell2010
+
+executable proj-exe
+ main-is: Exe.hs
+ build-depends: base, proj ,pkg-a,pkg-b,pkg-oot
+ default-language: Haskell2010
+
+test-suite proj-test
+ type: exitcode-stdio-1.0
+ main-is: Exe.hs
+ build-depends: base, proj ,pkg-a,pkg-b,pkg-oot
+
+benchmark proj-bench
+ type: exitcode-stdio-1.0
+ main-is: Exe.hs
+ build-depends: base, proj ,pkg-a,pkg-b,pkg-oot
diff --git a/tests/multipkg/proj/stack.yaml b/tests/multipkg/proj/stack.yaml
new file mode 100644
index 0000000..7e37d72
--- /dev/null
+++ b/tests/multipkg/proj/stack.yaml
@@ -0,0 +1,6 @@
+resolver: lts-0.0 # will be overridden on the commandline
+packages:
+- ./
+- ./pkg-a
+- ./pkg-b
+- ../pkg-oot