Commit 5ef696e4 authored by Andrey Mokhov's avatar Andrey Mokhov
Browse files

Differentiate between C and Haskell package

parent 74a6561d
......@@ -29,11 +29,11 @@ executable hadrian
, Hadrian.Expression
, Hadrian.Haskell.Cabal
, Hadrian.Haskell.Cabal.Parse
, Hadrian.Haskell.Package
, Hadrian.Oracles.ArgsHash
, Hadrian.Oracles.DirectoryContents
, Hadrian.Oracles.Path
, Hadrian.Oracles.TextFile
, Hadrian.Package
, Hadrian.Target
, Hadrian.Utilities
, Oracles.Flag
......
......@@ -15,7 +15,7 @@ module Base (
-- * Basic data types
module Builder,
module Hadrian.Haskell.Package,
module Hadrian.Package,
module Stage,
module Way,
......@@ -37,7 +37,7 @@ import Development.Shake.Classes
import Development.Shake.FilePath
import Development.Shake.Util
import Hadrian.Utilities
import Hadrian.Haskell.Package
import Hadrian.Package
import Builder
import Stage
......
......@@ -4,6 +4,7 @@ module Context (
-- * Expressions
getStage, getPackage, getWay, getStagedSettingList, getBuildPath,
withHsPackage,
-- * Paths
contextDir, buildPath, pkgInplaceConfig, pkgDataFile, pkgSetupConfigFile,
......@@ -55,23 +56,37 @@ getWay = way <$> getContext
getStagedSettingList :: (Stage -> SettingList) -> Args Context b
getStagedSettingList f = getSettingList . f =<< getStage
-- | Get the build path of the current 'Context'.
getBuildPath :: Expr Context b FilePath
getBuildPath = expr . buildPath =<< getContext
-- | Construct an expression that depends on the Cabal file of the current
-- package and is empty in a non-Haskell context.
withHsPackage :: (Monoid a, Semigroup a) => (FilePath -> Expr Context b a) -> Expr Context b a
withHsPackage expr = do
pkg <- getPackage
case pkgCabalFile pkg of
Just file -> expr file
Nothing -> mempty
-- | The directory in 'buildRoot' containing build artefacts of a given 'Context'.
contextDir :: Context -> FilePath
contextDir Context {..} = stageString stage -/- pkgPath package
-- | Path to the directory containing build artefacts of a given 'Context'.
buildPath :: Context -> Action FilePath
buildPath context = buildRoot <&> (-/- contextDir context)
-- | The directory in 'buildRoot' containing build artefacts of a given 'Context'.
contextDir :: Context -> FilePath
contextDir Context {..} = stageString stage -/- pkgPath package
-- | Get the build path of the current 'Context'.
getBuildPath :: Expr Context b FilePath
getBuildPath = expr . buildPath =<< getContext
pkgId :: Package -> Action FilePath
pkgId package = case pkgCabalFile package of
Just file -> pkgIdentifier file
Nothing -> return (pkgName package) -- Non-Haskell packages, e.g. rts
pkgFile :: Context -> String -> String -> Action FilePath
pkgFile context@Context {..} prefix suffix = do
path <- buildPath context
pkgId <- pkgIdentifier package
return $ path -/- prefix ++ pkgId ++ suffix
path <- buildPath context
pid <- pkgId package
return $ path -/- prefix ++ pid ++ suffix
-- | Path to inplace package configuration file of a given 'Context'.
pkgInplaceConfig :: Context -> Action FilePath
......@@ -122,10 +137,10 @@ pkgGhciLibraryFile context = pkgFile context "HS" ".o"
pkgConfFile :: Context -> Action FilePath
pkgConfFile Context {..} = do
root <- buildRoot
pkgId <- pkgIdentifier package
pid <- pkgId package
let dbDir | stage == Stage0 = root -/- stage0PackageDbDir
| otherwise = inplacePackageDbPath
return $ dbDir -/- pkgId <.> "conf"
return $ dbDir -/- pid <.> "conf"
-- | Given a 'Context' and a 'FilePath' to a source file, compute the 'FilePath'
-- to its object file. For example:
......
......@@ -41,82 +41,89 @@ defaultKnownPackages =
, win32, xhtml ]
-- | Package definitions, see 'Package'.
array = lib "array"
base = lib "base"
binary = lib "binary"
bytestring = lib "bytestring"
cabal = lib "Cabal" `setPath` "libraries/Cabal/Cabal"
checkApiAnnotations = util "check-api-annotations"
compareSizes = util "compareSizes" `setPath` "utils/compare_sizes"
compiler = top "ghc" `setPath` "compiler"
containers = lib "containers"
deepseq = lib "deepseq"
deriveConstants = util "deriveConstants"
directory = lib "directory"
dllSplit = util "dll-split"
filepath = lib "filepath"
genapply = util "genapply"
genprimopcode = util "genprimopcode"
ghc = prg "ghc-bin" `setPath` "ghc"
ghcBoot = lib "ghc-boot"
ghcBootTh = lib "ghc-boot-th"
ghcCabal = util "ghc-cabal"
ghcCompact = lib "ghc-compact"
ghci = lib "ghci"
ghcPkg = util "ghc-pkg"
ghcPrim = lib "ghc-prim"
ghcTags = util "ghctags"
ghcSplit = util "ghc-split"
haddock = util "haddock"
haskeline = lib "haskeline"
hsc2hs = util "hsc2hs"
hp2ps = util "hp2ps"
hpc = lib "hpc"
hpcBin = util "hpc-bin" `setPath` "utils/hpc"
integerGmp = lib "integer-gmp"
integerSimple = lib "integer-simple"
iservBin = prg "iserv-bin" `setPath` "iserv"
libffi = top "libffi"
mtl = lib "mtl"
parsec = lib "parsec"
parallel = lib "parallel"
pretty = lib "pretty"
primitive = lib "primitive"
process = lib "process"
rts = top "rts"
runGhc = util "runghc"
stm = lib "stm"
templateHaskell = lib "template-haskell"
terminfo = lib "terminfo"
text = lib "text"
time = lib "time"
touchy = util "touchy"
transformers = lib "transformers"
unlit = util "unlit"
unix = lib "unix"
win32 = lib "Win32"
xhtml = lib "xhtml"
-- | Construct a library package, e.g. @array@.
lib :: PackageName -> Package
lib name = library name ("libraries" -/- name)
-- | Construct a top-level library package, e.g. @compiler@.
top :: PackageName -> Package
top name = library name name
-- | Construct a top-level program package, e.g. @ghc@.
prg :: PackageName -> Package
prg name = program name name
-- | Construct a utility package, e.g. @haddock@.
util :: PackageName -> Package
util name = program name ("utils" -/- name)
array = hsLib "array"
base = hsLib "base"
binary = hsLib "binary"
bytestring = hsLib "bytestring"
cabal = hsLib "Cabal" `setPath` "libraries/Cabal/Cabal"
checkApiAnnotations = hsUtil "check-api-annotations"
compareSizes = hsUtil "compareSizes" `setPath` "utils/compare_sizes"
compiler = hsTop "ghc" `setPath` "compiler"
containers = hsLib "containers"
deepseq = hsLib "deepseq"
deriveConstants = hsUtil "deriveConstants"
directory = hsLib "directory"
dllSplit = hsUtil "dll-split"
filepath = hsLib "filepath"
genapply = hsUtil "genapply"
genprimopcode = hsUtil "genprimopcode"
ghc = hsPrg "ghc-bin" `setPath` "ghc"
ghcBoot = hsLib "ghc-boot"
ghcBootTh = hsLib "ghc-boot-th"
ghcCabal = hsUtil "ghc-cabal"
ghcCompact = hsLib "ghc-compact"
ghci = hsLib "ghci"
ghcPkg = hsUtil "ghc-pkg"
ghcPrim = hsLib "ghc-prim"
ghcTags = hsUtil "ghctags"
ghcSplit = hsUtil "ghc-split"
haddock = hsUtil "haddock"
haskeline = hsLib "haskeline"
hsc2hs = hsUtil "hsc2hs"
hp2ps = cUtil "hp2ps"
hpc = hsLib "hpc"
hpcBin = hsUtil "hpc-bin" `setPath` "utils/hpc"
integerGmp = hsLib "integer-gmp"
integerSimple = hsLib "integer-simple"
iservBin = hsPrg "iserv-bin" `setPath` "iserv"
libffi = cTop "libffi"
mtl = hsLib "mtl"
parsec = hsLib "parsec"
parallel = hsLib "parallel"
pretty = hsLib "pretty"
primitive = hsLib "primitive"
process = hsLib "process"
rts = cTop "rts"
runGhc = hsUtil "runghc"
stm = hsLib "stm"
templateHaskell = hsLib "template-haskell"
terminfo = hsLib "terminfo"
text = hsLib "text"
time = hsLib "time"
touchy = cUtil "touchy"
transformers = hsLib "transformers"
unlit = cUtil "unlit"
unix = hsLib "unix"
win32 = hsLib "Win32"
xhtml = hsLib "xhtml"
-- | Construct a Haskell library package, e.g. @array@.
hsLib :: PackageName -> Package
hsLib name = hsLibrary name ("libraries" -/- name)
-- | Construct a top-level Haskell library package, e.g. @compiler@.
hsTop :: PackageName -> Package
hsTop name = hsLibrary name name
-- | Construct a top-level C library package, e.g. @rts@.
cTop :: PackageName -> Package
cTop name = cLibrary name name
-- | Construct a top-level Haskell program package, e.g. @ghc@.
hsPrg :: PackageName -> Package
hsPrg name = hsProgram name name
-- | Construct a Haskell utility package, e.g. @haddock@.
hsUtil :: PackageName -> Package
hsUtil name = hsProgram name ("utils" -/- name)
-- | Construct a C utility package, e.g. @haddock@.
cUtil :: PackageName -> Package
cUtil name = cProgram name ("utils" -/- name)
-- | Amend a package path if it doesn't conform to a typical pattern.
setPath :: Package -> FilePath -> Package
setPath pkg path | isLibrary pkg = library (pkgName pkg) path
| otherwise = program (pkgName pkg) path
setPath pkg path = pkg { pkgPath = path }
-- | Some builders are built by this very build system, in which case
-- 'builderProvenance' returns the corresponding build 'Context' (which includes
......
......@@ -7,64 +7,38 @@
-- Stability : experimental
--
-- Basic functionality for extracting Haskell package metadata stored in
-- @.cabal@ files.
-- Cabal files.
-----------------------------------------------------------------------------
module Hadrian.Haskell.Cabal (
pkgVersion, pkgIdentifier, pkgDependencies, pkgSynopsis
) where
import Control.Monad
import Development.Shake
import Hadrian.Haskell.Cabal.Parse
import Hadrian.Haskell.Package
import Hadrian.Package
import Hadrian.Oracles.TextFile
import Hadrian.Utilities
-- | Read the @.cabal@ file of a given package and return the package version.
-- The @.cabal@ file is tracked.
pkgVersion :: Package -> Action String
pkgVersion pkg = do
cabal <- readCabalFile (pkgCabalFile pkg)
return (version cabal)
-- | Read a Cabal file and return the package version. The Cabal file is tracked.
pkgVersion :: FilePath -> Action String
pkgVersion cabalFile = version <$> readCabalFile cabalFile
-- | Read the @.cabal@ file of a given package and return the package identifier,
-- e.g. @base-4.10.0.0@. If the @.cabal@ file does not exist return just the
-- package name, e.g. @rts@. If the @.cabal@ file exists then it is tracked, and
-- furthermore we check that the recorded package name matches the name of the
-- package passed as the parameter, and raise an error otherwise.
pkgIdentifier :: Package -> Action String
pkgIdentifier pkg = do
cabalExists <- doesFileExist (pkgCabalFile pkg)
if not cabalExists
then return (pkgName pkg)
else do
cabal <- readCabalFile (pkgCabalFile pkg)
when (pkgName pkg /= name cabal) $
error $ "[Hadrian.Haskell.Cabal] Inconsistent package name: expected "
++ quote (pkgName pkg) ++ ", but " ++ quote (pkgCabalFile pkg)
++ " specifies " ++ quote (name cabal) ++ "."
return $ if (null $ version cabal)
then pkgName pkg
else pkgName pkg ++ "-" ++ version cabal
-- | Read a Cabal file and return the package identifier, e.g. @base-4.10.0.0@.
-- The Cabal file is tracked.
pkgIdentifier :: FilePath -> Action String
pkgIdentifier cabalFile = do
cabal <- readCabalFile cabalFile
return $ if (null $ version cabal)
then name cabal
else name cabal ++ "-" ++ version cabal
-- | Read the @.cabal@ file of a given package and return the sorted list of its
-- dependencies. The current version does not take care of Cabal conditionals
-- and therefore returns a crude overapproximation of actual dependencies. The
-- @.cabal@ file is tracked.
pkgDependencies :: Package -> Action [PackageName]
pkgDependencies pkg = do
cabal <- readCabalFile (pkgCabalFile pkg)
return (dependencies cabal)
-- | Read a Cabal file and return the sorted list of the package dependencies.
-- The current version does not take care of Cabal conditionals and therefore
-- returns a crude overapproximation of actual dependencies. The Cabal file is
-- tracked.
pkgDependencies :: FilePath -> Action [PackageName]
pkgDependencies cabalFile = dependencies <$> readCabalFile cabalFile
-- | Read the @.cabal@ file of a given package and return the package synopsis
-- or @Nothing@ if the @.cabal@ file does not exist. The existence and contents
-- of the @.cabal@ file are tracked.
pkgSynopsis :: Package -> Action (Maybe String)
pkgSynopsis pkg = do
cabalExists <- doesFileExist (pkgCabalFile pkg)
if not cabalExists
then return Nothing
else do
cabal <- readCabalFile (pkgCabalFile pkg)
return $ Just (synopsis cabal)
-- | Read a Cabal file and return the package synopsis. The Cabal file is tracked.
pkgSynopsis :: FilePath -> Action String
pkgSynopsis cabalFile = synopsis <$> readCabalFile cabalFile
......@@ -6,7 +6,7 @@
-- Maintainer : andrey.mokhov@gmail.com
-- Stability : experimental
--
-- Extracting Haskell package metadata stored in @.cabal@ files.
-- Extracting Haskell package metadata stored in Cabal files.
-----------------------------------------------------------------------------
module Hadrian.Haskell.Cabal.Parse (Cabal (..), parseCabal) where
......@@ -20,10 +20,10 @@ import qualified Distribution.Text as C
import qualified Distribution.Types.CondTree as C
import qualified Distribution.Verbosity as C
import Hadrian.Haskell.Package
import Hadrian.Package
-- TODO: Use fine-grain tracking instead of tracking the whole @.cabal@ file.
-- | Haskell package metadata extracted from a @.cabal@ file.
-- TODO: Use fine-grain tracking instead of tracking the whole Cabal file.
-- | Haskell package metadata extracted from a Cabal file.
data Cabal = Cabal
{ dependencies :: [PackageName]
, name :: PackageName
......@@ -41,7 +41,7 @@ instance Hashable Cabal where
instance NFData Cabal where
rnf (Cabal a b c d) = a `seq` b `seq` c `seq` d `seq` ()
-- | Parse a @.cabal@ file.
-- | Parse a Cabal file.
parseCabal :: FilePath -> IO Cabal
parseCabal file = do
gpd <- liftIO $ C.readGenericPackageDescription C.silent file
......
-----------------------------------------------------------------------------
-- |
-- Module : Hadrian.Haskell.Package
-- Copyright : (c) Andrey Mokhov 2014-2017
-- License : MIT (see the file LICENSE)
-- Maintainer : andrey.mokhov@gmail.com
-- Stability : experimental
--
-- Haskell packages and operations on them.
-----------------------------------------------------------------------------
module Hadrian.Haskell.Package (
-- * Data type
Package, PackageName,
-- * Construction and properties
library, program, pkgName, pkgPath, isLibrary, isProgram,
-- * Package directory structure
pkgCabalFile
) where
import Development.Shake.Classes
import Development.Shake.FilePath
import GHC.Generics
import Hadrian.Utilities
type PackageName = String
-- TODO: Make PackageType more precise, #12.
data PackageType = Library | Program deriving (Generic, Show)
-- | A Haskell package. The current implementation treats a package as either
-- a library or a program, which is a gross oversimplification as Haskell
-- packages can be both. This works for now, but in future we plan to support
-- general Haskell packages. Also note that we assume that all packages have
-- different names, hence two packages with the same name are considered equal.
data Package = Package PackageType PackageName FilePath deriving Generic
-- | The name of a Haskell package. Examples: @Cabal@, @ghc-bin@.
pkgName :: Package -> PackageName
pkgName (Package _ name _) = name
-- | 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 :: Package -> FilePath
pkgPath (Package _ _ path) = path
instance Show Package where
show (Package Library n p) = "library " ++ show n ++ " " ++ show p
show (Package Program n p) = "program " ++ show n ++ " " ++ show p
instance Eq Package where
p == q = pkgName p == pkgName q
instance Ord Package where
compare p q = compare (pkgName p) (pkgName q)
instance Binary Package
instance Hashable Package
instance NFData Package
instance Binary PackageType
instance Hashable PackageType
instance NFData PackageType
-- | Construct a library package.
library :: PackageName -> FilePath -> Package
library = Package Library
-- | Construct a program package.
program :: PackageName -> FilePath -> Package
program = Package Program
-- | Check whether a package is a library.
isLibrary :: Package -> Bool
isLibrary (Package Library _ _) = True
isLibrary _ = False
-- | Check whether a package is a program.
isProgram :: Package -> Bool
isProgram (Package Program _ _) = True
isProgram _ = False
-- | The path to a package cabal file, e.g.: @ghc/ghc-bin.cabal@.
pkgCabalFile :: Package -> FilePath
pkgCabalFile pkg = pkgPath pkg -/- pkgName pkg <.> "cabal"
-----------------------------------------------------------------------------
-- |
-- 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, #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
......@@ -14,7 +14,7 @@ import Utilities
buildPackageData :: Context -> Rules ()
buildPackageData context@Context {..} = do
let dir = "//" ++ contextDir context
cabalFile = pkgCabalFile package
cabalFile = unsafePkgCabalFile package -- TODO: improve
configure = pkgPath package -/- "configure"
-- TODO: Get rid of hardcoded file paths.
[dir -/- "package-data.mk", dir -/- "setup-config"] &%> \[mk, setupConfig] -> do
......
......@@ -189,8 +189,9 @@ installPackages = do
copyFile (pkgPath integerGmp -/- "gmp/ghc-gmp.h") (pkgPath integerGmp -/- "ghc-gmp.h")
forM_ installLibPkgs $ \pkg -> do
when (isLibrary pkg) $
withLatestBuildStage pkg $ \stage -> do
case pkgCabalFile pkg of
Nothing -> error $ "Non-Haskell project in installLibPkgs" ++ show pkg
Just cabalFile -> withLatestBuildStage pkg $ \stage -> do
let context = vanillaContext stage pkg
top <- topDirectory
installDistDir <- buildPath context
......@@ -206,8 +207,6 @@ installPackages = do
let ghcCabalInplace = inplaceBinPath -/- "ghc-cabal" <.> exe -- HACK?
need [ghcCabalInplace]
let cabalFile = pkgCabalFile pkg