7533a2757c23ca4863b46f3fa708275bc9b6350a
[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 targets <- mapM (f Stage1) =<< stagePackages Stage1
46 need targets
47 where
48 -- either the package database config file for libraries or
49 -- the programPath for programs. However this still does
50 -- not support multiple targets, where a cabal package has
51 -- a library /and/ a program.
52 f :: Stage -> Package -> Action FilePath
53 f stage pkg | isLibrary pkg = pkgConfFile (Context stage pkg (read "v"))
54 | otherwise = programPath =<< programContext stage pkg
55 g :: Stage -> Package -> Action String
56 g stage pkg | isLibrary pkg = return $ pkgName pkg
57 | otherwise = programName (Context stage pkg (read "v"))
58
59 -- TODO: Get rid of the @includeGhciLib@ hack.
60 -- | Return the list of targets associated with a given 'Stage' and 'Package'.
61 -- By setting the Boolean parameter to False it is possible to exclude the GHCi
62 -- library from the targets, and avoid running @ghc-cabal@ to determine whether
63 -- GHCi library needs to be built for this package. We typically want to set
64 -- this parameter to True, however it is important to set it to False when
65 -- computing 'topLevelTargets', as otherwise the whole build gets sequentialised
66 -- because we need to run @ghc-cabal@ in the order respecting package dependencies.
67 packageTargets :: Bool -> Stage -> Package -> Action [FilePath]
68 packageTargets includeGhciLib stage pkg = do
69 let context = vanillaContext stage pkg
70 activePackages <- stagePackages stage
71 if pkg `notElem` activePackages
72 then return [] -- Skip inactive packages.
73 else if isLibrary pkg
74 then do -- Collect all targets of a library package.
75 let pkgWays = if pkg == rts then getRtsWays else getLibraryWays
76 ways <- interpretInContext context pkgWays
77 libs <- mapM (pkgLibraryFile . Context stage pkg) ways
78 more <- libraryTargets includeGhciLib context
79 setup <- pkgSetupConfigFile context
80 return $ [ setup | not (nonCabalContext context) ] ++ libs ++ more
81 else do -- The only target of a program package is the executable.
82 prgContext <- programContext stage pkg
83 prgPath <- programPath prgContext
84 return [prgPath]
85
86 packageRules :: Rules ()
87 packageRules = do
88 -- We cannot register multiple GHC packages in parallel. Also we cannot run
89 -- GHC when the package database is being mutated by "ghc-pkg". This is a
90 -- classic concurrent read exclusive write (CREW) conflict.
91 let maxConcurrentReaders = 1000
92 packageDb <- newResource "package-db" maxConcurrentReaders
93 let readPackageDb = [(packageDb, 1)]
94 writePackageDb = [(packageDb, maxConcurrentReaders)]
95
96 let contexts = liftM3 Context allStages knownPackages allWays
97 vanillaContexts = liftM2 vanillaContext allStages knownPackages
98
99 -- TODO: we might want to look into converting more and more
100 -- rules to the style introduced in Rules.Library in
101 -- https://github.com/snowleopard/hadrian/pull/571,
102 -- where "catch-all" rules are used to "catch" the need
103 -- for library files, and we then use parsec parsers to
104 -- extract all sorts of information needed to build them, like
105 -- the package, the stage, the way, etc.
106
107 forM_ contexts (Rules.Compile.compilePackage readPackageDb)
108
109 Rules.Program.buildProgram readPackageDb
110
111 forM_ [Stage0 .. ] $ \stage -> do
112 -- we create a dummy context, that has the correct state, but contains
113 -- @dummyPackage@ as a... dummy package. The package isn't accessed but the record
114 -- need to be set properly. @undefined@ is not an option as it ends up
115 -- being forced.
116 Rules.Register.registerPackages writePackageDb (Context stage dummyPackage vanilla)
117
118 forM_ vanillaContexts $ mconcat
119 [ Rules.PackageData.buildPackageData
120 , Rules.Dependencies.buildPackageDependencies readPackageDb
121 , Rules.Documentation.buildPackageDocumentation
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 Rules.Library.libraryRules
133 packageRules
134
135 oracleRules :: Rules ()
136 oracleRules = do
137 Hadrian.Oracles.ArgsHash.argsHashOracle trackArgument getArgs
138 Hadrian.Oracles.DirectoryContents.directoryContentsOracle
139 Hadrian.Oracles.Path.pathOracle
140 Hadrian.Oracles.TextFile.textFileOracle
141 Oracles.ModuleFiles.moduleFilesOracle