Skip to content

Commit 45e9e27

Browse files
committed
Write manifest of pre-build rule monitored files
After executing pre-build rules for a component, `cabal-install` now writes a manifest file to the `pre-build-monitors` file in the component's build directory. This manifest contains: 1. The files that are monitored by the pre-build rules computation. When any of these change, we need to re-run the computation of pre-build rules. 2. File dependencies of the pre-build rules. When any of these change, we need to re-run rules (the recompilation checking logic will take care to only re-run stale rules). 3. Rule outputs, i.e. files generated by the pre-build rules. Writing this information to a file makes it available to external tools such as HLS, without needing to link against the Cabal library. This allows external tools to know when to re-run the pre-build rules.
1 parent 5ac41c2 commit 45e9e27

5 files changed

Lines changed: 123 additions & 4 deletions

File tree

Cabal/src/Distribution/Simple/Build.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1168,7 +1168,7 @@ runPreBuildHooks
11681168
pbcRules = do
11691169
let verbosity = mkVerbosity verbHandles $ buildingWhatVerbosity what
11701170
(rules, mons) <- SetupHooks.computeRules verbosity pbci pbcRules
1171-
SetupHooks.executeRules verbosity lbi tgt rules
1171+
SetupHooks.executeRules verbosity lbi tgt mons rules
11721172
return mons
11731173

11741174
-- | Built-in pre-build 'SetupHooks' for a given 'BuildType'.

Cabal/src/Distribution/Simple/BuildPaths.hs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ module Distribution.Simple.BuildPaths
2828
, autogenPackageModulesDir
2929
, autogenComponentModulesDir
3030
, preBuildRulesCacheFile
31+
, preBuildMonitorManifestFile
3132
, autogenPathsModuleName
3233
, autogenPackageInfoModuleName
3334
, cppHeaderName
@@ -170,6 +171,18 @@ preBuildRulesCacheFile
170171
preBuildRulesCacheFile lbi clbi =
171172
componentBuildDir lbi clbi </> makeRelativePathEx "setup-hooks-rules.cache"
172173

174+
-- | The path to the pre-build monitor manifest file for a component.
175+
--
176+
-- This file is written after running pre-build rules, so that external
177+
-- tools (e.g. HLS) can discover which files to watch for changes and which
178+
-- files were generated by the rules. See 'writePreBuildMonitorManifest'.
179+
preBuildMonitorManifestFile
180+
:: LocalBuildInfo
181+
-> ComponentLocalBuildInfo
182+
-> SymbolicPath Pkg File
183+
preBuildMonitorManifestFile lbi clbi =
184+
componentBuildDir lbi clbi </> makeRelativePathEx "pre-build-monitors"
185+
173186
-- NB: Look at 'checkForeignDeps' for where a simplified version of this
174187
-- has been copy-pasted.
175188

Cabal/src/Distribution/Simple/SetupHooks/Internal.hs

Lines changed: 86 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -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

9596
import Distribution.Compat.Lens ((.~))
9697
import Distribution.PackageDescription
98+
import Distribution.Pretty (prettyShow)
9799
import Distribution.Simple.BuildPaths
98100
import Distribution.Simple.Compiler (Compiler (..))
99101
import Distribution.Simple.Errors
@@ -135,7 +137,13 @@ import qualified Data.Map as Map
135137
import Data.Monoid (Ap (..))
136138
import 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 ()
855865
executeRules =
@@ -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+
10121096
directRuleDependencyMaybe :: Rule.Dependency -> Maybe RuleId
10131097
directRuleDependencyMaybe (RuleDependency dep) = Just $ outputOfRule dep
10141098
directRuleDependencyMaybe (FileDependency{}) = Nothing

changelog.d/rules-manifest.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
synopsis: Write pre-build rules manifest file
3+
packages: [Cabal, cabal-install]
4+
prs: 11776
5+
issues:
6+
---
7+
8+
After executing pre-build rules for a component, `Cabal` now writes a manifest
9+
file to the `pre-build-monitors` file in the component's build directory.
10+
11+
This manifest contains:
12+
13+
1. The files that are monitored by the pre-build rules computation. When any
14+
of these change, we need to re-run the computation of pre-build rules.
15+
2. File dependencies of the pre-build rules. When any of these change, we
16+
need to re-run rules (the recompilation checking logic will take care to
17+
only re-run stale rules).
18+
3. Rule outputs, i.e. files generated by the pre-build rules.
19+
20+
Writing this information to a file makes it available to external tools such as
21+
HLS without needing to link against the Cabal library. This allows external
22+
tools to know when to re-run the pre-build rules.

hooks-exe/cli/Distribution/Client/SetupHooks/CallHooksExe.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ runExternalPreBuildRules verbHandles hooksExe
223223
DynamicRuleCommands {} -> hook "runPreBuildRuleDeps" (rId, cmd)
224224
)
225225
( \ rId cmd -> hook "runPreBuildRule" (rId, cmd) )
226-
verbosity lbi tgt rulesMap
226+
verbosity lbi tgt monitors rulesMap
227227
return monitors
228228

229229
-- | The path to the external hooks executable.

0 commit comments

Comments
 (0)