I have a per-component custom action that I would like to run before the given component is built:
justBeforeBuilding :: LocalBuildInfo -> BuildFlags -> Component -> IO BuildInfo
Because the action might require dependencies to be already built, for example, if a given Cabal package has both a library and executables using that library, it is important that each component's justBeforeBuilding is run only before the actual component is built, but after all its dependencies are built.
So here's what I tried: instead of calling the default build hook once, I go over all enabled components one by one in dependency order, call my justBeforeBuilding function, and then sneakily change the buildArgs so that the default build hook would only build the single current component:
restrictBuildFlags :: PackageDescription -> Component -> BuildFlags -> BuildFlags
restrictBuildFlags pkg c buildFlags = buildFlags
{ buildArgs = selectedArgs
}
where
selectedArgs = [showBuildTarget (packageId pkg) $ BuildTargetComponent $ componentName c]
type BuildHook = PackageDescription -> LocalBuildInfo -> UserHooks -> BuildFlags -> IO ()
myBuildHook :: BuildHook -> BuildHook
myBuildHook nextBuildHook pkg lbi userHooks flags = do
let reqSpec = componentEnabledSpec lbi
withAllComponentsInBuildOrder pkg lbi $ \c clbi -> do
flags <- return $ restrictBuildFlags pkg c flags
when (componentEnabled reqSpec c && not (null $ buildArgs flags)) $ do
justBeforeBuilding lbi flags c
nextBuildHook pkg lbi userHooks flags
main :: IO ()
main = defaultMainWithHooks simpleUserHooks
{ buildHook = myBuildHook $ buildHook simpleUserHooks
}
The problem is that when justBeforeBuilding runs, it does not have access to library dependencies even if they are already built by that point. To demonstrate, here's a function that prints the contents of the Cabal-provided package DBs:
justBeforeBuilding :: LocalBuildInfo -> BuildFlags -> Component -> IO ()
justBeforeBuilding lbi flags c = do
pkgdbs <- absolutePackageDBPaths $ withPackageDB lbi
let dbpaths = nub . sort $ [ path | SpecificPackageDB path <- pkgdbs ]
dbflags = concat [ ["-package-db", path] | path <- dbpaths ]
putStrLn $ "!!! Processing component " <> show (componentName c)
putStrLn "!!! At this point, the package DB paths are:"
forM_ dbpaths $ \dir -> do
putStrLn dir
files <- listDirectory dir
mapM_ (printf " %s\n") files
For testing, I am using a Cabal package with one library and one exe; in HPack format:
name: cabal-component-hook
version: 0.1.0
custom-setup:
dependencies:
- base
- Cabal
- directory
dependencies:
- base >= 4.7 && < 5
library:
source-dirs: lib
exposed-modules:
- Foo
executables:
bar:
source-dirs: app
main: bar.hs
dependencies:
- cabal-component-hook
If I then do a stack build, first, the library is built, and the package DB of course doesn't contain the library yet:
!!! Processing component CLibName LMainLibName
!!! At this point, the package DB paths are:
/home/cactus/prog/clash/bugs/cabal-component-hook/.stack-work/install/x86_64-linux-tinfo6/82781a0829e0e0da301c1db4825858dea8980a6a982b679eb178870c6c0ec1ee/8.10.4/pkgdb
package.cache.lock
package.cache
/home/cactus/sdk/stack/snapshots/x86_64-linux-tinfo6/82781a0829e0e0da301c1db4825858dea8980a6a982b679eb178870c6c0ec1ee/8.10.4/pkgdb
package.cache.lock
StateVar-1.2.1-7vo3tV8mPssJqOV48TU4OF.conf
call-stack-0.2.0-4YzMVPI02PxAK0TxyBY7Iv.conf
cabal-doctest-1.0.8-I4vvWDvrsGA6v5uZj1lZJi.conf
base-orphans-0.8.4-JVJ8ttw51H7Dc3tJYgx6uK.conf
parallel-3.2.2.0-HTf2o2horULDwL7cXUB9uv.conf
hashable-1.3.0.0-K3FFBNAmsvaIvBv4Qg15rQ.conf
semigroups-0.19.1-CdJZL8lQRXTEuOuxFhFNIa.conf
contravariant-1.5.3-A4NbUFeaP6W4UzkL1ejoXa.conf
indexed-traversable-0.1.1-Dw8MIJyvCj8IZQDXOa8TBp.conf
tagged-0.8.6.1-A3E8I7zg2qBFE9O5vQsg6C.conf
void-0.7.3-FJLCa6y31Qt20kSi8TCcBC.conf
transformers-compat-0.6.6-CZr4aajtyBTi4Enjszw8H.conf
reflection-2.1.6-CzOlI803nFuvt8AikdOut.conf
distributive-0.6.2.1-Lhog8B4NdHk7JJdshqpkhJ.conf
transformers-base-0.4.5.2-2JwY8UMK4YFHH9faaNEDAI.conf
th-abstraction-0.4.2.0-CPMLTlyMgmr6dbHxHL95CG.conf
comonad-5.0.8-EA0Scey7jOW6LX5RvNTIb8.conf
primitive-0.7.1.0-Jxsyd70oUttYiCXCa0HqV.conf
unordered-containers-0.2.13.0-3awuPgUx2yvAACRZkw6am3.conf
bifunctors-5.5.10-1Xyw3zBBKdPGoolSEEYrSo.conf
profunctors-5.5.2-Jd7sxJvE4zaBkftBvoi6oJ.conf
semigroupoids-5.3.5-A5MCqcbuwFnHzZu6aqZxm4.conf
invariant-0.5.4-Ca6182XTMBJ4627vLKNFdU.conf
free-5.1.5-JCTHYv08sV0j7gsEXshfc.conf
adjunctions-4.4-4Q0IXuLBVoCBKmI2ZpS7bE.conf
kan-extensions-5.2.2-Z55rpCSAQY7rC9ino1jlr.conf
vector-0.12.1.2-6jlbObSa8iuJfxUVGBQC5r.conf
lens-4.19.2-86eTsWPqcVQ3qs5KiS7cYu.conf
package.cache
Preprocessing library for cabal-component-hook-0.1.0..
Building library for cabal-component-hook-0.1.0..
[1 of 2] Compiling Foo
[2 of 2] Compiling Paths_cabal_component_hook
However, then the executable is built, and the package DB still doesn't contain the just-built library:
!!! Processing component CExeName (UnqualComponentName "bar")
!!! At this point, the package DB paths are:
/home/cactus/prog/clash/bugs/cabal-component-hook/.stack-work/install/x86_64-linux-tinfo6/82781a0829e0e0da301c1db4825858dea8980a6a982b679eb178870c6c0ec1ee/8.10.4/pkgdb
package.cache.lock
package.cache
/home/cactus/sdk/stack/snapshots/x86_64-linux-tinfo6/82781a0829e0e0da301c1db4825858dea8980a6a982b679eb178870c6c0ec1ee/8.10.4/pkgdb
package.cache.lock
StateVar-1.2.1-7vo3tV8mPssJqOV48TU4OF.conf
call-stack-0.2.0-4YzMVPI02PxAK0TxyBY7Iv.conf
cabal-doctest-1.0.8-I4vvWDvrsGA6v5uZj1lZJi.conf
base-orphans-0.8.4-JVJ8ttw51H7Dc3tJYgx6uK.conf
parallel-3.2.2.0-HTf2o2horULDwL7cXUB9uv.conf
hashable-1.3.0.0-K3FFBNAmsvaIvBv4Qg15rQ.conf
semigroups-0.19.1-CdJZL8lQRXTEuOuxFhFNIa.conf
contravariant-1.5.3-A4NbUFeaP6W4UzkL1ejoXa.conf
indexed-traversable-0.1.1-Dw8MIJyvCj8IZQDXOa8TBp.conf
tagged-0.8.6.1-A3E8I7zg2qBFE9O5vQsg6C.conf
void-0.7.3-FJLCa6y31Qt20kSi8TCcBC.conf
transformers-compat-0.6.6-CZr4aajtyBTi4Enjszw8H.conf
reflection-2.1.6-CzOlI803nFuvt8AikdOut.conf
distributive-0.6.2.1-Lhog8B4NdHk7JJdshqpkhJ.conf
transformers-base-0.4.5.2-2JwY8UMK4YFHH9faaNEDAI.conf
th-abstraction-0.4.2.0-CPMLTlyMgmr6dbHxHL95CG.conf
comonad-5.0.8-EA0Scey7jOW6LX5RvNTIb8.conf
primitive-0.7.1.0-Jxsyd70oUttYiCXCa0HqV.conf
unordered-containers-0.2.13.0-3awuPgUx2yvAACRZkw6am3.conf
bifunctors-5.5.10-1Xyw3zBBKdPGoolSEEYrSo.conf
profunctors-5.5.2-Jd7sxJvE4zaBkftBvoi6oJ.conf
semigroupoids-5.3.5-A5MCqcbuwFnHzZu6aqZxm4.conf
invariant-0.5.4-Ca6182XTMBJ4627vLKNFdU.conf
free-5.1.5-JCTHYv08sV0j7gsEXshfc.conf
adjunctions-4.4-4Q0IXuLBVoCBKmI2ZpS7bE.conf
kan-extensions-5.2.2-Z55rpCSAQY7rC9ino1jlr.conf
vector-0.12.1.2-6jlbObSa8iuJfxUVGBQC5r.conf
lens-4.19.2-86eTsWPqcVQ3qs5KiS7cYu.conf
package.cache
Preprocessing library for cabal-component-hook-0.1.0..
Building library for cabal-component-hook-0.1.0..
Preprocessing executable 'bar' for cabal-component-hook-0.1.0..
Building executable 'bar' for cabal-component-hook-0.1.0..
[1 of 2] Compiling Main
[2 of 2] Compiling Paths_cabal_component_hook
Linking .stack-work/dist/x86_64-linux-tinfo6/Cabal-3.2.1.0/build/bar/bar ...
At the end of this whole process, though the package DB directory is correctly populated:
/home/cactus/prog/clash/bugs/cabal-component-hook/.stack-work/install/x86_64-linux-tinfo6/82781a0829e0e0da301c1db4825858dea8980a6a982b679eb178870c6c0ec1ee/8.10.4/pkgdb
├── cabal-component-hook-0.1.0-8kPvy0LMfqKAbvXmH5zWaP.conf
├── package.cache
└── package.cache.lock
So my question is, how can I arrange for justBeforeBuilding to run just before building each individual component, when its dependencies are already fully processed, i.e. library dependencies are copied to the build-internal package DB?