Doc: add Haddocks for quotRemWord2 primop
[ghc.git] / hadrian / src / Rules / Libffi.hs
1 {-# LANGUAGE TypeFamilies #-}
2
3 module Rules.Libffi (
4 LibffiDynLibs(..),
5 needLibffi, askLibffilDynLibs, libffiRules, libffiLibrary, libffiHeaderFiles,
6 libffiHeaders, libffiSystemHeaders, libffiName
7 ) where
8
9 import Hadrian.Utilities
10
11 import Packages
12 import Settings.Builders.Common
13 import Target
14 import Utilities
15
16 {- Note [Libffi indicating inputs]
17
18 First see https://gitlab.haskell.org/ghc/ghc/wikis/Developing-Hadrian for an
19 explanation of "indicating input". Part of the definition is copied here for
20 your convenience:
21
22 change in the vital output -> change in the indicating inputs
23
24 In the case of building libffi `vital output = built libffi library files` and
25 we can consider the libffi archive file (i.e. the "libffi-tarballs/libffi*.tar.gz"
26 file) to be the only indicating input besides the build tools (e.g. make).
27 Note building libffi is split into a few rules, but we also expect that:
28
29 no change in the archive file -> no change in the intermediate build artifacts
30
31 and so the archive file is still a valid choice of indicating input for
32 all libffi rules. Hence we can get away with `need`ing only the archive file and
33 don't have to `need` intermediate build artifacts (besides those to trigger
34 dependant libffi rules i.e. to generate vital inputs as is noted on the wiki).
35 It is then safe to `trackAllow` the libffi build directory as is done in
36 `needLibfffiArchive`.
37
38 A disadvantage to this approach is that changing the archive file forces a clean
39 build of libffi i.e. we cannot incrementally build libffi. This seems like a
40 performance issue, but is justified as building libffi is fast and the archive
41 file is rarely changed.
42
43 -}
44
45 -- | Oracle question type. The oracle returns the list of dynamic
46 -- libffi library file paths (all but one of which should be symlinks).
47 newtype LibffiDynLibs = LibffiDynLibs Stage
48 deriving (Eq, Show, Hashable, Binary, NFData)
49 type instance RuleResult LibffiDynLibs = [FilePath]
50
51 askLibffilDynLibs :: Stage -> Action [FilePath]
52 askLibffilDynLibs stage = askOracle (LibffiDynLibs stage)
53
54 -- | The path to the dynamic library manifest file. The file contains all file
55 -- paths to libffi dynamic library file paths.
56 -- The path is calculated but not `need`ed.
57 dynLibManifest' :: Monad m => m FilePath -> Stage -> m FilePath
58 dynLibManifest' getRoot stage = do
59 root <- getRoot
60 return $ root -/- stageString stage -/- pkgName libffi -/- ".dynamiclibs"
61
62 dynLibManifestRules :: Stage -> Rules FilePath
63 dynLibManifestRules = dynLibManifest' buildRootRules
64
65 dynLibManifest :: Stage -> Action FilePath
66 dynLibManifest = dynLibManifest' buildRoot
67
68 -- | Need the (locally built) libffi library.
69 needLibffi :: Stage -> Action ()
70 needLibffi stage = do
71 manifest <- dynLibManifest stage
72 need [manifest]
73
74 -- | Context for @libffi@.
75 libffiContext :: Stage -> Action Context
76 libffiContext stage = do
77 ways <- interpretInContext
78 (Context stage libffi (error "libffiContext: way not set"))
79 getLibraryWays
80 return . Context stage libffi $ if any (wayUnit Dynamic) ways
81 then dynamic
82 else vanilla
83
84 -- | The name of the (locally built) library
85 libffiName :: Expr String
86 libffiName = do
87 way <- getWay
88 return $ libffiName' (Dynamic `wayUnit` way)
89
90 -- | The name of the (locally built) library
91 libffiName' :: Bool -> String
92 libffiName' dynamic = (if dynamic then "" else "C")
93 ++ (if windowsHost then "ffi-6" else "ffi")
94
95 libffiLibrary :: FilePath
96 libffiLibrary = "inst/lib/libffi.a"
97
98 libffiHeaderFiles :: [FilePath]
99 libffiHeaderFiles = ["ffi.h", "ffitarget.h"]
100
101 libffiHeaders :: Stage -> Action [FilePath]
102 libffiHeaders stage = do
103 path <- libffiBuildPath stage
104 return $ fmap ((path -/- "inst/include") -/-) libffiHeaderFiles
105
106 libffiSystemHeaders :: Action [FilePath]
107 libffiSystemHeaders = do
108 ffiIncludeDir <- setting FfiIncludeDir
109 return $ fmap (ffiIncludeDir -/-) libffiHeaderFiles
110
111 fixLibffiMakefile :: FilePath -> String -> String
112 fixLibffiMakefile top =
113 replace "-MD" "-MMD"
114 . replace "@toolexeclibdir@" "$(libdir)"
115 . replace "@INSTALL@" ("$(subst ../install-sh," ++ top ++ "/install-sh,@INSTALL@)")
116
117 -- TODO: check code duplication w.r.t. ConfCcArgs
118 configureEnvironment :: Stage -> Action [CmdOption]
119 configureEnvironment stage = do
120 context <- libffiContext stage
121 cFlags <- interpretInContext context $ mconcat
122 [ cArgs
123 , getStagedSettingList ConfCcArgs ]
124 ldFlags <- interpretInContext context ldArgs
125 sequence [ builderEnvironment "CC" $ Cc CompileC stage
126 , builderEnvironment "CXX" $ Cc CompileC stage
127 , builderEnvironment "LD" (Ld stage)
128 , builderEnvironment "AR" (Ar Unpack stage)
129 , builderEnvironment "NM" Nm
130 , builderEnvironment "RANLIB" Ranlib
131 , return . AddEnv "CFLAGS" $ unwords cFlags ++ " -w"
132 , return . AddEnv "LDFLAGS" $ unwords ldFlags ++ " -w" ]
133
134 -- Need the libffi archive and `trackAllow` all files in the build directory.
135 -- See [Libffi indicating inputs].
136 needLibfffiArchive :: FilePath -> Action FilePath
137 needLibfffiArchive buildPath = do
138 top <- topDirectory
139 tarball <- unifyPath
140 . fromSingleton "Exactly one LibFFI tarball is expected"
141 <$> getDirectoryFiles top ["libffi-tarballs/libffi*.tar.gz"]
142 need [top -/- tarball]
143 trackAllow [buildPath -/- "**"]
144 return tarball
145
146 libffiRules :: Rules ()
147 libffiRules = do
148 _ <- addOracleCache $ \ (LibffiDynLibs stage)
149 -> readFileLines =<< dynLibManifest stage
150 forM_ [Stage1 ..] $ \stage -> do
151 root <- buildRootRules
152 let path = root -/- stageString stage
153 libffiPath = path -/- pkgName libffi -/- "build"
154
155 -- We set a higher priority because this rule overlaps with the build rule
156 -- for static libraries 'Rules.Library.libraryRules'.
157 dynLibMan <- dynLibManifestRules stage
158 let topLevelTargets = [ libffiPath -/- libffiLibrary
159 , dynLibMan
160 ]
161 priority 2 $ topLevelTargets &%> \_ -> do
162 _ <- needLibfffiArchive libffiPath
163 context <- libffiContext stage
164
165 -- Note this build needs the Makefile, triggering the rules bellow.
166 build $ target context (Make libffiPath) [] []
167
168 -- Find dynamic libraries.
169 dynLibFiles <- do
170 let libfilesDir = libffiPath -/-
171 (if windowsHost then "inst" -/- "bin" else "inst" -/- "lib")
172 libffiName'' = libffiName' True
173 dynlibext
174 | windowsHost = "dll"
175 | osxHost = "dylib"
176 | otherwise = "so"
177 filepat = "lib" ++ libffiName'' ++ "." ++ dynlibext ++ "*"
178 liftIO $ getDirectoryFilesIO "." [libfilesDir -/- filepat]
179
180 writeFileLines dynLibMan dynLibFiles
181 putSuccess "| Successfully build libffi."
182
183 fmap (libffiPath -/-) ["Makefile.in", "configure" ] &%> \[mkIn, _] -> do
184 -- Extract libffi tar file
185 context <- libffiContext stage
186 removeDirectory libffiPath
187 tarball <- needLibfffiArchive libffiPath
188 -- Go from 'libffi-3.99999+git20171002+77e130c.tar.gz' to 'libffi-3.99999'
189 let libname = takeWhile (/= '+') $ takeFileName tarball
190
191 -- Move extracted directory to libffiPath.
192 root <- buildRoot
193 removeDirectory (root -/- libname)
194 actionFinally (do
195 build $ target context (Tar Extract) [tarball] [path]
196 moveDirectory (path -/- libname) libffiPath) $
197 -- And finally:
198 removeFiles (path) [libname -/- "**"]
199
200 top <- topDirectory
201 fixFile mkIn (fixLibffiMakefile top)
202
203 files <- liftIO $ getDirectoryFilesIO "." [libffiPath -/- "**"]
204 produces files
205
206 fmap (libffiPath -/-) ["Makefile", "config.guess", "config.sub"] &%> \[mk, _, _] -> do
207 _ <- needLibfffiArchive libffiPath
208 context <- libffiContext stage
209
210 -- This need rule extracts the libffi tar file to libffiPath.
211 need [mk <.> "in"]
212
213 -- Configure.
214 forM_ ["config.guess", "config.sub"] $ \file -> do
215 copyFile file (libffiPath -/- file)
216 env <- configureEnvironment stage
217 buildWithCmdOptions env $
218 target context (Configure libffiPath) [mk <.> "in"] [mk]
219
220 dir <- setting BuildPlatform
221 files <- liftIO $ getDirectoryFilesIO "." [libffiPath -/- dir -/- "**"]
222 produces files