summaryrefslogtreecommitdiff
path: root/hadrian/src/Settings.hs
diff options
context:
space:
mode:
Diffstat (limited to 'hadrian/src/Settings.hs')
-rwxr-xr-xhadrian/src/Settings.hs212
1 files changed, 209 insertions, 3 deletions
diff --git a/hadrian/src/Settings.hs b/hadrian/src/Settings.hs
index d3096e7987..0548bccfae 100755
--- a/hadrian/src/Settings.hs
+++ b/hadrian/src/Settings.hs
@@ -1,13 +1,15 @@
module Settings (
getArgs, getLibraryWays, getRtsWays, flavour, knownPackages,
findPackageByName, unsafeFindPackageByName, unsafeFindPackageByPath,
- isLibrary, stagePackages, programContext, getIntegerPackage
+ isLibrary, stagePackages, programContext, getIntegerPackage,
+ completeSetting
) where
import CommandLine
import Expression
import Flavour
import Packages
+import Settings.Parser
import UserSettings (userFlavours, userPackages, userDefaultFlavour)
import {-# SOURCE #-} Settings.Default
@@ -21,6 +23,9 @@ import Settings.Flavours.Quickest
import Settings.Flavours.QuickCross
import Settings.Flavours.GhcInGhci
+import Control.Monad.Except
+import Data.Either
+
getArgs :: Args
getArgs = expr flavour >>= args
@@ -43,12 +48,22 @@ hadrianFlavours =
, performanceLlvmFlavour, profiledLlvmFlavour, quickLlvmFlavour
, ghcInGhciFlavour ]
+-- | This action looks up a flavour with the name given on the
+-- command line with @--flavour@, defaulting to 'userDefaultFlavour'
+-- when no explicit @--flavour@ is passed. It then applies any
+-- potential setting update specified on the command line or in a
+-- <build root>/hadrian.settings file, using @k = v@ or @k += v@ style
+-- syntax. See Note [Hadrian settings] at the bottom of this file.
flavour :: Action Flavour
flavour = do
flavourName <- fromMaybe userDefaultFlavour <$> cmdFlavour
+ kvs <- userSetting ([] :: [KeyVal])
let unknownFlavour = error $ "Unknown build flavour: " ++ flavourName
- flavours = hadrianFlavours ++ userFlavours
- return $ fromMaybe unknownFlavour $ find ((== flavourName) . name) flavours
+ flavours = hadrianFlavours ++ userFlavours
+ (_settingErrs, tweak) = applySettings kvs
+
+ return $ maybe unknownFlavour tweak $
+ find ((== flavourName) . name) flavours
getIntegerPackage :: Expr Package
getIntegerPackage = expr (integerLibrary =<< flavour)
@@ -88,3 +103,194 @@ unsafeFindPackageByPath :: FilePath -> Package
unsafeFindPackageByPath path = err $ find (\pkg -> pkgPath pkg == path) knownPackages
where
err = fromMaybe $ error ("findPackageByPath: No package for path " ++ path)
+
+-- * CLI and <root>/hadrian.settings options
+
+{-
+Note [Hadrian settings]
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Hadrian lets one customize GHC builds through the UserSettings module,
+where Hadrian users can override existing 'Flavour's or create entirely
+new ones, overriding/extending the options passed to some builder
+building the RTS in more ways and much more.
+
+It now also offers a more "old-school" interface, in the form of
+@foo.bar.baz = v@ or @foo.bar.baz += v@ expressions, that one can
+pass on the command line that invokes hadrian:
+
+> $ hadrian/build.sh --flavour=quickest -j "stage1.ghc-bin.ghc.link.opts += -v3"
+
+or in a file at <build root>/hadrian.settings, where <build root>
+is the build root to be used for the build, which is _build by default.
+For example, you could create a file at _build/hadrian.settings with the
+following contents:
+
+> stage1.ghc-bin.ghc.link.opts += -v3
+> stage1.base.ghc.hs.opts += -ddump-timings
+
+and issue:
+
+> $ hadrian/build.sh
+
+Hadrian would pick up the settings given in _build/hadrian.settings (as well as
+any settings that you may additionally be passing on the command line) and
+update the relevant flavour accordingly, to issue the additional arguments
+specified by the user.
+
+The supported settings are described by 'builderSetting' below, using
+operations from Applicative + two new primitives, 'matchString' and
+'matchOneOf', that come as members of the 'Match' class. This gives us
+a simple but powerful vocabulary to describe settings and parse them
+into values that we can use to compute interesting things, like a 'Predicate'
+that we can use to emit additional arguments, or a list of possible completions.
+
+> fmap, (<$>) :: Functor f => (a -> b) -> f a -> f b
+> pure :: Applicative f => a -> f a
+> (<*>) :: Applicative f => f (a -> b) -> f a -> f b
+> (*>) :: Applicative f => f a -> f b -> f b
+> (<*) :: Applicative f => f a -> f b -> f a
+> (<$) :: Functor f => a -> f b -> f a
+>
+> str :: Match f => String -> f ()
+> val :: Match f => String -> a -> f a
+> oneOf :: Match f => [f a] -> f a
+> choose :: Match f => [(String, a)] -> f a
+> wild :: Match f => [(String, a)] -> f (Wildcard a)
+
+For instance, to describe possible settings:
+ foo.bar.{x, y}
+ foo.baz.{a, b}.c
+
+we could write:
+
+> str "foo" *> oneOf [ str "bar" *> choose [ ("x", "x"), ("y", "y") ]
+> , str "baz" *> choose [ ("a", "ac"), ("b", "bc") <* str "c" ]
+> ]
+
+'builderSetting' uses these combinators to describe the setting keys that
+Hadrian supports. A user-oriented description of this mechanism is available
+in hadrian/doc/user-settings.md.
+
+-}
+
+-- | Try to interpret all the 'KeyVal' as flavour updates, keeping
+-- a list of errors for the ones which don't match known
+-- settings.
+applySettings :: [KeyVal] -> ([SettingError], Flavour -> Flavour)
+applySettings kvs = case partitionEithers (map applySetting kvs) of
+ (errs, fs) -> (errs, foldr (flip (.)) id fs)
+ -- we need to compose the reverse way to have the following settings
+ -- x = -b
+ -- x += -c
+ -- produce the final x = "-b -c" value. With just (.) we would apply
+ -- the x = -b assignment last, which would silently drop the -c adddition.
+ --
+ -- foldr (.) id [f, g, h] = f . g . h
+ -- -- first function (f) is applied last, we're applying them in
+ -- -- the wrong order!
+ --
+ -- foldr (flip (.)) id [f, g, h] = h . g . f
+ -- -- last function (f) is applied last, as desired
+
+
+-- | Try to interpret the given 'KeyVal' as a flavour update
+-- function, returning an error if it doesn't match a known
+-- setting.
+applySetting :: KeyVal -> Either SettingError (Flavour -> Flavour)
+applySetting (KeyVal ks op v) = case runSettingsM ks builderPredicate of
+ Left err -> throwError $
+ "error while setting " ++ show ks ++ ": " ++ err
+ Right pred -> Right $ \flav -> flav
+ { args = update (args flav) pred }
+
+ where override arguments predicate = do
+ holds <- predicate
+ if holds then pure (words v) else arguments
+
+ augment arguments predicate =
+ mconcat [arguments, predicate ? pure (words v)]
+
+ update
+ | op == Equal = override
+ | otherwise = augment
+
+-- | Try to auto-complete the given @Key@ using
+-- all known settings, as described by 'builderSetting'.
+--
+-- > completeSetting ["stage1","base", "ghc"]
+-- > -- returns [ ["stage1","base","ghc","c","opts"]
+-- > -- , ["stage1","base","ghc","hs","opts"]
+-- > -- , ["stage1","base","ghc","link","opts"]
+-- > -- , ["stage1","base","ghc","deps","opts"]
+-- > -- , ["stage1","base","ghc","toolargs","opts"]
+-- > -- ]
+completeSetting :: Key -> [Key]
+completeSetting ks = map snd (complete ks builderSetting)
+
+-- | Interpret a 'builderSetting' as a 'Predicate' that
+-- potentially constrains on the stage, package or
+-- (ghc or cc) builder mode.
+--
+-- For example, @stage1.base.ghc.link.opts@ gets mapped to
+-- a predicate that applies @'stage' 'Stage1'@,
+-- @'package' 'base'@ and @'builder' ('Ghc' 'LinkHs')@.
+builderPredicate :: SettingsM Predicate
+builderPredicate = builderSetting <&> (\(wstg, wpkg, builderMode) ->
+ wildcard (pure True) stage wstg <&&>
+ wildcard (pure True) package wpkg <&&>
+ (case builderMode of
+ Left ghcMode -> wildcard (builder Ghc) (builder . Ghc) ghcMode
+ Right ccMode -> wildcard (builder Cc) (builder . Cc) ccMode))
+
+ where (<&&>) = liftA2 (&&)
+
+-- | Interpretation-agnostic description of the builder settings
+-- supported by Hadrian.
+--
+-- Supported settings (to be kept in sync with the code):
+--
+-- > (<stage> or *).(<package name> or *).ghc.(<ghc mode> or *).opts
+-- > (<stage> or *).(<package name> or *).cc.(<cc mode> or *).opts
+--
+-- where:
+-- - @<stage>@ is one of @stage0@, @stage1@, @stage2@ or @stage3@;
+-- - @<package name>@ is the (Cabal) name of a package (@base@,
+-- @template-haskell@, ...);
+-- - @<ghc mode>@ is one of @c@ (building C files), @hs@ (building Haskell
+-- modules), @link@ (linking object files), @deps@ (finding Haskell
+-- dependencies with @ghc -M@) or @toolargs@ (getting necessary flags to
+-- make hadrian/ghci.sh work;
+-- - @<cc mode>@ is one of @c@ (building C files) or @deps@ (finding C
+-- dependencies);
+-- - locations that support a wildcard (@*@) entry are here to avoid
+-- repetition, a wildcard entry being equivalent to writing all the
+-- settings that the wildcard matches over; in our case, we can
+-- apply GHC or C compiler options uniformly over all stages, packages
+-- and compiler modes, if we so desire, by using a wildcard in the
+-- appropriate spot.
+builderSetting :: Match f => f (Wildcard Stage, Wildcard Package, Either (Wildcard GhcMode) (Wildcard CcMode))
+builderSetting = (,,)
+ <$> wild stages
+ <*> wild pkgs
+ <*> matchOneOf
+ [ str "ghc" *> fmap Left (wild ghcBuilder) <* str "opts"
+ , str "cc" *> fmap Right (wild ccBuilder) <* str "opts"
+ ]
+
+ where ghcBuilder =
+ [ ("c", CompileCWithGhc)
+ , ("deps", FindHsDependencies)
+ , ("hs", CompileHs)
+ , ("link", LinkHs)
+ , ("toolargs", ToolArgs)
+ ]
+
+ ccBuilder =
+ [ ("c", CompileC)
+ , ("deps", FindCDependencies)
+ ]
+
+ stages = map (\stg -> (stageString stg, stg)) [minBound..maxBound]
+
+ pkgs = map (\pkg -> (pkgName pkg, pkg)) knownPackages