Skip to content

Commit

Permalink
Add pre and post build hooks
Browse files Browse the repository at this point in the history
Run a program (named "preBuildHook") before doing a package build and
another program (named "postBuildHook") after the package is built.

These programs are project local and need to be in the `cabalHooks`
directory which is in the same directory as the `cabal.project` file.

Co-authored: Moritz Angermann <[email protected]>
  • Loading branch information
erikd committed May 7, 2024
1 parent fd8020f commit bb9c361
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ module Distribution.Client.ProjectBuilding.UnpackedPackage
import Distribution.Client.Compat.Prelude
import Prelude ()

import Distribution.Client.Config
import Distribution.Client.PackageHash (renderPackageHashInputs)
import Distribution.Client.ProjectBuilding.Types
import Distribution.Client.ProjectConfig
Expand Down Expand Up @@ -100,8 +101,8 @@ import qualified Data.ByteString.Lazy.Char8 as LBS.Char8
import qualified Data.List.NonEmpty as NE

import Control.Exception (ErrorCall, Handler (..), SomeAsyncException, assert, catches)
import System.Directory (canonicalizePath, createDirectoryIfMissing, doesDirectoryExist, doesFileExist, removeFile)
import System.FilePath (dropDrive, normalise, takeDirectory, (<.>), (</>))
import System.Directory (canonicalizePath, createDirectoryIfMissing, doesDirectoryExist, doesFileExist, getCurrentDirectory, removeFile)
import System.FilePath (dropDrive, dropFileName, normalise, takeDirectory, (<.>), (</>))
import System.IO (Handle, IOMode (AppendMode), withFile)
import System.Semaphore (SemaphoreName (..))

Expand Down Expand Up @@ -678,7 +679,32 @@ buildAndInstallUnpackedPackage
runConfigure
PBBuildPhase{runBuild} -> do
noticeProgress ProgressBuilding
runBuild
hooksDir <- (</> "cabalHooks") <$> getCurrentDirectory
-- run preBuildHook. If it returns with 0, we assume the build was
-- successful. If not, run the build.
code <-
rawSystemExitCode
verbosity
(Just srcdir)
(hooksDir </> "preBuildHook")
[ (unUnitId $ installedUnitId rpkg)
, (getSymbolicPath srcdir)
, (getSymbolicPath builddir)
]
`catchIO` (\_ -> return (ExitFailure 10))
when (code /= ExitSuccess) $ do
runBuild
-- not sure, if we want to care about a failed postBuildHook?
void $
rawSystemExitCode
verbosity
(Just srcdir)
(hooksDir </> "postBuildHook")
[ (unUnitId $ installedUnitId rpkg)
, (getSymbolicPath srcdir)
, (getSymbolicPath builddir)
]
`catchIO` (\_ -> return (ExitFailure 10))
PBHaddockPhase{runHaddock} -> do
noticeProgress ProgressHaddock
runHaddock
Expand Down
14 changes: 14 additions & 0 deletions changelog.d/pr-9899
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
synopsis: Add pre and post build hooks
packages: cabal-install
prs: #9899
issues: #9892
significance: significant

description: {

- Run a program (named "preBuildHook") before doing a package build and another program
(named "postBuildHook") after the package is built.
- These programs are project local and need to be in the `cabalHooks` directory which is
in the same directory as the `cabal.project` file.
- The absence of these programs will be ignored.
}
43 changes: 43 additions & 0 deletions doc/build-hooks.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
Build Hooks
===========

Build hooks are programs that are run before (pre-build hook) and
after (post-build hook) a package (including package dependencies)
is built. The hooks are completely generic and can even be absent
(their absence is ignored).

Build hooks are project local rather than global to the user
because a single user may want to use one set of hooks in one
project and another set of hoos (or even none at all) for another
project.


Possible Use Cases
------------------

Possible use cases include:

* Fine grained benchmarking of individual package build times.
* Build product caching.


Location of Hook Files
----------------------

The two hook files are `cabalHooks/preBuildHook` and
`cabalHooks/postBuildHook` where the `cabalHooks` directory is in
the same directory as the `cabal.project` file. On UNIX style
systems, these hooks need to be marked as user executable programs.


Security Considerations
-----------------------

These build hooks are generic executable programs. They can potentially
be malicious. For example, one might clone a Haskell project from
say Github, that includes malicious build hooks so that when the user runs
`cabal build all` these hooks will be run as the user. The most obvious
malicious behaviour would be to delete all the user's files.

For this reason, it is highly advisable to check for the existence
of and the contents of any build hook files.
1 change: 1 addition & 0 deletions doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Welcome to the Cabal User Guide
how-to-run-in-windows
how-to-use-backpack
how-to-report-bugs
build-hooks

.. toctree::
:caption: Cabal Reference
Expand Down

0 comments on commit bb9c361

Please sign in to comment.