-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathSetup.hs
218 lines (197 loc) · 8.21 KB
/
Setup.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE CPP #-}
import Distribution.Simple
import Distribution.PackageDescription
import Data.Version
import Data.List
import Data.Char(isSpace)
import Data.Maybe
import System.Directory(getCurrentDirectory, removeFile)
import System.IO(IOMode(..), openFile, hPutStr, hFlush, hClose)
import System.IO.Error(catchIOError)
import System.Process(readProcess)
import System.Environment(lookupEnv, getEnv, setEnv)
import System.FilePath.Posix((</>))
import Control.Applicative((<$>))
import Control.Exception(bracket)
import Distribution.Simple.LocalBuildInfo
import Distribution.Simple.UserHooks
import Distribution.Simple.Program(
getDbProgramOutput,
simpleProgram,
progInvokeInput,
programInvocation,
runProgramInvocation,
arProgram,
lookupProgram
)
import Distribution.Simple.Setup(BuildFlags, ConfigFlags, configConfigurationsFlags, fromFlag, buildVerbosity)
import Distribution.PackageDescription(FlagName)
import Distribution.Verbosity
import Distribution.System(buildOS, OS(OSX))
import Distribution.Simple.Utils(copyFileVerbose, die)
import Distribution.Simple.BuildPaths(mkProfLibName, mkLibName)
import Distribution.Package(ComponentId)
import Control.Monad
import System.FilePath
getLLVMDetails :: IO (String, FilePath, FilePath)
getLLVMDetails =
let tryElse act failAct = catchIOError act (const failAct)
in
getLLVMDetails' "llvm-config-3.8" `tryElse`
getLLVMDetails' "llvm-config" `tryElse`
(getEnv "LLVM_CONFIG" >>= getLLVMDetails') `tryElse`
(error "ERROR: cannot find llvm-config, please setup environment variable LLVM_CONFIG with location of llvm-config and try again")
where
rtrim :: String -> String
rtrim = f . f
where f = reverse . dropWhile isSpace
getLLVMDetails' :: FilePath -> IO (String, FilePath, FilePath)
getLLVMDetails' llvmConfigPath = do
version <- rtrim <$> readProcess llvmConfigPath ["--version"] []
libPath <- rtrim <$> readProcess llvmConfigPath ["--libdir"] []
includePath <- rtrim <$> readProcess llvmConfigPath ["--includedir"] []
return (version,libPath,includePath)
libclangLibraries :: [String]
libclangLibraries =
[ "clangARCMigrate"
, "clangAST"
, "clangAnalysis"
, "clangBasic"
, "clangCodeGen"
, "clangDriver"
, "clangEdit"
, "clangFormat"
, "clangFrontend"
, "clangFrontendTool"
, "clangIndex"
, "clangLex"
, "clangParse"
, "clangRewrite"
, "clangRewriteFrontend"
, "clangSema"
, "clangSerialization"
, "clangTooling"
, "LLVMBitReader"
, "LLVMCore"
, "LLVMMCParser"
, "LLVMMC"
, "LLVMOption"
, "LLVMSupport"
, "LLVMTransformUtils"
]
setupLLVMPkgConfig tmpPCPath outf = do
let tryElse act failAct = catchIOError act (const failAct)
(version,libPath,includePath) <- getLLVMDetails
let pc = unlines $ ["Name: LLVM"
,"Description: Low-level Virtual Machine compiler framework"
,"Version: " ++ version
,"URL: http://www.llvm.org/"
,"Requires:"
,"Conflicts:"
,"Libs: -L" ++ libPath ++ " -lLLVM-" ++ version
,"Cflags: -I" ++ includePath]
pkgConfigPath = "PKG_CONFIG_PATH"
hPutStr outf pc
hFlush outf
mpc <- lookupEnv pkgConfigPath
let pkgConfigPathVal = case mpc of
Just pc -> pc ++ ":" ++ tmpPCPath
Nothing -> tmpPCPath
setEnv pkgConfigPath pkgConfigPathVal
withTmpPC runM = do
cwd <- getCurrentDirectory
let llvmPCFname = cwd </> "llvm.pc"
bracket
(openFile llvmPCFname WriteMode)
(\outf -> hClose outf >> removeFile llvmPCFname)
(runM cwd)
linkWithLLVMLibs :: LocalBuildInfo -> BuildFlags -> IO ()
linkWithLLVMLibs lbi flags =
let verbosity = fromFlag (buildVerbosity flags)
componentLibs = concatMap componentLibNames $ componentsConfigs lbi
in do
(_,libPath,_) <- getLLVMDetails
cwd <- getCurrentDirectory
-- OS X's linker _really_ wants to link dynamically, and it doesn't support
-- the options you'd usually use to control that on Linux. We rename the
-- libclang library to make sure the linker does what we intend.
-- Merge the libclang static libraries into each Haskell static library.
let localClangCopy = cwd </> mkStaticLib "clang_static"
copyFileVerbose verbosity (libPath </> mkStaticLib "clang")
localClangCopy
let libClangLibs = map (libPath </>) (map mkStaticLib libclangLibraries)
mergeLibs = mergeLibraries verbosity lbi (localClangCopy : libClangLibs)
forM_ componentLibs $ \componentLib -> do
when (withVanillaLib lbi) $
mergeLibs $ mkLibName componentLib
when (withProfLib lbi) $
mergeLibs $ mkProfLibName componentLib
removeFile localClangCopy
where
mkStaticLib :: String -> String
mkStaticLib lname = "lib" ++ lname <.> "a"
componentLibNames :: (ComponentName, ComponentLocalBuildInfo, [ComponentName]) -> [UnitId]
componentLibNames (_, LibComponentLocalBuildInfo {..}, _) = [componentUnitId]
componentLibNames _ = []
mergeLibraries :: Verbosity -> LocalBuildInfo -> [FilePath] -> FilePath -> IO ()
mergeLibraries verbosity lbi libclangLibs libName = do
let libtool = runLibtool verbosity lbi
ar = runAr verbosity lbi
bdir = buildDir lbi
finalLib = bdir </> libName
origLib = bdir </> libName <.> "orig"
allLibs = origLib : libclangLibs
copyFileVerbose verbosity finalLib origLib
case buildOS of
OSX -> libtool $ ["-static", "-o", finalLib] ++ allLibs
_ -> ar ["-M"] $ arScript finalLib allLibs
arScript :: FilePath -> [FilePath] -> String
arScript finalLib allLibs = unlines $ ["CREATE " ++ finalLib]
++ map ("ADDLIB " ++) allLibs
++ ["SAVE", "END"]
runAr :: Verbosity -> LocalBuildInfo -> [String] -> String -> IO ()
runAr v lbi args script =
case lookupProgram arProgram (withPrograms lbi) of
Nothing -> die "Couldn't find required program 'ar'"
Just cProg -> runProgramInvocation v (progWithStdin cProg)
where
progWithStdin prog = (programInvocation prog args') { progInvokeInput = Just script }
args' = if v >= deafening then "-v" : args else args
runLibtool :: Verbosity -> LocalBuildInfo -> [String] -> IO ()
runLibtool verbosity lbi args = do
output <- getDbProgramOutput verbosity libtoolProgram (withPrograms lbi) args
when (verbosity >= deafening) $
putStrLn output
libtoolProgram = simpleProgram "libtool"
buildHookM :: PackageDescription -> LocalBuildInfo -> UserHooks -> BuildFlags -> IO ()
buildHookM pd lbi uh bf = do
let staticBuildingSpecified = lookup (FlagName "staticbuild") (configConfigurationsFlags (configFlags lbi))
buildNormally = (buildHook simpleUserHooks) pd lbi uh bf
maybe
((buildHook simpleUserHooks) pd lbi uh bf)
(\shouldStaticBuild->
if shouldStaticBuild
then do
buildNormally
linkWithLLVMLibs lbi bf
else buildNormally)
staticBuildingSpecified
pcHook = simpleUserHooks { confHook = confHookM, buildHook = buildHookM }
where confHookM (gpd, hbi) cf = do
let cdt = fromJust $ condLibrary gpd
lib = condTreeData cdt
lbi = libBuildInfo lib
vRange = withinVersion $ makeVersion [3,8]
lbi' = lbi { pkgconfigDepends = pkgconfigDepends lbi ++
[Dependency (PackageName "llvm") vRange] }
lib' = lib { libBuildInfo = lbi' }
gpd' = gpd { condLibrary = Just (cdt { condTreeData = lib' }) }
(confHook simpleUserHooks) (gpd', hbi) cf
#if __GLASGOW_HASKELL__ < 710
makeVersion xs = Version xs []
#endif
main =
withTmpPC $ \cwd outf -> do
setupLLVMPkgConfig cwd outf
defaultMainWithHooks pcHook