diff --git a/Distribution/Compiler.hs b/Distribution/Compiler.hs index e4258ad8ff5e05d6aed29d3b4c94fcdb3bfa4bea..4232131fa9b95500d14a673e2a03dbb8a7c50763 100644 --- a/Distribution/Compiler.hs +++ b/Distribution/Compiler.hs @@ -78,8 +78,8 @@ data CompilerFlavor data Compiler = Compiler { compilerFlavor :: CompilerFlavor, compilerId :: PackageIdentifier, - compilerProg :: Program, - compilerPkgTool :: Program, + compilerProg :: ConfiguredProgram, + compilerPkgTool :: ConfiguredProgram, compilerExtensions :: [(Extension, Flag)] } deriving (Show, Read) diff --git a/Distribution/PreProcess.hs b/Distribution/PreProcess.hs index 6e5bca3f186ba963bc2279fb0f12afce41839d30..3ea4b77ec38f40d7c92b7572505c93fbb5437703 100644 --- a/Distribution/PreProcess.hs +++ b/Distribution/PreProcess.hs @@ -64,8 +64,11 @@ import Distribution.Compiler (CompilerFlavor(..), Compiler(..), compilerVersion) import Distribution.Simple.LocalBuildInfo (LocalBuildInfo(..)) import Distribution.Simple.Utils (createDirectoryIfMissingVerbose, die, moduleToFilePath, moduleToFilePath2) -import Distribution.Program (Program(..), lookupProgram', rawSystemProgramConf, - rawSystemProgram) +import Distribution.Program (Program(..), ConfiguredProgram(..), lookupProgram, + rawSystemProgramConf, rawSystemProgram, + greencardProgram, cpphsProgram, hsc2hsProgram, + c2hsProgram, happyProgram, alexProgram, + haddockProgram) import Distribution.Version (Version(..)) import Distribution.Verbosity @@ -275,7 +278,7 @@ ppGreenCard _ lbi = PreProcessor { platformIndependent = False, runPreProcessor = mkSimplePreProcessor $ \inFile outFile verbosity -> - rawSystemProgramConf verbosity "greencard" (withPrograms lbi) + rawSystemProgramConf verbosity greencardProgram (withPrograms lbi) (["-tffi", "-o" ++ outFile, inFile]) } @@ -330,7 +333,7 @@ ppCpphs extraArgs _bi lbi = PreProcessor { platformIndependent = False, runPreProcessor = mkSimplePreProcessor $ \inFile outFile verbosity -> - rawSystemProgramConf verbosity "cpphs" (withPrograms lbi) $ + rawSystemProgramConf verbosity cpphsProgram (withPrograms lbi) $ ("-O" ++ outFile) : inFile : "--noline" : "--strip" : extraArgs @@ -341,14 +344,14 @@ ppCpphs extraArgs _bi lbi = -- -optP-P only if the Haddock version is prior to 0.8. use_optP_P :: LocalBuildInfo -> Bool use_optP_P lbi - = case lookupProgram' "haddock" (withPrograms lbi) of - Just (Program { programVersion = Just version }) + = case lookupProgram haddockProgram (withPrograms lbi) of + Just (ConfiguredProgram { programVersion = Just version }) | version >= Version [0,8] [] -> False _ -> True ppHsc2hs :: BuildInfo -> LocalBuildInfo -> PreProcessor ppHsc2hs bi lbi = pp - where pp = standardPP lbi "hsc2hs" flags + where pp = standardPP lbi hsc2hsProgram flags flags = hcDefines (compiler lbi) ++ map ("--cflag=" ++) (getCcFlags bi) ++ map ("--lflag=" ++) (getLdFlags bi) @@ -370,7 +373,7 @@ ppC2hs bi lbi platformIndependent = False, runPreProcessor = \(inBaseDir, inRelativeFile) (outBaseDir, outRelativeFile) verbosity -> - rawSystemProgramConf verbosity "c2hs" (withPrograms lbi) $ + rawSystemProgramConf verbosity c2hsProgram (withPrograms lbi) $ ["--include=" ++ dir | dir <- hsSourceDirs bi ] ++ ["--cppopts=" ++ opt | opt <- cppOptions bi lbi] ++ ["--output-dir=" ++ outBaseDir, @@ -405,24 +408,24 @@ versionInt (Version { versionBranch = n1:n2:_ }) ppHappy :: BuildInfo -> LocalBuildInfo -> PreProcessor ppHappy _ lbi = pp { platformIndependent = True } - where pp = standardPP lbi "happy" (hcFlags hc) + where pp = standardPP lbi happyProgram (hcFlags hc) hc = compilerFlavor (compiler lbi) hcFlags GHC = ["-agc"] hcFlags _ = [] ppAlex :: BuildInfo -> LocalBuildInfo -> PreProcessor ppAlex _ lbi = pp { platformIndependent = True } - where pp = standardPP lbi "alex" (hcFlags hc) + where pp = standardPP lbi alexProgram (hcFlags hc) hc = compilerFlavor (compiler lbi) hcFlags GHC = ["-g"] hcFlags _ = [] -standardPP :: LocalBuildInfo -> String -> [String] -> PreProcessor -standardPP lbi progName args = +standardPP :: LocalBuildInfo -> Program -> [String] -> PreProcessor +standardPP lbi prog args = PreProcessor { platformIndependent = False, runPreProcessor = mkSimplePreProcessor $ \inFile outFile verbosity -> - rawSystemProgramConf verbosity progName (withPrograms lbi) + rawSystemProgramConf verbosity prog (withPrograms lbi) (args ++ ["-o", outFile, inFile]) } diff --git a/Distribution/Program.hs b/Distribution/Program.hs index 8fcd11625191ee91c26113bc25305b0f349c2669..538efe6e794ec6e6082be2340bc914072297920f 100644 --- a/Distribution/Program.hs +++ b/Distribution/Program.hs @@ -23,219 +23,485 @@ -- hook user the ability to get the above flags and such so that they -- don't have to write all the PATH logic inside Setup.lhs. -module Distribution.Program( - -- * Program-Related types - Program(..) - , ProgramLocation(..) - , ProgramConfiguration(..) - -- * Helper functions - , withProgramFlag - , programOptsFlag - , programOptsField - , programPath - , findProgram - , findProgramAndVersion - , defaultProgramConfiguration - , updateProgram - , maybeUpdateProgram - , userSpecifyPath - , userSpecifyArgs - , lookupProgram - , lookupProgram' --TODO eliminate this export - , lookupPrograms - , rawSystemProgram - , rawSystemProgramConf - , simpleProgram - , simpleProgramAt - -- * Programs that Cabal knows about - , ghcProgram - , ghcPkgProgram - , nhcProgram - , jhcProgram - , hugsProgram - , ranlibProgram - , arProgram - , happyProgram - , alexProgram - , hsc2hsProgram - , c2hsProgram - , cpphsProgram - , hscolourProgram - , haddockProgram - , greencardProgram - , ldProgram - , cppProgram - , pfesetupProgram - ) where +module Distribution.Program ( + -- * Program and functions for constructing them + Program(..) + , simpleProgram + , searchPath + , findProgramVersion + + -- * Configured program and related functions + , ConfiguredProgram(..) + , programPath + , ProgArg + , ProgramLocation(..) + , rawSystemProgram + + -- * The collection of unconfigured and configured progams + , builtinPrograms + + -- * The collection of configured programs we can run + , ProgramConfiguration + , emptyProgramConfiguration + , defaultProgramConfiguration + , addKnownProgram + , knownPrograms + , userSpecifyPath + , userMaybeSpecifyPath + , userSpecifyArgs + , lookupProgram + , updateProgram + , configureAllKnownPrograms + , requireProgram + , rawSystemProgramConf + + -- * Programs that Cabal knows about + , ghcProgram + , ghcPkgProgram + , nhcProgram + , hmakeProgram + , jhcProgram + , hugsProgram + , ffihugsProgram + , ranlibProgram + , arProgram + , happyProgram + , alexProgram + , hsc2hsProgram + , c2hsProgram + , cpphsProgram + , hscolourProgram + , haddockProgram + , greencardProgram + , ldProgram + , tarProgram + , cppProgram + , pfesetupProgram + ) where import qualified Distribution.Compat.Map as Map import Distribution.Compat.Directory (findExecutable) import Distribution.Simple.Utils (die, rawSystemExit, rawSystemStdout) -import Distribution.System -import Distribution.Version (Version, readVersion) +import Distribution.Version (Version, readVersion, showVersion, + VersionRange(..), withinRange, showVersionRange) import Distribution.Verbosity import System.Directory (doesFileExist) -import Control.Monad (when) - --- |Represents a program which cabal may call. -data Program - = Program { -- |The simple name of the program, eg. ghc - programName :: String - -- |The name of this program's binary, eg. ghc-6.4 - ,programBinName :: String - -- |The version of this program, if it is known - ,programVersion :: Maybe Version - -- |Default command-line args for this program - ,programArgs :: [String] - -- |Location of the program. eg. \/usr\/bin\/ghc-6.4 - ,programLocation :: ProgramLocation - } deriving (Read, Show) - --- |Similar to Maybe, but tells us whether it's specifed by user or +import Control.Monad (when, join, foldM) +import Control.Exception as Exception (catch) + +-- | Represents a program which can be configured. +data Program = Program { + -- | The simple name of the program, eg. ghc + programName :: String, + + -- | A function to search for the program if it's location was not + -- specified by the user. Usually this will just be a + programFindLocation :: Verbosity -> IO (Maybe FilePath), + + -- | Try to find the version of the program. For many programs this is + -- not possible or is not necessary so it's ok to return Nothing. + programFindVersion :: Verbosity -> FilePath -> IO (Maybe Version) + } + +type ProgArg = String + +data ConfiguredProgram = ConfiguredProgram { + -- | Just the name again + programId :: String, + + -- | The version of this program, if it is known. + programVersion :: Maybe Version, + + -- | Default command-line args for this program. + -- These flags will appear first on the command line, so they can be + -- overridden by subsequent flags. + programArgs :: [ProgArg], + + -- | Location of the program. eg. @\/usr\/bin\/ghc-6.4@ + programLocation :: ProgramLocation + } deriving (Read, Show) + +-- | Where a program was found. Also tells us whether it's specifed by user or -- not. This includes not just the path, but the program as well. -data ProgramLocation - = EmptyLocation -- ^Like Nothing - | UserSpecified FilePath +data ProgramLocation + = UserSpecified { locationPath :: FilePath } -- ^The user gave the path to this program, -- eg. --ghc-path=\/usr\/bin\/ghc-6.6 - | FoundOnSystem FilePath + | FoundOnSystem { locationPath :: FilePath } -- ^The location of the program, as located by searching PATH. deriving (Read, Show) --- |The configuration is a collection of 'Program's. It's a mapping from the --- name of the program (eg. ghc) to the Program. -data ProgramConfiguration = ProgramConfiguration (Map.Map String Program) +-- ------------------------------------------------------------ +-- * Programs functions +-- ------------------------------------------------------------ --- Read & Show instances are based on listToFM +-- | The full path of a configured program. +programPath :: ConfiguredProgram -> FilePath +programPath = locationPath . programLocation +-- | Make a simple named program. +-- +-- By default we'll just search for it in the path and not try to find the +-- version name. You can override these behaviours if necessary, eg: +-- +-- > simpleProgram "foo" { programFindLocation = ... , programFindVersion ... } +-- +simpleProgram :: String -> Program +simpleProgram name = + Program name (searchPath name) (\_ _ -> return Nothing) + +-- | Look for a program on the path. +searchPath :: FilePath -> Verbosity -> IO (Maybe FilePath) +searchPath prog verbosity = do + when (verbosity >= deafening) $ + putStrLn $ "searching for " ++ prog ++ " in path." + res <- findExecutable prog + when (verbosity >= deafening) $ case res of + Nothing -> putStrLn ("Cannot find " ++ prog ++ " on the path") + Just path -> putStrLn ("found " ++ prog ++ " at "++ path) + return res + +-- | Look for a program and try to find it's version number. It can accept +-- either an absolute path or the name of a program binary, in which case we +-- will look for the program on the path. +-- +findProgramVersion :: ProgArg -- ^ version args + -> (String -> String) -- ^ function to select version + -- number from program output + -> Verbosity + -> FilePath -- ^ location + -> IO (Maybe Version) +findProgramVersion versionArg selectVersion verbosity path = do + str <- rawSystemStdout verbosity path [versionArg] + `Exception.catch` \_ -> return "" + let version = readVersion (selectVersion str) + case version of + Nothing -> when (verbosity >= normal) $ + putStrLn $ "cannot determine version of " ++ path ++ " :\n" + ++ show str + Just v -> when (verbosity >= deafening) $ + putStrLn $ path ++ " is version " ++ showVersion v + return version + +-- ------------------------------------------------------------ +-- * Programs database +-- ------------------------------------------------------------ + +-- | The configuration is a collection of information about programs. It +-- contains information both about configured programs and also about programs +-- that we are yet to configure. +-- +-- The idea is that we start from a collection of unconfigured programs and one +-- by one we try to configure them at which point we move them into the +-- configured collection. For unconfigured programs we record not just the +-- 'Program' but also any user-provided arguments and location for the program. +data ProgramConfiguration = ProgramConfiguration { + unconfiguredProgs :: UnconfiguredProgs, + configuredProgs :: ConfiguredProgs + } +type UnconfiguredProgram = (Program, Maybe FilePath, [ProgArg]) +type UnconfiguredProgs = Map.Map String UnconfiguredProgram +type ConfiguredProgs = Map.Map String ConfiguredProgram + +emptyProgramConfiguration :: ProgramConfiguration +emptyProgramConfiguration = ProgramConfiguration Map.empty Map.empty + +defaultProgramConfiguration :: ProgramConfiguration +defaultProgramConfiguration = + foldl (flip addKnownProgram) emptyProgramConfiguration builtinPrograms + +-- internal helpers: +updateUnconfiguredProgs :: (UnconfiguredProgs -> UnconfiguredProgs) + -> ProgramConfiguration -> ProgramConfiguration +updateUnconfiguredProgs update conf = + conf { unconfiguredProgs = update (unconfiguredProgs conf) } +updateConfiguredProgs :: (ConfiguredProgs -> ConfiguredProgs) + -> ProgramConfiguration -> ProgramConfiguration +updateConfiguredProgs update conf = + conf { configuredProgs = update (configuredProgs conf) } + +-- Read & Show instances are based on listToFM +-- Note that we only serialise the configured part of the database, this is +-- because we don't need the unconfigured part after the configure stage, and +-- additionally because we cannot read/show 'Program' as it contains functions. instance Show ProgramConfiguration where - show (ProgramConfiguration s) = show $ Map.toAscList s + show = show . Map.toAscList . configuredProgs instance Read ProgramConfiguration where - readsPrec p s = [(ProgramConfiguration $ Map.fromList $ s', r) - | (s', r) <- readsPrec p s ] + readsPrec p s = + [ (emptyProgramConfiguration { configuredProgs = Map.fromList s' }, r) + | (s', r) <- readsPrec p s ] --- |The default list of programs and their arguments. These programs --- are typically used internally to Cabal. +-- ------------------------------- +-- Managing unconfigured programs -defaultProgramConfiguration :: ProgramConfiguration -defaultProgramConfiguration = progListToFM - [ hscolourProgram - , haddockProgram - , happyProgram - , alexProgram - , hsc2hsProgram - , c2hsProgram - , cpphsProgram - , greencardProgram - , pfesetupProgram - , ranlibProgram - , simpleProgram "runghc" - , simpleProgram "runhugs" - , arProgram - , ldProgram - , tarProgram - ] --- haddock is currently the only one that really works. -{- [ ghcProgram - , ghcPkgProgram - , nhcProgram - , hugsProgram - , cppProgram - ]-} - --- |The flag for giving a path to this program. eg. --with-alex=\/usr\/bin\/alex -withProgramFlag :: Program -> String -withProgramFlag Program{programName=n} = "with-" ++ n - --- |The flag for giving args for this program. --- eg. --haddock-options=-s http:\/\/foo -programOptsFlag :: Program -> String -programOptsFlag Program{programName=n} = n ++ "-options" - --- |The foo.cabal field for giving args for this program. --- eg. haddock-options: -s http:\/\/foo -programOptsField :: Program -> String -programOptsField = programOptsFlag - --- |The full path of a configured program. +-- | Add a known program that we may configure later +addKnownProgram :: Program -> ProgramConfiguration -> ProgramConfiguration +addKnownProgram prog = updateUnconfiguredProgs $ + Map.insert (programName prog) (prog, Nothing, []) + +knownPrograms :: ProgramConfiguration -> [(Program, Maybe ConfiguredProgram)] +knownPrograms conf = + [ (p,p') | (p,_,_) <- Map.elems (unconfiguredProgs conf) + , let p' = Map.lookup (programName p) (configuredProgs conf) ] + +-- |User-specify this path. Basically override any path information +-- for this program in the configuration. If it's not a known +-- program ignore it. +userSpecifyPath :: String -- ^Program name + -> FilePath -- ^user-specified path to the program + -> ProgramConfiguration -> ProgramConfiguration +userSpecifyPath name path = updateUnconfiguredProgs $ + flip Map.update name $ \(prog, _, args) -> Just (prog, Just path, args) + +userMaybeSpecifyPath :: String -> Maybe FilePath + -> ProgramConfiguration -> ProgramConfiguration +userMaybeSpecifyPath _ Nothing conf = conf +userMaybeSpecifyPath name (Just path) conf = userSpecifyPath name path conf + +-- |User-specify the arguments for this program. Basically override +-- any args information for this program in the configuration. If it's +-- not a known program, ignore it.. +userSpecifyArgs :: String -- ^Program name + -> [ProgArg] -- ^user-specified args + -> ProgramConfiguration + -> ProgramConfiguration +userSpecifyArgs name args' = updateUnconfiguredProgs $ + flip Map.update name $ \(prog, path, args) -> Just (prog, path, args ++ args') + +userSpecifiedPath :: Program -> ProgramConfiguration -> Maybe FilePath +userSpecifiedPath prog = + join . fmap (\(_,p,_)->p) . Map.lookup (programName prog) . unconfiguredProgs + +userSpecifiedArgs :: Program -> ProgramConfiguration -> [ProgArg] +userSpecifiedArgs prog = + maybe [] (\(_,_,as)->as) . Map.lookup (programName prog) . unconfiguredProgs + +-- ----------------------------- +-- Managing configured programs + +-- | Try to find a configured program +lookupProgram :: Program -> ProgramConfiguration -> Maybe ConfiguredProgram +lookupProgram prog = Map.lookup (programName prog) . configuredProgs + +-- | Update a configured program in the database. +updateProgram :: ConfiguredProgram -> ProgramConfiguration + -> ProgramConfiguration +updateProgram prog = updateConfiguredProgs $ + Map.insert (programId prog) prog + +-- --------------------------- +-- Configuring known programs + +-- | Try to configure a specific program. If the program is already included in +-- the colleciton of unconfigured programs then we use any user-supplied +-- location and arguments. If the program gets configured sucessfully it gets +-- added to the configured collection. -- --- * This is a partial function, it is not defined for programs with an --- EmptyLocation. -programPath :: Program -> FilePath -programPath program = - case programLocation program of - UserSpecified p -> p - FoundOnSystem p -> p - EmptyLocation -> error "programPath EmptyLocation" - --- | Look for a program. It can accept either an absolute path or the name of --- a program binary, in which case we will look for the program on the path. +-- Note that it is not a failure if the program cannot be configured. It's only +-- a failure if the user supplied a location and the program could not be found +-- at that location. -- -findProgram :: Verbosity -> String -> Maybe FilePath -> IO Program -findProgram verbosity prog maybePath = do - location <- case maybePath of - Nothing -> searchPath verbosity prog +-- The reason for it not being a failure at this stage is that we don't know up +-- front all the programs we will need, so we try to configure them all. +-- To verify that a program was actually sucessfully configured use +-- 'requireProgram'. +-- +configureProgram :: Verbosity + -> Program + -> ProgramConfiguration + -> IO ProgramConfiguration +configureProgram verbosity prog conf = do + let name = programName prog + maybeLocation <- case userSpecifiedPath prog conf of + Nothing -> programFindLocation prog verbosity + >>= return . fmap FoundOnSystem Just path -> do absolute <- doesFileExist path if absolute - then return (UserSpecified path) - else searchPath verbosity path - return (simpleProgramAt prog location) + then return (Just (UserSpecified path)) + else searchPath path verbosity + >>= maybe (die notFound) (return . Just . UserSpecified) + where notFound = "Cannot find " ++ name ++ " at " + ++ path ++ " or on the path" + case maybeLocation of + Nothing -> return conf + Just location -> do + version <- programFindVersion prog verbosity (locationPath location) + let configuredProg = ConfiguredProgram { + programId = name, + programVersion = version, + programArgs = userSpecifiedArgs prog conf, + programLocation = location + } + return (updateConfiguredProgs (Map.insert name configuredProg) conf) + +-- | Try to configure all the known programs that have not yet been configured. +configureAllKnownPrograms :: Verbosity + -> ProgramConfiguration + -> IO ProgramConfiguration +configureAllKnownPrograms verbosity conf = + foldM (flip (configureProgram verbosity)) conf + [ prog | (prog,_,_) <- Map.elems (unconfiguredProgs conf + `Map.difference` configuredProgs conf) ] + +-- | Check that a program is configured and available to be run. +-- +-- Additionally check that the version of the program number is suitable. +-- For example 'AnyVersion' or @'orLaterVersion' ('Version' [1,0] [])@ +-- +-- It raises an exception if the program could not be configured or the version +-- is unsuitable, otherwise it returns the configured program. +requireProgram :: Verbosity -> Program -> VersionRange -> ProgramConfiguration + -> IO (ConfiguredProgram, ProgramConfiguration) +requireProgram verbosity prog range conf = do + + -- If it's not already been configured, try to configure it now + conf' <- case lookupProgram prog conf of + Nothing -> configureProgram verbosity prog conf + Just _ -> return conf + + case lookupProgram prog conf' of + Nothing -> die notFound + Just configuredProg + | range == AnyVersion -> return (configuredProg, conf') + Just configuredProg@ConfiguredProgram { programLocation = location } -> + case programVersion configuredProg of + Just version + | withinRange version range -> return (configuredProg, conf') + | otherwise -> die (badVersion version location) + Nothing -> die (noVersion location) + + where notFound = programName prog ++ versionRequirement + ++ " is required but it could not be found." + badVersion v l = programName prog ++ versionRequirement + ++ " is required but the version found at " + ++ locationPath l ++ " is version " ++ showVersion v + noVersion l = programName prog ++ versionRequirement + ++ " is required but the version of " + ++ locationPath l ++ " could not be determined." + versionRequirement + | range == AnyVersion = "" + | otherwise = " version " ++ showVersionRange range -searchPath :: Verbosity -> FilePath -> IO ProgramLocation -searchPath verbosity prog = do - when (verbosity >= verbose) $ - putStrLn $ "searching for " ++ prog ++ " in path." - res <- findExecutable prog - case res of - Nothing -> die ("Cannot find " ++ prog ++ " on the path") - Just path -> do when (verbosity >= verbose) $ - putStrLn ("found " ++ prog ++ " at "++ path) - return (FoundOnSystem path) +-- ------------------------------------------------------------ +-- * Running programs +-- ------------------------------------------------------------ --- | Look for a program and try to find it's version number. It can accept --- either an absolute path or the name of a program binary, in which case we --- will look for the program on the path. --- -findProgramAndVersion :: Verbosity - -> String -- ^ program binary name - -> Maybe FilePath -- ^ possible location - -> String -- ^ version args - -> (String -> String) -- ^ function to select version - -- number from program output - -> IO Program -findProgramAndVersion verbosity name maybePath versionArg selectVersion = do - prog <- findProgram verbosity name maybePath - str <- rawSystemStdout verbosity (programPath prog) [versionArg] - case readVersion (selectVersion str) of - Just v -> return prog { programVersion = Just v } - _ -> die ("cannot determine version of " ++ name ++ " :\n" ++ show str) +-- | Runs the given configured program. +rawSystemProgram :: Verbosity -- ^Verbosity + -> ConfiguredProgram -- ^The program to run + -> [ProgArg] -- ^Any /extra/ arguments to add + -> IO () +rawSystemProgram verbosity prog extraArgs + = rawSystemExit verbosity (programPath prog) (programArgs prog ++ extraArgs) + +-- | Looks up the given program in the program configuration and runs it. +rawSystemProgramConf :: Verbosity -- ^verbosity + -> Program -- ^The program to run + -> ProgramConfiguration -- ^look up the program here + -> [ProgArg] -- ^Any /extra/ arguments to add + -> IO () +rawSystemProgramConf verbosity prog programConf extraArgs = + case lookupProgram prog programConf of + Nothing -> die (programName prog ++ " command not found") + Just configuredProg -> rawSystemProgram verbosity configuredProg extraArgs -- ------------------------------------------------------------ --- * cabal programs +-- * Known programs -- ------------------------------------------------------------ +-- | The default list of programs. +-- These programs are typically used internally to Cabal. +builtinPrograms :: [Program] +builtinPrograms = + [ + -- compilers and related progs + ghcProgram + , ghcPkgProgram + , hugsProgram + , ffihugsProgram + , nhcProgram + , hmakeProgram + , jhcProgram + -- preprocessors + , hscolourProgram + , haddockProgram + , happyProgram + , alexProgram + , hsc2hsProgram + , c2hsProgram + , cpphsProgram + , greencardProgram + , pfesetupProgram + -- platform toolchain + , ranlibProgram + , arProgram + , ldProgram + , tarProgram + ] + ghcProgram :: Program -ghcProgram = simpleProgram "ghc" +ghcProgram = (simpleProgram "ghc") { + programFindVersion = findProgramVersion "--numeric-version" id + } ghcPkgProgram :: Program -ghcPkgProgram = simpleProgram "ghc-pkg" +ghcPkgProgram = (simpleProgram "ghc-pkg") { + programFindVersion = findProgramVersion "--version" $ \str -> + -- Invoking "ghc-pkg --version" gives a string like + -- "GHC package manager version 6.4.1" + case words str of + (_:_:_:_:ver:_) -> ver + _ -> "" + } nhcProgram :: Program nhcProgram = simpleProgram "nhc" -jhcProgram :: Program -jhcProgram = simpleProgram "jhc" +hmakeProgram :: Program +hmakeProgram = (simpleProgram "hmake") { + programFindVersion = findProgramVersion "--version" $ \str -> + case words str of + (_:ver:_) -> ver + _ -> "" + } +jhcProgram :: Program +jhcProgram = (simpleProgram "jhc") { + programFindVersion = findProgramVersion "--version" $ \str -> + case words str of + (_:ver:_) -> ver + _ -> "" + } + +-- AArgh! Finding the version of hugs or ffihugs is almost impossible. hugsProgram :: Program hugsProgram = simpleProgram "hugs" +ffihugsProgram :: Program +ffihugsProgram = simpleProgram "ffihugs" + happyProgram :: Program -happyProgram = simpleProgram "happy" +happyProgram = (simpleProgram "happy") { + programFindVersion = findProgramVersion "--version" $ \str -> + -- Invoking "happy --version" gives a string like + -- "Happy Version 1.16 Copyright (c) ...." + case words str of + (_:_:ver:_) -> ver + _ -> "" + } alexProgram :: Program -alexProgram = simpleProgram "alex" +alexProgram = (simpleProgram "alex") { + programFindVersion = findProgramVersion "--version" $ \str -> + -- Invoking "alex --version" gives a string like + -- "Alex version 2.1.0, (c) 2003 Chris Dornan and Simon Marlow" + case words str of + (_:_:ver:_) -> takeWhile (`elem` ('.':['0'..'9'])) ver + _ -> "" + } + ranlibProgram :: Program ranlibProgram = simpleProgram "ranlib" @@ -244,29 +510,53 @@ arProgram :: Program arProgram = simpleProgram "ar" hsc2hsProgram :: Program -hsc2hsProgram = simpleProgram "hsc2hs" +hsc2hsProgram = (simpleProgram "hsc2hs") { + programFindVersion = findProgramVersion "--version" $ \str -> + -- Invoking "hsc2hs --version" gives a string like "hsc2hs version 0.66" + case words str of + (_:_:ver:_) -> ver + _ -> "" + } c2hsProgram :: Program -c2hsProgram = simpleProgram "c2hs" +c2hsProgram = (simpleProgram "c2hs") { + programFindVersion = findProgramVersion "--numeric-version" id + } cpphsProgram :: Program -cpphsProgram = simpleProgram "cpphs" +cpphsProgram = (simpleProgram "cpphs") { + programFindVersion = findProgramVersion "--version" $ \str -> + -- Invoking "cpphs --version" gives a string like "cpphs 1.3" + case words str of + (_:ver:_) -> ver + _ -> "" + } hscolourProgram :: Program -hscolourProgram = (simpleProgram "hscolour"){ programBinName = "HsColour" } +hscolourProgram = (simpleProgram "hscolour") { + programFindLocation = searchPath "HsColour", + programFindVersion = findProgramVersion "-version" $ \str -> + -- Invoking "HsColour -version" gives a string like "HsColour 1.7" + case words str of + (_:ver:_) -> ver + _ -> "" + } haddockProgram :: Program -haddockProgram = simpleProgram "haddock" +haddockProgram = (simpleProgram "haddock") { + programFindVersion = findProgramVersion "--version" $ \str -> + -- Invoking "haddock --version" gives a string like + -- "Haddock version 0.8, (c) Simon Marlow 2006" + case words str of + (_:_:ver:_) -> takeWhile (`elem` ('.':['0'..'9'])) ver + _ -> "" + } greencardProgram :: Program greencardProgram = simpleProgram "greencard" ldProgram :: Program -ldProgram = case os of - Windows MingW -> - Program "ld" "ld" Nothing [] - (FoundOnSystem "<what-your-hs-compiler-shipped-with>") - _ -> simpleProgram "ld" +ldProgram = simpleProgram "ld" tarProgram :: Program tarProgram = simpleProgram "tar" @@ -276,114 +566,3 @@ cppProgram = simpleProgram "cpp" pfesetupProgram :: Program pfesetupProgram = simpleProgram "pfesetup" - --- ------------------------------------------------------------ --- * helpers --- ------------------------------------------------------------ - --- |Looks up a program in the given configuration. If there's no --- location information in the configuration, then we use IO to look --- on the system in PATH for the program. If the program is not in --- the configuration at all, we return Nothing. FIX: should we build --- a simpleProgram in that case? Do we want a way to specify NOT to --- find it on the system (populate programLocation). - -lookupProgram :: String -- simple name of program - -> ProgramConfiguration - -> IO (Maybe Program) -- the full program -lookupProgram name conf = - case lookupProgram' name conf of - Nothing -> return Nothing - Just p@Program{ programLocation= configLoc - , programBinName = binName} - -> do newLoc <- case configLoc of - EmptyLocation - -> do maybeLoc <- findExecutable binName - return $ maybe EmptyLocation FoundOnSystem maybeLoc - a -> return a - return $ Just p{programLocation=newLoc} - -lookupPrograms :: ProgramConfiguration -> IO [(String, Maybe Program)] -lookupPrograms conf@(ProgramConfiguration fm) = do - let l = Map.elems fm - mapM (\p -> do fp <- lookupProgram (programName p) conf - return (programName p, fp) - ) l - --- |User-specify this path. Basically override any path information --- for this program in the configuration. If it's not a known --- program, add it. -userSpecifyPath :: String -- ^Program name - -> FilePath -- ^user-specified path to filename - -> ProgramConfiguration - -> ProgramConfiguration -userSpecifyPath name path conf'@(ProgramConfiguration conf) - = case Map.lookup name conf of - Just p -> updateProgram p{programLocation=UserSpecified path} conf' - Nothing -> updateProgram (simpleProgramAt name (UserSpecified path)) - conf' - --- |User-specify the arguments for this program. Basically override --- any args information for this program in the configuration. If it's --- not a known program, add it. -userSpecifyArgs :: String -- ^Program name - -> String -- ^user-specified args - -> ProgramConfiguration - -> ProgramConfiguration -userSpecifyArgs name args conf'@(ProgramConfiguration conf) - = case Map.lookup name conf of - Just p -> updateProgram p{programArgs=(words args)} conf' - Nothing -> updateProgram (Program name name Nothing (words args) EmptyLocation) conf' - --- |Update this program's entry in the configuration. -updateProgram :: Program -> ProgramConfiguration -> ProgramConfiguration -updateProgram p@Program{programName=n} (ProgramConfiguration conf) - = ProgramConfiguration $ Map.insert n p conf - --- |Same as updateProgram but no changes if you pass in Nothing. -maybeUpdateProgram :: Maybe Program -> ProgramConfiguration -> ProgramConfiguration -maybeUpdateProgram m c = maybe c (\p -> updateProgram p c) m - --- |Runs the given program. -rawSystemProgram :: Verbosity -- ^Verbosity - -> Program -- ^The program to run - -> [String] -- ^Any /extra/ arguments to add - -> IO () -rawSystemProgram _ prog@(Program { programLocation = EmptyLocation }) _ - = die ("Error: Could not find location for program: " ++ programName prog) -rawSystemProgram verbosity prog extraArgs - = rawSystemExit verbosity (programPath prog) (programArgs prog ++ extraArgs) - -rawSystemProgramConf :: Verbosity -- ^verbosity - -> String -- ^The name of the program to run - -> ProgramConfiguration -- ^look up the program here - -> [String] -- ^Any /extra/ arguments to add - -> IO () -rawSystemProgramConf verbosity progName programConf extraArgs - = do prog <- do mProg <- lookupProgram progName programConf - case mProg of - Nothing -> (die (progName ++ " command not found")) - Just h -> return h - rawSystemProgram verbosity prog extraArgs - - --- ------------------------------------------------------------ --- * Internal helpers --- ------------------------------------------------------------ - -lookupProgram' :: String -> ProgramConfiguration -> Maybe Program -lookupProgram' s (ProgramConfiguration conf) = Map.lookup s conf - -progListToFM :: [Program] -> ProgramConfiguration -progListToFM progs = foldl - (\ (ProgramConfiguration conf') - p@(Program {programName=n}) - -> ProgramConfiguration (Map.insert n p conf')) - (ProgramConfiguration Map.empty) - progs - -simpleProgram :: String -> Program -simpleProgram s = simpleProgramAt s EmptyLocation - -simpleProgramAt :: String -> ProgramLocation -> Program -simpleProgramAt s l = Program s s Nothing [] l diff --git a/Distribution/Setup.hs b/Distribution/Setup.hs index 8e5119b3a4bf991b1e16b1fcacd80f10e86ff5df..b8d1d3aa889fa97ecaac555363bdcf4938db5ae1 100644 --- a/Distribution/Setup.hs +++ b/Distribution/Setup.hs @@ -77,11 +77,10 @@ import Test.HUnit (Test(..)) import Distribution.Compiler (CompilerFlavor(..), Compiler(..)) import Distribution.Simple.Utils (die) -import Distribution.Program(ProgramConfiguration(..), - userSpecifyPath, userSpecifyArgs) +import Distribution.Program (Program(..), ProgramConfiguration, + knownPrograms, userSpecifyPath, userSpecifyArgs) import Data.List(find) import Data.Char( toLower ) -import Distribution.Compat.Map (keys) import Distribution.GetOpt import Distribution.Verbosity import System.FilePath (normalise) @@ -571,14 +570,16 @@ configureCmd progConf = Cmd { } programArgsOptions :: ProgramConfiguration -> [OptDescr (Flag a)] -programArgsOptions (ProgramConfiguration conf) = map f (keys conf) - where f name = Option "" [name ++ "-args"] (reqArgArg (ProgramArgs name)) - ("give the args to " ++ name) +programArgsOptions conf = + [ Option "" [name ++ "-args"] (reqArgArg (ProgramArgs name)) + ("give the args to " ++ name) + | (Program { programName = name }, _) <- knownPrograms conf ] withProgramOptions :: ProgramConfiguration -> [OptDescr (Flag a)] -withProgramOptions (ProgramConfiguration conf) = map f (keys conf) - where f name = Option "" ["with-" ++ name] (reqPathArg (WithProgram name)) - ("give the path to " ++ name) +withProgramOptions conf = + [ Option "" ["with-" ++ name] (reqPathArg (WithProgram name)) + ("give the path to " ++ name) + | (Program { programName = name }, _) <- knownPrograms conf ] reqPathArg :: (FilePath -> a) -> ArgDescr a reqPathArg constr = ReqArg (constr . normalise) "PATH" @@ -606,12 +607,15 @@ parseConfigureArgs progConf = parseArgs (configureCmd progConf) updateCfg updateCfg t HugsFlag = t { configHcFlavor = Just Hugs } updateCfg t (WithCompiler path) = t { configHcPath = Just path } updateCfg t (WithHcPkg path) = t { configHcPkg = Just path } - updateCfg t (ProgramArgs name args) = t { configPrograms = (userSpecifyArgs - name - args (configPrograms t))} - updateCfg t (WithProgram name path) = t { configPrograms = (userSpecifyPath - name - path (configPrograms t))} + updateCfg t (ProgramArgs name args) = t { configPrograms = + userSpecifyArgs + --TODO: using words here is not good, it breaks for paths with spaces + name (words args) + (configPrograms t) } + updateCfg t (WithProgram name path) = t { configPrograms = + userSpecifyPath + name path + (configPrograms t) } updateCfg t WithVanillaLib = t { configVanillaLib = True } updateCfg t WithoutVanillaLib = t { configVanillaLib = False, configGHCiLib = False } updateCfg t WithProfLib = t { configProfLib = True } diff --git a/Distribution/Simple.hs b/Distribution/Simple.hs index 228b0c4ab45cd13280574b56c99bc8da8b8bcf71..5b1077dd79b6ff157e3342ed5445b725501cfb67 100644 --- a/Distribution/Simple.hs +++ b/Distribution/Simple.hs @@ -69,8 +69,8 @@ module Distribution.Simple ( import Distribution.Compiler import Distribution.Package --must not specify imports, since we're exporting moule. import Distribution.PackageDescription -import Distribution.Program(Program(..), ProgramConfiguration(..), - defaultProgramConfiguration, updateProgram, +import Distribution.Program(Program(..), ProgramConfiguration, + defaultProgramConfiguration, addKnownProgram, pfesetupProgram, rawSystemProgramConf) import Distribution.PreProcess (knownSuffixHandlers, ppSuffixes, removePreprocessedPackage, @@ -277,7 +277,7 @@ defaultMain__ margs mhooks mdescr = do -- into cabal. allPrograms :: UserHooks -> ProgramConfiguration -- combine defaults w/ user programs -allPrograms h = foldl (flip updateProgram) +allPrograms h = foldl (flip addKnownProgram) defaultProgramConfiguration (hookedPrograms h) @@ -449,11 +449,8 @@ pfe pkg_descr _lbi hooks (PFEFlags verbosity) = do preprocessSources pkg_descr lbi False verbosity pps inFiles <- getModulePaths lbi bi mods let verbFlags = if verbosity >= deafening then ["-v"] else [] - rawSystemProgramConf verbosity - (programName pfesetupProgram) - (withPrograms lbi) + rawSystemProgramConf verbosity pfesetupProgram (withPrograms lbi) ("noplogic" : "cpp" : verbFlags ++ inFiles) - return () -- -------------------------------------------------------------------------- diff --git a/Distribution/Simple/Configure.hs b/Distribution/Simple/Configure.hs index 25456f43bb4b14f5c69d574d96e84e14f4465845..f7a32eede1cd455bc8d870150e2a7e38cae542b0 100644 --- a/Distribution/Simple/Configure.hs +++ b/Distribution/Simple/Configure.hs @@ -81,9 +81,8 @@ import Distribution.PackageDescription import Distribution.ParseUtils ( showDependency ) import Distribution.Program - ( Program(..), ProgramLocation(..), ProgramConfiguration(..), programPath - , lookupProgram, lookupPrograms, updateProgram, maybeUpdateProgram - , findProgramAndVersion ) + ( Program(..), ProgramLocation(..), ConfiguredProgram(..), programPath + , ProgramConfiguration, configureAllKnownPrograms, knownPrograms ) import Distribution.Setup ( ConfigFlags(..), CopyDest(..) ) import Distribution.Simple.InstallDirs @@ -189,7 +188,7 @@ configure :: ( Either GenericPackageDescription PackageDescription configure (pkg_descr0, pbi) cfg = do -- detect compiler - comp <- configCompilerAux cfg + (comp, programsConfig) <- configCompilerAux cfg let version = compilerVersion comp flavor = compilerFlavor comp @@ -270,14 +269,8 @@ configure (pkg_descr0, pbi) cfg show flavor ++ " does not support the following extensions:\n " ++ concat (intersperse ", " (map show exts)) - foundPrograms <- lookupPrograms (configPrograms cfg) - - let newConfig = foldr (\(_, p) c -> maybeUpdateProgram p c) - (configPrograms cfg) foundPrograms - - -- TODO: get version checking integrated into the Program abstraction - newConfig' <- findHaddockVersion (configVerbose cfg) newConfig - newConfig'' <- findHscolourVersion (configVerbose cfg) newConfig' + programsConfig' <- configureAllKnownPrograms (configVerbose cfg) + programsConfig split_objs <- if not (configSplitObjs cfg) @@ -296,7 +289,7 @@ configure (pkg_descr0, pbi) cfg scratchDir=distPref </> "scratch", packageDeps=dep_pkgs, localPkgDescr=pkg_descr, - withPrograms=newConfig'', + withPrograms=programsConfig', withVanillaLib=configVanillaLib cfg, withProfLib=configProfLib cfg, withProfExe=configProfExe cfg, @@ -323,7 +316,8 @@ configure (pkg_descr0, pbi) cfg message $ "Compiler version: " ++ showVersion version message $ "Using package tool: " ++ compilerPkgToolPath comp - mapM_ (uncurry reportProgram) foundPrograms + sequence_ [ reportProgram prog configuredProg + | (prog, configuredProg) <- knownPrograms programsConfig' ] return lbi @@ -346,18 +340,17 @@ setDepByVersion (Dependency s (ThisVersion v)) = PackageIdentifier s v -- otherwise, just set it to empty setDepByVersion (Dependency s _) = PackageIdentifier s (Version [] []) - -reportProgram :: String -> Maybe Program -> IO () -reportProgram _ (Just Program{ programName=name - , programLocation=EmptyLocation}) - = message ("No " ++ name ++ " found") -reportProgram _ (Just Program{ programName=name - , programLocation=FoundOnSystem p}) - = message ("Using " ++ name ++ " found on system at: " ++ p) -reportProgram _ (Just Program{ programName=name - , programLocation=UserSpecified p}) - = message ("Using " ++ name ++ " given by user at: " ++ p) -reportProgram name Nothing = message ("No " ++ name ++ " found") +reportProgram :: Program -> Maybe ConfiguredProgram -> IO () +reportProgram prog Nothing + = message $ "No " ++ programName prog ++ " found" +reportProgram prog (Just configuredProg) + = message $ "Using " ++ programName prog ++ version ++ location + where location = case programLocation configuredProg of + FoundOnSystem p -> " found on system at: " ++ p + UserSpecified p -> " given by user at: " ++ p + version = case programVersion configuredProg of + Nothing -> "" + Just v -> " version " ++ showVersion v hackageUrl :: String hackageUrl = "http://hackage.haskell.org/cgi-bin/hackage-scripts/package/" @@ -404,54 +397,25 @@ getInstalledPackages comp user verbosity = do -- ----------------------------------------------------------------------------- -- Determining the compiler details -configCompilerAux :: ConfigFlags -> IO Compiler +configCompilerAux :: ConfigFlags -> IO (Compiler, ProgramConfiguration) configCompilerAux cfg = configCompiler (configHcFlavor cfg) (configHcPath cfg) (configHcPkg cfg) + (configPrograms cfg) (configVerbose cfg) configCompiler :: Maybe CompilerFlavor -> Maybe FilePath -> Maybe FilePath - -> Verbosity -> IO Compiler -configCompiler Nothing _ _ _ = die "Unknown compiler" -configCompiler (Just hcFlavor) hcPath hcPkg verbosity - = case hcFlavor of - GHC -> GHC.configure hcPath hcPkg verbosity - JHC -> JHC.configure hcPath hcPkg verbosity - Hugs -> Hugs.configure hcPath hcPkg verbosity - NHC -> NHC.configure hcPath hcPkg verbosity + -> ProgramConfiguration -> Verbosity + -> IO (Compiler, ProgramConfiguration) +configCompiler Nothing _ _ _ _ = die "Unknown compiler" +configCompiler (Just hcFlavor) hcPath hcPkg conf verbosity = do + case hcFlavor of + GHC -> GHC.configure verbosity hcPath hcPkg conf + JHC -> JHC.configure verbosity hcPath hcPkg conf + Hugs -> Hugs.configure verbosity hcPath hcPkg conf + NHC -> NHC.configure verbosity hcPath hcPkg conf _ -> die "Unknown compiler" -findHscolourVersion :: Verbosity -> ProgramConfiguration -> IO ProgramConfiguration -findHscolourVersion verbosity conf = do - mHsColour <- lookupProgram "hscolour" conf - case mHsColour of - Nothing -> return conf - Just Program { programLocation = EmptyLocation } -> return conf - Just prog -> do - -- Invoking "HsColour -version" gives a string like "HsColour 1.7" - prog' <- findProgramAndVersion verbosity (programBinName prog) - (Just $ programPath prog) "-version" $ \str -> - case words str of - (_:ver:_) -> ver - _ -> "" - return (updateProgram prog' { programName = "hscolour" } conf) - -findHaddockVersion :: Verbosity -> ProgramConfiguration -> IO ProgramConfiguration -findHaddockVersion verbosity conf = do - mHaddock <- lookupProgram "haddock" conf - case mHaddock of - Nothing -> return conf - Just Program { programLocation = EmptyLocation } -> return conf - Just prog -> do - -- Invoking "haddock --version" gives a string like - -- "Haddock version 0.8, (c) Simon Marlow 2006" - prog' <- findProgramAndVersion verbosity (programBinName prog) - (Just $ programPath prog) "--version" $ \str -> - case words str of - (_:_:ver:_) -> takeWhile (`elem` ('.':['0'..'9'])) ver - _ -> "" - return (updateProgram prog' conf) - pCheck :: [(a, [Char])] -> [a] pCheck rs = [ r | (r,s) <- rs, all isSpace s ] diff --git a/Distribution/Simple/GHC.hs b/Distribution/Simple/GHC.hs index 99e895f0d3722e13e0d87bd48cdbd3c8688df88f..7f62fb6b2898d1c4b7af64a59b47258fec01c48b 100644 --- a/Distribution/Simple/GHC.hs +++ b/Distribution/Simple/GHC.hs @@ -59,13 +59,15 @@ import Distribution.Simple.LocalBuildInfo import Distribution.Simple.Utils import Distribution.Package ( PackageIdentifier(..), showPackageId ) import Distribution.Program ( rawSystemProgram, rawSystemProgramConf, - Program(..), ProgramConfiguration(..), - ProgramLocation(..), programPath, - findProgram, findProgramAndVersion, - simpleProgramAt, lookupProgram, - arProgram, ranlibProgram ) + Program(..), ConfiguredProgram(..), + ProgramConfiguration, addKnownProgram, + userMaybeSpecifyPath, requireProgram, + programPath, lookupProgram, + ghcProgram, ghcPkgProgram, + arProgram, ranlibProgram, ldProgram ) import Distribution.Compiler -import Distribution.Version ( Version(..) ) +import Distribution.Version ( Version(..), showVersion, + VersionRange(..), orLaterVersion ) import qualified Distribution.Simple.GHCPackageConfig as GHC ( localPackageConfig, canReadLocalPackageConfig ) @@ -92,17 +94,33 @@ import IO as Try -- ----------------------------------------------------------------------------- -- Configuring -configure :: Maybe FilePath -> Maybe FilePath -> Verbosity -> IO Compiler -configure hcPath hcPkgPath verbosity = do - - -- find ghc and version number - ghcProg <- findProgramAndVersion verbosity "ghc" hcPath "--numeric-version" id - - -- find ghc-pkg - ghcPkgProg <- case hcPkgPath of - Just _ -> findProgram verbosity "ghc" hcPkgPath - Nothing -> guessGhcPkgFromGhcPath verbosity ghcProg - -- TODO: santity check: the versions of ghc-pkg and ghc should be the same. +configure :: Verbosity -> Maybe FilePath -> Maybe FilePath + -> ProgramConfiguration -> IO (Compiler, ProgramConfiguration) +configure verbosity hcPath hcPkgPath conf = do + + (ghcProg, conf') <- requireProgram verbosity ghcProgram + (orLaterVersion (Version [6,2] [])) + (userMaybeSpecifyPath "ghc" hcPath conf) + + -- This is slightly tricky, we have to configure ghc first, then we use the + -- location of ghc to help find ghc-pkg in the case that the user did not + -- specify the location of ghc-pkg directly: + (ghcPkgProg, conf'') <- requireProgram verbosity ghcPkgProgram { + programFindLocation = guessGhcPkgFromGhcPath ghcProg + } + (orLaterVersion (Version [0] [])) + (userMaybeSpecifyPath "ghc-pkg" hcPkgPath conf') + + -- finding ghc's local ld is a bit tricky as it's not on the path: + let conf''' = case os of + Windows _ -> + let compilerDir = takeDirectory (programPath ghcProg) + baseDir = takeDirectory compilerDir + binInstallLd = baseDir </> "gcc-lib" </> "ld.exe" + in addKnownProgram ldProgram { + programFindLocation = \_ -> return (Just binInstallLd) + } conf'' + _ -> conf'' let Just version = programVersion ghcProg isSep c = isSpace c || (c == ',') @@ -114,13 +132,15 @@ configure hcPath hcPkgPath verbosity = do | extStr <- breaks isSep exts , (ext, "") <- reads extStr ++ reads ("No" ++ extStr) ] else return oldLanguageExtensions - return Compiler { + + let comp = Compiler { compilerFlavor = GHC, compilerId = PackageIdentifier "ghc" version, compilerProg = ghcProg, compilerPkgTool = ghcPkgProg, compilerExtensions = languageExtensions - } + } + return (comp, conf''') -- | Given something like /usr/local/bin/ghc-6.6.1(.exe) we try and find a -- corresponding ghc-pkg, we try looking for both a versioned and unversioned @@ -129,8 +149,8 @@ configure hcPath hcPkgPath verbosity = do -- > /usr/local/bin/ghc-pkg-6.6.1(.exe) -- > /usr/local/bin/ghc-pkg(.exe) -- -guessGhcPkgFromGhcPath :: Verbosity -> Program -> IO Program -guessGhcPkgFromGhcPath verbosity ghcProg +guessGhcPkgFromGhcPath :: ConfiguredProgram -> Verbosity -> IO (Maybe FilePath) +guessGhcPkgFromGhcPath ghcProg verbosity = do let path = programPath ghcProg dir = takeDirectory path versionSuffix = takeVersionSuffix (dropExeExtension path) @@ -142,10 +162,10 @@ guessGhcPkgFromGhcPath verbosity ghcProg putStrLn $ "looking for package tool: ghc-pkg near compiler in " ++ dir exists <- mapM doesFileExist guesses case [ file | (file, True) <- zip guesses exists ] of - [] -> die "Cannot find package tool: ghc-pkg" + [] -> return Nothing (pkgtool:_) -> do when (verbosity >= verbose) $ putStrLn $ "found package tool in " ++ pkgtool - return (simpleProgramAt "ghc-pkg" (FoundOnSystem pkgtool)) + return (Just pkgtool) where takeVersionSuffix :: FilePath -> String takeVersionSuffix = reverse . takeWhile (`elem ` "0123456789.-") . reverse @@ -281,7 +301,6 @@ build pkg_descr lbi verbosity = do Try.try (removeFile profLibName) -- first remove library if it exists Try.try (removeFile ghciLibName) -- first remove library if it exists - ld <- findLdProgram lbi let arArgs = ["q"++ (if verbosity >= deafening then "v" else "")] ++ [libName] arObjArgs = @@ -306,15 +325,12 @@ build pkg_descr lbi verbosity = do exists <- doesFileExist ghciLibName -- SDM: we always remove ghciLibName above, so isn't this -- always False? What is this stuff for anyway? - rawSystemLd verbosity ld - (args ++ if exists then [ghciLibName] else []) + rawSystemProgramConf verbosity ldProgram (withPrograms lbi) + (args ++ if exists then [ghciLibName] else []) renameFile (ghciLibName <.> "tmp") ghciLibName - runAr = rawSystemProgramConf verbosity "ar" (withPrograms lbi) + runAr = rawSystemProgramConf verbosity arProgram (withPrograms lbi) - rawSystemLd = case os of - Windows MingW -> rawSystemExit - _ -> rawSystemPathExit --TODO: discover this at configure time on unix maxCommandLineSize = 30 * 1024 @@ -466,21 +482,6 @@ mkGHCiLibName :: FilePath -- ^file Prefix -> String mkGHCiLibName pref lib = pref </> ("HS" ++ lib) <.> ".o" - -findLdProgram :: LocalBuildInfo -> IO FilePath -findLdProgram lbi - = case os of - Windows MingW -> - do let compilerDir = takeDirectory $ compilerPath (compiler lbi) - baseDir = takeDirectory compilerDir - binInstallLd = baseDir </> "gcc-lib" </> "ld.exe" - mb <- lookupProgram "ld" (withPrograms lbi) - case fmap programLocation mb of - Just (UserSpecified s) -> return s - -- assume we're using an installed copy of GHC.. - _ -> return binInstallLd - _ -> return "ld" - -- ----------------------------------------------------------------------------- -- Building a Makefile @@ -501,9 +502,10 @@ makefile pkg_descr lbi flags = do packageId | versionBranch ghc_vers >= [6,4] = showPackageId (package pkg_descr) | otherwise = pkgName (package pkg_descr) - mbAr <- lookupProgram "ar" (withPrograms lbi) - let arProg = mbAr `programOrElse` "ar" - ld <- findLdProgram lbi + (arProg, _) <- requireProgram (makefileVerbose flags) arProgram AnyVersion + (withPrograms lbi) + (ldProg, _) <- requireProgram (makefileVerbose flags) ldProgram AnyVersion + (withPrograms lbi) let builddir = buildDir lbi let decls = [ ("modules", unwords (exposedModules lib ++ otherModules bi)), @@ -521,8 +523,8 @@ makefile pkg_descr lbi flags = do ("C_SRCS", unwords (cSources bi)), ("GHC_CC_OPTS", unwords (ghcCcOptions lbi bi (buildDir lbi))), ("GHCI_LIB", mkGHCiLibName builddir (showPackageId (package pkg_descr))), - ("AR", arProg), - ("LD", ld) + ("AR", programPath arProg), + ("LD", programPath ldProg) ] hPutStrLn h "# DO NOT EDIT! Automatically generated by Cabal\n" hPutStrLn h (unlines (map (\(a,b)-> a ++ " = " ++ munge b) decls)) @@ -574,15 +576,11 @@ installLib verbosity programConf hasVanilla hasProf hasGHCi pref buildPref -- use ranlib or ar -s to build an index. this is necessary -- on some systems like MacOS X. If we can't find those, -- don't worry too much about it. - let ranlibProgName = programName $ ranlibProgram - mRanlibProg <- lookupProgram ranlibProgName programConf - case foundProg mRanlibProg of + case lookupProgram ranlibProgram programConf of Just rl -> do ifVanilla $ rawSystemProgram verbosity rl [libTargetLoc] ifProf $ rawSystemProgram verbosity rl [profLibTargetLoc] - Nothing -> do let arProgName = programName $ arProgram - mArProg <- lookupProgram arProgName programConf - case mArProg of + Nothing -> case lookupProgram arProgram programConf of Just ar -> do ifVanilla $ rawSystemProgram verbosity ar ["-s", libTargetLoc] ifProf $ rawSystemProgram verbosity ar ["-s", profLibTargetLoc] Nothing -> setupMessage verbosity "Warning: Unable to generate index for library (missing ranlib and ar)" pd @@ -592,19 +590,3 @@ installLib verbosity programConf hasVanilla hasProf hasGHCi pref buildPref ifGHCi action = when hasGHCi (action >> return ()) installLib _ _ _ _ _ _ _ PackageDescription{library=Nothing} = die $ "Internal Error. installLibGHC called with no library." - --- Also checks whether the program was actually found. -foundProg :: Maybe Program -> Maybe Program -foundProg Nothing = Nothing -foundProg (Just Program{programLocation=EmptyLocation}) = Nothing -foundProg x = x - -programOrElse :: Maybe Program -> FilePath -> FilePath -mb_prog `programOrElse` q = - case mb_prog of - Nothing -> q - Just Program{programLocation=l} -> - case l of - UserSpecified x -> x - FoundOnSystem x -> x - EmptyLocation -> q diff --git a/Distribution/Simple/Haddock.hs b/Distribution/Simple/Haddock.hs index 5341f397d1cdc04c5ebbe34bbf163629493da87d..7109835d15a2606d1104af35ceec3f6b34da2f9a 100644 --- a/Distribution/Simple/Haddock.hs +++ b/Distribution/Simple/Haddock.hs @@ -51,7 +51,7 @@ import Distribution.Compat.ReadP(readP_to_S) import Distribution.Package (showPackageId) import Distribution.PackageDescription import Distribution.ParseUtils(Field(..), readFields, parseCommaList, parseFilePathQ) -import Distribution.Program(lookupProgram, Program(..), programPath, +import Distribution.Program(ConfiguredProgram(..), requireProgram, programPath, hscolourProgram, haddockProgram, rawSystemProgram) import Distribution.PreProcess (ppCpp', ppUnlit, preprocessSources, PPSuffixHandler, runSimplePreProcessor) @@ -72,8 +72,8 @@ import Language.Haskell.Extension -- Base import System.Directory(removeFile) -import Control.Monad(liftM, when, unless, join) -import Data.Maybe ( isJust, fromMaybe, catMaybes ) +import Control.Monad (liftM, when, join) +import Data.Maybe ( isJust, catMaybes ) import Distribution.Compat.Directory(removeDirectoryRecursive, copyFile) import System.FilePath((</>), (<.>), splitFileName, splitExtension, @@ -99,10 +99,8 @@ haddock pkg_descr lbi suffixes haddockFlags@HaddockFlags { when hsColour $ hscolour pkg_descr lbi suffixes $ HscolourFlags hsColourCss doExes verbosity - confHaddock <- do let programConf = withPrograms lbi - let haddockPath = programName haddockProgram - mHaddock <- lookupProgram haddockPath programConf - maybe (die "haddock command not found") return mHaddock + (confHaddock, _) <- requireProgram verbosity haddockProgram + (orLaterVersion (Version [0,6] [])) (withPrograms lbi) let tmpDir = buildDir lbi </> "tmp" createDirectoryIfMissingVerbose verbosity True tmpDir @@ -117,8 +115,7 @@ haddock pkg_descr lbi suffixes haddockFlags@HaddockFlags { let outputFlag = if haddockHoogle haddockFlags then "--hoogle" else "--html" - let version = fromMaybe (error "could not find haddock version") - (programVersion confHaddock) + let Just version = programVersion confHaddock let have_src_hyperlink_flags = version >= Version [0,8] [] have_new_flags = version > Version [0,8] [] let comp = compiler lbi @@ -260,15 +257,8 @@ haddock pkg_descr lbi suffixes haddockFlags@HaddockFlags { hscolour :: PackageDescription -> LocalBuildInfo -> [PPSuffixHandler] -> HscolourFlags -> IO () hscolour pkg_descr lbi suffixes (HscolourFlags stylesheet doExes verbosity) = do - confHscolour <- do let programConf = withPrograms lbi - let hscolourPath = programName hscolourProgram - mHscol <- lookupProgram hscolourPath programConf - maybe (die "HsColour command not found") return mHscol - - let version = fromMaybe (error "could not find hscolour version") - (programVersion confHscolour) - haveLines = version >= Version [1,8] [] - unless haveLines $ die "hscolour version >= 1.8 required" + (confHscolour, _) <- requireProgram verbosity hscolourProgram + (orLaterVersion (Version [1,8] [])) (withPrograms lbi) createDirectoryIfMissingVerbose verbosity True $ hscolourPref pkg_descr preprocessSources pkg_descr lbi False verbosity suffixes diff --git a/Distribution/Simple/Hugs.hs b/Distribution/Simple/Hugs.hs index 68597d636c4a519629fddfd824ed85006f1ea07e..45b650b2eb6c84f8dd268ff0226655dc8e401503 100644 --- a/Distribution/Simple/Hugs.hs +++ b/Distribution/Simple/Hugs.hs @@ -51,7 +51,10 @@ import Distribution.PackageDescription Executable(..), withExe, Library(..), libModules, hcOptions, autogenModuleName ) import Distribution.Compiler ( Compiler(..), CompilerFlavor(..), Flag ) -import Distribution.Program ( rawSystemProgram, findProgram ) +import Distribution.Program ( ProgramConfiguration, userMaybeSpecifyPath, + requireProgram, rawSystemProgram, + ffihugsProgram, hugsProgram ) +import Distribution.Version ( Version(..), VersionRange(AnyVersion) ) import Distribution.PreProcess ( ppCpp, runSimplePreProcessor ) import Distribution.PreProcess.Unlit ( unlit ) @@ -81,28 +84,27 @@ import IO ( try ) import Data.List ( nub, sort, isSuffixOf ) import System.Directory ( Permissions(..), getPermissions, setPermissions ) -import Distribution.Version -- ----------------------------------------------------------------------------- -- Configuring -configure :: Maybe FilePath -> Maybe FilePath -> Verbosity -> IO Compiler -configure hcPath _hcPkgPath verbosity = do +configure :: Verbosity -> Maybe FilePath -> Maybe FilePath + -> ProgramConfiguration -> IO (Compiler, ProgramConfiguration) +configure verbosity hcPath _hcPkgPath conf = do - -- find ffihugs - ffihugsProg <- findProgram verbosity "ffihugs" hcPath + (ffihugsProg, conf') <- requireProgram verbosity ffihugsProgram AnyVersion + (userMaybeSpecifyPath "ffihugs" hcPath conf) + (hugsProg, conf'') <- requireProgram verbosity hugsProgram AnyVersion conf' - -- find hugs - hugsProg <- findProgram verbosity "hugs" hcPath - - return Compiler { + let comp = Compiler { compilerFlavor = Hugs, compilerId = PackageIdentifier "hugs" (Version [] []), compilerProg = ffihugsProg, compilerPkgTool = hugsProg, compilerExtensions = hugsLanguageExtensions - } + } + return (comp, conf'') -- | The flags for the supported extensions hugsLanguageExtensions :: [(Extension, Flag)] diff --git a/Distribution/Simple/JHC.hs b/Distribution/Simple/JHC.hs index 0c667a7a8c644c784e9653901e68423301aa70b0..7ff58418c991ad845dd65b87e43be03573409e7f 100644 --- a/Distribution/Simple/JHC.hs +++ b/Distribution/Simple/JHC.hs @@ -55,8 +55,10 @@ import Distribution.Simple.LocalBuildInfo import Distribution.Compiler ( Compiler(..), CompilerFlavor(..), Flag, extensionsToFlags ) import Language.Haskell.Extension (Extension(..)) -import Distribution.Program ( Program(..), rawSystemProgram, - findProgramAndVersion ) +import Distribution.Program ( ConfiguredProgram(..), jhcProgram, + ProgramConfiguration, userMaybeSpecifyPath, + requireProgram, rawSystemProgram ) +import Distribution.Version ( VersionRange(AnyVersion) ) import Distribution.Package ( PackageIdentifier(..), showPackageId ) import Distribution.Simple.Utils( createDirectoryIfMissingVerbose, copyFileVerbose, exeExtension ) @@ -70,23 +72,22 @@ import Data.List ( nub, intersperse ) -- ----------------------------------------------------------------------------- -- Configuring -configure :: Maybe FilePath -> Maybe FilePath -> Verbosity -> IO Compiler -configure hcPath _hcPkgPath verbosity = do +configure :: Verbosity -> Maybe FilePath -> Maybe FilePath + -> ProgramConfiguration -> IO (Compiler, ProgramConfiguration) +configure verbosity hcPath _hcPkgPath conf = do - -- find jhc - jhcProg <- findProgramAndVersion verbosity "jhc" hcPath "--version" $ \str -> - case words str of - (_:ver:_) -> ver - _ -> "" + (jhcProg, conf') <- requireProgram verbosity jhcProgram AnyVersion + (userMaybeSpecifyPath "jhc" hcPath conf) let Just version = programVersion jhcProg - return Compiler { + comp = Compiler { compilerFlavor = JHC, compilerId = PackageIdentifier "jhc" version, compilerProg = jhcProg, compilerPkgTool = jhcProg, compilerExtensions = jhcLanguageExtensions - } + } + return (comp, conf') -- | The flags for the supported extensions jhcLanguageExtensions :: [(Extension, Flag)] diff --git a/Distribution/Simple/NHC.hs b/Distribution/Simple/NHC.hs index a1304f7586df59ebe9270e5e67794c1f2f372ae8..a7a9c4065750df54bb2d47a9e8f6ad8b588d41f5 100644 --- a/Distribution/Simple/NHC.hs +++ b/Distribution/Simple/NHC.hs @@ -53,30 +53,31 @@ import Distribution.Simple.LocalBuildInfo import Distribution.Compiler ( Compiler(..), CompilerFlavor(..), Flag, extensionsToFlags ) import Language.Haskell.Extension (Extension(..)) -import Distribution.Program ( rawSystemProgram, findProgramAndVersion ) +import Distribution.Program ( ProgramConfiguration, userMaybeSpecifyPath, + requireProgram, hmakeProgram, + rawSystemProgram ) +import Distribution.Version ( VersionRange(AnyVersion) ) import Distribution.Verbosity -- ----------------------------------------------------------------------------- -- Configuring -configure :: Maybe FilePath -> Maybe FilePath -> Verbosity -> IO Compiler -configure hcPath _hcPkgPath verbosity = do +configure :: Verbosity -> Maybe FilePath -> Maybe FilePath + -> ProgramConfiguration -> IO (Compiler, ProgramConfiguration) +configure verbosity hcPath _hcPkgPath conf = do - -- find hmake - -- TODO: why are we checking the version of hmake rather than nhc? - hmakeProg <- findProgramAndVersion verbosity "hmake" hcPath "--version" $ \str -> - case words str of - (_:ver:_) -> ver - _ -> "" + (hmakeProg, conf') <- requireProgram verbosity hmakeProgram AnyVersion + (userMaybeSpecifyPath "hmake" hcPath conf) - return Compiler { + let comp = Compiler { compilerFlavor = NHC, compilerId = error "TODO: nhc compilerId", --PackageIdentifier "nhc" version compilerProg = hmakeProg, compilerPkgTool = hmakeProg, compilerExtensions = nhcLanguageExtensions - } + } + return (comp, conf') -- | The flags for the supported extensions nhcLanguageExtensions :: [(Extension, Flag)] diff --git a/Distribution/Simple/Register.hs b/Distribution/Simple/Register.hs index 21378b2e7cea60d96a0b4ccb41bc9875db42fffc..0e4fb8cbb7814fa1e638790f7d6cb52542e00013 100644 --- a/Distribution/Simple/Register.hs +++ b/Distribution/Simple/Register.hs @@ -67,7 +67,7 @@ import Distribution.Simple.LocalBuildInfo (LocalBuildInfo(..), distPref, absoluteInstallDirs, toPathTemplate) import Distribution.Compiler (CompilerFlavor(..), Compiler(..), compilerPkgToolPath, compilerVersion) -import Distribution.Program (Program(..), ProgramLocation(..)) +import Distribution.Program (ConfiguredProgram(..), ProgramLocation(..)) import Distribution.Setup (RegisterFlags(..), CopyDest(..), userOverride) import Distribution.PackageDescription (setupMessage, PackageDescription(..), BuildInfo(..), Library(..), haddockName) diff --git a/Distribution/Simple/SetupWrapper.hs b/Distribution/Simple/SetupWrapper.hs index 18dea4200a7dfab806532ad873f7032c08fb4d6b..66e056a07d2f6034302c190b1b86713e8a2298f2 100644 --- a/Distribution/Simple/SetupWrapper.hs +++ b/Distribution/Simple/SetupWrapper.hs @@ -27,7 +27,8 @@ import Distribution.PackageDescription packageDescription, PackageDescription(..), BuildType(..), cabalVersion ) -import System.Console.GetOpt +import Distribution.Program ( emptyProgramConfiguration ) +import Distribution.GetOpt import System.Directory import Distribution.Compat.Exception ( finally ) import Distribution.Verbosity @@ -63,8 +64,8 @@ setupWrapper args mdir = inDir mdir $ do pkg_descr_file <- defaultPackageDesc (verbosity flags) ppkg_descr <- readPackageDescription (verbosity flags) pkg_descr_file - comp <- configCompiler (Just GHC) (withCompiler flags) (withHcPkg flags) - normal + (comp, _) <- configCompiler (Just GHC) (withCompiler flags) (withHcPkg flags) + emptyProgramConfiguration normal cabal_flag <- configCabalFlag flags (descCabalVersion (packageDescription ppkg_descr)) comp let diff --git a/Distribution/Simple/SrcDist.hs b/Distribution/Simple/SrcDist.hs index 1df47aeea56fdcd8d4e00424eea9f2436823180d..bc4139bed68d699cd029dfcf997634c5c40f27f9 100644 --- a/Distribution/Simple/SrcDist.hs +++ b/Distribution/Simple/SrcDist.hs @@ -61,14 +61,15 @@ import Distribution.PackageDescription (PackageDescription(..), BuildInfo(..), Executable(..), Library(..), withLib, withExe, setupMessage) import Distribution.Package (showPackageId, PackageIdentifier(pkgVersion)) -import Distribution.Version (Version(versionBranch)) +import Distribution.Version (Version(versionBranch), VersionRange(AnyVersion)) import Distribution.Simple.Utils (createDirectoryIfMissingVerbose, smartCopySources, die, findPackageDesc, - findFile, copyFileVerbose, rawSystemPathExit) + findFile, copyFileVerbose) import Distribution.Setup (SDistFlags(..)) import Distribution.PreProcess (PPSuffixHandler, ppSuffixes, preprocessSources) import Distribution.Simple.LocalBuildInfo ( LocalBuildInfo(..) ) -import Distribution.Program ( lookupProgram, ProgramLocation(..), Program(programLocation) ) +import Distribution.Program ( defaultProgramConfiguration, requireProgram, + rawSystemProgram, tarProgram ) #ifndef __NHC__ import Control.Exception (finally) @@ -198,19 +199,14 @@ createArchive :: PackageDescription -- ^info from cabal file createArchive pkg_descr verbosity mb_lbi tmpDir targetPref = do let tarBallFilePath = targetPref </> tarBallName pkg_descr - let tarDefault = "tar" - tarProgram <- - case mb_lbi of - Nothing -> return tarDefault - Just lbi -> do - mb <- lookupProgram "tar" (withPrograms lbi) - case fmap programLocation mb of - Just (UserSpecified s) -> return s - _ -> return tarDefault + + (tarProg, _) <- requireProgram verbosity tarProgram AnyVersion + (maybe defaultProgramConfiguration withPrograms mb_lbi) + -- Hmm: I could well be skating on thinner ice here by using the -C option (=> GNU tar-specific?) -- [The prev. solution used pipes and sub-command sequences to set up the paths correctly, -- which is problematic in a Windows setting.] - rawSystemPathExit verbosity tarProgram + rawSystemProgram verbosity tarProg ["-C", tmpDir, "-czf", tarBallFilePath, nameVersion pkg_descr] -- XXX this should be done back where tmpDir is made, not here `finally` removeDirectoryRecursive tmpDir