{-# LANGUAGE PatternSynonyms #-} module Development.Shake.C ( -- * Types CConfig (..) , CCompiler (GCC, Clang, GHC, Other, GCCStd, GHCStd) -- * Rules , staticLibR , objectFileR , dynLibR , cBin , cToLib -- * Actions , ccAction , staticLibA -- * Helper functions , cconfigToArgs , ccToString , ccFromString , host ) where import Control.Monad import Data.List (isSuffixOf) import Data.Semigroup import Development.Shake import Development.Shake.FilePath import System.Info mkQualified :: Monoid a => Maybe a -> a -> a mkQualified suff = h (g <$> [suff]) where g = maybe id mappend h = foldr fmap id host :: String host = arch ++ withManufacturer os where withManufacturer "darwin" = "-apple-" ++ os withManufacturer _ = "-unknown-" ++ os pattern GCCStd :: CCompiler pattern GCCStd = GCC Nothing pattern GHCStd :: CCompiler pattern GHCStd = GHC Nothing ccToString :: CCompiler -> String ccToString Clang = "clang" ccToString (Other s) = s ccToString (GCC pre) = mkQualified pre "gcc" ccToString (GHC pre) = mkQualified pre "ghc" arToString :: CCompiler -> String arToString (GCC pre) = mkQualified pre "ar" arToString (GHC pre) = mkQualified pre "ar" arToString _ = "ar" ccFromString :: String -> CCompiler ccFromString "gcc" = GCC Nothing ccFromString "clang" = Clang ccFromString "ghc" = GHC Nothing ccFromString s | "gcc" `isSuffixOf` s = GCC (Just (reverse . drop 3 . reverse $ s)) | "ghc" `isSuffixOf` s = GHC (Just (reverse . drop 3 . reverse $ s)) ccFromString _ = GCC Nothing data CCompiler = GCC { _prefix :: Maybe String } | Clang | Other String | GHC { _prefix :: Maybe String } deriving (Eq) mapFlags :: String -> ([String] -> [String]) mapFlags s = fmap (s <>) data CConfig = CConfig { includes :: [String] -- ^ Directories to be included. , libraries :: [String] -- ^ Libraries against which to link. , libDirs :: [String] -- ^ Directories to find libraries. , extras :: [String] -- ^ Extra flags to be passed to the compiler } -- | Rules for making a static library from C source files cToLib :: CCompiler -> [FilePath] -- ^ C source files -> FilePattern -- ^ Static libary output -> CConfig -> Rules () cToLib cc sources lib cfg = mconcat [ foldr (>>) (pure ()) objRules , staticLibR cc (g sources) lib cfg ] where objRules = objectFileR cc <$> g sources <*> pure lib <*> pure cfg g = fmap (-<.> "o") cBin :: CCompiler -> [FilePath] -- ^ C source files -> FilePattern -- ^ Binary file output -> CConfig -> Rules () cBin cc sources bin cfg = bin %> \out -> ccAction cc sources out cfg ccAction :: CmdResult r => CCompiler -> [FilePath] -- ^ Source files -> FilePath -- ^ Binary file output -> CConfig -> Action r ccAction cc sources out cfg = need sources >> (command [EchoStderr False] (ccToString cc) . (("-o" : out : sources) <>) . cconfigToArgs) cfg cconfigToArgs :: CConfig -> [String] cconfigToArgs (CConfig is ls ds es) = join [ mapFlags "-I" is, mapFlags "-l" ls, mapFlags "-L" ds, es ] dynLibR :: CCompiler -> [FilePath] -> FilePattern -> CConfig -> Rules () dynLibR cc objFiles shLib cfg = shLib %> \out -> need objFiles >> command mempty (ccToString cc) ("-shared" : "-o" : out : objFiles <> cconfigToArgs cfg) objectFileR :: CCompiler -> FilePath -- ^ C source file -> FilePattern -- ^ Object file output -> CConfig -> Rules () objectFileR cc srcFile objFile cfg = objFile %> \out -> need [srcFile] >> command mempty (ccToString cc) (srcFile : "-c" : "-fPIC" : "-o" : out : cconfigToArgs cfg) staticLibA :: CmdResult r => CCompiler -> [FilePath] -- ^ Object files to be linked -> FilePattern -- ^ File pattern for static library outputs -> CConfig -> Action r staticLibA ar objFiles stalib _ = need objFiles >> command mempty (arToString ar) ("rcs" : stalib : objFiles) staticLibR :: CCompiler -- ^ ar binary -> [FilePath] -- ^ Object files to be linked -> FilePattern -- ^ File pattern for static library outputs -> CConfig -> Rules () staticLibR ar objFiles stalib cfg = stalib %> \out -> staticLibA ar objFiles out cfg