{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} ----------------------------------------------------------------------------- -- | -- Module : FFICXX.Generate.Code.Cabal -- Copyright : (c) 2011-2019 Ian-Woo Kim -- -- License : BSD3 -- Maintainer : Ian-Woo Kim -- Stability : experimental -- Portability : GHC -- ----------------------------------------------------------------------------- module FFICXX.Generate.Code.Cabal where import Data.Aeson.Encode.Pretty (encodePretty) import qualified Data.ByteString.Lazy as BL import Data.List (intercalate, nub) import Data.Monoid ((<>)) import Data.Text (Text) import Data.Text.Template (substitute) import qualified Data.Text as T (intercalate,pack,replicate,unlines) import qualified Data.Text.Lazy as TL (toStrict) import qualified Data.Text.IO as TIO (writeFile) import System.FilePath ((<.>),()) -- import FFICXX.Generate.Type.Cabal ( AddCInc(..) , AddCSrc(..) , BuildType(..) , CabalName(..) , Cabal(..) , GeneratedCabalInfo(..) ) import FFICXX.Generate.Type.Module import FFICXX.Generate.Type.PackageInterface import FFICXX.Generate.Util cabalIndentation :: Text cabalIndentation = T.replicate 23 " " unlinesWithIndent = T.unlines . map (cabalIndentation <>) -- for source distribution genCsrcFiles :: (TopLevelImportHeader,[ClassModule]) -> [AddCInc] -> [AddCSrc] -> [String] genCsrcFiles (tih,cmods) acincs acsrcs = let -- indent = cabalIndentation selfheaders' = do x <- cmods y <- cmCIH x return (cihSelfHeader y) selfheaders = nub selfheaders' selfcpp' = do x <- cmods y <- cmCIH x return (cihSelfCpp y) selfcpp = nub selfcpp' tlh = tihHeaderFileName tih <.> "h" tlcpp = tihHeaderFileName tih <.> "cpp" includeFileStrsWithCsrc = map (\x->"csrc" x) $ (if (null.tihFuncs) tih then map unHdrName selfheaders else tlh:(map unHdrName selfheaders)) ++ map (\(AddCInc hdr _) -> hdr) acincs cppFilesWithCsrc = map (\x->"csrc"x) $ (if (null.tihFuncs) tih then selfcpp else tlcpp:selfcpp) ++ map (\(AddCSrc src _) -> src) acsrcs in includeFileStrsWithCsrc <> cppFilesWithCsrc -- for library genIncludeFiles :: String -- ^ package name -> ([ClassImportHeader],[TemplateClassImportHeader]) -> [AddCInc] -> [String] genIncludeFiles pkgname (cih,tcih) acincs = let -- indent = cabalIndentation selfheaders = map cihSelfHeader cih <> map tcihSelfHeader tcih includeFileStrs = map unHdrName (selfheaders ++ map (\(AddCInc hdr _) -> HdrName hdr) acincs) in (pkgname<>"Type.h") : includeFileStrs -- unlines ((indent<> -- for library genCppFiles :: (TopLevelImportHeader,[ClassModule]) -> [AddCSrc] -> [String] genCppFiles (tih,cmods) acsrcs = let -- indent = cabalIndentation selfcpp' = do x <- cmods y <- cmCIH x return (cihSelfCpp y) selfcpp = nub selfcpp' tlcpp = tihHeaderFileName tih <.> "cpp" cppFileStrs = map (\x -> "csrc" x) $ (if (null.tihFuncs) tih then selfcpp else tlcpp:selfcpp) ++ map (\(AddCSrc src _) -> src) acsrcs in cppFileStrs -- | generate exposed module list in cabal file genExposedModules :: String -> ([ClassModule],[TemplateClassModule]) -> [String] genExposedModules summarymod (cmods,tmods) = let -- indentspace = cabalIndentation -- summarystrs = summarymod cmodstrs = map cmModule cmods rawType = map ((\x -> x <> ".RawType").cmModule) cmods ffi = map ((\x -> x <> ".FFI").cmModule) cmods interface= map ((\x-> x <> ".Interface").cmModule) cmods cast = map ((\x-> x <> ".Cast").cmModule) cmods implementation = map ((\x-> x <> ".Implementation").cmModule) cmods template = map ((\x-> x <> ".Template").tcmModule) tmods th = map ((\x-> x <> ".TH").tcmModule) tmods in -- unlines [summarymod]<>cmodstrs<>rawType<>ffi<>interface<>cast<>implementation<>template<>th -- | generate other modules in cabal file genOtherModules :: [ClassModule] -> [String] genOtherModules _cmods = [""] -- | generate additional package dependencies. genPkgDeps :: [CabalName] -> [String] genPkgDeps cs = [ "base > 4 && < 5" , "fficxx >= 0.5" , "fficxx-runtime >= 0.5" , "template-haskell" ] ++ map unCabalName cs -- | cabalTemplate :: Text cabalTemplate = "Name: $pkgname\n\ \Version: $version\n\ \Synopsis: $synopsis\n\ \Description: $description\n\ \Homepage: $homepage\n\ \$licenseField\n\ \$licenseFileField\n\ \Author: $author\n\ \Maintainer: $maintainer\n\ \Category: $category\n\ \Tested-with: GHC >= 7.6\n\ \$buildtype\n\ \cabal-version: >= 2\n\ \Extra-source-files:\n\ \$extraFiles\n\ \$csrcFiles\n\ \\n\ \$sourcerepository\n\ \\n\ \Library\n\ \ default-language: Haskell2010\n\ \ hs-source-dirs: src\n\ \ ghc-options: -Wall -funbox-strict-fields -fno-warn-unused-do-bind -fno-warn-orphans -fno-warn-unused-imports\n\ \ ghc-prof-options: -caf-all -auto-all\n\ \ cxx-options: $cxxOptions\n\ \ Build-Depends: $pkgdeps\n\ \ Exposed-Modules:\n\ \$exposedModules\n\ \ Other-Modules:\n\ \$otherModules\n\ \ extra-lib-dirs: $extralibdirs\n\ \ extra-libraries: $extraLibraries\n\ \ Include-dirs: csrc $extraincludedirs\n\ \ pkgconfig-depends: $pkgconfigDepends\n\ \ Install-includes:\n\ \$includeFiles\n\ \ C-sources:\n\ \$cppFiles\n" -- TODO: remove all T.pack after we switch over to Text genCabalInfo :: Cabal -> String -> PackageConfig -> [String] -- ^ extra libs -> GeneratedCabalInfo genCabalInfo cabal summarymodule pkgconfig extralibs = let tih = pcfg_topLevelImportHeader pkgconfig classmodules = pcfg_classModules pkgconfig cih = pcfg_classImportHeaders pkgconfig tmods = pcfg_templateClassModules pkgconfig tcih = pcfg_templateClassImportHeaders pkgconfig acincs = pcfg_additional_c_incs pkgconfig acsrcs = pcfg_additional_c_srcs pkgconfig extrafiles = cabal_extrafiles cabal in GeneratedCabalInfo { gci_pkgname = T.pack (unCabalName (cabal_pkgname cabal)) , gci_version = T.pack (cabal_version cabal) , gci_synopsis = "" , gci_description = "" , gci_homepage = "" , gci_license = maybe "" T.pack (cabal_license cabal) , gci_licenseFile = maybe "" T.pack (cabal_licensefile cabal) , gci_author = "" , gci_maintainer = "" , gci_category = "" , gci_buildtype = case cabal_buildType cabal of Simple -> "Build-Type: Simple" Custom deps -> "Build-Type: Custom\ncustom-setup\n setup-depends: " <> T.pack (intercalate ", " (map unCabalName deps)) <> "\n" , gci_extraFiles = map T.pack extrafiles , gci_csrcFiles = map T.pack $ genCsrcFiles (tih,classmodules) acincs acsrcs , gci_sourcerepository = "" , gci_cxxOptions = ["-std=c++14"] , gci_pkgdeps = map T.pack $ genPkgDeps (cabal_additional_pkgdeps cabal) , gci_exposedModules = map T.pack $ genExposedModules summarymodule (classmodules,tmods) , gci_otherModules = map T.pack $ genOtherModules classmodules , gci_extraLibDirs = map T.pack $ cabal_extralibdirs cabal , gci_extraLibraries = map T.pack extralibs , gci_extraIncludeDirs = map T.pack $ cabal_extraincludedirs cabal , gci_pkgconfigDepends = map T.pack $ cabal_pkg_config_depends cabal , gci_includeFiles = map T.pack $ genIncludeFiles (unCabalName (cabal_pkgname cabal)) (cih,tcih) acincs , gci_cppFiles = map T.pack $ genCppFiles (tih,classmodules) acsrcs } genCabalFile :: GeneratedCabalInfo -> Text genCabalFile GeneratedCabalInfo {..} = TL.toStrict $ substitute cabalTemplate $ contextT [ ("licenseField" , "license: " <> gci_license) , ("licenseFileField", "license-file: " <> gci_licenseFile) , ("pkgname" , gci_pkgname) , ("version" , gci_version) , ("buildtype" , gci_buildtype) , ("synopsis" , gci_synopsis) , ("description" , gci_description) , ("homepage" , gci_homepage) , ("author" , gci_author) , ("maintainer" , gci_maintainer) , ("category" , gci_category) , ("sourcerepository", gci_sourcerepository) , ("cxxOptions" , T.intercalate " " gci_cxxOptions) , ("pkgdeps" , T.intercalate ", " gci_pkgdeps) , ("extraFiles" , unlinesWithIndent gci_extraFiles) , ("csrcFiles" , unlinesWithIndent gci_csrcFiles) , ("includeFiles" , unlinesWithIndent gci_includeFiles) , ("cppFiles" , unlinesWithIndent gci_cppFiles) , ("exposedModules" , unlinesWithIndent gci_exposedModules) , ("otherModules" , unlinesWithIndent gci_otherModules) , ("extralibdirs" , T.intercalate ", " gci_extraLibDirs) , ("extraincludedirs", T.intercalate ", " gci_extraIncludeDirs) , ("extraLibraries" , T.intercalate ", " gci_extraLibraries) , ("cabalIndentation", cabalIndentation) , ("pkgconfigDepends", T.intercalate ", " gci_pkgconfigDepends) ] -- | buildCabalFile :: Cabal -> String -> PackageConfig -> [String] -- ^ Extra libs -> FilePath -- ^ Cabal file path -> IO () buildCabalFile cabal summarymodule pkgconfig extralibs cabalfile = do let cinfo = genCabalInfo cabal summarymodule pkgconfig extralibs txt = genCabalFile cinfo TIO.writeFile cabalfile txt -- | buildJSONFile :: Cabal -> String -> PackageConfig -> [String] -- ^ Extra libs -> FilePath -- ^ JSON file path -> IO () buildJSONFile cabal summarymodule pkgconfig extralibs jsonfile = do let cinfo = genCabalInfo cabal summarymodule pkgconfig extralibs BL.writeFile jsonfile (encodePretty cinfo)