Skip to content

Commit

Permalink
SetupHooks: add tests
Browse files Browse the repository at this point in the history
This adds tests for error messages related to SetupHooks:

  - error when returning an invalid component diff in a
    per-component pre-configure hook
  - error when declaring pre-build rules whose dependency
    graph contains cycles
  - error when we cannot find a dependency of a pre-build rule
  - warning when there are pre-build rules that are declared but
    never demanded

It also adds some correctness tests for SetupHooks, e.g. that
pre-build rules are run in dependency order (see the
`SetupHooksRuleOrdering` test).
  • Loading branch information
sheaf committed Jan 11, 2024
1 parent 4382956 commit 7b94e0b
Show file tree
Hide file tree
Showing 55 changed files with 505 additions and 18 deletions.
12 changes: 5 additions & 7 deletions Cabal-hooks/src/Distribution/Simple/SetupHooks.hs
Expand Up @@ -428,10 +428,10 @@ rather than directly using the v'Rules', v'Rule' and v'Action' constructors,
which insulates us from internal changes to the t'Rules', t'Rule' and t'Action'
datatypes, respectively.
We use 'declareRuleDependencies' to declare that the collection of rules as a
whole depends on. In this case, we declare that they depend on the contents of
the "searchDir" directory. This means that the rules will be computed anew
whenever the contents of this directory change.
We use 'addRuleMonitorss' to declare a monitored directory that the collection
of rules as a whole depends on. In this case, we declare that they depend on the
contents of the "searchDir" directory. This means that the rules will be
computed anew whenever the contents of this directory change.
Additional convenience functions are also provided, such as the 'generateModules'
function which can be used to generate a collection of modules ex nihilo without
Expand Down Expand Up @@ -633,16 +633,14 @@ register_helper mkId mkDupIdErr i newX = do
[] -> ""
(_, SrcLoc { srcLocPackage = pkg }) : _ -> toShortText pkg

-- | Declare a dependency for the collection of all rules.
-- | Declare additional monitored objects for the collection of all rules.
--
-- When these monitored objects change, the rules are re-computed.
addRuleMonitors :: Monad m => [ MonitorFileOrDir ] -> RulesT m ()
addRuleMonitors = RulesT . lift . lift . Writer.tell
{-# INLINEABLE addRuleMonitors #-}

-- | Find a file in the given search directories.
--
--
findFileInDirs :: FilePath -> [FilePath] -> IO (Maybe Location)
findFileInDirs file dirs =
findFirstFile
Expand Down
10 changes: 5 additions & 5 deletions Cabal/src/Distribution/Simple/SetupHooks/Internal.hs
Expand Up @@ -203,7 +203,7 @@ data PreConfPackageInputs = PreConfPackageInputs
-- of this datatype.
data PreConfPackageOutputs = PreConfPackageOutputs
{ buildOptions :: BuildOptions
, extraConfiguredPrograms :: ConfiguredProgs
, extraConfiguredProgs :: ConfiguredProgs
}
deriving (Generic, Show)

Expand All @@ -217,7 +217,7 @@ noPreConfPackageOutputs :: PreConfPackageInputs -> PreConfPackageOutputs
noPreConfPackageOutputs (PreConfPackageInputs{localBuildConfig = lbc}) =
PreConfPackageOutputs
{ buildOptions = LBC.withBuildOptions lbc
, extraConfiguredPrograms = Map.empty
, extraConfiguredProgs = Map.empty
}

-- | Package-wide post-configure step.
Expand Down Expand Up @@ -349,12 +349,12 @@ instance Semigroup PreConfPkgSemigroup where
do
PreConfPackageOutputs
{ buildOptions = opts1
, extraConfiguredPrograms = progs1
, extraConfiguredProgs = progs1
} <-
f1 inputs
PreConfPackageOutputs
{ buildOptions = opts2
, extraConfiguredPrograms = progs2
, extraConfiguredProgs = progs2
} <-
f2 $
PreConfPackageInputs
Expand All @@ -372,7 +372,7 @@ instance Semigroup PreConfPkgSemigroup where
return $
PreConfPackageOutputs
{ buildOptions = opts2
, extraConfiguredPrograms = progs1 <> progs2
, extraConfiguredProgs = progs1 <> progs2
}

-- | A newtype to hang off the @Semigroup PreConfComponentHook@ instance.
Expand Down
10 changes: 6 additions & 4 deletions Cabal/src/Distribution/Simple/SetupHooks/Rule.hs
Expand Up @@ -249,7 +249,8 @@ instance Binary Rule
instance Structured Rule

-- | A (fully resolved) location of a dependency or result of a rule,
-- consisting of an absolute path and of a file path relative to that base path.
-- consisting of a base directory and of a file path relative to that base
-- directory path.
--
-- In practice, this will be something like @( dir, toFilePath modName )@,
-- where:
Expand Down Expand Up @@ -324,7 +325,7 @@ newtype Action
-- \^ Locations of the __dependencies__ of this action,
-- as declared by the rule that this action is executing.
-> NE.NonEmpty Location
-- \^ Locations in which the __results_ of this action
-- \^ Locations in which the __results__ of this action
-- should be put.
-> IO ()
}
Expand All @@ -341,8 +342,9 @@ simpleAction f = Action{action = f}
-- constructor.
--
-- Actions are registered using 'registerAction', and rules are registered
-- using 'registerRule'. Additional rule dependencies (whose changes should
-- trigger rule recompilation) are declared using 'declareRuleDependencies'.
-- using 'registerRule'. One can declare additional monitored files or
-- directories using 'addRuleMonitors'; a change in these will trigger the
-- recomputation of all rules.
--
-- The @env@ type parameter represents an extra argument, which usually
-- consists of information known to Cabal such as 'LocalBuildInfo' and
Expand Down
7 changes: 7 additions & 0 deletions cabal-testsuite/PackageTests/SetupHooksBadDiff1/Setup.hs
@@ -0,0 +1,7 @@
module Main where

import Distribution.Simple ( defaultMainWithSetupHooks )
import SetupHooks ( setupHooks )

main :: IO ()
main = defaultMainWithSetupHooks setupHooks
18 changes: 18 additions & 0 deletions cabal-testsuite/PackageTests/SetupHooksBadDiff1/SetupHooks.hs
@@ -0,0 +1,18 @@
module SetupHooks where

import Distribution.Simple.SetupHooks

import Control.Monad ( void )

setupHooks :: SetupHooks
setupHooks =
noSetupHooks
{ configureHooks =
noConfigureHooks
{ preConfComponentHook = Just pccHook }
}

pccHook :: PreConfComponentHook
pccHook _ = return $
PreConfComponentOutputs $ ComponentDiff $ CExe emptyExecutable
-- Bad: component is a library, but we returned an executable!
@@ -0,0 +1 @@
packages: .
@@ -0,0 +1,16 @@
cabal-version: 2.2
name: setup-hooks-bad-diff1-test
version: 0.1.0.0
synopsis: Test 1 for a bad component diff
license: BSD-3-Clause
author: NA
maintainer: NA
category: Testing
build-type: Hooks

custom-setup
setup-depends: Cabal-hooks, base

library
build-depends: base
default-language: Haskell2010
5 changes: 5 additions & 0 deletions cabal-testsuite/PackageTests/SetupHooksBadDiff1/setup.out
@@ -0,0 +1,5 @@
# Setup configure
Configuring setup-hooks-bad-diff1-test-0.1.0.0...
Error: [Cabal-9491]
Hooks: mismatched component types in per-component configure hook.
Trying to apply an executable diff to a library.
3 changes: 3 additions & 0 deletions cabal-testsuite/PackageTests/SetupHooksBadDiff1/setup.test.hs
@@ -0,0 +1,3 @@
import Test.Cabal.Prelude
main = setupTest $ do
fails $ setup "configure" []
7 changes: 7 additions & 0 deletions cabal-testsuite/PackageTests/SetupHooksBadDiff2/Setup.hs
@@ -0,0 +1,7 @@
module Main where

import Distribution.Simple ( defaultMainWithSetupHooks )
import SetupHooks ( setupHooks )

main :: IO ()
main = defaultMainWithSetupHooks setupHooks
27 changes: 27 additions & 0 deletions cabal-testsuite/PackageTests/SetupHooksBadDiff2/SetupHooks.hs
@@ -0,0 +1,27 @@
{-# LANGUAGE OverloadedStrings #-}

module SetupHooks where

import Distribution.Simple.SetupHooks

import Control.Monad ( void )

setupHooks :: SetupHooks
setupHooks =
noSetupHooks
{ configureHooks =
noConfigureHooks
{ preConfComponentHook = Just pccHook }
}

pccHook :: PreConfComponentHook
pccHook _ = return $
-- Make invalid changes to a library
PreConfComponentOutputs $ ComponentDiff $ CLib $
emptyLibrary
{ libName = LSubLibName "hocus-pocus"
, libExposed = False
, libBuildInfo =
emptyBuildInfo
{ buildable = False }
}
@@ -0,0 +1 @@
packages: .
@@ -0,0 +1,16 @@
cabal-version: 2.2
name: setup-hooks-bad-diff2-test
version: 0.1.0.0
synopsis: Test 2 for a bad component diff
license: BSD-3-Clause
author: NA
maintainer: NA
category: Testing
build-type: Hooks

custom-setup
setup-depends: Cabal-hooks, base

library
build-depends: base
default-language: Haskell2010
7 changes: 7 additions & 0 deletions cabal-testsuite/PackageTests/SetupHooksBadDiff2/setup.out
@@ -0,0 +1,7 @@
# Setup configure
Configuring setup-hooks-bad-diff2-test-0.1.0.0...
Error: [Cabal-7634]
Hooks: illegal component diff in per-component pre-configure hook for main library:
- cannot change the name of a component.
- cannot change component field 'libExposed'.
- cannot change BuildInfo field 'buildable'.
3 changes: 3 additions & 0 deletions cabal-testsuite/PackageTests/SetupHooksBadDiff2/setup.test.hs
@@ -0,0 +1,3 @@
import Test.Cabal.Prelude
main = setupTest $ do
fails $ setup "configure" []
7 changes: 7 additions & 0 deletions cabal-testsuite/PackageTests/SetupHooksCyclicRules/Setup.hs
@@ -0,0 +1,7 @@
module Main where

import Distribution.Simple ( defaultMainWithSetupHooks )
import SetupHooks ( setupHooks )

main :: IO ()
main = defaultMainWithSetupHooks setupHooks
34 changes: 34 additions & 0 deletions cabal-testsuite/PackageTests/SetupHooksCyclicRules/SetupHooks.hs
@@ -0,0 +1,34 @@
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecursiveDo #-}

module SetupHooks where

import Distribution.Simple.SetupHooks

import qualified Data.List.NonEmpty as NE ( NonEmpty(..) )

setupHooks :: SetupHooks
setupHooks =
noSetupHooks
{ buildHooks =
noBuildHooks
{ preBuildComponentRules = Just cyclicPreBuildRules
}
}

cyclicPreBuildRules :: Rules PreBuildComponentInputs
cyclicPreBuildRules = rules $ \ (PreBuildComponentInputs { localBuildInfo = lbi, targetInfo = tgt }) -> do
let clbi = targetCLBI tgt
autogenDir = autogenComponentModulesDir lbi clbi
actId <- registerAction "a" $ simpleAction $ \ _ _ -> error "This should not run"
return $ mdo
r1 <- registerRule "r1" $
simpleRule actId
[ RuleDependency $ RuleOutput r2 0 ]
( ( autogenDir, "G2.hs" ) NE.:| [] )
r2 <- registerRule "r2" $
simpleRule actId
[ RuleDependency $ RuleOutput r1 0 ]
( ( autogenDir, "G1.hs" ) NE.:| [] )
return ()
@@ -0,0 +1 @@
packages: .
@@ -0,0 +1,18 @@
cabal-version: 2.2
name: setup-hooks-cyclic-rules-test
version: 0.1.0.0
synopsis: Test for cyclic rules
license: BSD-3-Clause
author: NA
maintainer: NA
category: Testing
build-type: Hooks

custom-setup
setup-depends: Cabal-hooks, base

library
exposed-modules: G1, G2
autogen-modules: G1, G2
build-depends: base
default-language: Haskell2010
9 changes: 9 additions & 0 deletions cabal-testsuite/PackageTests/SetupHooksCyclicRules/setup.out
@@ -0,0 +1,9 @@
# Setup configure
Configuring setup-hooks-cyclic-rules-test-0.1.0.0...
# Setup build
Error: [Cabal-9077]
Hooks: cycle in dependency structure of rules:
Rule: [(RuleId {ruleUnitId = "main", ruleId = "r2"})[0]] --> [setup.dist/work/dist/build/autogen/G2.hs]
|
`- Rule: [(RuleId {ruleUnitId = "main", ruleId = "r1"})[0]] --> [setup.dist/work/dist/build/autogen/G1.hs]

@@ -0,0 +1,4 @@
import Test.Cabal.Prelude
main = setupTest $ do
setup "configure" []
fails $ setup "build" []
@@ -0,0 +1,7 @@
module Main where

import Distribution.Simple ( defaultMainWithSetupHooks )
import SetupHooks ( setupHooks )

main :: IO ()
main = defaultMainWithSetupHooks setupHooks
@@ -0,0 +1,28 @@
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE OverloadedStrings #-}

module SetupHooks where

import Distribution.Simple.SetupHooks

import qualified Data.List.NonEmpty as NE ( NonEmpty(..) )

setupHooks :: SetupHooks
setupHooks =
noSetupHooks
{ buildHooks =
noBuildHooks
{ preBuildComponentRules = Just missingDepRules
}
}

missingDepRules :: Rules PreBuildComponentInputs
missingDepRules = rules $ \ (PreBuildComponentInputs { localBuildInfo = lbi, targetInfo = tgt }) -> do
let clbi = targetCLBI tgt
autogenDir = autogenComponentModulesDir lbi clbi
actId <- registerAction "a" $ simpleAction $ \ _ _ -> error "This should not run"
return $
registerRule_ "r" $
simpleRule actId
[ FileDependency ( ".", "Missing.hs" ) ]
( ( autogenDir, "G.hs" ) NE.:| [] )
@@ -0,0 +1 @@
packages: .
@@ -0,0 +1,18 @@
cabal-version: 2.2
name: setup-hooks-missing-rule-dep-test
version: 0.1.0.0
synopsis: Test for missing dependency in rules
license: BSD-3-Clause
author: NA
maintainer: NA
category: Testing
build-type: Hooks

custom-setup
setup-depends: Cabal-hooks, base

library
exposed-modules: G
autogen-modules: G
build-depends: base
default-language: Haskell2010
@@ -0,0 +1,6 @@
# Setup configure
Configuring setup-hooks-missing-rule-dep-test-0.1.0.0...
# Setup build
Error: [Cabal-1071]
Pre-build rules: can't find source for rule dependency:
- Missing.hs
@@ -0,0 +1,4 @@
import Test.Cabal.Prelude
main = setupTest $ do
setup "configure" []
fails $ setup "build" []
@@ -0,0 +1,7 @@
module Main where

import Distribution.Simple ( defaultMainWithSetupHooks )
import SetupHooks ( setupHooks )

main :: IO ()
main = defaultMainWithSetupHooks setupHooks

0 comments on commit 7b94e0b

Please sign in to comment.