Hadrian: Add Mising Libffi Dependencies #16653
[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 -- | Oracle question type. The oracle returns the list of dynamic
17 -- libffi library file paths (all but one of which should be symlinks).
18 newtype LibffiDynLibs = LibffiDynLibs Stage
19 deriving (Eq, Show, Hashable, Binary, NFData)
20 type instance RuleResult LibffiDynLibs = [FilePath]
21
22 askLibffilDynLibs :: Stage -> Action [FilePath]
23 askLibffilDynLibs stage = askOracle (LibffiDynLibs stage)
24
25 -- | The path to the dynamic library manifest file. The file contains all file
26 -- paths to libffi dynamic library file paths.
27 -- The path is calculated but not `need`ed.
28 dynLibManifest' :: Monad m => m FilePath -> Stage -> m FilePath
29 dynLibManifest' getRoot stage = do
30 root <- getRoot
31 return $ root -/- stageString stage -/- pkgName libffi -/- ".dynamiclibs"
32
33 dynLibManifestRules :: Stage -> Rules FilePath
34 dynLibManifestRules = dynLibManifest' buildRootRules
35
36 dynLibManifest :: Stage -> Action FilePath
37 dynLibManifest = dynLibManifest' buildRoot
38
39 -- | Need the (locally built) libffi library.
40 needLibffi :: Stage -> Action ()
41 needLibffi stage = do
42 manifest <- dynLibManifest stage
43 need [manifest]
44
45 -- | Context for @libffi@.
46 libffiContext :: Stage -> Action Context
47 libffiContext stage = do
48 ways <- interpretInContext
49 (Context stage libffi (error "libffiContext: way not set"))
50 getLibraryWays
51 return . Context stage libffi $ if any (wayUnit Dynamic) ways
52 then dynamic
53 else vanilla
54
55 -- | The name of the (locally built) library
56 libffiName :: Expr String
57 libffiName = do
58 windows <- expr windowsHost
59 way <- getWay
60 return $ libffiName' windows (Dynamic `wayUnit` way)
61
62 -- | The name of the (locally built) library
63 libffiName' :: Bool -> Bool -> String
64 libffiName' windows dynamic
65 = (if dynamic then "" else "C")
66 ++ (if windows then "ffi-6" else "ffi")
67
68 libffiLibrary :: FilePath
69 libffiLibrary = "inst/lib/libffi.a"
70
71 libffiHeaderFiles :: [FilePath]
72 libffiHeaderFiles = ["ffi.h", "ffitarget.h"]
73
74 libffiHeaders :: Stage -> Action [FilePath]
75 libffiHeaders stage = do
76 path <- libffiBuildPath stage
77 return $ fmap ((path -/- "inst/include") -/-) libffiHeaderFiles
78
79 libffiSystemHeaders :: Action [FilePath]
80 libffiSystemHeaders = do
81 ffiIncludeDir <- setting FfiIncludeDir
82 return $ fmap (ffiIncludeDir -/-) libffiHeaderFiles
83
84 fixLibffiMakefile :: FilePath -> String -> String
85 fixLibffiMakefile top =
86 replace "-MD" "-MMD"
87 . replace "@toolexeclibdir@" "$(libdir)"
88 . replace "@INSTALL@" ("$(subst ../install-sh," ++ top ++ "/install-sh,@INSTALL@)")
89
90 -- TODO: check code duplication w.r.t. ConfCcArgs
91 configureEnvironment :: Stage -> Action [CmdOption]
92 configureEnvironment stage = do
93 context <- libffiContext stage
94 cFlags <- interpretInContext context $ mconcat
95 [ cArgs
96 , getStagedSettingList ConfCcArgs ]
97 ldFlags <- interpretInContext context ldArgs
98 sequence [ builderEnvironment "CC" $ Cc CompileC stage
99 , builderEnvironment "CXX" $ Cc CompileC stage
100 , builderEnvironment "LD" (Ld stage)
101 , builderEnvironment "AR" (Ar Unpack stage)
102 , builderEnvironment "NM" Nm
103 , builderEnvironment "RANLIB" Ranlib
104 , return . AddEnv "CFLAGS" $ unwords cFlags ++ " -w"
105 , return . AddEnv "LDFLAGS" $ unwords ldFlags ++ " -w" ]
106
107 -- Need the libffi archive and `trackAllow` all files in the build directory.
108 -- As all libffi build files are derived from this archive, we can safely
109 -- `trackAllow` the libffi build dir. I.e the archive file can be seen as a
110 -- shallow dependency of the libffi build. This is much simpler than working out
111 -- the dependencies of each rule (within the build dir).
112 -- This means changing the archive file forces a clean build of libffi. This
113 -- seems like a performance issue, but is justified as building libffi is fast
114 -- and the archive file is rarely changed.
115 needLibfffiArchive :: FilePath -> Action FilePath
116 needLibfffiArchive buildPath = do
117 top <- topDirectory
118 tarball <- unifyPath
119 . fromSingleton "Exactly one LibFFI tarball is expected"
120 <$> getDirectoryFiles top ["libffi-tarballs/libffi*.tar.gz"]
121 need [top -/- tarball]
122 trackAllow [buildPath -/- "//*"]
123 return tarball
124
125 libffiRules :: Rules ()
126 libffiRules = do
127 _ <- addOracleCache $ \ (LibffiDynLibs stage)
128 -> readFileLines =<< dynLibManifest stage
129 forM_ [Stage1 ..] $ \stage -> do
130 root <- buildRootRules
131 let path = root -/- stageString stage
132 libffiPath = path -/- pkgName libffi -/- "build"
133
134 -- We set a higher priority because this rule overlaps with the build rule
135 -- for static libraries 'Rules.Library.libraryRules'.
136 dynLibMan <- dynLibManifestRules stage
137 let topLevelTargets = [ libffiPath -/- libffiLibrary
138 , dynLibMan
139 ]
140 priority 2 $ topLevelTargets &%> \_ -> do
141 _ <- needLibfffiArchive libffiPath
142 context <- libffiContext stage
143
144 -- Note this build needs the Makefile, triggering the rules bellow.
145 build $ target context (Make libffiPath) [] []
146
147 -- Find dynamic libraries.
148 dynLibFiles <- do
149 windows <- windowsHost
150 osx <- osxHost
151 let libffiName'' = libffiName' windows True
152 if windows
153 then
154 let libffiDll = "lib" ++ libffiName'' ++ ".dll"
155 in return [libffiPath -/- "inst/bin" -/- libffiDll]
156 else do
157 let libffiLibPath = libffiPath -/- "inst/lib"
158 dynLibsRelative <- liftIO $ getDirectoryFilesIO
159 libffiLibPath
160 (if osx
161 then ["lib" ++ libffiName'' ++ ".dylib*"]
162 else ["lib" ++ libffiName'' ++ ".so*"])
163 return (fmap (libffiLibPath -/-) dynLibsRelative)
164
165 writeFileLines dynLibMan dynLibFiles
166 putSuccess "| Successfully build libffi."
167
168 fmap (libffiPath -/-) ["Makefile.in", "configure" ] &%> \[mkIn, _] -> do
169 -- Extract libffi tar file
170 context <- libffiContext stage
171 removeDirectory libffiPath
172 tarball <- needLibfffiArchive libffiPath
173 -- Go from 'libffi-3.99999+git20171002+77e130c.tar.gz' to 'libffi-3.99999'
174 let libname = takeWhile (/= '+') $ takeFileName tarball
175
176 -- Move extracted directory to libffiPath.
177 root <- buildRoot
178 removeDirectory (root -/- libname)
179 actionFinally (do
180 build $ target context (Tar Extract) [tarball] [path]
181 moveDirectory (path -/- libname) libffiPath) $
182 -- And finally:
183 removeFiles (path) [libname <//> "*"]
184
185 top <- topDirectory
186 fixFile mkIn (fixLibffiMakefile top)
187
188 files <- liftIO $ getDirectoryFilesIO "." [libffiPath <//> "*"]
189 produces files
190
191 fmap (libffiPath -/-) ["Makefile", "config.guess", "config.sub"] &%> \[mk, _, _] -> do
192 _ <- needLibfffiArchive libffiPath
193 context <- libffiContext stage
194
195 -- This need rule extracts the libffi tar file to libffiPath.
196 need [mk <.> "in"]
197
198 -- Configure.
199 forM_ ["config.guess", "config.sub"] $ \file -> do
200 copyFile file (libffiPath -/- file)
201 env <- configureEnvironment stage
202 buildWithCmdOptions env $
203 target context (Configure libffiPath) [mk <.> "in"] [mk]
204
205 dir <- setting BuildPlatform
206 files <- liftIO $ getDirectoryFilesIO "." [libffiPath -/- dir <//> "*"]
207 produces files