glean/lang/clang/Setup.hs (174 lines of code) (raw):

{- Copyright (c) 2013, Benjamin S. Scarlet and Google Inc. This a slight adaptation of the llvm-hs Setup.hs: https://github.com/llvm-hs/llvm-hs/blob/llvm-12/llvm-hs/Setup.hs # @lint-ignore-every LICENSELINT HLINT LINEWRAP -} {-# LANGUAGE CPP, FlexibleInstances, TupleSections #-} import Control.Exception (SomeException, try) import Control.Monad import Data.Char import Data.List import Data.Maybe import Data.Monoid import Distribution.PackageDescription hiding (buildInfo, includeDirs) import Distribution.Simple import Distribution.Simple.LocalBuildInfo import Distribution.Simple.PreProcess import Distribution.Simple.Program import Distribution.Simple.Setup hiding (Flag) import Distribution.System import qualified Distribution.Types.Executable as Exe import System.Environment -- define these selectively in C files (we are _not_ using HsFFI.h), -- rather than universally in the ccOptions, because HsFFI.h currently defines them -- without checking they're already defined and so causes warnings. uncheckedHsFFIDefines :: [String] uncheckedHsFFIDefines = ["__STDC_LIMIT_MACROS"] llvmVersions :: [Version] llvmVersions = map mkVersion $ concat [ concat [ [ [a, b, c] | c <- versions_c ] ++ [[a, b]] | b <- versions_b ] ++ [[a]] | a <- versions_a ] where versions_a = [11, 12] versions_b = [0, 1] versions_c = [0, 1] -- Ordered by decreasing specificty so we will prefer llvm-config-9.0 -- over llvm-config-9 over llvm-config. llvmConfigNames :: [String] llvmConfigNames = reverse versionedConfigs ++ ["llvm-config"] where versionedConfigs = map (\vs -> "llvm-config-" ++ intercalate "." (map show $ versionNumbers vs)) llvmVersions findJustBy :: Monad m => (a -> m (Maybe b)) -> [a] -> m (Maybe b) findJustBy f (x:xs) = do x' <- f x case x' of Nothing -> findJustBy f xs j -> return j findJustBy _ [] = return Nothing llvmProgram :: Program llvmProgram = (simpleProgram "llvm-config") { programFindLocation = \v p -> findJustBy (\n -> programFindLocation (simpleProgram n) v p) llvmConfigNames, programFindVersion = \verbosity path -> let stripVcsSuffix = takeWhile (\c -> isDigit c || c == '.') trim = dropWhile isSpace . reverse . dropWhile isSpace . reverse in findProgramVersion "--version" (stripVcsSuffix . trim) verbosity path } getLLVMConfig :: ConfigFlags -> IO ([String] -> IO String) getLLVMConfig confFlags = do let verbosity = fromFlag $ configVerbosity confFlags (program, _, _) <- requireProgramVersion verbosity llvmProgram llvmVersionsRange (configPrograms confFlags) return $ getProgramOutput verbosity program where llvmVersionsRange = foldr1 unionVersionRanges [ thisVersion v | v <- llvmVersions ] addToLdLibraryPath :: String -> IO () addToLdLibraryPath path = do let (ldLibraryPathVar, ldLibraryPathSep) = case buildOS of OSX -> ("DYLD_LIBRARY_PATH",":") _ -> ("LD_LIBRARY_PATH",":") v <- try $ getEnv ldLibraryPathVar :: IO (Either SomeException String) setEnv ldLibraryPathVar (path ++ either (const "") (ldLibraryPathSep ++) v) addLLVMToLdLibraryPath :: ConfigFlags -> IO () addLLVMToLdLibraryPath confFlags = do llvmConfig <- getLLVMConfig confFlags [libDir] <- liftM lines $ llvmConfig ["--libdir"] addToLdLibraryPath libDir -- | These flags are not relevant for us and dropping them allows -- linking against LLVM build with Clang using GCC ignoredCxxFlags :: [String] ignoredCxxFlags = ["-fcolor-diagnostics"] ++ map ("-D" ++) uncheckedHsFFIDefines ignoredCFlags :: [String] ignoredCFlags = ["-fcolor-diagnostics"] -- | Header directories are added separately to configExtraIncludeDirs isIncludeFlag :: String -> Bool isIncludeFlag flag = "-I" `isPrefixOf` flag isWarningFlag :: String -> Bool isWarningFlag flag = "-W" `isPrefixOf` flag isIgnoredCFlag :: String -> Bool isIgnoredCFlag flag = flag `elem` ignoredCFlags || isIncludeFlag flag || isWarningFlag flag isIgnoredCxxFlag :: String -> Bool isIgnoredCxxFlag flag = flag `elem` ignoredCxxFlags || isIncludeFlag flag || isWarningFlag flag main :: IO () main = do let origUserHooks = simpleUserHooks defaultMainWithHooks (origUserHooks { hookedPrograms = [ llvmProgram ], confHook = \(genericPackageDescription, hookedBuildInfo) confFlags -> do llvmConfig <- getLLVMConfig confFlags llvmCxxFlags <- do rawLlvmCxxFlags <- llvmConfig ["--cxxflags"] return . filter (not . isIgnoredCxxFlag) $ words rawLlvmCxxFlags let stdLib = maybe "stdc++" (drop (length stdlibPrefix)) (find (isPrefixOf stdlibPrefix) llvmCxxFlags) where stdlibPrefix = "-stdlib=lib" includeDirs <- liftM lines $ llvmConfig ["--includedir"] libDirs <- liftM lines $ llvmConfig ["--libdir"] [llvmVersion] <- liftM lines $ llvmConfig ["--version"] let getLibs = liftM (map (fromJust . stripPrefix "-l") . words) . llvmConfig flags = configConfigurationsFlags confFlags linkFlag = "--link-shared" libs <- getLibs ["--libs", linkFlag] systemLibs <- getLibs ["--system-libs", linkFlag] let llvmBuildInfo = mempty { extraLibs = libs ++ stdLib : systemLibs } genericPackageDescription' = genericPackageDescription { condLibrary = do -- maybe monad libraryCondTree <- condLibrary genericPackageDescription return libraryCondTree { condTreeData = condTreeData libraryCondTree <> mempty { libBuildInfo = llvmBuildInfo } } , condExecutables = do -- list monad (name, exeCondTree) <- condExecutables genericPackageDescription return . (name,) $ exeCondTree { condTreeData = condTreeData exeCondTree <> mempty { Exe.buildInfo = llvmBuildInfo } } } configFlags' = confFlags { configExtraLibDirs = libDirs ++ configExtraLibDirs confFlags, configExtraIncludeDirs = includeDirs ++ configExtraIncludeDirs confFlags } addLLVMToLdLibraryPath configFlags' confHook simpleUserHooks (genericPackageDescription', hookedBuildInfo) configFlags', hookedPreProcessors = let origHookedPreprocessors = hookedPreProcessors origUserHooks newHsc buildInfo localBuildInfo componentLocalBuildInfo = PreProcessor { platformIndependent = platformIndependent (origHsc buildInfo), runPreProcessor = \inFiles outFiles verbosity -> do llvmConfig <- getLLVMConfig (configFlags localBuildInfo) llvmCFlags <- do rawLlvmCFlags <- llvmConfig ["--cflags"] return . filter (not . isIgnoredCFlag) $ words rawLlvmCFlags let buildInfo' = buildInfo { ccOptions = "-Wno-variadic-macros" : llvmCFlags } runPreProcessor (origHsc buildInfo') inFiles outFiles verbosity } where origHsc buildInfo' = fromMaybe ppHsc2hs (lookup "hsc" origHookedPreprocessors) buildInfo' localBuildInfo componentLocalBuildInfo in [("hsc", newHsc)] ++ origHookedPreprocessors, buildHook = \packageDesc localBuildInfo userHooks buildFlags -> do addLLVMToLdLibraryPath (configFlags localBuildInfo) buildHook origUserHooks packageDesc localBuildInfo userHooks buildFlags, testHook = \args packageDesc localBuildInfo userHooks testFlags -> do addLLVMToLdLibraryPath (configFlags localBuildInfo) testHook origUserHooks args packageDesc localBuildInfo userHooks testFlags })