mirror of
https://github.com/commercialhaskell/stackage.git
synced 2026-03-11 11:16:34 +01:00
167 lines
6.0 KiB
Haskell
167 lines
6.0 KiB
Haskell
-- | Build everything with Shake.
|
|
|
|
module Stackage.ShakeBuild where
|
|
|
|
import Stackage.BuildConstraints
|
|
import Stackage.BuildPlan
|
|
import Stackage.PackageDescription
|
|
import Stackage.PerformBuild (PerformBuild(..),copyBuiltInHaddocks)
|
|
|
|
import Control.Monad hiding (forM_)
|
|
import qualified Data.Map.Strict as M
|
|
import qualified Data.Text as T
|
|
import Development.Shake hiding (doesDirectoryExist)
|
|
import Distribution.Package (PackageName)
|
|
import Distribution.Text (display)
|
|
import qualified Filesystem.Path.CurrentOS as FP
|
|
import Stackage.Prelude (unFlagName)
|
|
import System.Directory
|
|
import System.Environment
|
|
|
|
-- | Run the shake builder.
|
|
performBuild :: PerformBuild -> IO ()
|
|
performBuild pb = do
|
|
shakeDir <- fmap (<//> "shake/") (getCurrentDirectory >>= canonicalizePath)
|
|
createDirectoryIfMissing True shakeDir
|
|
withArgs
|
|
[]
|
|
(shakeArgs
|
|
shakeOptions {shakeFiles = shakeDir}
|
|
(shakePlan pb shakeDir))
|
|
|
|
-- | The complete build plan as far as Shake is concerned.
|
|
shakePlan :: PerformBuild -> FilePath -> Rules ()
|
|
shakePlan pb shakeDir = do
|
|
fetched <- target (targetForFetched shakeDir) $
|
|
fetchedTarget shakeDir pb
|
|
db <- target
|
|
(targetForDb shakeDir pb)
|
|
(databaseTarget shakeDir pb)
|
|
_ <- forM corePackages $
|
|
\name ->
|
|
let fp =
|
|
targetForPackage shakeDir name
|
|
in target fp (makeFile fp)
|
|
packageTargets <- forM normalPackages $
|
|
\(name,plan) ->
|
|
target
|
|
(targetForPackage shakeDir name)
|
|
(do need [db, fetched]
|
|
packageTarget pb shakeDir name plan)
|
|
want packageTargets
|
|
where corePackages =
|
|
M.keys $ siCorePackages $ bpSystemInfo $ pbPlan pb
|
|
normalPackages =
|
|
filter (not . (`elem` corePackages) . fst) $
|
|
M.toList $ bpPackages $ pbPlan pb
|
|
|
|
-- | Initialize the database if there one needs to be, and in any case
|
|
-- create the target file.
|
|
databaseTarget :: FilePath -> PerformBuild -> Action ()
|
|
databaseTarget shakeDir pb =
|
|
if pbGlobalInstall pb
|
|
then liftIO (createDirectoryIfMissing True dir)
|
|
else do liftIO (createDirectoryIfMissing True (dir))
|
|
liftIO (removeDirectoryRecursive dir)
|
|
() <- cmd "ghc-pkg" "init" dir
|
|
liftIO (copyBuiltInHaddocks (FP.decodeString (pbDocDir pb)))
|
|
where dir = targetForDb shakeDir pb
|
|
|
|
-- | Make sure all package archives have been fetched.
|
|
fetchedTarget :: FilePath -> PerformBuild -> Action ()
|
|
fetchedTarget shakeDir pb = do
|
|
() <- cmd "cabal" "fetch" "--no-dependencies" $
|
|
map
|
|
(\(name,plan) ->
|
|
display name ++
|
|
"-" ++
|
|
display (ppVersion plan))
|
|
(M.toList
|
|
(bpPackages
|
|
(pbPlan pb)))
|
|
makeFile (targetForFetched shakeDir)
|
|
|
|
-- | Build, test and generate documentation for the package.
|
|
packageTarget :: PerformBuild -> FilePath -> PackageName -> PackagePlan -> Action ()
|
|
packageTarget pb shakeDir name plan = do
|
|
need (map (targetForPackage shakeDir)
|
|
(M.keys (sdPackages (ppDesc plan))))
|
|
pwd <- liftIO getCurrentDirectory
|
|
env <- liftIO (fmap (Env . (++ defaultEnv pwd)) getEnvironment)
|
|
() <- cmd (Cwd shakeDir) "cabal" "unpack" nameVer
|
|
() <- cmd cwd env "cabal" "configure" (opts pwd)
|
|
() <- cmd cwd env "cabal" "build"
|
|
() <- cmd cwd env "cabal" "copy"
|
|
() <- cmd cwd env "cabal" "register"
|
|
makeFile (targetForPackage shakeDir name)
|
|
where cwd =
|
|
Cwd pkgDir
|
|
defaultEnv pwd =
|
|
[ ( "HASKELL_PACKAGE_SANDBOX"
|
|
, pwd <//>
|
|
targetForDb shakeDir pb)]
|
|
opts pwd =
|
|
[ "--package-db=clear"
|
|
, "--package-db=global"
|
|
, "--libdir=" ++ pwd <//> pbLibDir pb
|
|
, "--bindir=" ++ pwd <//> pbBinDir pb
|
|
, "--datadir=" ++ pwd <//> pbDataDir pb
|
|
, "--docdir=" ++ pwd <//> pbDocDir pb
|
|
, "--flags=" ++ flags] ++
|
|
["--package-db=" ++
|
|
pwd <//>
|
|
targetForDb shakeDir pb | not (pbGlobalInstall pb)]
|
|
pkgDir =
|
|
shakeDir <//> nameVer
|
|
nameVer =
|
|
display name ++
|
|
"-" ++
|
|
display (ppVersion plan)
|
|
flags =
|
|
unwords $
|
|
map go $
|
|
M.toList
|
|
(pcFlagOverrides
|
|
(ppConstraints plan))
|
|
where
|
|
go (name',isOn) =
|
|
concat
|
|
[ if isOn
|
|
then ""
|
|
else "-"
|
|
, T.unpack (unFlagName name')]
|
|
|
|
-- | Get the target file for confirming that all packages have been
|
|
-- pre-fetched.
|
|
targetForFetched :: FilePath -> FilePath
|
|
targetForFetched shakeDir =
|
|
shakeDir <//> "fetched"
|
|
|
|
-- | Get the target file for a package.
|
|
targetForPackage :: FilePath -> PackageName -> FilePath
|
|
targetForPackage shakeDir name =
|
|
shakeDir <//> "packages" <//> display name
|
|
|
|
-- | Get a package database path.
|
|
targetForDb :: FilePath -> PerformBuild -> FilePath
|
|
targetForDb shakeDir pb =
|
|
if pbGlobalInstall pb
|
|
then shakeDir <//> "pkgdb-global"
|
|
else FP.encodeString (pbInstallDest pb) <//> "pkgdb"
|
|
|
|
-- | Declare a target, returning the target name.
|
|
target :: FilePattern -> Action () -> Rules FilePattern
|
|
target name act = do
|
|
name *> const act
|
|
return name
|
|
|
|
-- | Make a file of this name.
|
|
makeFile :: FilePath -> Action ()
|
|
makeFile fp = liftIO $ writeFile fp ""
|
|
|
|
pbBinDir, pbLibDir, pbDataDir, pbDocDir :: PerformBuild -> FilePath
|
|
pbBinDir pb = FP.encodeString (pbInstallDest pb) <//> "bin"
|
|
pbLibDir pb = FP.encodeString (pbInstallDest pb) <//> "lib"
|
|
pbDataDir pb = FP.encodeString (pbInstallDest pb) <//> "share"
|
|
pbDocDir pb = FP.encodeString (pbInstallDest pb) <//> "doc"
|