haskell / haskell/cabal

Project in-place builds mask issues with `install-includes`

Open
#11,172 2 comments 0 reactions 0 assignees View on GitHub
cabal-install: cmd/install re: install --lib type: bug
Dominant language
Haskell
Stars
1.7k
Forks
750
Avg merge
4d 3h
Merged PRs (30d)
28

Description

## Issue

There is some subtle divergence of dealing with include directories when doing `cabal build` vs `cabal install` in a cabal project with multiple packages.

## Reproducer

Reproduce:

* `git clone https://github.com/hasufell/cabal-bug.git`
* `cd cabal-bug`
* `cabal build file-io`
* `cabal clean`
* `cabal --store-dir=$(pwd)/store install --package-env=. --lib file-io`

The last command will fail:

```
[53 of 53] Compiling System.Posix ( System/Posix.hs, dist/build/System/Posix.o, dist/build/System/Posix.dyn_o )

cbits/execvpe.c:25:10: error:
fatal error: execvpe.h: No such file or directory
25 | #include "execvpe.h"
| ^~~~~~~~~~~
|
25 | #include "execvpe.h"
| ^
compilation terminated.
`gcc' failed in phase `C Compiler'. (Exit code: 1)
Error: [Cabal-7125]
Failed to build unix-2.22.7.0 (which is required by file-io-0.1.5). See the build log above for details.
```

## Explanation

The reason is that I commented out `install-includes`:

https://github.com/hasufell/cabal-bug/blob/7b1210dba32d67308fb402d4c9e359a1cd4b05d2/unix/unix.cabal#L162-L164

The question now is... why does `cabal build` work? When a `cabal.project` file is present with multiple packages, it performs an "in-place" build with a real `package.conf.d/`:

```
$ ls dist-newstyle/packagedb/ghc-9.6.7/
file-io-0.1.5-inplace.conf package.cache package.cache.lock unix-2.22.7.0-inplace.conf
$ ghc-pkg --package-db=dist-newstyle/packagedb/ghc-9.6.7/ field unix include-dirs
include-dirs: /home/hasufell/git/cabal-bug/unix/include
/home/hasufell/git/cabal-bug/dist-newstyle/build/x86_64-linux/ghc-9.6.7/unix-2.22.7.0/build/include
$ ls /home/hasufell/git/cabal-bug/unix/include
execvpe.h HsUnixConfig.h.in HsUnix.h
$ ls /home/hasufell/git/cabal-bug/dist-newstyle/build/x86_64-linux/ghc-9.6.7/unix-2.22.7.0/build/include
HsUnixConfig.h
```

Unlike a properly pre-installed unix, the in-place unix now contains two `include-dirs` instead of one in the package conf file:

* `/home/hasufell/git/cabal-bug/unix/include`: this is the plain source directory that contains the headers
* `/home/hasufell/git/cabal-bug/dist-newstyle/build/x86_64-linux/ghc-9.6.7/unix-2.22.7.0/build/include`: this is a subdirectory of the build directory, which usually only includes `autogen-headers`

Installing headers is not done during a build and so those headers will remain in the project source directory. But that also means in order to create a usable `*.conf` file for the package database, cabal has to somehow make those non-installed headers visible. It appears it just adds the source directories of `include-dirs` from the cabal file to it, ignoring the fact whether their contents are listed in `install-includes` or not.

A proper installation fails. Here it fails because unix itself can't find the `execvpe.h` header, but this could also manifest in other ways (e.g. a partial install if we only exclude `HsUnix.h`... and then cause some other dependent package to fail).

## Expected behavior and solution

This type of subtle divergence between `cabal build` and `cabal install` is problematic and surprising. `cabal build` should just be a variant of `cabal install` without merging it to an actual store. That could be designed in many different ways.

A stop-gap solution would be to:

* in `Cabal`: during build, also copy the `install-includes` to the build output directory
* in `cabal-install`: only add the build output directory to the package conf's `include-dirs`.

Long-term, I guess we do want a restructuring of how these in-place multi-package builds are carried out. It seems like quite a hack overall.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.