diff options
author | Daniel Gröber <dxld@darkboxed.org> | 2019-01-22 00:34:05 +0100 |
---|---|---|
committer | Daniel Gröber <dxld@darkboxed.org> | 2019-01-26 02:59:23 +0100 |
commit | 541d219dbcf097c0c50b4ee0216f270c9c8c1342 (patch) | |
tree | d4c15bf12e74d3bc4be880c20b176045e1d961f1 | |
parent | a6a20f17279e31e35861d52a16232897915918fc (diff) |
Add support and test coverage for mulit-pkg projects
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 |