Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cabal/src/Distribution/Simple/Build.hs
Original file line number Diff line number Diff line change
Expand Up @@ -1168,7 +1168,7 @@ runPreBuildHooks
pbcRules = do
let verbosity = mkVerbosity verbHandles $ buildingWhatVerbosity what
(rules, mons) <- SetupHooks.computeRules verbosity pbci pbcRules
SetupHooks.executeRules verbosity lbi tgt rules
SetupHooks.executeRules verbosity lbi tgt mons rules
return mons

-- | Built-in pre-build 'SetupHooks' for a given 'BuildType'.
Expand Down
13 changes: 13 additions & 0 deletions Cabal/src/Distribution/Simple/BuildPaths.hs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ module Distribution.Simple.BuildPaths
, autogenPackageModulesDir
, autogenComponentModulesDir
, preBuildRulesCacheFile
, preBuildMonitorManifestFile
, autogenPathsModuleName
, autogenPackageInfoModuleName
, cppHeaderName
Expand Down Expand Up @@ -170,6 +171,18 @@ preBuildRulesCacheFile
preBuildRulesCacheFile lbi clbi =
componentBuildDir lbi clbi </> makeRelativePathEx "setup-hooks-rules.cache"

-- | The path to the pre-build monitor manifest file for a component.
--
-- This file is written after running pre-build rules, so that external
-- tools (e.g. HLS) can discover which files to watch for changes and which
-- files were generated by the rules. See 'writePreBuildMonitorManifest'.
preBuildMonitorManifestFile
:: LocalBuildInfo
-> ComponentLocalBuildInfo
-> SymbolicPath Pkg File
preBuildMonitorManifestFile lbi clbi =
componentBuildDir lbi clbi </> makeRelativePathEx "pre-build-monitors"

-- NB: Look at 'checkForeignDeps' for where a simplified version of this
-- has been copy-pasted.

Expand Down
88 changes: 86 additions & 2 deletions Cabal/src/Distribution/Simple/SetupHooks/Internal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ module Distribution.Simple.SetupHooks.Internal
-- ** Executing build rules
, executeRules
, executeRulesUserOrSystem
, writePreBuildMonitorManifest

-- ** HookedBuildInfo compatibility code
, hookedBuildInfoComponents
Expand All @@ -94,6 +95,7 @@ import Prelude ()

import Distribution.Compat.Lens ((.~))
import Distribution.PackageDescription
import Distribution.Pretty (prettyShow)
import Distribution.Simple.BuildPaths
import Distribution.Simple.Compiler (Compiler (..))
import Distribution.Simple.Errors
Expand Down Expand Up @@ -135,7 +137,13 @@ import qualified Data.Map as Map
import Data.Monoid (Ap (..))
import qualified Data.Set as Set

import System.Directory (doesFileExist, getModificationTime)
import System.Directory
( createDirectoryIfMissing
, doesFileExist
, getModificationTime
, makeAbsolute
)
import System.FilePath (takeDirectory)

--------------------------------------------------------------------------------
-- SetupHooks
Expand Down Expand Up @@ -850,6 +858,8 @@ executeRules
:: Verbosity
-> LocalBuildInfo
-> TargetInfo
-> [MonitorFilePath]
-- ^ files monitored by the pre-build rules
-> Map RuleId Rule
-> IO ()
executeRules =
Expand All @@ -872,9 +882,11 @@ executeRulesUserOrSystem
-> Verbosity
-> LocalBuildInfo
-> TargetInfo
-> [MonitorFilePath]
-- ^ files monitored by the pre-build rules
-> Map RuleId (RuleData userOrSystem)
-> IO ()
executeRulesUserOrSystem scope runDepsCmdData runCmdData verbosity lbi tgtInfo allRules = do
executeRulesUserOrSystem scope runDepsCmdData runCmdData verbosity lbi tgtInfo monitors allRules = do
-- Load the rule cache from the previous build.
-- Used to detect when rule definitions have changed.
oldRules <- handleDoesNotExist Map.empty $ do
Expand Down Expand Up @@ -995,6 +1007,19 @@ executeRulesUserOrSystem scope runDepsCmdData runCmdData verbosity lbi tgtInfo a
errorOut $ MissingRuleOutputs (toRuleBinary r) missingResults
-- Save the current rules to the cache for use in the next build.
structuredEncodeFile rulesCacheFile allRules
-- Write the monitor manifest for external tools (e.g. HLS).
let allFileDeps =
[ loc
| (rId, Rule{staticDependencies}) <- Map.toList allRules
, let dynDeps = maybe [] fst (Map.lookup rId dynDepsEdges)
, FileDependency loc <- staticDependencies ++ dynDeps
]
allOutputs =
[ loc
| Rule{results} <- Map.elems allRules
, loc <- NE.toList results
]
writePreBuildMonitorManifest lbi tgtInfo monitors allFileDeps allOutputs
where
toRuleBinary :: RuleData userOrSystem -> RuleBinary
toRuleBinary = case scope of
Expand All @@ -1009,6 +1034,65 @@ executeRulesUserOrSystem scope runDepsCmdData runCmdData verbosity lbi tgtInfo a
SetupHooksException $
RulesException e

-- | Write the pre-build monitor manifest for a component.
--
-- This plain-text file (stored at 'preBuildMonitorManifestFile') is intended
-- for external tools such as HLS that invoke @cabal build@ but do not link
-- against the Cabal library. It records enough information for such tools
-- to detect when pre-build rules need to be re-computed or re-run, as per
-- the 'SetupHooks' documentation in @Cabal-hooks@.
--
-- The manifest contains three sections:
--
-- * @[monitors]@: values monitored by the computation of pre-build rules.
-- * @[inputs]@: file dependencies of pre-build rules (static & dynamic).
-- * @[outputs]@: file outputs of pre-build rules.
--
-- All relative paths in the manifest are relative to the @pkg-root@ listed in
-- the file header.
writePreBuildMonitorManifest
:: LocalBuildInfo
-> TargetInfo
-> [MonitorFilePath]
-- ^ recompute pre-build rules when these change
-> [Location]
-- ^ combined static and dynamic file dependencies of pre-build rules
-> [Location]
-- ^ outputs (files generated by the pre-build rules)
-> IO ()
writePreBuildMonitorManifest lbi tgtInfo monitors fileDeps outputs = do
pkgRoot <- makeAbsolute (maybe "." getSymbolicPath (mbWorkDirLBI lbi))
let clbi = targetCLBI tgtInfo
manifestPath = interpretSymbolicPathLBI lbi (preBuildMonitorManifestFile lbi clbi)
content =
unlines $
concat
[
[ "pre-build-monitors-v1"
, "pkg-root:" ++ pkgRoot
]
, ["", "[monitors]"]
, map serialiseMonitor monitors
, ["", "[inputs]"]
, map (getSymbolicPath . location) fileDeps
, ["", "[outputs]"]
, map (getSymbolicPath . location) outputs
]
createDirectoryIfMissing True (takeDirectory manifestPath)
writeFile manifestPath content
where
serialiseMonitor (MonitorFile kf kd path) =
"file:" ++ showKindFile kf ++ ":" ++ showKindDir kd ++ ":" ++ path
serialiseMonitor (MonitorFileGlob kf kd glob) =
"glob:" ++ showKindFile kf ++ ":" ++ showKindDir kd ++ ":" ++ prettyShow glob
showKindFile FileExists = "exists"
showKindFile FileModTime = "modtime"
showKindFile FileHashed = "hashed"
showKindFile FileNotExists = "notexists"
showKindDir DirExists = "exists"
showKindDir DirModTime = "modtime"
showKindDir DirNotExists = "notexists"

directRuleDependencyMaybe :: Rule.Dependency -> Maybe RuleId
directRuleDependencyMaybe (RuleDependency dep) = Just $ outputOfRule dep
directRuleDependencyMaybe (FileDependency{}) = Nothing
Expand Down
22 changes: 22 additions & 0 deletions changelog.d/rules-manifest.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
synopsis: Write pre-build rules manifest file
packages: [Cabal, cabal-install]
prs: 11776
issues:
---

After executing pre-build rules for a component, `Cabal` 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.
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ runExternalPreBuildRules verbHandles hooksExe
DynamicRuleCommands {} -> hook "runPreBuildRuleDeps" (rId, cmd)
)
( \ rId cmd -> hook "runPreBuildRule" (rId, cmd) )
verbosity lbi tgt rulesMap
verbosity lbi tgt monitors rulesMap
return monitors

-- | The path to the external hooks executable.
Expand Down
Loading