cabal-dev-0.8: src/Distribution/Dev/CabalInstall.hs
{-# LANGUAGE TemplateHaskell #-}
module Distribution.Dev.CabalInstall
( findOnPath
, program
, getFeatures
, CabalFeatures
, needsQuotes
, hasOnlyDependencies
, configDir
, CabalCommand(..)
, Option(..)
, OptionName(..)
, ArgType(..)
, matchLongOption
, commandToString
, stringToCommand
, allCommands
, commandOptions
, supportsLongOption
, supportedOptions
, getUserConfig
)
where
import Data.Maybe ( fromMaybe )
import Control.Applicative ( (<$>), pure )
import System.FilePath ( (</>) )
import System.Environment ( getEnvironment )
import Distribution.Version ( Version(..), withinRange
, earlierVersion, orLaterVersion )
import Distribution.Verbosity ( Verbosity )
import Distribution.Simple.Program ( Program( programFindVersion
)
, ConfiguredProgram
, emptyProgramConfiguration
, findProgramVersion
, programLocation
, programVersion
, requireProgram
, getProgramOutput
, simpleProgram
)
import Distribution.Simple.Utils ( debug )
import Distribution.Text ( display, simpleParse )
import System.Directory ( getAppUserDataDirectory )
import Distribution.Dev.InterrogateCabalInstall
( Option(..), OptionName(..), ArgType(..) )
import Distribution.Dev.TH.DeriveCabalCommands
( deriveCabalCommands )
-- XXX This is duplicated in Setup.hs
-- |Definition of the cabal-install program
program :: Maybe String -> Program
program p =
(simpleProgram $ fromMaybe "cabal" p) { programFindVersion =
findProgramVersion "--numeric-version" id
}
-- |Find cabal-install on the user's PATH
findOnPath :: Verbosity -> Maybe FilePath -> IO ConfiguredProgram
findOnPath v ci = do
(cabal, _) <- requireProgram v (program ci) emptyProgramConfiguration
debug v $ concat [ "Using cabal-install "
, maybe "(unknown version)" display $ programVersion cabal
, " at "
, show (programLocation cabal)
]
return cabal
-- |Parse the Cabal library version from the output of cabal --version
parseVersionOutput :: String -> Either String Version
parseVersionOutput str =
case lines str of
[] -> Left "No version string provided."
[_] -> Left "Could not find Cabal version line."
(_:ln:_) -> case simpleParse ((words ln)!!2) of
Just v -> Right v
Nothing -> Left $ err ln
where err line = "Could not parse Cabal verison.\n"
++ "(simpleParse "++show line++")"
-- |The information necessary to properly invoke cabal-install
data CabalFeatures = CabalFeatures { cfLibVersion :: Version
, cfExeVersion :: Version
}
mkVer :: [Int] -> Version
mkVer l = Version l []
-- |Extract the features of this cabal-install executable
getFeatures :: Verbosity -> ConfiguredProgram ->
IO (Either String CabalFeatures)
getFeatures v cabal = do
case programVersion cabal of
Nothing -> return $ Left "Failed to find cabal-install version"
Just exeVer -> do
verRes <- parseVersionOutput <$> getProgramOutput v cabal ["--version"]
case verRes of
Left err -> return $ Left $ "Detecting cabal-install's Cabal: " ++ err
Right libVer -> return $ Right $
CabalFeatures { cfLibVersion = libVer
, cfExeVersion = exeVer
}
-- |Does the cabal-install configuration file use quoted paths in the
-- install-dirs section?
needsQuotes :: CabalFeatures -> Bool
needsQuotes = (`withinRange` earlierVersion (mkVer [1,10])) . cfLibVersion
-- |Does this cabal-install executable support the --dependencies-only
-- flag to install?
hasOnlyDependencies :: CabalFeatures -> Bool
hasOnlyDependencies =
(`withinRange` orLaterVersion (mkVer [0, 10])) . cfExeVersion
$(deriveCabalCommands)
supportsLongOption :: CabalCommand -> String -> Bool
supportsLongOption cc s = any ((`matchLongOption` s) . optionName) $ supportedOptions cc
optionName :: Option -> OptionName
optionName (Option n _) = n
supportedOptions :: CabalCommand -> [Option]
supportedOptions cc = commonOptions ++ commandOptions cc
matchLongOption :: OptionName -> String -> Bool
matchLongOption (Short _) = const False
matchLongOption (LongOption s) = (== s)
commonOptions :: [Option]
commonOptions = [Option (LongOption "config-file") Req]
-- |What is the configuration directory for this cabal-install executable?
-- XXX: This needs to do something different for certain platforms for
-- new versions of cabal-install (look at the tickets on creswick's
-- cabal-dev repo)
configDir :: CabalFeatures -> IO FilePath
configDir _ = getAppUserDataDirectory "cabal"
getUserConfig :: CabalFeatures -> IO FilePath
getUserConfig cf = do
env <- lookup "CABAL_CONFIG" <$> getEnvironment
case env of
Nothing -> (</> "config") <$> configDir cf
Just f -> pure f