summaryrefslogtreecommitdiff
path: root/hadrian/src/Hadrian/Builder.hs
diff options
context:
space:
mode:
Diffstat (limited to 'hadrian/src/Hadrian/Builder.hs')
-rw-r--r--hadrian/src/Hadrian/Builder.hs157
1 files changed, 157 insertions, 0 deletions
diff --git a/hadrian/src/Hadrian/Builder.hs b/hadrian/src/Hadrian/Builder.hs
new file mode 100644
index 0000000000..5d645eea8c
--- /dev/null
+++ b/hadrian/src/Hadrian/Builder.hs
@@ -0,0 +1,157 @@
+-----------------------------------------------------------------------------
+-- |
+-- Module : Hadrian.Builder
+-- Copyright : (c) Andrey Mokhov 2014-2017
+-- License : MIT (see the file LICENSE)
+-- Maintainer : andrey.mokhov@gmail.com
+-- Stability : experimental
+--
+-- A typical build system invokes several build tools, or /builders/, such as
+-- compilers, linkers, etc., some of which may be built by the build system
+-- itself. This module defines the 'Builder' type class and a few associated
+-- functions that can be used to invoke builders.
+-----------------------------------------------------------------------------
+module Hadrian.Builder (
+ Builder (..), BuildInfo (..), needBuilder, runBuilder,
+ runBuilderWithCmdOptions, build, buildWithResources, buildWithCmdOptions,
+ getBuilderPath, builderEnvironment, askWithResources
+ ) where
+
+import Data.List
+import Development.Shake
+
+import Hadrian.Expression hiding (inputs, outputs)
+import Hadrian.Oracles.ArgsHash
+import Hadrian.Target
+import Hadrian.Utilities
+
+-- | This data structure captures all information relevant to invoking a builder.
+data BuildInfo = BuildInfo {
+ -- | Command line arguments.
+ buildArgs :: [String],
+ -- | Input files.
+ buildInputs :: [FilePath],
+ -- | Output files.
+ buildOutputs :: [FilePath],
+ -- | Options to be passed to Shake's 'cmd' function.
+ buildOptions :: [CmdOption],
+ -- | Resources to be aquired.
+ buildResources :: [(Resource, Int)] }
+
+class ShakeValue b => Builder b where
+ -- | The path to a builder.
+ builderPath :: b -> Action FilePath
+
+ -- | Ask the builder for information.
+ -- E.g. ask @ghc-pkg@ for package dependencies
+ -- capture the @stdout@ result and return it.
+ askBuilderWith :: b -> BuildInfo -> Action String
+
+ -- | Runtime dependencies of a builder. For example, on Windows GHC requires
+ -- the utility @touchy.exe@ to be avilable on a specific path.
+ runtimeDependencies :: b -> Action [FilePath]
+ runtimeDependencies _ = return []
+
+ -- | Run a builder with a given 'BuildInfo'. Also see 'runBuilder'.
+ runBuilderWith :: b -> BuildInfo -> Action ()
+ runBuilderWith builder buildInfo = do
+ let args = buildArgs buildInfo
+ needBuilder builder
+ path <- builderPath builder
+ let msg = if null args then "" else " (" ++ intercalate ", " args ++ ")"
+ putBuild $ "| Run " ++ show builder ++ msg
+ quietly $ cmd (buildOptions buildInfo) [path] args
+
+-- | Make sure a builder and its runtime dependencies are up-to-date.
+needBuilder :: Builder b => b -> Action ()
+needBuilder builder = do
+ path <- builderPath builder
+ deps <- runtimeDependencies builder
+ need (path : deps)
+
+-- | Run a builder with a specified list of command line arguments, reading a
+-- list of input files and writing a list of output files. A lightweight version
+-- of 'runBuilderWith'.
+runBuilder :: Builder b => b -> [String] -> [FilePath] -> [FilePath] -> Action ()
+runBuilder = runBuilderWithCmdOptions []
+
+-- | Like 'runBuilder' but passes given options to Shake's 'cmd'.
+runBuilderWithCmdOptions :: Builder b => [CmdOption] -> b -> [String] -> [FilePath] -> [FilePath] -> Action ()
+runBuilderWithCmdOptions opts builder args inputs outputs =
+ runBuilderWith builder $ BuildInfo { buildArgs = args
+ , buildInputs = inputs
+ , buildOutputs = outputs
+ , buildOptions = opts
+ , buildResources = [] }
+
+-- | Build a 'Target' using the list of command line arguments computed from a
+-- given 'Args' expression. Force a rebuild if the argument list has changed
+-- since the last build.
+build :: (Builder b, ShakeValue c) => Target c b -> Args c b -> Action ()
+build = buildWith [] []
+
+-- | Like 'build' but acquires necessary resources.
+buildWithResources :: (Builder b, ShakeValue c) => [(Resource, Int)] -> Target c b -> Args c b -> Action ()
+buildWithResources rs = buildWith rs []
+
+askWithResources :: (Builder b, ShakeValue c) => [(Resource, Int)] -> Target c b -> Args c b -> Action String
+askWithResources rs = askWith rs []
+
+-- | Like 'build' but passes given options to Shake's 'cmd'.
+buildWithCmdOptions :: (Builder b, ShakeValue c) => [CmdOption] -> Target c b -> Args c b -> Action ()
+buildWithCmdOptions = buildWith []
+
+doWith :: (Builder b, ShakeValue c)
+ => (b -> BuildInfo -> Action a)
+ -> (Target c b -> Action ())
+ -> [(Resource, Int)] -> [CmdOption] -> Target c b -> Args c b -> Action a
+doWith f info rs opts target args = do
+ needBuilder (builder target)
+ argList <- interpret target args
+ trackArgsHash target -- Rerun the rule if the hash of argList has changed.
+ info target
+ verbose <- interpret target verboseCommand
+ let quietlyUnlessVerbose = if verbose then withVerbosity Loud else quietly
+ quietlyUnlessVerbose $ f (builder target) $
+ BuildInfo { buildArgs = argList
+ , buildInputs = inputs target
+ , buildOutputs = outputs target
+ , buildOptions = opts
+ , buildResources = rs }
+
+buildWith :: (Builder b, ShakeValue c) => [(Resource, Int)] -> [CmdOption] -> Target c b -> Args c b -> Action ()
+buildWith = doWith runBuilderWith runInfo
+
+askWith :: (Builder b, ShakeValue c) => [(Resource, Int)] -> [CmdOption] -> Target c b -> Args c b -> Action String
+askWith = doWith askBuilderWith askInfo
+
+-- | Print out information about the command being executed.
+runInfo :: Show b => Target c b -> Action ()
+runInfo t = putProgressInfo =<< renderAction
+ ("Run " ++ show (builder t)) -- TODO: Bring back contextInfo.
+ (digest $ inputs t)
+ (digest $ outputs t)
+ where
+ digest [] = "none"
+ digest [x] = x
+ digest (x:xs) = x ++ " (and " ++ show (length xs) ++ " more)"
+
+askInfo :: Show b => Target c b -> Action ()
+askInfo t = putProgressInfo =<< renderActionNoOutput
+ ("Run " ++ show (builder t)) -- TODO: Bring back contextInfo.
+ (digest $ inputs t)
+ where
+ digest [] = "none"
+ digest [x] = x
+ digest (x:xs) = x ++ " (and " ++ show (length xs) ++ " more)"
+
+-- | Get the path to the current builder.
+getBuilderPath :: Builder b => b -> Expr c b FilePath
+getBuilderPath = expr . builderPath
+
+-- | Write a builder path into a given environment variable.
+builderEnvironment :: Builder b => String -> b -> Action CmdOption
+builderEnvironment variable builder = do
+ needBuilder builder
+ path <- builderPath builder
+ return $ AddEnv variable path