haskell / haskell/cabal

Relative paths of extra-include-dirs and extra-lib-dirs are resolved from each package's root instead of current working directory

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

Description

# TLDR

Relative paths in command line flags `--extra-include-dirs`, `--extra-lib-dirs` and their counterparts in `cabal.project` resolved against each package's root which causes surprising and user-unfriendly build errors.

# Details

I created following repository to reproduce: https://github.com/random-random-stuff/test-cabal-relative-extra-include-and-lib-dirs and guide the following explanation.

I have a package `cabal-test-dependency` which wraps a foreign library. When building it a `cabal_test_foo.h` header and a `libcabal_test_foo.so` file must be available.

Then I have my main package `cabal-test-main` that uses `cabal-test-dependency`:

```
packages:
cabal-test-main.cabal
../dependency/cabal-test-dependency.cabal
```

The directory with `cabal-test-main` is where I, as a user, is working in and is where I’m calling cabal from.

I have local copy of `.h` and `.so` that are needed to complete the build in root of `cabal-test-main` package under directory `deps` (in this though experiment a user puts `.so` where needed beforehand like here).

```
$ ls deps/
cabal_test_foo.c cabal_test_foo.h libcabal_test_foo.so
```

Now from `cabal-test-main` root I’m calling `cabal build` and pass `deps/` directory so that dependency would build but the build *fails*:

```
$ cabal build --extra-lib-dirs deps --extra-include-dirs deps
Resolving dependencies...
Build profile: -w ghc-9.14.1 -O1
In order, the following will be built (use -v for more details):
- cabal-test-dependency-0.1 (lib) (first run)
- cabal-test-main-0.1 (exe:main) (first run)
Configuring library for cabal-test-dependency-0.1...
Warning: [unknown-directory] 'extra-lib-dirs: deps' specifies a directory
which does not exist.
Warning: [unknown-directory] 'include-dirs: deps' specifies a directory which
does not exist.
Error: [Cabal-4345]
Missing dependency on a foreign library:
* Missing (or bad) header file: cabal_test_foo.h
* Missing (or bad) C library: cabal_test_foo
If the header file does exist, it may contain errors that are caught by the C compiler at the preprocessing stage. In this case you can re-run 'Setup configure' with the verbosity flag -v3 to see the error messages.
Error: [Cabal-7125]
Failed to build cabal-test-dependency-0.1 (which is required by exe:main from cabal-test-main-0.1). The failure occurred during the configure step.
```

The errors are

```
Warning: [unknown-directory] 'extra-lib-dirs: deps' specifies a directory
which does not exist.
Warning: [unknown-directory] 'include-dirs: deps' specifies a directory which
does not exist.
```

apparently because `deps` directory is being resolved under root directory of `cabal-test-dependency` package where it does not exist.

If I specify absolute paths, e.g. `cabal build --extra-lib-dirs "$(pwd)/deps" --extra-include-dirs "$(pwd)/deps"` then build succeeds.

It seems like relative paths should work too and should resolve against where user is currently situated. For command line options that would be the current working directory and for `cabal.project` options it seems the location of the `cabal.project` file should serve as a root to resolve relative paths against.

According to https://github.com/haskell/cabal/issues/2641 the relative paths for `extra-include-dirs` and `extra-lib-dirs` are allowed but neither that PR nor manual provide concrete information as to how relative paths are resolved.

Current behavior seems like an artefact of the implementation where `extra-include-dirs` and `extra-lib-dirs` are added to each package’s `include-dirs` and `library-dirs` respectively and only then are resolved in each package’s context.

It does not seems like a good idea that that standing in package `A` and specifying a path relative to `A`’s root, e.g. `foo/` results in dependent package `B` getting a relative `foo/` directory in any part of its package description. While looking at `A` the user has no visibility under package `B` and what directories it contains.

**System information**
- Operating system - Linux x86_64
- `cabal` - 3.17.0.0, built from master, `ghc` - 9.14.1

Contributor guide

Open the contributing guide

Research direction

Start with the linked reproduction repository and run `cabal build --extra-lib-dirs deps --extra-include-dirs deps` from the main package root. Trace how these command-line options and the corresponding `cabal.project` settings are applied to dependency packages; done means relative paths resolve from the working directory for command-line options and from the `cabal.project` location for project options, with the reproduction build succeeding.

Written by the indexing model from the issue text.

Assessment

Tech stack
haskell
Domain
build-system, cli
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.