1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369
|
\begin{code}
{-# LANGUAGE CPP #-}
-- copyright (c) 2008 Duncan Coutts
-- portions copyright (c) 2008 David Roundy
-- portions copyright (c) 2007-2009 Judah Jacobson
import Prelude hiding ( catch )
import qualified Prelude
import qualified Distribution.InstalledPackageInfo as Installed
import Distribution.Simple
( defaultMainWithHooks, UserHooks(..), simpleUserHooks )
import Distribution.Simple.Configure
( checkForeignDeps )
import Distribution.ModuleName( toFilePath )
import Distribution.PackageDescription
( PackageDescription(executables), Executable(buildInfo, exeName)
, BuildInfo(customFieldsBI), emptyBuildInfo
, FlagName(FlagName)
, updatePackageDescription
, cppOptions, ccOptions, ldOptions
, library, libBuildInfo, otherModules
, extraLibs, extraLibDirs, includeDirs )
import Distribution.Package
( packageVersion, packageName, PackageName(..) )
import Distribution.Version
( Version(versionBranch) )
import Data.Version( showVersion )
import Distribution.Simple.LocalBuildInfo
( LocalBuildInfo(..), absoluteInstallDirs, externalPackageDeps )
import Distribution.Simple.InstallDirs (mandir, CopyDest (NoCopyDest))
import Distribution.Simple.PackageIndex ( topologicalOrder )
import Distribution.Simple.Program ( gccProgram, rawSystemProgramStdoutConf )
import Distribution.Simple.Setup
(buildVerbosity, copyDest, copyVerbosity, fromFlag,
haddockVerbosity, installVerbosity, sDistVerbosity,
configVerbosity, ConfigFlags, configConfigurationsFlags)
import Distribution.Simple.BuildPaths
( autogenModulesDir, exeExtension )
import Distribution.System
( OS(Windows), buildOS )
import Distribution.Simple.Utils
(copyFiles, createDirectoryIfMissingVerbose, rawSystemStdout,
rewriteFile, withTempFile)
import Distribution.Verbosity
( Verbosity )
import Distribution.Text
( display )
import Distribution.Package (Package)
import Control.Monad ( zipWithM_, when, unless, filterM )
import Control.Exception ( bracket, handle, IOException )
import System.Directory
(copyFile, createDirectory, createDirectoryIfMissing,
doesDirectoryExist, doesFileExist,
getCurrentDirectory, getDirectoryContents,
removeDirectoryRecursive, removeFile, setCurrentDirectory,
getTemporaryDirectory
)
import System.Exit ( ExitCode(ExitSuccess) )
import System.IO
( openFile, IOMode (..), stdout
, hPutStr, hFlush, hClose
)
import System.Process (runProcess)
import System.IO.Error ( isDoesNotExistError )
import Data.List( isPrefixOf, isSuffixOf, sort, partition )
import System.Cmd( rawSystem )
import System.Exit( exitWith )
import System.FilePath ( (</>), (<.>), splitDirectories, isAbsolute )
import Foreign.Marshal.Utils ( with )
import Foreign.Storable ( peek )
import Foreign.Ptr ( castPtr )
import Data.Word ( Word8, Word32 )
import qualified Control.Exception as Exception
-- Handle exceptions migration. We could use extensible-exceptions
-- but Cabal can't handle package dependencies of Setup.lhs
-- automatically so it'd be disruptive for users.
-- Once we drop older GHCs we can clean up the use sites properly
-- and perhaps think about being more restrictive in which exceptions
-- are caught at each site.
#if __GLASGOW_HASKELL__ >= 610
catchAny f h = Exception.catch f (\e -> h (e :: Exception.SomeException))
#else
catchAny = Exception.catch
#endif
main :: IO ()
main = defaultMainWithHooks simpleUserHooks {
buildHook = \ pkg lbi hooks flags ->
let verb = fromFlag $ buildVerbosity flags
in commonBuildHook buildHook pkg lbi hooks verb >>= ($ flags),
haddockHook = \ pkg lbi hooks flags ->
let verb = fromFlag $ haddockVerbosity flags
in commonBuildHook haddockHook pkg lbi hooks verb >>= ($ flags) ,
postBuild = \ _ _ _ lbi -> buildManpage lbi,
postCopy = \ _ flags pkg lbi ->
installManpage pkg lbi (fromFlag $ copyVerbosity flags) (fromFlag $ copyDest flags),
postInst = \ _ flags pkg lbi ->
installManpage pkg lbi (fromFlag $ installVerbosity flags) NoCopyDest,
runTests = \ args0 _ _ lbi -> do
cwd <- getCurrentDirectory
let isabs = isAbsolute $ buildDir lbi
builddir = (if isabs then id else (cwd </>)) $ buildDir lbi
darcs = builddir </> "darcs" </> "darcs" <.> exeExtension
darcstest = builddir </> "darcs-test" </> "darcs-test" <.> exeExtension
(flags, args1) = partition ('=' `elem`) args0
(what', args2) = partition (`elem` ["bugs", "network", "tests", "unit"]) args1
what = if null what' then ["tests"] else what'
opts = [ "--darcs", darcs ] ++ concat [ ["-t", x] | x <- args2 ] ++
[ "--no-unit" | "unit" `notElem` what] ++
[ "--network" | "network" <- what ] ++ [ "--failing" | "bugs" <- what ] ++
[ "--no-shell" | _ <- [()], "tests" `notElem` what ] ++
[ "--threads=" ++ drop 8 t | t <- flags, "threads" `isPrefixOf` t ] ++
[ "--plain" | "output=plain" <- flags ]
rawSystem darcstest opts >>= exitWith
,
sDistHook = \ pkg lbi hooks flags -> do
let pkgVer = packageVersion pkg
verb = fromFlag $ sDistVerbosity flags
x <- versionPatches verb pkgVer
y <- context verb pkgVer
rewriteFile "release/distributed-version" $ show x
rewriteFile "release/distributed-context" $ show y
putStrLn "about to hand over"
let pkg' = pkg { library = sanity (library pkg) }
sanity (Just lib) = Just $ lib { libBuildInfo = sanity' $ libBuildInfo lib }
sanity _ = error "eh"
sanity' bi = bi { otherModules = [ m | m <- otherModules bi, toFilePath m /= "Version" ] }
sDistHook simpleUserHooks pkg' lbi hooks flags
,
confHook =
if buildOS == Windows
then confHook simpleUserHooks
else
\genericDescript flags -> do
lbi <- confHook simpleUserHooks genericDescript flags
let pkgDescr = localPkgDescr lbi
let verb = fromFlag (configVerbosity flags)
checkForeignDeps pkgDescr lbi verb
let Just lib = library pkgDescr
let bi = libBuildInfo lib
bi' <- maybeSetLibiconv flags bi lbi
return lbi {localPkgDescr = pkgDescr {
library = Just lib {
libBuildInfo = bi'}}}
,
postConf = \_ _ _ _ -> return () --- Usually this checked for external C
--- dependencies, but we already have performed such
--- check in the confHook
}
-- | For @./Setup build@ and @./Setup haddock@, do some unusual
-- things, then invoke the base behaviour ("simple hook").
commonBuildHook :: (UserHooks -> PackageDescription -> LocalBuildInfo -> t -> a)
-> PackageDescription -> LocalBuildInfo -> t -> Verbosity -> IO a
commonBuildHook runHook pkg lbi hooks verbosity = do
(version, state) <- determineVersion verbosity pkg
-- Create our own context file.
generateVersionModule verbosity pkg lbi version state
-- Add custom -DFOO[=BAR] flags to the cpp (for .hs) and cc (for .c)
-- invocations, doing a dance to make the base hook aware of them.
littleEndian <- testEndianness
let args = ("-DPACKAGE_VERSION=" ++ show' version) :
("-DPACKAGE_VERSION_STATE=" ++ show' state) :
[arg | (arg, True) <- -- include fst iff snd.
[("-DHAVE_HTTP", "x-have-http" `elem` customFields),
("-DUSE_COLOR", "x-use-color" `elem` customFields),
-- We have MAPI iff building on/for Windows.
("-DHAVE_MAPI", buildOS == Windows),
("-DBIGENDIAN", not littleEndian)]]
bi = emptyBuildInfo { cppOptions = args, ccOptions = args }
hbi = (Just bi, [(exeName exe, bi) | exe <- executables pkg])
pkg' = updatePackageDescription hbi pkg
lbi' = lbi { localPkgDescr = pkg' }
return $ runHook simpleUserHooks pkg' lbi' hooks
where
customFields = map fst . customFieldsBI . buildInfo $ darcsExe
darcsExe = head [e | e <- executables pkg, exeName e == "darcs"]
show' :: String -> String -- Petr was worried that we might
show' = show -- allow non-String arguments.
testEndianness :: IO Bool
testEndianness = with (1 :: Word32) $ \p -> do o <- peek $ castPtr p
return $ o == (1 :: Word8)
buildManpage :: LocalBuildInfo -> IO ()
buildManpage lbi = do
let darcs = buildDir lbi </> "darcs/darcs"
manpage = buildDir lbi </> "darcs/darcs.1"
manpageHandle <- openFile manpage WriteMode
runProcess darcs ["help","manpage"]
Nothing Nothing Nothing (Just manpageHandle) Nothing
return ()
installManpage :: PackageDescription -> LocalBuildInfo
-> Verbosity -> CopyDest -> IO ()
installManpage pkg lbi verbosity copy =
copyFiles verbosity
(mandir (absoluteInstallDirs pkg lbi copy) </> "man1")
[(buildDir lbi </> "darcs", "darcs.1")]
determineVersion :: Verbosity -> PackageDescription -> IO (String, String)
determineVersion verbosity pkg = do
let darcsVersion = packageVersion pkg
numPatches <- versionPatches verbosity darcsVersion
return (display darcsVersion, versionStateString numPatches darcsVersion)
where
versionStateString :: Maybe Int -> Version -> String
versionStateString Nothing _ = "unknown"
versionStateString (Just 0) v = case versionBranch v of
x | 97 `elem` x -> "alpha " ++ show (after 97 x)
| 98 `elem` x -> "beta " ++ show (after 98 x)
| 99 `elem` x ->
"release candidate " ++ show (after 99 x)
_ -> "release"
versionStateString (Just 1) _ = "+ 1 patch"
versionStateString (Just n) _ = "+ " ++ show n ++ " patches"
after w (x:r) | w == x = head r
| otherwise = after w r
after _ [] = undefined
versionPatches :: Verbosity -> Version -> IO (Maybe Int)
versionPatches verbosity darcsVersion = do
numPatchesDarcs <- do
out <- rawSystemStdout verbosity "darcs"
["changes", "-a", "--from-tag", display darcsVersion, "--count"]
case reads (out) of
((n,_):_) -> return $ Just ((n :: Int) - 1)
_ -> return Nothing
`catchAny` \_ -> return Nothing
numPatchesDist <- parseFile versionFile
return $ case (numPatchesDarcs, numPatchesDist) of
(Just x, _) -> Just x
(Nothing, Just x) -> Just x
(Nothing, Nothing) -> Nothing
where
versionFile = "release/distributed-version"
generateVersionModule :: Verbosity -> PackageDescription -> LocalBuildInfo
-> String -> String -> IO ()
generateVersionModule verbosity pkg lbi version state = do
let dir = autogenModulesDir lbi
createDirectoryIfMissingVerbose verbosity True dir
ctx <- context verbosity (packageVersion pkg)
rewriteFile (dir </> "Version.hs") $ unlines
["module Version where"
,"builddeps, version, context :: String"
,"version = \"" ++ version ++ " (" ++ state ++ ")\""
,"builddeps = " ++ (show $ formatdeps (externalPackageDeps lbi))
,"context = " ++ case ctx of
Just x -> show x
Nothing -> show "context not available"
]
where formatdeps = unlines . map (formatone . snd)
formatone p = case packageName p of PackageName n -> n ++ "-" ++ showVersion (packageVersion p)
context :: Verbosity -> Version -> IO (Maybe String)
context verbosity version = do
contextDarcs <- do
inrepo <- doesDirectoryExist "_darcs"
unless inrepo $ fail "Not a repository."
out <- rawSystemStdout verbosity "darcs" ["changes", "-a", "--context"]
return $ Just out
`catchAny` \_ -> return Nothing
contextDist <- parseFile contextFile
return $ case (contextDarcs, contextDist) of
(Just x, _) -> Just x
(Nothing, Just x) -> Just x
(Nothing, Nothing) -> Nothing
where contextFile = "release/distributed-context"
parseFile :: (Read a) => String -> IO (Maybe a)
parseFile f = do
exist <- doesFileExist f
if exist then do
content <- readFile f -- ^ ratify readFile: we don't care here.
case reads content of
((s,_):_) -> return s
_ -> return Nothing
else return Nothing
-- Test whether compiling a c program that links against libiconv needs -liconv.
maybeSetLibiconv :: ConfigFlags -> BuildInfo -> LocalBuildInfo -> IO BuildInfo
maybeSetLibiconv flags bi lbi = do
let biWithIconv = addIconv bi
let verb = fromFlag (configVerbosity flags)
if hasFlagSet flags (FlagName "libiconv")
then do
putStrLn "Using -liconv."
return biWithIconv
else do
putStr "checking whether to use -liconv... "
hFlush stdout
worksWithout <- tryCompile iconv_prog bi lbi verb
if worksWithout
then do
putStrLn "not needed."
return bi
else do
worksWith <- tryCompile iconv_prog biWithIconv lbi verb
if worksWith
then do
putStrLn "using -liconv."
return biWithIconv
else error "Unable to link against the iconv library."
hasFlagSet :: ConfigFlags -> FlagName -> Bool
hasFlagSet cflags flag = Just True == lookup flag (configConfigurationsFlags cflags)
tryCompile :: String -> BuildInfo -> LocalBuildInfo -> Verbosity -> IO Bool
tryCompile program bi lbi verb = handle processExit $ handle processException $ do
tempDir <- getTemporaryDirectory
withTempFile tempDir ".c" $ \fname cH ->
withTempFile tempDir "" $ \execName oH -> do
hPutStr cH program
hClose cH
hClose oH
-- TODO take verbosity from the args.
rawSystemProgramStdoutConf verb gccProgram (withPrograms lbi)
(fname : "-o" : execName : args)
return True
where
processException :: IOException -> IO Bool
processException e = return False
processExit = return . (==ExitSuccess)
-- Mimicing Distribution.Simple.Configure
deps = topologicalOrder (installedPkgs lbi)
args = concat
[ ccOptions bi
, cppOptions bi
, ldOptions bi
-- --extra-include-dirs and --extra-lib-dirs are included
-- in the below fields.
-- Also sometimes a dependency like rts points to a nonstandard
-- include/lib directory where iconv can be found.
, map ("-I" ++) (includeDirs bi ++ concatMap Installed.includeDirs deps)
, map ("-L" ++) (extraLibDirs bi ++ concatMap Installed.libraryDirs deps)
, map ("-l" ++) (extraLibs bi)
]
addIconv :: BuildInfo -> BuildInfo
addIconv bi = bi {extraLibs = "iconv" : extraLibs bi}
iconv_prog :: String
iconv_prog = unlines $
[ "#include <iconv.h>"
, "int main(void) {"
, " iconv_t t = iconv_open(\"UTF-8\", \"UTF-8\");"
, " return 0;"
, "}"
]
\end{code}
|