Drop cycles

This commit is contained in:
Chris Done 2015-01-14 01:24:25 +01:00
parent 585e8bd1c7
commit 794881627e
2 changed files with 136 additions and 117 deletions

View File

@ -8,6 +8,7 @@
-- | Confirm that a build plan has a consistent set of dependencies. -- | Confirm that a build plan has a consistent set of dependencies.
module Stackage.CheckBuildPlan module Stackage.CheckBuildPlan
( checkBuildPlan ( checkBuildPlan
, libAndExe
, BadBuildPlan , BadBuildPlan
) where ) where
@ -29,10 +30,13 @@ checkBuildPlan BuildPlan {..}
map (ppVersion &&& M.keys . M.filter libAndExe . sdPackages . ppDesc) bpPackages map (ppVersion &&& M.keys . M.filter libAndExe . sdPackages . ppDesc) bpPackages
errs@(BadBuildPlan errs') = errs@(BadBuildPlan errs') =
execWriter $ mapM_ (checkDeps allPackages) $ mapToList bpPackages execWriter $ mapM_ (checkDeps allPackages) $ mapToList bpPackages
-- Only looking at libraries and executables, benchmarks and tests
-- are allowed to create cycles (e.g. test-framework depends on
-- text, which uses test-framework in its test-suite). -- Only looking at libraries and executables, benchmarks and tests
libAndExe (DepInfo cs _) = any (flip elem [CompLibrary,CompExecutable]) cs -- are allowed to create cycles (e.g. test-framework depends on
-- text, which uses test-framework in its test-suite).
libAndExe :: DepInfo -> Bool
libAndExe (DepInfo cs _) = any (flip elem [CompLibrary,CompExecutable]) cs
-- | For a given package name and plan, check that its dependencies are: -- | For a given package name and plan, check that its dependencies are:
-- --

View File

@ -4,9 +4,10 @@
module Stackage.ShakeBuild where module Stackage.ShakeBuild where
import Data.Monoid import Control.Concurrent.MVar
import Stackage.BuildConstraints import Stackage.BuildConstraints
import Stackage.BuildPlan import Stackage.BuildPlan
import Stackage.CheckBuildPlan
import Stackage.PackageDescription import Stackage.PackageDescription
import Stackage.PerformBuild (PerformBuild(..),copyBuiltInHaddocks,renameOrCopy) import Stackage.PerformBuild (PerformBuild(..),copyBuiltInHaddocks,renameOrCopy)
import Stackage.Prelude (unFlagName) import Stackage.Prelude (unFlagName)
@ -17,6 +18,7 @@ import Control.Exception
import Control.Monad hiding (forM_) import Control.Monad hiding (forM_)
import Data.Map.Strict (Map) import Data.Map.Strict (Map)
import qualified Data.Map.Strict as M import qualified Data.Map.Strict as M
import Data.Monoid
import qualified Data.Set as S import qualified Data.Set as S
import qualified Data.Text as T import qualified Data.Text as T
import Development.Shake hiding (doesFileExist,doesDirectoryExist) import Development.Shake hiding (doesFileExist,doesDirectoryExist)
@ -32,81 +34,94 @@ performBuild pb = do
shakeDir <- fmap (<//> "shake/") (getCurrentDirectory >>= canonicalizePath) shakeDir <- fmap (<//> "shake/") (getCurrentDirectory >>= canonicalizePath)
createDirectoryIfMissing True shakeDir createDirectoryIfMissing True shakeDir
haddockFiles <- liftIO (newTVarIO mempty) haddockFiles <- liftIO (newTVarIO mempty)
withArgs registerLock <- liftIO (newMVar ())
[] withArgs [] $
(shakeArgs shakeArgs
shakeOptions {shakeFiles = shakeDir shakeOptions
,shakeVerbosity = Diagnostic} { shakeFiles = shakeDir
(shakePlan haddockFiles pb shakeDir)) , shakeThreads = 2
} $
shakePlan haddockFiles registerLock pb shakeDir
-- | The complete build plan as far as Shake is concerned. -- | The complete build plan as far as Shake is concerned.
shakePlan :: TVar (Map String FilePath) -> PerformBuild -> FilePath -> Rules () shakePlan :: TVar (Map String FilePath)
shakePlan haddockFiles pb shakeDir = do -> MVar ()
-> PerformBuild
-> FilePath
-> Rules ()
shakePlan haddockFiles registerLock pb shakeDir = do
fetched <- target (targetForFetched shakeDir) $ fetched <- target (targetForFetched shakeDir) $
fetchedTarget shakeDir pb fetchedTarget shakeDir pb
db <- target db <- target (targetForDb' shakeDir) $
(targetForDb' shakeDir) databaseTarget shakeDir pb
(databaseTarget shakeDir pb)
_ <- forM corePackages $ _ <- forM corePackages $
\name -> \name ->
let fp = let fp = targetForPackage shakeDir name
targetForPackage shakeDir name
in target fp (makeFile fp) in target fp (makeFile fp)
packageTargets <- forM normalPackages $ packageTargets <- forM normalPackages $
\(name,plan) -> \(name,plan) ->
target target (targetForPackage shakeDir name) $
(targetForPackage shakeDir name) do need [db, fetched]
(do need [db, fetched] packageTarget
packageTarget haddockFiles pb shakeDir name plan) haddockFiles
registerLock
pb
shakeDir
name
plan
want packageTargets want packageTargets
where corePackages = where corePackages = M.keys $ siCorePackages $ bpSystemInfo $ pbPlan pb
M.keys $ siCorePackages $ bpSystemInfo $ pbPlan pb normalPackages = filter (not . (`elem` corePackages) . fst) $
normalPackages =
filter (not . (`elem` corePackages) . fst) $
M.toList $ bpPackages $ pbPlan pb M.toList $ bpPackages $ pbPlan pb
-- | Initialize the database if there one needs to be, and in any case -- | Initialize the database if there one needs to be, and in any case
-- create the target file. -- create the target file.
databaseTarget :: FilePath -> PerformBuild -> Action () databaseTarget :: FilePath -> PerformBuild -> Action ()
databaseTarget shakeDir pb = databaseTarget shakeDir pb = do
do if pbGlobalInstall pb if pbGlobalInstall pb
then return () then return ()
else do liftIO (createDirectoryIfMissing True dir) else do
liftIO (removeDirectoryRecursive dir) liftIO (createDirectoryIfMissing True dir)
() <- cmd "ghc-pkg" "init" dir liftIO (removeDirectoryRecursive dir)
liftIO (copyBuiltInHaddocks (FP.decodeString (pbDocDir pb))) () <- cmd "ghc-pkg" "init" dir
makeFile (targetForDb' shakeDir) liftIO
where dir = buildDatabase pb (copyBuiltInHaddocks
(FP.decodeString
(pbDocDir pb)))
makeFile (targetForDb' shakeDir)
where dir = buildDatabase pb
-- | Build, test and generate documentation for the package. -- | Build, test and generate documentation for the package.
packageTarget :: TVar (Map String FilePath) packageTarget :: TVar (Map String FilePath)
-> PerformBuild -> FilePath -> PackageName -> PackagePlan -> MVar ()
-> PerformBuild
-> FilePath
-> PackageName
-> PackagePlan
-> Action () -> Action ()
packageTarget haddockFiles pb shakeDir name plan = do packageTarget haddockFiles registerLock pb shakeDir name plan = do
need (map (targetForPackage shakeDir) need $
(M.keys (sdPackages (ppDesc plan)))) map (targetForPackage shakeDir) $
filter (/= name) $
M.keys $ M.filter libAndExe $ sdPackages $ ppDesc plan
pwd <- liftIO getCurrentDirectory pwd <- liftIO getCurrentDirectory
env <- liftIO (fmap (Env . (++ defaultEnv pwd)) getEnvironment) env <- liftIO (fmap (Env . (++ defaultEnv pwd)) getEnvironment)
unpack shakeDir nameVer unpack shakeDir nameVer
configure pkgDir env pb plan configure pkgDir env pb plan
() <- cmd cwd env "cabal" "build" () <- cmd cwd env "cabal" "build" "--ghc-options=-O0"
register pkgDir env register pkgDir env registerLock
when (pcHaddocks (ppConstraints plan) /= Don'tBuild) $
(generateHaddocks haddockFiles pb pkgDir env name nameVer)
makeFile (targetForPackage shakeDir name) makeFile (targetForPackage shakeDir name)
where cwd = where cwd = Cwd pkgDir
Cwd pkgDir defaultEnv pwd = [( "HASKELL_PACKAGE_SANDBOX"
defaultEnv pwd = , pwd <//> buildDatabase pb) | pbGlobalInstall pb]
[ ( "HASKELL_PACKAGE_SANDBOX"
, pwd <//>
buildDatabase pb)
| pbGlobalInstall pb]
pkgDir = shakeDir <//> nameVer pkgDir = shakeDir <//> nameVer
nameVer = nameVer = display name ++
display name ++
"-" ++ "-" ++
display (ppVersion plan) display (ppVersion plan)
{-when (pcHaddocks (ppConstraints plan) /= Don'tBuild) $
(generateHaddocks haddockFiles pb pkgDir env name nameVer)-}
-- | Make sure all package archives have been fetched. -- | Make sure all package archives have been fetched.
fetchedTarget :: FilePath -> PerformBuild -> Action () fetchedTarget :: FilePath -> PerformBuild -> Action ()
fetchedTarget shakeDir pb = do fetchedTarget shakeDir pb = do
@ -115,63 +130,64 @@ fetchedTarget shakeDir pb = do
(\(name,plan) -> (\(name,plan) ->
display name ++ display name ++
"-" ++ "-" ++
display (ppVersion plan)) display (ppVersion plan)) $
(M.toList M.toList $ bpPackages $ pbPlan pb
(bpPackages
(pbPlan pb)))
makeFile (targetForFetched shakeDir) makeFile (targetForFetched shakeDir)
-- | Unpack the package. -- | Unpack the package.
unpack :: FilePath -> String -> Action () unpack :: FilePath -> String -> Action ()
unpack shakeDir nameVer = do unpack shakeDir nameVer = do
unpacked <- liftIO (doesDirectoryExist pkgDir) unpacked <- liftIO (doesDirectoryExist pkgDir)
unless unpacked (cmd (Cwd shakeDir) "cabal" "unpack" nameVer) unless unpacked $
where pkgDir = cmd (Cwd shakeDir) "cabal" "unpack" nameVer
shakeDir <//> nameVer where pkgDir = shakeDir <//> nameVer
-- | Configure the given package. -- | Configure the given package.
configure :: FilePath -> CmdOption -> PerformBuild -> PackagePlan -> Action () configure :: FilePath -> CmdOption -> PerformBuild -> PackagePlan -> Action ()
configure pkgDir env pb plan = do configure pkgDir env pb plan = do
configured <- liftIO configured <- liftIO $ doesFileExist $ pkgDir <//> "dist" <//>
(doesFileExist "setup-config"
(pkgDir <//> "dist" <//> "setup-config")) unless configured $
unless do pwd <- liftIO getCurrentDirectory
configured cmd
(do pwd <- liftIO getCurrentDirectory (Cwd pkgDir)
cmd (Cwd pkgDir) env "cabal" "configure" (opts pwd)) env
where opts pwd = "cabal"
[ "--package-db=clear" "configure"
, "--package-db=global" (opts pwd)
, "--libdir=" ++ pwd <//> pbLibDir pb where opts pwd = [ "--package-db=clear"
, "--bindir=" ++ pwd <//> pbBinDir pb , "--package-db=global"
, "--datadir=" ++ pwd <//> pbDataDir pb , "--libdir=" ++ pwd <//> pbLibDir pb
, "--docdir=" ++ pwd <//> pbDocDir pb , "--bindir=" ++ pwd <//> pbBinDir pb
, "--flags=" ++ planFlags plan] ++ , "--datadir=" ++ pwd <//> pbDataDir pb
["--package-db=" ++ , "--docdir=" ++ pwd <//> pbDocDir pb
pwd <//> , "--flags=" ++ planFlags plan] ++
buildDatabase pb | not (pbGlobalInstall pb)] ["--package-db=" ++ pwd <//> buildDatabase pb | not (pbGlobalInstall pb)]
-- | Register the package. -- | Register the package.
-- --
-- TODO: Do a mutex lock in here. Does Shake already support doing -- TODO: Do a mutex lock in here. Does Shake already support doing
-- this out of the box? -- this out of the box?
register :: FilePath -> CmdOption -> Action () register :: FilePath -> CmdOption -> MVar () -> Action ()
register pkgDir env = register pkgDir env registerLock = do
do () <- cmd cwd env "cabal" "copy" () <- cmd cwd env "cabal" "copy"
cmd cwd env "cabal" "register" -- FIXME:
where cwd = Cwd pkgDir liftIO
(takeMVar registerLock)
() <- cmd cwd env "cabal" "register"
liftIO (putMVar registerLock ())
where cwd = Cwd pkgDir
-- | Generate haddocks for the package. -- | Generate haddocks for the package.
generateHaddocks generateHaddocks :: TVar (Map String FilePath)
:: TVar (Map String FilePath) -> PerformBuild
-> PerformBuild -> FilePath
-> FilePath -> CmdOption
-> CmdOption -> PackageName
-> PackageName -> FilePattern
-> FilePattern -> Action ()
-> Action ()
generateHaddocks haddockFiles pb pkgDir env name nameVer = do generateHaddocks haddockFiles pb pkgDir env name nameVer = do
hfs <- liftIO (readTVarIO haddockFiles) hfs <- liftIO $ readTVarIO haddockFiles
() <- cmd () <- cmd
(Cwd pkgDir) (Cwd pkgDir)
env env
@ -190,36 +206,35 @@ generateHaddocks haddockFiles pb pkgDir env name nameVer = do
, "/," , "/,"
, hf]) , hf])
(M.toList hfs)) (M.toList hfs))
liftIO liftIO $
(renameOrCopy renameOrCopy
(FP.decodeString (FP.decodeString
(pkgDir <//> "dist" <//> "doc" <//> "html" <//> display name)) (pkgDir <//> "dist" <//> "doc" <//> "html" <//> display name))
(FP.decodeString (FP.decodeString
(pbDocDir pb <//> nameVer))) (pbDocDir pb <//> nameVer))
enewPath <- liftIO enewPath <- liftIO $
(try $ try $
canonicalizePath canonicalizePath
(pbDocDir pb <//> nameVer <//> display name ++ (pbDocDir pb <//> nameVer <//> display name ++ ".haddock")
".haddock"))
case enewPath of case enewPath of
Left (e :: IOException) -> Left (e :: IOException) -> return () -- FIXME: log it with Shake.
return () -- FIXME: log it with Shake. Right newPath -> liftIO $
Right newPath -> atomically $
liftIO modifyTVar haddockFiles $
(atomically $ M.insert nameVer newPath
modifyTVar haddockFiles $
M.insert nameVer newPath)
-- | Generate a flags string for the package plan. -- | Generate a flags string for the package plan.
planFlags :: PackagePlan -> String planFlags :: PackagePlan -> String
planFlags plan = unwords $ map go $ M.toList (pcFlagOverrides (ppConstraints plan)) planFlags plan = unwords $
where map go $
go (name',isOn) = M.toList
concat (pcFlagOverrides
[ if isOn (ppConstraints plan))
then "" where go (name',isOn) = concat
else "-" [ if isOn
, T.unpack (unFlagName name')] then ""
else "-"
, T.unpack (unFlagName name')]
-- | Database location. -- | Database location.
buildDatabase :: PerformBuild -> FilePattern buildDatabase :: PerformBuild -> FilePattern