-----------------------------------------------------------------------------
-- |
-- Module : Distribution.SetupWrapper
-- Copyright : (c) The University of Glasgow 2006
--
-- Maintainer : http://hackage.haskell.org/trac/hackage
-- Stability : alpha
-- Portability : portable
--
-- The user interface to building and installing Cabal packages.
-- If the @Built-Type@ field is specified as something other than
-- 'Custom', and the current version of Cabal is acceptable, this performs
-- setup actions directly. Otherwise it builds the setup script and
-- runs it with the given arguments.
module Distribution.SetupWrapper (setupWrapper) where
import qualified Distribution.Make as Make
import Distribution.Simple
import Distribution.Simple.Utils
import Distribution.Simple.Configure
( configCompiler, getInstalledPackages,
configDependency )
import Distribution.Setup ( reqPathArg )
import Distribution.PackageDescription
( readPackageDescription,
PackageDescription(..),
BuildType(..), cabalVersion )
import System.Console.GetOpt
import System.Directory
import Distribution.Compat.Exception ( finally )
import Distribution.Verbosity
import System.FilePath (pathSeparator)
import Control.Monad ( when, unless )
-- read the .cabal file
-- - attempt to find the version of Cabal required
-- if the Cabal file specifies the build type (not Custom),
-- - behave like a boilerplate Setup.hs of that type
-- otherwise,
-- - if we find GHC,
-- - build the Setup script with the right version of Cabal
-- - invoke it with args
-- - if we find runhaskell (TODO)
-- - use runhaskell to invoke it
--
-- Later:
-- - add support for multiple packages, by figuring out
-- dependencies here and building/installing the sub packages
-- in the right order.
setupWrapper ::
[String] -- ^ Command-line arguments.
-> Maybe FilePath -- ^ Directory to run in. If 'Nothing', the current directory is used.
-> IO ()
setupWrapper args mdir = inDir mdir $ do
let (flag_fn, non_opts, unrec_opts, errs) = getOpt' Permute opts args
when (not (null errs)) $ die (unlines errs)
let flags = foldr (.) id flag_fn defaultFlags
let setup_args = unrec_opts ++ non_opts
pkg_descr_file <- defaultPackageDesc (verbosity flags)
pkg_descr <- readPackageDescription (verbosity flags) pkg_descr_file
comp <- configCompiler (Just GHC) (withCompiler flags) (withHcPkg flags)
normal
cabal_flag <- configCabalFlag flags (descCabalVersion pkg_descr) comp
let
trySetupScript f on_fail = do
b <- doesFileExist f
if not b then on_fail else do
hasSetup <- do b' <- doesFileExist "setup"
if not b' then return False else do
t1 <- getModificationTime f
t2 <- getModificationTime "setup"
return (t1 < t2)
unless hasSetup $
rawSystemExit (verbosity flags)
(compilerPath comp)
(cabal_flag ++
["--make", f, "-o", "setup", "-v"++showForGHC (verbosity flags)])
rawSystemExit (verbosity flags)
('.':pathSeparator:"setup")
setup_args
case lookup (buildType pkg_descr) buildTypes of
Just (mainAction, mainText) ->
if withinRange cabalVersion (descCabalVersion pkg_descr)
then mainAction setup_args -- current version is OK, so no need
-- to compile a special Setup.hs.
else do writeFile ".Setup.hs" mainText
trySetupScript ".Setup.hs" $ error "panic! shouldn't happen"
Nothing ->
trySetupScript "Setup.hs" $
trySetupScript "Setup.lhs" $
die "no special Build-Type, but lacks Setup.hs or Setup.lhs"
buildTypes :: [(BuildType, ([String] -> IO (), String))]
buildTypes = [
(Simple, (defaultMainArgs, "import Distribution.Simple; main=defaultMain")),
(Configure, (defaultMainWithHooksArgs defaultUserHooks,
"import Distribution.Simple; main=defaultMainWithHooks defaultUserHooks")),
(Make, (Make.defaultMainArgs, "import Distribution.Make; main=defaultMain"))]
inDir :: Maybe FilePath -> IO () -> IO ()
inDir Nothing m = m
inDir (Just d) m = do
old <- getCurrentDirectory
setCurrentDirectory d
m `finally` setCurrentDirectory old
data Flags
= Flags {
withCompiler :: Maybe FilePath,
withHcPkg :: Maybe FilePath,
verbosity :: Verbosity
}
defaultFlags :: Flags
defaultFlags = Flags {
withCompiler = Nothing,
withHcPkg = Nothing,
verbosity = normal
}
setWithCompiler :: Maybe FilePath -> Flags -> Flags
setWithCompiler f flags = flags{ withCompiler=f }
setWithHcPkg :: Maybe FilePath -> Flags -> Flags
setWithHcPkg f flags = flags{ withHcPkg=f }
setVerbosity :: Verbosity -> Flags -> Flags
setVerbosity v flags = flags{ verbosity=v }
opts :: [OptDescr (Flags -> Flags)]
opts = [
Option "w" ["with-setup-compiler"] (reqPathArg (setWithCompiler.Just))
"give the path to a particular compiler to use on setup",
Option "" ["with-setup-hc-pkg"] (reqPathArg (setWithHcPkg.Just))
"give the path to the package tool to use on setup",
Option "v" ["verbosity"] (OptArg (setVerbosity . flagToVerbosity) "n") "Control verbosity (n is 0--5, normal verbosity level is 1, -v alone is equivalent to -v3)"
]
configCabalFlag :: Flags -> VersionRange -> Compiler -> IO [String]
configCabalFlag _flags AnyVersion _ = return []
configCabalFlag flags range comp = do
ipkgs <- getInstalledPackages comp True (verbosity flags)
-- user packages are *allowed* here, no portability problem
cabal_pkgid <- configDependency ipkgs (Dependency "Cabal" range)
return ["-package", showPackageId cabal_pkgid]
|