-- Copyright 2019 Evan Laforge
-- This program is distributed under the terms of the GNU General Public
-- License 3.0, see COPYING or http://www.gnu.org/licenses/gpl-3.0.txt

-- | Specify C targets and dependencies.
--
-- This is beginning of me trying to modularize build rules.
module Shake.C where
import qualified Development.Shake as Shake


type Flag = String

data ExternalLibrary = ExternalLibrary {
    -- | Add to the link line, e.g. ["-L/path/to/lib", "-lthing"] or
    -- ["/path/to/libthing.a"].
    ExternalLibrary -> [Flag]
libLink :: [Flag]
    -- | Add to the compile line, e.g. ["-I/path/to/include"].
    , ExternalLibrary -> [Flag]
libCompile :: [Flag]
    } deriving (Int -> ExternalLibrary -> ShowS
[ExternalLibrary] -> ShowS
ExternalLibrary -> Flag
forall a.
(Int -> a -> ShowS) -> (a -> Flag) -> ([a] -> ShowS) -> Show a
showList :: [ExternalLibrary] -> ShowS
$cshowList :: [ExternalLibrary] -> ShowS
show :: ExternalLibrary -> Flag
$cshow :: ExternalLibrary -> Flag
showsPrec :: Int -> ExternalLibrary -> ShowS
$cshowsPrec :: Int -> ExternalLibrary -> ShowS
Show)

library :: String -> ExternalLibrary
library :: Flag -> ExternalLibrary
library Flag
name = ExternalLibrary
    { libLink :: [Flag]
libLink = [Flag
"-l" forall a. Semigroup a => a -> a -> a
<> Flag
name]
    , libCompile :: [Flag]
libCompile = []
    }

data Binary config = Binary {
    forall config. Binary config -> Flag
binName :: String
    -- | Object files required, relative to build/<mode>/obj.
    , forall config. Binary config -> [Flag]
binObjs :: [FilePath]
    , forall config. Binary config -> config -> [Flag]
binCompile :: config -> [Flag]
    , forall config. Binary config -> config -> [Flag]
binLink :: config -> [Flag]
    , forall config. Binary config -> config -> [ExternalLibrary]
binLibraries :: config -> [ExternalLibrary]
    -- | Run this after building, with a complete path to the binary.
    , forall config. Binary config -> Flag -> Action ()
binPostproc :: FilePath -> Shake.Action ()
    }
    -- TODO I'd much prefer to have all fields be plain data, not functions,
    -- but they sometimes need access to dynamic config, such as paths
    -- discovered at runtime.  I could just create the Binary from a config,
    -- but then all the fields are dependent on having the config.
    --
    -- A way around this would be to allow variables in Flag, like
    -- "-I${prefix}/include", which are then resolved at runtime.

{- | Describe a C++ binary target.  Unlike haskell binary targets, this has all
    the binary's obj file dependencies explicitly listed.  This is because
    C source files import separate include files, so I can't infer all the
    dependencies just by chasing imports, unless I want to assume that each
    name.h has a corresponding name.cc.  In any case, I have relatively little
    C++ and it changes rarely, so I don't mind a hardcoded list.  An explicit
    list of deps means I can also give compile flags per source file, instead
    of having a global list of flags that applies to all sources.
-}
binary :: String -> [FilePath] -> Binary config
binary :: forall config. Flag -> [Flag] -> Binary config
binary Flag
name [Flag]
objs = Binary
    { binName :: Flag
binName = Flag
name
    , binObjs :: [Flag]
binObjs = [Flag]
objs
    , binCompile :: config -> [Flag]
binCompile = forall a b. a -> b -> a
const []
    , binLink :: config -> [Flag]
binLink = forall a b. a -> b -> a
const []
    , binLibraries :: config -> [ExternalLibrary]
binLibraries = forall a b. a -> b -> a
const []
    , binPostproc :: Flag -> Action ()
binPostproc = forall a b. a -> b -> a
const forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) a. Monad m => a -> m a
return ()
    }

binCompileFlags :: Binary config -> config -> [Flag]
binCompileFlags :: forall config. Binary config -> config -> [Flag]
binCompileFlags Binary config
bin config
config =
    forall config. Binary config -> config -> [Flag]
binCompile Binary config
bin config
config forall a. [a] -> [a] -> [a]
++ forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap ExternalLibrary -> [Flag]
libCompile (forall config. Binary config -> config -> [ExternalLibrary]
binLibraries Binary config
bin config
config)

binLinkFlags :: Binary config -> config -> [Flag]
binLinkFlags :: forall config. Binary config -> config -> [Flag]
binLinkFlags Binary config
bin config
config =
    forall config. Binary config -> config -> [Flag]
binLink Binary config
bin config
config forall a. [a] -> [a] -> [a]
++ forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap ExternalLibrary -> [Flag]
libLink (forall config. Binary config -> config -> [ExternalLibrary]
binLibraries Binary config
bin config
config)