summaryrefslogtreecommitdiff
path: root/hadrian/src/Hadrian/Package.hs
blob: 8eff8df25c556c5ca59d52a808859eee00f56df9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
-----------------------------------------------------------------------------
-- |
-- Module     : Hadrian.Package
-- Copyright  : (c) Andrey Mokhov 2014-2017
-- License    : MIT (see the file LICENSE)
-- Maintainer : andrey.mokhov@gmail.com
-- Stability  : experimental
--
-- A /package/ is a collection of files. We currently only support C and Haskell
-- packages and treat a package as either a library or a program. The latter is
-- a gross oversimplification as, for example, Haskell packages can be both.
-- This works for now, but should be improved in future.
-----------------------------------------------------------------------------
module Hadrian.Package (
    -- * Data types
    Package (..), PackageName, PackageType, PackageLocation(..),

    -- * Construction and properties
    library, program, external, dummyPackage
    , isLibrary, isProgram,

    Plugin(..), PluginArgs(..), addPlugin

    ) where

import Development.Shake.Classes
import Development.Shake
import GHC.Generics

-- TODO: Make PackageType more precise.
-- See https://github.com/snowleopard/hadrian/issues/12.
data PackageType = Library | Program deriving (Eq, Generic, Ord, Show)

data PackageLocation =
          Internal FilePath -- ^ Path to the file contents relative to
                            --   root directory.
        | External String   -- ^ Version string to fetch package from Hackage.
        deriving (Eq, Generic, Ord, Show)

type PackageName = String

-- TODO: Consider turning Package into a GADT indexed with language and type.
data Package = Package {
    -- | The package type. 'Library' and 'Program' packages are supported.
    pkgType :: PackageType,
    -- | The package name. We assume that all packages have different names,
    -- hence two packages with the same name are considered equal.
    pkgName :: PackageName,
    -- | The path to the package source code relative to the root of the build
    -- system. For example, @libraries/Cabal/Cabal@ and @ghc@ are paths to the
    -- @Cabal@ and @ghc-bin@ packages in GHC.
    pkgLocation :: PackageLocation,

    -- | Plugins to run when building the package
    pkgPlugins :: [String]
    } deriving (Eq, Generic, Ord, Show)

-- | Construct a library package.
library :: PackageName -> FilePath -> Package
library p fp = Package Library p (Internal fp) []

external :: PackageName -> String -> Package
external p v = Package Library p (External v) []

-- | Construct a program package.
program :: PackageName -> FilePath -> Package
program p fp = Package Program p (Internal fp) []

-- TODO: Remove this hack.
-- | A dummy package that we never try to build but use when we need a 'Package'
-- to construct a 'Context' but do not need to access the package field.
dummyPackage :: Package
dummyPackage = library "dummy" "dummy/path/"

-- | Is this a library package?
isLibrary :: Package -> Bool
isLibrary (Package (Library {}) _ _ _) = True
isLibrary _ = False

-- | Is this a program package?
isProgram :: Package -> Bool
isProgram (Package Program _ _ _) = True
isProgram _ = False

instance Binary   PackageType
instance Hashable PackageType
instance NFData   PackageType

instance Binary   PackageLocation
instance Hashable PackageLocation
instance NFData   PackageLocation

instance Binary   Package
instance Hashable Package
instance NFData   Package

{- Plugins -}

data Plugin = Plugin { pluginPackage :: Package
                     , pluginName :: String
                     , pluginOpts :: PluginArgs -> [String]
                     , initPhase  :: PluginArgs -> Action ()
                     , finalPhase :: PluginArgs -> Action ()
                     }


data PluginArgs = PluginArgs { pluginOutput :: FilePath
                             , pluginTargetPackage :: Package }

addPlugin :: Plugin -> Package -> Package
addPlugin pl p = p { pkgPlugins = pkgName (pluginPackage pl) : pkgPlugins p }