packages feed

fficxx-0.7.0.0: src/FFICXX/Generate/ContentMaker.hs

{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedStrings #-}

module FFICXX.Generate.ContentMaker where

import Control.Lens (at, (&), (.~))
import Control.Monad.Trans.Reader (runReader)
import Data.Either (rights)
import Data.Functor.Identity (Identity)
import Data.List (intercalate, nub)
import qualified Data.Map as M
import Data.Maybe (mapMaybe)
import FFICXX.Generate.Code.Cpp
  ( genAllCppHeaderInclude,
    genCppDefInstAccessor,
    genCppDefInstNonVirtual,
    genCppDefInstVirtual,
    genCppDefMacroAccessor,
    genCppDefMacroNonVirtual,
    genCppDefMacroTemplateMemberFunction,
    genCppDefMacroVirtual,
    genCppHeaderInstAccessor,
    genCppHeaderInstNonVirtual,
    genCppHeaderInstVirtual,
    genCppHeaderMacroAccessor,
    genCppHeaderMacroNonVirtual,
    genCppHeaderMacroType,
    genCppHeaderMacroVirtual,
    genTopLevelCppDefinition,
    topLevelDecl,
  )
import FFICXX.Generate.Code.HsCast
  ( genHsFrontInstCastable,
    genHsFrontInstCastableSelf,
  )
import FFICXX.Generate.Code.HsFFI
  ( genHsFFI,
    genImportInFFI,
    genTopLevelFFI,
  )
import FFICXX.Generate.Code.HsFrontEnd
  ( genExport,
    genExtraImport,
    genHsFrontDecl,
    genHsFrontDowncastClass,
    genHsFrontInst,
    genHsFrontInstNew,
    genHsFrontInstNonVirtual,
    genHsFrontInstStatic,
    genHsFrontInstVariables,
    genHsFrontUpcastClass,
    genImportForTLOrdinary,
    genImportForTLTemplate,
    genImportInCast,
    genImportInImplementation,
    genImportInInterface,
    genImportInModule,
    genImportInTopLevel,
    genTopLevelDef,
    hsClassRawType,
  )
import FFICXX.Generate.Code.HsProxy (genProxyInstance)
import FFICXX.Generate.Code.HsTemplate
  ( genImportInTH,
    genImportInTemplate,
    genTLTemplateImplementation,
    genTLTemplateInstance,
    genTLTemplateInterface,
    genTemplateMemberFunctions,
    genTmplImplementation,
    genTmplInstance,
    genTmplInterface,
  )
import FFICXX.Generate.Dependency
  ( class_allparents,
    mkDaughterMap,
    mkDaughterSelfMap,
  )
import FFICXX.Generate.Name
  ( ffiClassName,
    hsClassName,
    hsFrontNameForTopLevel,
  )
import FFICXX.Generate.Type.Annotate (AnnotateMap)
import FFICXX.Generate.Type.Class
  ( Class (..),
    ClassGlobal (..),
    DaughterMap,
    ProtectedMethod (..),
    TopLevel (TLOrdinary, TLTemplate),
    filterTLOrdinary,
    filterTLTemplate,
    isAbstractClass,
  )
import FFICXX.Generate.Type.Module
  ( ClassImportHeader (..),
    ClassModule (..),
    DepCycles,
    TemplateClassImportHeader (..),
    TemplateClassModule (..),
    TopLevelImportHeader (..),
  )
import FFICXX.Generate.Type.PackageInterface
  ( ClassName (..),
    PackageInterface,
    PackageName (..),
  )
import FFICXX.Generate.Util (firstUpper)
import FFICXX.Generate.Util.HaskellSrcExts
  ( emodule,
    evar,
    lang,
    mkImport,
    mkModule,
    mkModuleE,
    unqual,
  )
import FFICXX.Runtime.CodeGen.Cxx (HeaderName (..))
import qualified FFICXX.Runtime.CodeGen.Cxx as R
import Language.Haskell.Exts.Syntax
  ( Decl (..),
    EWildcard (EWildcard),
    ExportSpec (EThingWith),
    Module (..),
  )
import System.FilePath ((<.>), (</>))

srcDir :: FilePath -> FilePath
srcDir installbasedir = installbasedir </> "src"

csrcDir :: FilePath -> FilePath
csrcDir installbasedir = installbasedir </> "csrc"

---- common function for daughter

-- |
mkGlobal :: [Class] -> ClassGlobal
mkGlobal = ClassGlobal <$> mkDaughterSelfMap <*> mkDaughterMap

-- |
buildDaughterDef ::
  ((String, [Class]) -> String) ->
  DaughterMap ->
  String
buildDaughterDef f m =
  let lst = M.toList m
      f' (x, xs) = f (x, filter (not . isAbstractClass) xs)
   in (concatMap f' lst)

-- |
buildParentDef :: ((Class, Class) -> [R.CStatement Identity]) -> Class -> [R.CStatement Identity]
buildParentDef f cls = concatMap (\p -> f (p, cls)) . class_allparents $ cls

-- |
mkProtectedFunctionList :: Class -> [R.CMacro Identity]
mkProtectedFunctionList c =
  map (\x -> R.Define (R.sname ("IS_" <> class_name c <> "_" <> x <> "_PROTECTED")) [] [R.CVerbatim "()"])
    . unProtected
    . class_protected
    $ c

-- |
buildTypeDeclHeader ::
  [Class] ->
  String
buildTypeDeclHeader classes =
  let typeDeclBodyStmts =
        intercalate [R.EmptyLine] $
          map (map R.CRegular . genCppHeaderMacroType) classes
   in R.renderBlock $
        R.ExternC $
          [R.Pragma R.Once, R.EmptyLine] <> typeDeclBodyStmts

-- |
buildDeclHeader ::
  -- | C prefix
  String ->
  ClassImportHeader ->
  String
buildDeclHeader cprefix header =
  let classes = [cihClass header]
      aclass = cihClass header
      declHeaderStmts =
        [R.Include (HdrName (cprefix ++ "Type.h"))]
          <> map R.Include (cihIncludedHPkgHeadersInH header)
      vdecl = map genCppHeaderMacroVirtual classes
      nvdecl = map genCppHeaderMacroNonVirtual classes
      acdecl = map genCppHeaderMacroAccessor classes
      vdef = map genCppDefMacroVirtual classes
      nvdef = map genCppDefMacroNonVirtual classes
      acdef = map genCppDefMacroAccessor classes
      tmpldef = map (\c -> map (genCppDefMacroTemplateMemberFunction c) (class_tmpl_funcs c)) classes
      declDefStmts =
        intercalate [R.EmptyLine] $ [vdecl, nvdecl, acdecl, vdef, nvdef, acdef] ++ tmpldef
      classDeclStmts =
        -- NOTE: Deletable is treated specially.
        -- TODO: We had better make it as a separate constructor in Class.
        if (fst . hsClassName) aclass /= "Deletable"
          then
            buildParentDef (\(p, c) -> [genCppHeaderInstVirtual (p, c), R.CEmptyLine]) aclass
              <> [genCppHeaderInstVirtual (aclass, aclass), R.CEmptyLine]
              <> concatMap (\c -> [genCppHeaderInstNonVirtual c, R.CEmptyLine]) classes
              <> concatMap (\c -> [genCppHeaderInstAccessor c, R.CEmptyLine]) classes
          else []
   in R.renderBlock $
        R.ExternC $
          [R.Pragma R.Once, R.EmptyLine]
            <> declHeaderStmts
            <> [R.EmptyLine]
            <> declDefStmts
            <> [R.EmptyLine]
            <> map R.CRegular classDeclStmts

-- |
buildDefMain ::
  ClassImportHeader ->
  String
buildDefMain cih =
  let classes = [cihClass cih]
      headerStmts =
        [R.Include "MacroPatternMatch.h"]
          <> genAllCppHeaderInclude cih
          <> [R.Include (cihSelfHeader cih)]
      namespaceStmts =
        (map R.UsingNamespace . cihNamespace) cih
      aclass = cihClass cih
      aliasStr =
        intercalate "\n" $
          mapMaybe typedefstmt $
            aclass : rights (cihImportedClasses cih)
        where
          typedefstmt c =
            let n1 = class_name c
                n2 = ffiClassName c
             in if n1 == n2
                  then Nothing
                  else Just ("typedef " <> n1 <> " " <> n2 <> ";")
      cppBodyStmts =
        mkProtectedFunctionList (cihClass cih)
          <> map
            R.CRegular
            ( buildParentDef (\(p, c) -> [genCppDefInstVirtual (p, c), R.CEmptyLine]) (cihClass cih)
                <> ( if isAbstractClass aclass
                       then []
                       else [genCppDefInstVirtual (aclass, aclass), R.CEmptyLine]
                   )
                <> concatMap (\c -> [genCppDefInstNonVirtual c, R.CEmptyLine]) classes
                <> concatMap (\c -> [genCppDefInstAccessor c, R.CEmptyLine]) classes
            )
   in concatMap
        R.renderCMacro
        ( headerStmts
            <> [R.EmptyLine]
            <> map R.CRegular namespaceStmts
            <> [ R.EmptyLine,
                 R.Verbatim aliasStr,
                 R.EmptyLine,
                 R.Verbatim "#define CHECKPROTECT(x,y) FXIS_PAREN(IS_ ## x ## _ ## y ## _PROTECTED)\n",
                 R.EmptyLine,
                 R.Verbatim
                   "#define TYPECASTMETHOD(cname,mname,oname) \\\n\
                   \  FXIIF( CHECKPROTECT(cname,mname) ) ( \\\n\
                   \  (from_nonconst_to_nonconst<oname,cname ## _t>), \\\n\
                   \  (from_nonconst_to_nonconst<cname,cname ## _t>) )\n",
                 R.EmptyLine
               ]
            <> cppBodyStmts
        )

-- |
buildTopLevelHeader ::
  -- | C prefix
  String ->
  TopLevelImportHeader ->
  String
buildTopLevelHeader cprefix tih =
  let declHeaderStmts =
        [R.Include (HdrName (cprefix ++ "Type.h"))]
          <> map R.Include (map cihSelfHeader (tihClassDep tih) ++ tihExtraHeadersInH tih)
      declBodyStmts = map (R.CDeclaration . topLevelDecl) $ filterTLOrdinary (tihFuncs tih)
   in R.renderBlock $
        R.ExternC $
          [R.Pragma R.Once, R.EmptyLine]
            <> declHeaderStmts
            <> [R.EmptyLine]
            <> map R.CRegular declBodyStmts

-- |
buildTopLevelCppDef :: TopLevelImportHeader -> String
buildTopLevelCppDef tih =
  let cihs = tihClassDep tih
      extclasses = tihExtraClassDep tih
      declHeaderStmts =
        [ R.Include "MacroPatternMatch.h",
          R.Include (HdrName (tihHeaderFileName tih <.> "h"))
        ]
          <> concatMap genAllCppHeaderInclude cihs
          <> otherHeaderStmts
      otherHeaderStmts =
        map R.Include (map cihSelfHeader cihs ++ tihExtraHeadersInCPP tih)
      allns = nub ((tihClassDep tih >>= cihNamespace) ++ tihNamespaces tih)
      namespaceStmts = map R.UsingNamespace allns
      aliasStr =
        intercalate "\n" $
          mapMaybe typedefstmt $
            rights (concatMap cihImportedClasses cihs ++ extclasses)
        where
          typedefstmt c =
            let n1 = class_name c
                n2 = ffiClassName c
             in if n1 == n2
                  then Nothing
                  else Just ("typedef " <> n1 <> " " <> n2 <> ";")
      declBodyStr =
        intercalate "\n" $
          map (R.renderCStmt . genTopLevelCppDefinition) $
            filterTLOrdinary (tihFuncs tih)
   in concatMap
        R.renderCMacro
        ( declHeaderStmts
            <> [R.EmptyLine]
            <> map R.CRegular namespaceStmts
            <> [ R.EmptyLine,
                 R.Verbatim aliasStr,
                 R.EmptyLine,
                 R.Verbatim "#define CHECKPROTECT(x,y) FXIS_PAREN(IS_ ## x ## _ ## y ## _PROTECTED)\n",
                 R.EmptyLine,
                 R.Verbatim
                   "#define TYPECASTMETHOD(cname,mname,oname) \\\n\
                   \  FXIIF( CHECKPROTECT(cname,mname) ) ( \\\n\
                   \  (to_nonconst<oname,cname ## _t>), \\\n\
                   \  (to_nonconst<cname,cname ## _t>) )\n",
                 R.EmptyLine,
                 R.Verbatim declBodyStr
               ]
        )

-- |
buildFFIHsc :: ClassModule -> Module ()
buildFFIHsc m =
  mkModule
    (mname <.> "FFI")
    [lang ["ForeignFunctionInterface", "InterruptibleFFI"]]
    ffiImports
    hscBody
  where
    mname = cmModule m
    ffiImports =
      [ mkImport "Data.Word",
        mkImport "Data.Int",
        mkImport "Foreign.C",
        mkImport "Foreign.Ptr",
        mkImport (mname <.> "RawType")
      ]
        <> genImportInFFI m
        <> genExtraImport m
    hscBody = genHsFFI (cmCIH m)

-- |
buildRawTypeHs :: ClassModule -> Module ()
buildRawTypeHs m =
  mkModule
    (cmModule m <.> "RawType")
    [ lang
        [ "ForeignFunctionInterface",
          "TypeFamilies",
          "MultiParamTypeClasses",
          "FlexibleInstances",
          "TypeSynonymInstances",
          "EmptyDataDecls",
          "ExistentialQuantification",
          "ScopedTypeVariables"
        ]
    ]
    rawtypeImports
    rawtypeBody
  where
    rawtypeImports =
      [ mkImport "Foreign.Ptr",
        mkImport "FFICXX.Runtime.Cast"
      ]
    rawtypeBody =
      let c = cihClass (cmCIH m)
       in if isAbstractClass c then [] else hsClassRawType c

-- |
buildInterfaceHs ::
  AnnotateMap ->
  DepCycles ->
  ClassModule ->
  Module ()
buildInterfaceHs amap depCycles m =
  mkModule
    (cmModule m <.> "Interface")
    [ lang
        [ "EmptyDataDecls",
          "ExistentialQuantification",
          "FlexibleContexts",
          "FlexibleInstances",
          "ForeignFunctionInterface",
          "MultiParamTypeClasses",
          "ScopedTypeVariables",
          "TypeFamilies",
          "TypeSynonymInstances"
        ]
    ]
    ifaceImports
    ifaceBody
  where
    classes = [cihClass (cmCIH m)]
    ifaceImports =
      [ mkImport "Data.Word",
        mkImport "Data.Int",
        mkImport "Foreign.C",
        mkImport "Foreign.Ptr",
        mkImport "FFICXX.Runtime.Cast"
      ]
        <> genImportInInterface False depCycles m
        <> genExtraImport m
    ifaceBody =
      runReader (mapM (genHsFrontDecl False) classes) amap
        <> (concatMap genHsFrontUpcastClass . filter (not . isAbstractClass)) classes
        <> (concatMap genHsFrontDowncastClass . filter (not . isAbstractClass)) classes

-- |
buildInterfaceHsBoot :: DepCycles -> ClassModule -> Module ()
buildInterfaceHsBoot depCycles m =
  mkModule
    (cmModule m <.> "Interface")
    [ lang
        [ "EmptyDataDecls",
          "ExistentialQuantification",
          "FlexibleContexts",
          "FlexibleInstances",
          "ForeignFunctionInterface",
          "MultiParamTypeClasses",
          "ScopedTypeVariables",
          "TypeFamilies",
          "TypeSynonymInstances"
        ]
    ]
    hsbootImports
    hsbootBody
  where
    c = cihClass (cmCIH m)
    hsbootImports =
      [ mkImport "Data.Word",
        mkImport "Data.Int",
        mkImport "Foreign.C",
        mkImport "Foreign.Ptr",
        mkImport "FFICXX.Runtime.Cast"
      ]
        <> genImportInInterface True depCycles m
        <> genExtraImport m
    hsbootBody =
      runReader (mapM (genHsFrontDecl True) [c]) M.empty

-- |
buildCastHs :: ClassModule -> Module ()
buildCastHs m =
  mkModule
    (cmModule m <.> "Cast")
    [ lang
        [ "FlexibleInstances",
          "FlexibleContexts",
          "TypeFamilies",
          "MultiParamTypeClasses",
          "OverlappingInstances",
          "IncoherentInstances"
        ]
    ]
    castImports
    body
  where
    classes = [cihClass (cmCIH m)]
    castImports =
      [ mkImport "Foreign.Ptr",
        mkImport "FFICXX.Runtime.Cast",
        mkImport "System.IO.Unsafe"
      ]
        <> genImportInCast m
    body =
      mapMaybe genHsFrontInstCastable classes
        <> mapMaybe genHsFrontInstCastableSelf classes

-- |
buildImplementationHs :: AnnotateMap -> ClassModule -> Module ()
buildImplementationHs amap m =
  mkModule
    (cmModule m <.> "Implementation")
    [ lang
        [ "EmptyDataDecls",
          "FlexibleContexts",
          "FlexibleInstances",
          "ForeignFunctionInterface",
          "IncoherentInstances",
          "MultiParamTypeClasses",
          "OverlappingInstances",
          "TemplateHaskell",
          "TypeFamilies",
          "TypeSynonymInstances"
        ]
    ]
    implImports
    implBody
  where
    classes = [cihClass (cmCIH m)]
    implImports =
      [ mkImport "Data.Monoid", -- for template member
        mkImport "Data.Word",
        mkImport "Data.Int",
        mkImport "Foreign.C",
        mkImport "Foreign.Ptr",
        mkImport "Language.Haskell.TH", -- for template member
        mkImport "Language.Haskell.TH.Syntax", -- for template member
        mkImport "System.IO.Unsafe",
        mkImport "FFICXX.Runtime.Cast",
        mkImport "FFICXX.Runtime.CodeGen.Cxx", -- for template member
        mkImport "FFICXX.Runtime.TH" -- for template member
      ]
        <> genImportInImplementation m
        <> genExtraImport m
    f :: Class -> [Decl ()]
    f y = concatMap (flip genHsFrontInst y) (y : class_allparents y)
    implBody =
      concatMap f classes
        <> runReader (concat <$> mapM genHsFrontInstNew classes) amap
        <> concatMap genHsFrontInstNonVirtual classes
        <> concatMap genHsFrontInstStatic classes
        <> concatMap genHsFrontInstVariables classes
        <> genTemplateMemberFunctions (cmCIH m)

buildProxyHs :: ClassModule -> Module ()
buildProxyHs m =
  mkModule
    (cmModule m <.> "Proxy")
    [ lang
        [ "FlexibleInstances",
          "OverloadedStrings",
          "TemplateHaskell"
        ]
    ]
    [ mkImport "Foreign.Ptr",
      mkImport "FFICXX.Runtime.Cast",
      mkImport "Language.Haskell.TH",
      mkImport "Language.Haskell.TH.Syntax",
      mkImport "FFICXX.Runtime.CodeGen.Cxx"
    ]
    body
  where
    body = genProxyInstance

buildTemplateHs :: TemplateClassModule -> Module ()
buildTemplateHs m =
  mkModule
    (tcmModule m <.> "Template")
    [ lang
        [ "EmptyDataDecls",
          "FlexibleInstances",
          "MultiParamTypeClasses",
          "TypeFamilies"
        ]
    ]
    imports
    body
  where
    t = tcihTClass $ tcmTCIH m
    imports =
      [ mkImport "Foreign.C.Types",
        mkImport "Foreign.Ptr",
        mkImport "FFICXX.Runtime.Cast"
      ]
        <> genImportInTemplate t
    body = genTmplInterface t

buildTHHs :: TemplateClassModule -> Module ()
buildTHHs m =
  mkModule
    (tcmModule m <.> "TH")
    [lang ["TemplateHaskell"]]
    ( [ mkImport "Data.Char",
        mkImport "Data.List",
        mkImport "Data.Monoid",
        mkImport "Foreign.C.Types",
        mkImport "Foreign.Ptr",
        mkImport "Language.Haskell.TH",
        mkImport "Language.Haskell.TH.Syntax",
        mkImport "FFICXX.Runtime.CodeGen.Cxx",
        mkImport "FFICXX.Runtime.TH"
      ]
        <> imports
    )
    body
  where
    t = tcihTClass $ tcmTCIH m
    imports =
      [mkImport (tcmModule m <.> "Template")]
        <> genImportInTH t
    body = tmplImpls <> tmplInsts
    tmplImpls = genTmplImplementation t
    tmplInsts = genTmplInstance (tcmTCIH m)

-- |
buildModuleHs :: ClassModule -> Module ()
buildModuleHs m = mkModuleE (cmModule m) [] (genExport c) (genImportInModule c) []
  where
    c = cihClass (cmCIH m)

-- |
buildTopLevelHs ::
  String ->
  ([ClassModule], [TemplateClassModule]) ->
  Module ()
buildTopLevelHs modname (mods, tmods) =
  mkModuleE modname pkgExtensions pkgExports pkgImports pkgBody
  where
    pkgExtensions =
      [ lang
          [ "FlexibleContexts",
            "FlexibleInstances",
            "ForeignFunctionInterface",
            "InterruptibleFFI"
          ]
      ]
    pkgExports =
      map (emodule . cmModule) mods
        ++ map emodule [modname <.> "Ordinary", modname <.> "Template", modname <.> "TH"]
    pkgImports = genImportInTopLevel modname (mods, tmods)
    pkgBody = [] --    map (genTopLevelFFI tih) (filterTLOrdinary tfns)
    -- ++ concatMap genTopLevelDef (filterTLOrdinary tfns)

buildTopLevelOrdinaryHs ::
  String ->
  ([ClassModule], [TemplateClassModule]) ->
  TopLevelImportHeader ->
  Module ()
buildTopLevelOrdinaryHs modname (_mods, tmods) tih =
  mkModuleE modname pkgExtensions pkgExports pkgImports pkgBody
  where
    tfns = tihFuncs tih
    pkgExtensions =
      [ lang
          [ "FlexibleContexts",
            "FlexibleInstances",
            "ForeignFunctionInterface",
            "InterruptibleFFI"
          ]
      ]
    pkgExports = map (evar . unqual . hsFrontNameForTopLevel . TLOrdinary) (filterTLOrdinary tfns)
    pkgImports =
      map mkImport ["Foreign.C", "Foreign.Ptr", "FFICXX.Runtime.Cast"]
        ++ map (\m -> mkImport (tcmModule m <.> "Template")) tmods
        ++ concatMap genImportForTLOrdinary (filterTLOrdinary tfns)
    pkgBody =
      map (genTopLevelFFI tih) (filterTLOrdinary tfns)
        ++ concatMap genTopLevelDef (filterTLOrdinary tfns)

-- |
buildTopLevelTemplateHs ::
  String ->
  TopLevelImportHeader ->
  Module ()
buildTopLevelTemplateHs modname tih =
  mkModuleE modname pkgExtensions pkgExports pkgImports pkgBody
  where
    tfns = filterTLTemplate (tihFuncs tih)
    pkgExtensions =
      [ lang
          [ "EmptyDataDecls",
            "FlexibleInstances",
            "ForeignFunctionInterface",
            "InterruptibleFFI",
            "MultiParamTypeClasses",
            "TypeFamilies"
          ]
      ]
    pkgExports =
      map
        ( (\n -> EThingWith () (EWildcard () 1) n [])
            . unqual
            . firstUpper
            . hsFrontNameForTopLevel
            . TLTemplate
        )
        tfns
    pkgImports =
      [ mkImport "Foreign.C.Types",
        mkImport "Foreign.Ptr",
        mkImport "FFICXX.Runtime.Cast"
      ]
        ++ concatMap genImportForTLTemplate tfns
    pkgBody = concatMap genTLTemplateInterface tfns

-- |
buildTopLevelTHHs ::
  String ->
  TopLevelImportHeader ->
  Module ()
buildTopLevelTHHs modname tih =
  mkModuleE modname pkgExtensions pkgExports pkgImports pkgBody
  where
    tfns = filterTLTemplate (tihFuncs tih)
    pkgExtensions =
      [ lang
          [ "FlexibleContexts",
            "FlexibleInstances",
            "ForeignFunctionInterface",
            "InterruptibleFFI",
            "TemplateHaskell"
          ]
      ]
    pkgExports =
      map
        ( evar
            . unqual
            . (\x -> "gen" <> x <> "InstanceFor")
            . firstUpper
            . hsFrontNameForTopLevel
            . TLTemplate
        )
        tfns
    pkgImports =
      [ mkImport "Data.Char",
        mkImport "Data.List",
        mkImport "Data.Monoid",
        mkImport "Foreign.C.Types",
        mkImport "Foreign.Ptr",
        mkImport "Language.Haskell.TH",
        mkImport "Language.Haskell.TH.Syntax",
        mkImport "FFICXX.Runtime.CodeGen.Cxx",
        mkImport "FFICXX.Runtime.TH"
      ]
        ++ concatMap genImportForTLTemplate tfns
    pkgBody =
      concatMap genTLTemplateImplementation tfns
        <> concatMap (genTLTemplateInstance tih) tfns

-- |
buildPackageInterface ::
  PackageInterface ->
  PackageName ->
  [ClassImportHeader] ->
  PackageInterface
buildPackageInterface pinfc pkgname = foldr f pinfc
  where
    f cih repo =
      let name = (class_name . cihClass) cih
          header = cihSelfHeader cih
       in repo & at (pkgname, ClsName name) .~ (Just header)