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>

Reply via email to