summaryrefslogtreecommitdiff
path: root/hadrian/src/Hadrian/Package.hs
blob: 11a6998f65008103563a717e0f8d9ab569a165d1 (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
112
113
114
115
116
117
118
119
120
-----------------------------------------------------------------------------
-- |
-- 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, PackageLanguage, PackageType,

    -- * Construction and properties
    cLibrary, cProgram, hsLibrary, hsProgram,
    isLibrary, isProgram, isCPackage, isHsPackage,

    -- * Package directory structure
    pkgCabalFile, unsafePkgCabalFile
    ) where

import Data.Maybe
import Development.Shake.Classes
import Development.Shake.FilePath
import GHC.Generics
import GHC.Stack
import Hadrian.Utilities

data PackageLanguage = C | Haskell deriving (Generic, Show)

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

type PackageName = String

-- TODO: Consider turning Package into a GADT indexed with language and type.
data Package = Package {
    -- | The package language. 'C' and 'Haskell' packages are supported.
    pkgLanguage :: PackageLanguage,
    -- | 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.
    pkgPath :: FilePath
    } deriving (Generic, Show)

instance Eq Package where
    p == q = pkgName p == pkgName q

instance Ord Package where
    compare p q = compare (pkgName p) (pkgName q)

instance Binary   PackageLanguage
instance Hashable PackageLanguage
instance NFData   PackageLanguage

instance Binary   PackageType
instance Hashable PackageType
instance NFData   PackageType

instance Binary   Package
instance Hashable Package
instance NFData   Package

-- | Construct a C library package.
cLibrary :: PackageName -> FilePath -> Package
cLibrary = Package C Library

-- | Construct a C program package.
cProgram :: PackageName -> FilePath -> Package
cProgram = Package C Program

-- | Construct a Haskell library package.
hsLibrary :: PackageName -> FilePath -> Package
hsLibrary = Package Haskell Library

-- | Construct a Haskell program package.
hsProgram :: PackageName -> FilePath -> Package
hsProgram = Package Haskell Program

-- | 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

-- | Is this a C package?
isCPackage :: Package -> Bool
isCPackage (Package C _ _ _) = True
isCPackage _ = False

-- | Is this a Haskell package?
isHsPackage :: Package -> Bool
isHsPackage (Package Haskell _ _ _) = True
isHsPackage _ = False

-- | The path to the Cabal file of a Haskell package, e.g. @ghc/ghc-bin.cabal@,
-- or @Nothing@ if the argument is not a Haskell package.
pkgCabalFile :: Package -> Maybe FilePath
pkgCabalFile p | isHsPackage p = Just $ pkgPath p -/- pkgName p <.> "cabal"
               | otherwise     = Nothing

-- | Like 'pkgCabalFile' but raises an error on a non-Haskell package.
unsafePkgCabalFile :: HasCallStack => Package -> FilePath
unsafePkgCabalFile p = fromMaybe (error msg) (pkgCabalFile p)
  where
    msg = "[unsafePkgCabalFile] Not a Haskell package: " ++ show p