Preliminary bindist rule (#555)
[hadrian.git] / src / Rules.hs
1 module Rules (buildRules, oracleRules, packageTargets, topLevelTargets) where
2
3 import qualified Hadrian.Oracles.ArgsHash
4 import qualified Hadrian.Oracles.DirectoryContents
5 import qualified Hadrian.Oracles.Path
6 import qualified Hadrian.Oracles.TextFile
7
8 import Expression
9 import GHC
10 import qualified Oracles.ModuleFiles
11 import qualified Rules.BinaryDist
12 import qualified Rules.Compile
13 import qualified Rules.Configure
14 import qualified Rules.Dependencies
15 import qualified Rules.Documentation
16 import qualified Rules.Generate
17 import qualified Rules.Gmp
18 import qualified Rules.Libffi
19 import qualified Rules.Library
20 import qualified Rules.PackageData
21 import qualified Rules.Program
22 import qualified Rules.Register
23 import Settings
24 import Target
25 import Utilities
26
27 allStages :: [Stage]
28 allStages = [minBound .. maxBound]
29
30 -- | This rule calls 'need' on all top-level build targets, respecting the
31 -- 'Stage1Only' flag.
32 topLevelTargets :: Rules ()
33 topLevelTargets = action $ do
34 (programs, libraries) <- partition isProgram <$> stagePackages Stage1
35 pgmNames <- mapM (g Stage1) programs
36 libNames <- mapM (g Stage1) libraries
37
38 verbosity <- getVerbosity
39 when (verbosity >= Loud) $ do
40 putNormal "Building stage2"
41 putNormal . unlines $
42 [ "| Building Programs: " ++ intercalate ", " pgmNames
43 , "| Building Libraries: " ++ intercalate ", " libNames
44 ]
45
46 targets <- mapM (f Stage1) =<< stagePackages Stage1
47 need targets
48
49 where
50 -- either the package database config file for libraries or
51 -- the programPath for programs. However this still does
52 -- not support multiple targets, where a cabal package has
53 -- a library /and/ a program.
54 f :: Stage -> Package -> Action FilePath
55 f stage pkg | isLibrary pkg = pkgConfFile (Context stage pkg (read "v"))
56 | otherwise = programPath =<< programContext stage pkg
57 g :: Stage -> Package -> Action String
58 g stage pkg | isLibrary pkg = return $ pkgName pkg
59 | otherwise = programName (Context stage pkg (read "v"))
60
61 -- TODO: Get rid of the @includeGhciLib@ hack.
62 -- | Return the list of targets associated with a given 'Stage' and 'Package'.
63 -- By setting the Boolean parameter to False it is possible to exclude the GHCi
64 -- library from the targets, and avoid running @ghc-cabal@ to determine whether
65 -- GHCi library needs to be built for this package. We typically want to set
66 -- this parameter to True, however it is important to set it to False when
67 -- computing 'topLevelTargets', as otherwise the whole build gets sequentialised
68 -- because we need to run @ghc-cabal@ in the order respecting package dependencies.
69 packageTargets :: Bool -> Stage -> Package -> Action [FilePath]
70 packageTargets includeGhciLib stage pkg = do
71 let context = vanillaContext stage pkg
72 activePackages <- stagePackages stage
73 if pkg `notElem` activePackages
74 then return [] -- Skip inactive packages.
75 else if isLibrary pkg
76 then do -- Collect all targets of a library package.
77 let pkgWays = if pkg == rts then getRtsWays else getLibraryWays
78 ways <- interpretInContext context pkgWays
79 libs <- mapM (pkgLibraryFile . Context stage pkg) ways
80 more <- libraryTargets includeGhciLib context
81 setup <- pkgSetupConfigFile context
82 return $ [ setup | not (nonCabalContext context) ] ++ libs ++ more
83 else do -- The only target of a program package is the executable.
84 prgContext <- programContext stage pkg
85 prgPath <- programPath prgContext
86 return [prgPath]
87
88 packageRules :: Rules ()
89 packageRules = do
90 -- We cannot register multiple GHC packages in parallel. Also we cannot run
91 -- GHC when the package database is being mutated by "ghc-pkg". This is a
92 -- classic concurrent read exclusive write (CREW) conflict.
93 let maxConcurrentReaders = 1000
94 packageDb <- newResource "package-db" maxConcurrentReaders
95 let readPackageDb = [(packageDb, 1)]
96 writePackageDb = [(packageDb, maxConcurrentReaders)]
97
98 let contexts = liftM3 Context allStages knownPackages allWays
99 vanillaContexts = liftM2 vanillaContext allStages knownPackages
100
101 forM_ contexts $ mconcat
102 [ Rules.Compile.compilePackage readPackageDb
103 , Rules.Library.buildPackageLibrary ]
104
105 let dynamicContexts = liftM3 Context [Stage1 ..] knownPackages [dynamic]
106 forM_ dynamicContexts Rules.Library.buildDynamicLib
107
108 Rules.Program.buildProgram readPackageDb
109
110 forM_ [Stage0 .. ] $ \stage -> do
111 -- we create a dummy context, that has the correct state, but contains
112 -- @dummyPackage@ as a... dummy package. The package isn't accessed but the record
113 -- need to be set properly. @undefined@ is not an option as it ends up
114 -- being forced.
115 Rules.Register.registerPackages writePackageDb (Context stage dummyPackage vanilla)
116
117 forM_ vanillaContexts $ mconcat
118 [ Rules.PackageData.buildPackageData
119 , Rules.Dependencies.buildPackageDependencies readPackageDb
120 , Rules.Documentation.buildPackageDocumentation
121 , Rules.Library.buildPackageGhciLibrary
122 , Rules.Generate.generatePackageCode ]
123
124 buildRules :: Rules ()
125 buildRules = do
126 Rules.BinaryDist.bindistRules
127 Rules.Configure.configureRules
128 Rules.Generate.copyRules
129 Rules.Generate.generateRules
130 Rules.Gmp.gmpRules
131 Rules.Libffi.libffiRules
132 packageRules
133
134 oracleRules :: Rules ()
135 oracleRules = do
136 Hadrian.Oracles.ArgsHash.argsHashOracle trackArgument getArgs
137 Hadrian.Oracles.DirectoryContents.directoryContentsOracle
138 Hadrian.Oracles.Path.pathOracle
139 Hadrian.Oracles.TextFile.textFileOracle
140 Oracles.ModuleFiles.moduleFilesOracle