--- Begin Message ---
Package: ghc6
Version: 6.6.1-2
Severity: wishlist
Tags: patch
Hi,
It would be nice if there was a simple way to build and run tests for
Cabalized packages.
Cabal provides a "test" target, but by default it does nothing.
Furthermore, you can't really build test cases using the Cabal
infrastructure, since any executables that you list get installed.
Searching on Google for how to integrate a test suite into Cabal turns
up suggestions such as "make a system() call from runTests to
The attached patch adds two new flags to the build information for
executables and libraries:
* do-not-install: if set to True, keeps an executable that it's set on
from being installed. This is necessary to keep test suites from
ending up in $prefix/bin, but may be useful for other build-time
utilities.
* is-test: if set to True on an executable, the executable will be
invoked by the "test" target of the setup script. Note that this
doesn't attempt to be at all smart about building the executable(s);
it just blindly invokes the test command(s) and returns a failure if
any of them fail.
The patch should be fairly straightforward. The need to do suppression
of installing executables in compiler-specific code is a bit ugly;
this could maybe be cleaned up with an equivalent to withExe that drops
non-installed executables and by writing and using a similar routine for
libraries.
This also changes the API of Cabal: runTests now takes an integer as
its first argument, indicating the verbosity level provided as an
argument on the command-line. The Boolean that was passed before
didn't have any purpose I could see and was always False, so it
shouldn't be hard to adapt existing code to this change. On the other
hand, the API can be preserved by just hard-coding a verbosity level.
Daniel
-- System Information:
Debian Release: lenny/sid
APT prefers unstable
APT policy: (500, 'unstable'), (500, 'stable')
Architecture: i386 (i686)
Kernel: Linux 2.6.22-3-686 (SMP w/1 CPU core)
Locale: LANG=en_US.UTF-8, LC_CTYPE=en_US.UTF-8 (charmap=UTF-8) (ignored: LC_ALL
set to en_US.UTF-8)
Shell: /bin/sh linked to /bin/bash
Versions of packages ghc6 depends on:
ii gcc 4:4.2.2-1 The GNU C compiler
ii haskell-utils 1.9 Utilities used by the Debian Haske
ii libc6 2.7-5 GNU C Library: Shared libraries
ii libgmp3-dev 2:4.2.2+dfsg-1 Multiprecision arithmetic library
ii libgmp3c2 2:4.2.2+dfsg-1 Multiprecision arithmetic library
ii libncurses5 5.6+20071215-1 Shared libraries for terminal hand
ii libreadline5 5.2-3 GNU readline and history libraries
ii libreadline5-dev 5.2-3 GNU readline and history libraries
ii perl [perl5] 5.8.8-12 Larry Wall's Practical Extraction
ghc6 recommends no packages.
-- no debconf information
diff -x configure -x config.status -x config.guess -x config.sub -Nru ghc6-6.6.1.orig/libraries/Cabal/Cabal.cabal ghc6-6.6.1/libraries/Cabal/Cabal.cabal
--- ghc6-6.6.1.orig/libraries/Cabal/Cabal.cabal 2007-04-25 10:23:22.000000000 -0700
+++ ghc6-6.6.1/libraries/Cabal/Cabal.cabal 2007-12-31 17:31:56.000000000 -0800
@@ -42,6 +42,7 @@
Distribution.Simple.LocalBuildInfo,
Distribution.Simple.NHC,
Distribution.Simple.Register,
+ Distribution.Simple.RunTests,
Distribution.Simple.SrcDist,
Distribution.Simple.Utils,
Distribution.Version,
diff -x configure -x config.status -x config.guess -x config.sub -Nru ghc6-6.6.1.orig/libraries/Cabal/Distribution/PackageDescription.hs ghc6-6.6.1/libraries/Cabal/Distribution/PackageDescription.hs
--- ghc6-6.6.1.orig/libraries/Cabal/Distribution/PackageDescription.hs 2007-04-25 10:23:22.000000000 -0700
+++ ghc6-6.6.1/libraries/Cabal/Distribution/PackageDescription.hs 2007-12-31 17:45:51.000000000 -0800
@@ -213,6 +213,8 @@
-- Consider refactoring into executable and library versions.
data BuildInfo = BuildInfo {
buildable :: Bool, -- ^ component is buildable here
+ doNotInstall :: Bool, -- ^ set to true to suppress installing an exe or library
+ isTest :: Bool, -- ^ set to true to run an executable from the test target
ccOptions :: [String], -- ^ options for C compiler
ldOptions :: [String], -- ^ options for linker
frameworks :: [String], -- ^support frameworks for Mac OS X
@@ -233,6 +235,8 @@
emptyBuildInfo :: BuildInfo
emptyBuildInfo = BuildInfo {
buildable = True,
+ doNotInstall = False,
+ isTest = False,
ccOptions = [],
ldOptions = [],
frameworks = [],
@@ -322,6 +326,8 @@
unionBuildInfo :: BuildInfo -> BuildInfo -> BuildInfo
unionBuildInfo b1 b2
= b1{buildable = buildable b1 && buildable b2,
+ doNotInstall = doNotInstall b1 && doNotInstall b2,
+ isTest = isTest b1 && isTest b2,
ccOptions = combine ccOptions,
ldOptions = combine ldOptions,
frameworks = combine frameworks,
@@ -443,9 +449,12 @@
binfoFields :: [StanzaField BuildInfo]
binfoFields =
- [ simpleField "buildable"
- (text . show) parseReadS
+ [ boolField "buildable"
buildable (\val binfo -> binfo{buildable=val})
+ , boolField "do-not-install"
+ doNotInstall (\val binfo -> binfo{doNotInstall=val})
+ , boolField "is-test"
+ isTest (\val binfo -> binfo{isTest=val})
, listField "cc-options"
showToken parseTokenQ
ccOptions (\val binfo -> binfo{ccOptions=val})
diff -x configure -x config.status -x config.guess -x config.sub -Nru ghc6-6.6.1.orig/libraries/Cabal/Distribution/ParseUtils.hs ghc6-6.6.1/libraries/Cabal/Distribution/ParseUtils.hs
--- ghc6-6.6.1.orig/libraries/Cabal/Distribution/ParseUtils.hs 2007-04-25 10:23:22.000000000 -0700
+++ ghc6-6.6.1/libraries/Cabal/Distribution/ParseUtils.hs 2007-12-31 17:25:04.000000000 -0800
@@ -53,7 +53,7 @@
parsePackageNameQ, parseVersionRangeQ,
parseTestedWithQ, parseLicenseQ, parseExtensionQ, parseCommaList, parseOptCommaList,
showFilePath, showToken, showTestedWith, showDependency, showFreeText,
- liftField, simpleField, listField, commaListField, optsField,
+ liftField, simpleField, boolField, listField, commaListField, optsField,
parseReadS, parseQuoted,
) where
@@ -140,6 +140,9 @@
x <- runP lineNo name readF val
return (set x st))
+boolField :: String -> (a -> Bool) -> (Bool -> a -> a) -> StanzaField a
+boolField name get set = simpleField name (text . show) parseReadS get set
+
commaListField :: String -> (a -> Doc) -> (ReadP [a] a) -> (b -> [a]) -> ([a] -> b -> b) -> StanzaField b
commaListField name showF readF get set = StanzaField name
(\st -> fsep (punctuate comma (map showF (get st))))
diff -x configure -x config.status -x config.guess -x config.sub -Nru ghc6-6.6.1.orig/libraries/Cabal/Distribution/Simple/GHC.hs ghc6-6.6.1/libraries/Cabal/Distribution/Simple/GHC.hs
--- ghc6-6.6.1.orig/libraries/Cabal/Distribution/Simple/GHC.hs 2007-04-25 10:23:22.000000000 -0700
+++ ghc6-6.6.1/libraries/Cabal/Distribution/Simple/GHC.hs 2007-12-31 17:28:26.000000000 -0800
@@ -362,8 +362,8 @@
-> FilePath -- ^Build location
-> PackageDescription -> IO ()
installExe verbose pref buildPref pkg_descr
- = do createDirectoryIfMissing True pref
- withExe pkg_descr $ \ (Executable e _ b) -> do
+ = do withExe pkg_descr $ \ (Executable e _ b) -> when (not $ doNotInstall b) $ do
+ createDirectoryIfMissing True pref
let exeName = e `joinFileExt` exeExtension
copyFileVerbose verbose (buildPref `joinFileName` e `joinFileName` exeName) (pref `joinFileName` exeName)
@@ -379,7 +379,8 @@
installLib verbose programConf hasVanilla hasProf hasGHCi pref buildPref
pd@PackageDescription{library=Just l,
package=p}
- = do ifVanilla $ smartCopySources verbose [buildPref] pref (libModules pd) ["hi"] True False
+ = when (not $ doNotInstall $ libBuildInfo l) $ do
+ ifVanilla $ smartCopySources verbose [buildPref] pref (libModules pd) ["hi"] True False
ifProf $ smartCopySources verbose [buildPref] pref (libModules pd) ["p_hi"] True False
let libTargetLoc = mkLibName pref (showPackageId p)
profLibTargetLoc = mkProfLibName pref (showPackageId p)
diff -x configure -x config.status -x config.guess -x config.sub -Nru ghc6-6.6.1.orig/libraries/Cabal/Distribution/Simple/Hugs.hs ghc6-6.6.1/libraries/Cabal/Distribution/Simple/Hugs.hs
--- ghc6-6.6.1.orig/libraries/Cabal/Distribution/Simple/Hugs.hs 2007-04-25 10:23:22.000000000 -0700
+++ ghc6-6.6.1/libraries/Cabal/Distribution/Simple/Hugs.hs 2007-12-31 17:28:05.000000000 -0800
@@ -306,13 +306,14 @@
-> PackageDescription
-> IO ()
install verbose libDir installProgDir binDir targetProgDir buildPref pkg_descr = do
- withLib pkg_descr () $ \ libInfo -> do
+ withLib pkg_descr () $ \ libInfo -> when (not $ doNotInstall $ libBuildInfo libInfo) $ do
try $ removeDirectoryRecursive libDir
smartCopySources verbose [buildPref] libDir (libModules pkg_descr) hugsInstallSuffixes True False
- let buildProgDir = buildPref `joinFileName` "programs"
- when (any (buildable . buildInfo) (executables pkg_descr)) $
+ let buildProgDir = buildPref `joinFileName` "programs"
+ okToInstall bInfo = buildable bInfo && not (doNotInstall bInfo)
+ when (any (okToInstall . buildInfo) (executables pkg_descr)) $
createDirectoryIfMissing True binDir
- withExe pkg_descr $ \ exe -> do
+ withExe pkg_descr $ \ exe -> when (okToInstall $ buildInfo exe) $ do
let buildDir = buildProgDir `joinFileName` exeName exe
let installDir = installProgDir `joinFileName` exeName exe
let targetDir = targetProgDir `joinFileName` exeName exe
diff -x configure -x config.status -x config.guess -x config.sub -Nru ghc6-6.6.1.orig/libraries/Cabal/Distribution/Simple/JHC.hs ghc6-6.6.1/libraries/Cabal/Distribution/Simple/JHC.hs
--- ghc6-6.6.1.orig/libraries/Cabal/Distribution/Simple/JHC.hs 2007-04-25 10:23:21.000000000 -0700
+++ ghc6-6.6.1/libraries/Cabal/Distribution/Simple/JHC.hs 2007-12-31 17:25:23.000000000 -0800
@@ -111,13 +111,13 @@
]
installLib :: Int -> FilePath -> FilePath -> PackageDescription -> Library -> IO ()
-installLib verb dest build pkg_descr _ = do
+installLib verb dest build pkg_descr lib = when (not $ doNotInstall $ libBuildInfo lib) $ do
let p = showPackageId (package pkg_descr)++".hl"
createDirectoryIfMissing True dest
copyFileVerbose verb (joinFileName build p) (joinFileName dest p)
installExe :: Int -> FilePath -> FilePath -> PackageDescription -> Executable -> IO ()
-installExe verb dest build pkg_descr exe = do
+installExe verb dest build pkg_descr exe = when (not $ doNotInstall $ buildInfo exe) $ do
let out = exeName exe `joinFileName` exeExtension
createDirectoryIfMissing True dest
copyFileVerbose verb (joinFileName build out) (joinFileName dest out)
diff -x configure -x config.status -x config.guess -x config.sub -Nru ghc6-6.6.1.orig/libraries/Cabal/Distribution/Simple/RunTests.hs ghc6-6.6.1/libraries/Cabal/Distribution/Simple/RunTests.hs
--- ghc6-6.6.1.orig/libraries/Cabal/Distribution/Simple/RunTests.hs 1969-12-31 16:00:00.000000000 -0800
+++ ghc6-6.6.1/libraries/Cabal/Distribution/Simple/RunTests.hs 2007-12-31 18:47:44.000000000 -0800
@@ -0,0 +1,50 @@
+-- |
+-- Module : RunTests
+-- Copyright : Daniel Burrows 2007
+-- Maintainer : Daniel Burrows <[email protected]>
+-- Stability : alpha
+-- Portability : portable
+--
+-- Explanation: Support for running tests from the "test" target or at
+-- other points in the build.
+
+module Distribution.Simple.RunTests where
+
+import Data.List ( foldl' )
+
+import Distribution.Compat.FilePath
+ ( joinFileName, exeExtension, joinFileExt )
+import Distribution.PackageDescription
+ ( BuildInfo(..),
+ Executable(..),
+ PackageDescription(..) )
+import Distribution.Simple.LocalBuildInfo
+ ( LocalBuildInfo(..) )
+import Distribution.Simple.Utils
+ ( rawSystemVerbose )
+
+import System.Cmd
+import System.Exit(ExitCode(..), exitWith)
+
+-- | Run all the executables flagged in the .cabal file via 'isTest'.
+--
+-- All tests are run even if one fails; the first failing error code
+-- is returned. Executables that have not been built will fail with
+-- \"command not found\" or the system equivalent.
+doTests :: Int -> PackageDescription -> LocalBuildInfo -> IO ExitCode
+doTests verbose pkgDesc lbi =
+ do ecs <- sequence [invokeTest exe
+ | exe <- (executables pkgDesc),
+ isTest $ buildInfo exe]
+ return $ foldl' combineExitCodes ExitSuccess ecs
+ where
+ -- The builder creates an executable named
+ -- $buildDir/$exeName/$exeName$exeExtension.
+ invokeTest exe =
+ let exeBase = (exeName exe) `joinFileExt` exeExtension
+ buildPath = (buildDir lbi) `joinFileName` (exeName exe)
+ exePath = buildPath `joinFileName` exeBase in
+ rawSystemVerbose verbose exePath []
+
+ combineExitCodes ExitSuccess e2 = e2
+ combineExitCodes e1 _ = e1
diff -x configure -x config.status -x config.guess -x config.sub -Nru ghc6-6.6.1.orig/libraries/Cabal/Distribution/Simple.hs ghc6-6.6.1/libraries/Cabal/Distribution/Simple.hs
--- ghc6-6.6.1.orig/libraries/Cabal/Distribution/Simple.hs 2007-04-25 10:23:22.000000000 -0700
+++ ghc6-6.6.1/libraries/Cabal/Distribution/Simple.hs 2007-12-31 18:41:36.000000000 -0800
@@ -82,6 +82,7 @@
writeInstalledConfig, installedPkgConfigFile,
regScriptLocation, unregScriptLocation
)
+import Distribution.Simple.RunTests ( doTests )
import Distribution.Simple.Configure(getPersistBuildConfig, maybeGetPersistBuildConfig,
findProgram, configure, writePersistBuildConfig,
@@ -130,7 +131,7 @@
-- to specify additional preprocessors.
data UserHooks = UserHooks
{
- runTests :: Args -> Bool -> PackageDescription -> LocalBuildInfo -> IO ExitCode, -- ^Used for @.\/setup test@
+ runTests :: Args -> Int -> PackageDescription -> LocalBuildInfo -> IO ExitCode, -- ^Used for @.\/setup test@
readDesc :: IO (Maybe PackageDescription), -- ^Read the description file
hookedPreProcessors :: [ PPSuffixHandler ],
-- ^Custom preprocessors in addition to and overriding 'knownSuffixHandlers'.
@@ -348,7 +349,7 @@
case hooks of
Nothing -> return ExitSuccess
Just h -> do localbuildinfo <- getPersistBuildConfig
- out <- (runTests h) args False pkg_descr_in localbuildinfo
+ out <- (runTests h) args verbose pkg_descr_in localbuildinfo
when (isFailure out) (exitWith out)
return out
@@ -613,6 +614,7 @@
defaultUserHooks
= emptyUserHooks
{
+ runTests = defaultRunTests,
postConf = defaultPostConf,
confHook = configure,
preBuild = readHook buildVerbose,
@@ -669,6 +671,9 @@
register pkg_descr localbuildinfo
emptyRegisterFlags{ regUser=uInstFlag, regVerbose=verbose }
+defaultRunTests :: Args -> Int -> PackageDescription -> LocalBuildInfo -> IO ExitCode
+defaultRunTests _ = doTests
+
defaultBuildHook :: PackageDescription -> LocalBuildInfo
-> Maybe UserHooks -> BuildFlags -> IO ()
defaultBuildHook pkg_descr localbuildinfo hooks flags = do
diff -x configure -x config.status -x config.guess -x config.sub -Nru ghc6-6.6.1.orig/libraries/Cabal/doc/Cabal.xml ghc6-6.6.1/libraries/Cabal/doc/Cabal.xml
--- ghc6-6.6.1.orig/libraries/Cabal/doc/Cabal.xml 2007-04-25 10:23:22.000000000 -0700
+++ ghc6-6.6.1/libraries/Cabal/doc/Cabal.xml 2007-12-31 18:26:54.000000000 -0800
@@ -652,6 +652,36 @@
</listitem>
</varlistentry>
+ <varlistentry>
+ <term>
+ <literal>do-not-install:</literal> <replaceable>Boolean</replaceable>
+ (default: <literal>False</literal>)
+ </term>
+
+ <listitem>
+ <para>Should the component be installed? Setting this
+ flag to <literal>True</literal> will prevent the
+ executable or library from being installed by the
+ <quote><literal>install</literal></quote> target of the
+ setup script. A typical use case is a test suite that
+ should be built but not installed.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <literal>is-test:</literal> <replaceable>Boolean</replaceable>
+ (default: <literal>False</literal>)
+ </term>
+
+ <listitem>
+ <para>Setting this flag to <literal>True</literal> will
+ cause the executable to be invoked by the
+ <quote><literal>test</literal></quote> target of the
+ setup script.</para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term>
<literal>other-modules:</literal>
--- End Message ---