diff --git a/Cabal/Distribution/Simple/Program/Db.hs b/Cabal/Distribution/Simple/Program/Db.hs index d15368d6e16ddf86ab471b31dd49fd1d000dd783..18c35ce9b0956e8df46f0cea3af69993023a9bcc 100644 --- a/Cabal/Distribution/Simple/Program/Db.hs +++ b/Cabal/Distribution/Simple/Program/Db.hs @@ -52,6 +52,7 @@ module Distribution.Simple.Program.Db ( -- ** Query and manipulate the program db configureProgram, configureAllKnownPrograms, + unconfigureProgram, lookupProgramVersion, reconfigurePrograms, requireProgram, @@ -365,6 +366,13 @@ configurePrograms verbosity progs progdb = foldM (flip (configureProgram verbosity)) progdb progs +-- | Unconfigure a program. This is basically a hack and you shouldn't +-- use it, but it can be handy for making sure a 'requireProgram' +-- actually reconfigures. +unconfigureProgram :: String -> ProgramDb -> ProgramDb +unconfigureProgram progname = + updateConfiguredProgs $ Map.delete progname + -- | Try to configure all the known programs that have not yet been configured. -- configureAllKnownPrograms :: Verbosity diff --git a/cabal-testsuite/PackageTests/Exec/sandbox-hc-pkg.test.hs b/cabal-testsuite/PackageTests/Exec/sandbox-hc-pkg.test.hs index 5bf979e68211f63013799cdb126d698414800542..dee03490f88c31a2e9c23f2175c4b34937306842 100644 --- a/cabal-testsuite/PackageTests/Exec/sandbox-hc-pkg.test.hs +++ b/cabal-testsuite/PackageTests/Exec/sandbox-hc-pkg.test.hs @@ -1,5 +1,7 @@ import Test.Cabal.Prelude import Data.Maybe +import System.Directory +import Control.Monad.IO.Class main = cabalTest $ do withPackageDb $ do withSandbox $ do @@ -10,9 +12,10 @@ main = cabalTest $ do -- When run inside 'cabal-exec' the 'sandbox hc-pkg list' sub-command -- should find the library. env <- getTestEnv - -- TODO: libify me - let cabal_path = fromMaybe (error "No cabal-install path configured") - (testCabalInstallPath env) + -- NB: cabal_path might be relative, so we have to + -- turn it absolute + rel_cabal_path <- programPathM cabalProgram + cabal_path <- liftIO $ makeAbsolute rel_cabal_path cabal' "exec" ["sh", "--", "-c" , "cd subdir && " ++ show cabal_path ++ -- TODO: Ugh. Test abstractions leaking diff --git a/cabal-testsuite/Test/Cabal/Monad.hs b/cabal-testsuite/Test/Cabal/Monad.hs index dd872f56ed6d072a4d4334e00fc36d28a2f30b77..46e85d60c1ed8f17b67d9e951610010c92887a10 100644 --- a/cabal-testsuite/Test/Cabal/Monad.hs +++ b/cabal-testsuite/Test/Cabal/Monad.hs @@ -12,6 +12,9 @@ module Test.Cabal.Monad ( -- * Helper functions programPathM, requireProgramM, + isAvailableProgram, + hackageRepoToolProgram, + cabalProgram, -- * The test environment TestEnv(..), getTestEnv, @@ -54,7 +57,6 @@ import Distribution.Simple.Configure ( getPersistBuildConfig, configCompilerEx ) import Distribution.Types.LocalBuildInfo - import Distribution.Verbosity import qualified Control.Exception as E @@ -71,10 +73,10 @@ import System.IO.Error (isDoesNotExistError) import Options.Applicative data CommonArgs = CommonArgs { - argCabalInstallPath :: Maybe FilePath, - argGhcPath :: Maybe FilePath, - argHackageRepoToolPath :: FilePath, - argSkipSetupTests :: Bool + argCabalInstallPath :: Maybe FilePath, + argGhcPath :: Maybe FilePath, + argHackageRepoToolPath :: Maybe FilePath, + argSkipSetupTests :: Bool } commonArgParser :: Parser CommonArgs @@ -90,25 +92,24 @@ commonArgParser = CommonArgs <> long "with-ghc" <> metavar "PATH" )) - <*> option str + <*> optional (option str ( help "Path to hackage-repo-tool to use for repository manipulation" <> long "with-hackage-repo-tool" <> metavar "PATH" - <> value "hackage-repo-tool" - ) + )) <*> switch (long "skip-setup-tests" <> help "Skip setup tests") renderCommonArgs :: CommonArgs -> [String] renderCommonArgs args = - maybe [] (\x -> ["--with-cabal", x]) (argCabalInstallPath args) ++ - maybe [] (\x -> ["--with-ghc", x]) (argGhcPath args) ++ - ["--with-hackage-repo-tool", argHackageRepoToolPath args] ++ + maybe [] (\x -> ["--with-cabal", x]) (argCabalInstallPath args) ++ + maybe [] (\x -> ["--with-ghc", x]) (argGhcPath args) ++ + maybe [] (\x -> ["--with-hackage-repo-tool", x]) (argHackageRepoToolPath args) ++ (if argSkipSetupTests args then ["--skip-setup-tests"] else []) data TestArgs = TestArgs { - testArgDistDir :: FilePath, + testArgDistDir :: FilePath, testArgScriptPath :: FilePath, - testCommonArgs :: CommonArgs + testCommonArgs :: CommonArgs } testArgParser :: Parser TestArgs @@ -153,15 +154,14 @@ unexpectedSuccessExitCode = 66 setupAndCabalTest :: TestM () -> IO () setupAndCabalTest m = runTestM $ do env <- getTestEnv - skipIf (testSkipSetupTests env && isNothing (testCabalInstallPath env)) + have_cabal <- isAvailableProgram cabalProgram + skipIf (testSkipSetupTests env && not have_cabal) when (not (testSkipSetupTests env)) $ do liftIO $ putStrLn "Test with Setup:" m - case testCabalInstallPath env of - Nothing -> return () - Just _ -> do - liftIO $ putStrLn "Test with cabal-install:" - withReaderT (\nenv -> nenv { testCabalInstallAsSetup = True }) m + when have_cabal $ do + liftIO $ putStrLn "Test with cabal-install:" + withReaderT (\nenv -> nenv { testCabalInstallAsSetup = True }) m setupTest :: TestM () -> IO () setupTest m = runTestM $ do @@ -171,12 +171,20 @@ setupTest m = runTestM $ do cabalTest :: TestM () -> IO () cabalTest m = runTestM $ do - env <- getTestEnv - skipIf (isNothing (testCabalInstallPath env)) + skipUnless =<< isAvailableProgram cabalProgram withReaderT (\nenv -> nenv { testCabalInstallAsSetup = True }) m type TestM = ReaderT TestEnv IO +hackageRepoToolProgram :: Program +hackageRepoToolProgram = simpleProgram "hackage-repo-tool" + +cabalProgram :: Program +cabalProgram = (simpleProgram "cabal") { + -- Do NOT search for executable named cabal + programFindLocation = \_ _ -> return Nothing + } + -- | Run a test in the test monad according to program's arguments. runTestM :: TestM () -> IO () runTestM m = do @@ -189,20 +197,61 @@ runTestM m = do lbi <- getPersistBuildConfig dist_dir let verbosity = normal -- TODO: configurable senv <- mkScriptEnv verbosity lbi - (program_db, db_stack) <- case argGhcPath (testCommonArgs args) of - Nothing -> return (withPrograms lbi, withPackageDB lbi) + -- Add test suite specific programs + let program_db0 = + addKnownPrograms + ([hackageRepoToolProgram, cabalProgram] ++ builtinPrograms) + (withPrograms lbi) + -- Reconfigure according to user flags + let cargs = testCommonArgs args + program_db1 <- + reconfigurePrograms verbosity + ([("cabal", p) | p <- maybeToList (argCabalInstallPath cargs)] ++ + [("ghc", p) | p <- maybeToList (argGhcPath cargs)] ++ + [("hackage-repo-tool", p) + | p <- maybeToList (argHackageRepoToolPath cargs)]) + [] -- --prog-options not supported ATM + program_db0 + + -- Reconfigure the rest of GHC + program_db <- case argGhcPath cargs of + Nothing -> return program_db1 Just ghc_path -> do + -- All the things that get updated paths from + -- configCompilerEx. The point is to make sure + -- we reconfigure these when we need them. + let program_db2 = unconfigureProgram "ghc" + . unconfigureProgram "ghc-pkg" + . unconfigureProgram "hsc2hs" + . unconfigureProgram "haddock" + . unconfigureProgram "hpc" + . unconfigureProgram "runghc" + . unconfigureProgram "gcc" + . unconfigureProgram "ld" + . unconfigureProgram "ar" + . unconfigureProgram "strip" + $ program_db1 (_, _, program_db) <- configCompilerEx (Just (compilerFlavor (compiler lbi))) (Just ghc_path) Nothing - defaultProgramDb -- don't use lbi; it won't reconfigure + program_db2 verbosity - -- TODO: configurable - let db_stack = [GlobalPackageDB] - return (program_db, db_stack) - let env = TestEnv { + -- TODO: this actually leaves a pile of things unconfigured. + -- Optimal strategy for us is to lazily configure them, so + -- we don't pay for things we don't need. A bit difficult + -- to do in the current design. + return program_db + + let db_stack = + case argGhcPath (testCommonArgs args) of + Nothing -> withPackageDB lbi + -- Can't use the build package db stack since they + -- are all for the wrong versions! TODO: Make + -- this configurable + Just _ -> [GlobalPackageDB] + env = TestEnv { testSourceDir = script_dir, testSubName = script_base, testProgramDb = program_db, @@ -211,8 +260,6 @@ runTestM m = do testMtimeChangeDelay = Nothing, testScriptEnv = senv, testSetupPath = dist_dir </> "setup" </> "setup", - testCabalInstallPath = argCabalInstallPath (testCommonArgs args), - testHackageRepoToolPath = argHackageRepoToolPath (testCommonArgs args), testSkipSetupTests = argSkipSetupTests (testCommonArgs args), testEnvironment = -- Try to avoid Unicode output @@ -253,6 +300,18 @@ programPathM :: Program -> TestM FilePath programPathM program = do fmap programPath (requireProgramM program) +isAvailableProgram :: Program -> TestM Bool +isAvailableProgram program = do + env <- getTestEnv + case lookupProgram program (testProgramDb env) of + Just _ -> return True + Nothing -> do + -- It might not have been configured. Try to configure. + progdb <- liftIO $ configureProgram (testVerbosity env) program (testProgramDb env) + case lookupProgram program progdb of + Just _ -> return True + Nothing -> return False + -- | Run an IO action, and suppress a "does not exist" error. onlyIfExists :: MonadIO m => IO () -> m () onlyIfExists m = @@ -285,13 +344,6 @@ data TestEnv = TestEnv , testScriptEnv :: ScriptEnv -- | Setup script path , testSetupPath :: FilePath - -- | cabal-install path (or Nothing if we are not testing - -- cabal-install). NB: This does NOT default to @cabal@ in PATH as - -- this is unlikely to be the cabal you want to test. - , testCabalInstallPath :: Maybe FilePath - -- | hackage-repo-tool path (defaults to hackage-repo-tool found in - -- PATH) - , testHackageRepoToolPath :: FilePath -- | Skip Setup tests? , testSkipSetupTests :: Bool diff --git a/cabal-testsuite/Test/Cabal/Prelude.hs b/cabal-testsuite/Test/Cabal/Prelude.hs index dc0889ae41913b42156a4c2b57b9e94bc1f5b5d3..6e068c697479758279f0729aa4d7224f183b0cbb 100644 --- a/cabal-testsuite/Test/Cabal/Prelude.hs +++ b/cabal-testsuite/Test/Cabal/Prelude.hs @@ -135,8 +135,7 @@ setup' cmd args = do full_args = cmd : ["-v", "--distdir", rel_dist_dir] ++ args' r <- if testCabalInstallAsSetup env - then runM (fromMaybe (error "No cabal-install path configured") - (testCabalInstallPath env)) full_args + then runProgramM cabalProgram full_args else do pdfile <- liftIO $ tryFindPackageDesc (testCurrentDir env) pdesc <- liftIO $ readPackageDescription (testVerbosity env) pdfile @@ -262,13 +261,7 @@ cabal_sandbox' cmd args = do cabal_raw' :: [String] -> TestM Result cabal_raw' cabal_args = do - env <- getTestEnv - r <- liftIO $ run (testVerbosity env) - (Just (testCurrentDir env)) - (testEnvironment env) - (fromMaybe (error "No cabal-install path configured") - (testCabalInstallPath env)) - cabal_args + r <- runProgramM cabalProgram cabal_args record r requireSuccess r @@ -426,8 +419,7 @@ hackageRepoTool cmd args = void $ hackageRepoTool' cmd args hackageRepoTool' :: String -> [String] -> TestM Result hackageRepoTool' cmd args = do - env <- getTestEnv - r <- runM (testHackageRepoToolPath env) (cmd : args) + r <- runProgramM hackageRepoToolProgram (cmd : args) record r _ <- requireSuccess r return r @@ -456,6 +448,10 @@ infixr 4 `archiveTo` withRepo :: FilePath -> TestM a -> TestM a withRepo repo_dir m = do env <- getTestEnv + + -- Check if hackage-repo-tool is available, and skip if not + skipUnless =<< isAvailableProgram hackageRepoToolProgram + -- 1. Generate keys hackageRepoTool "create-keys" ["--keys", testKeysDir env] -- 2. Initialize repo directory