@@ -82,6 +82,7 @@ module Distribution.Simple.SetupHooks.Internal
8282 -- ** Executing build rules
8383 , executeRules
8484 , executeRulesUserOrSystem
85+ , writePreBuildMonitorManifest
8586
8687 -- ** HookedBuildInfo compatibility code
8788 , hookedBuildInfoComponents
@@ -94,6 +95,7 @@ import Prelude ()
9495
9596import Distribution.Compat.Lens ((.~) )
9697import Distribution.PackageDescription
98+ import Distribution.Pretty (prettyShow )
9799import Distribution.Simple.BuildPaths
98100import Distribution.Simple.Compiler (Compiler (.. ))
99101import Distribution.Simple.Errors
@@ -135,7 +137,13 @@ import qualified Data.Map as Map
135137import Data.Monoid (Ap (.. ))
136138import qualified Data.Set as Set
137139
138- import System.Directory (doesFileExist , getModificationTime )
140+ import System.Directory
141+ ( createDirectoryIfMissing
142+ , doesFileExist
143+ , getModificationTime
144+ , makeAbsolute
145+ )
146+ import System.FilePath (takeDirectory )
139147
140148--------------------------------------------------------------------------------
141149-- SetupHooks
@@ -850,6 +858,8 @@ executeRules
850858 :: Verbosity
851859 -> LocalBuildInfo
852860 -> TargetInfo
861+ -> [MonitorFilePath ]
862+ -- ^ files monitored by the pre-build rules
853863 -> Map RuleId Rule
854864 -> IO ()
855865executeRules =
@@ -872,9 +882,11 @@ executeRulesUserOrSystem
872882 -> Verbosity
873883 -> LocalBuildInfo
874884 -> TargetInfo
885+ -> [MonitorFilePath ]
886+ -- ^ files monitored by the pre-build rules
875887 -> Map RuleId (RuleData userOrSystem )
876888 -> IO ()
877- executeRulesUserOrSystem scope runDepsCmdData runCmdData verbosity lbi tgtInfo allRules = do
889+ executeRulesUserOrSystem scope runDepsCmdData runCmdData verbosity lbi tgtInfo monitors allRules = do
878890 -- Load the rule cache from the previous build.
879891 -- Used to detect when rule definitions have changed.
880892 oldRules <- handleDoesNotExist Map. empty $ do
@@ -995,6 +1007,19 @@ executeRulesUserOrSystem scope runDepsCmdData runCmdData verbosity lbi tgtInfo a
9951007 errorOut $ MissingRuleOutputs (toRuleBinary r) missingResults
9961008 -- Save the current rules to the cache for use in the next build.
9971009 structuredEncodeFile rulesCacheFile allRules
1010+ -- Write the monitor manifest for external tools (e.g. HLS).
1011+ let allFileDeps =
1012+ [ loc
1013+ | (rId, Rule {staticDependencies}) <- Map. toList allRules
1014+ , let dynDeps = maybe [] fst (Map. lookup rId dynDepsEdges)
1015+ , FileDependency loc <- staticDependencies ++ dynDeps
1016+ ]
1017+ allOutputs =
1018+ [ loc
1019+ | Rule {results} <- Map. elems allRules
1020+ , loc <- NE. toList results
1021+ ]
1022+ writePreBuildMonitorManifest lbi tgtInfo monitors allFileDeps allOutputs
9981023 where
9991024 toRuleBinary :: RuleData userOrSystem -> RuleBinary
10001025 toRuleBinary = case scope of
@@ -1009,6 +1034,65 @@ executeRulesUserOrSystem scope runDepsCmdData runCmdData verbosity lbi tgtInfo a
10091034 SetupHooksException $
10101035 RulesException e
10111036
1037+ -- | Write the pre-build monitor manifest for a component.
1038+ --
1039+ -- This plain-text file (stored at 'preBuildMonitorManifestFile') is intended
1040+ -- for external tools such as HLS that invoke @cabal build@ but do not link
1041+ -- against the Cabal library. It records enough information for such tools
1042+ -- to detect when pre-build rules need to be re-computed or re-run, as per
1043+ -- the 'SetupHooks' documentation in @Cabal-hooks@.
1044+ --
1045+ -- The manifest contains three sections:
1046+ --
1047+ -- * @[monitors]@: values monitored by the computation of pre-build rules.
1048+ -- * @[inputs]@: file dependencies of pre-build rules (static & dynamic).
1049+ -- * @[outputs]@: file outputs of pre-build rules.
1050+ --
1051+ -- All relative paths in the manifest are relative to the @pkg-root@ listed in
1052+ -- the file header.
1053+ writePreBuildMonitorManifest
1054+ :: LocalBuildInfo
1055+ -> TargetInfo
1056+ -> [MonitorFilePath ]
1057+ -- ^ recompute pre-build rules when these change
1058+ -> [Location ]
1059+ -- ^ combined static and dynamic file dependencies of pre-build rules
1060+ -> [Location ]
1061+ -- ^ outputs (files generated by the pre-build rules)
1062+ -> IO ()
1063+ writePreBuildMonitorManifest lbi tgtInfo monitors fileDeps outputs = do
1064+ pkgRoot <- makeAbsolute (maybe " ." getSymbolicPath (mbWorkDirLBI lbi))
1065+ let clbi = targetCLBI tgtInfo
1066+ manifestPath = interpretSymbolicPathLBI lbi (preBuildMonitorManifestFile lbi clbi)
1067+ content =
1068+ unlines $
1069+ concat
1070+ [
1071+ [ " pre-build-monitors-v1"
1072+ , " pkg-root:" ++ pkgRoot
1073+ ]
1074+ , [" " , " [monitors]" ]
1075+ , map serialiseMonitor monitors
1076+ , [" " , " [inputs]" ]
1077+ , map (getSymbolicPath . location) fileDeps
1078+ , [" " , " [outputs]" ]
1079+ , map (getSymbolicPath . location) outputs
1080+ ]
1081+ createDirectoryIfMissing True (takeDirectory manifestPath)
1082+ writeFile manifestPath content
1083+ where
1084+ serialiseMonitor (MonitorFile kf kd path) =
1085+ " file:" ++ showKindFile kf ++ " :" ++ showKindDir kd ++ " :" ++ path
1086+ serialiseMonitor (MonitorFileGlob kf kd glob) =
1087+ " glob:" ++ showKindFile kf ++ " :" ++ showKindDir kd ++ " :" ++ prettyShow glob
1088+ showKindFile FileExists = " exists"
1089+ showKindFile FileModTime = " modtime"
1090+ showKindFile FileHashed = " hashed"
1091+ showKindFile FileNotExists = " notexists"
1092+ showKindDir DirExists = " exists"
1093+ showKindDir DirModTime = " modtime"
1094+ showKindDir DirNotExists = " notexists"
1095+
10121096directRuleDependencyMaybe :: Rule. Dependency -> Maybe RuleId
10131097directRuleDependencyMaybe (RuleDependency dep) = Just $ outputOfRule dep
10141098directRuleDependencyMaybe (FileDependency {}) = Nothing
0 commit comments