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 [EMAIL PROTECTED] 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>