I've been learning recently about the relationship between Linux distributions and their respective repositories and packages from various sources including this, this, and this.

I will use the example of libuv for this question.

From this recent learning, I've realized that a situation may occur where a package exists at its standard search locations in the Unix Filesystem Hierarchy Standard (FHS), e.g. /usr/include and /usr/lib as well as non-standard locations.

For example, my Ubuntu 22.04.3 WSL "jammy" release includes /usr/include/uv.h and /usr/lib/x86_64-linux-gnu/libuv* for version 1.43.0. But I was in a position where I needed 1.45.0+, and since that version was not available to my Linux release's package repository, I had to (build and) install from source. The default install location when installing from source appears to be /usr/local/, but this answer specifically advises that installing to /usr/local/ (as well as /usr/) was a bad idea, so my understanding is that I should install to a nonstandard path, like /usr/local/libuv-1.45.

But this then seems to introduce a split in how you handle -I, and -L compiler and linker flags:

On my system, where I've needed to install my desired libuv:1.45 to a nonstandard path, because /usr/local/libuv-1.45/include/uv.h is not in the standard search path, I ought to include compiler flag -I /usr/local/libuv-1.45/include in order for the #include <uv.h> line of code to pick the correct header.
Similarly, because /usr/local/libuv-1.45/lib/libuv.so is not in the standard search path, I ought to include linker flag -L /usr/local/libuv-1.45/lib.
(Extreme care seems necessary here because the libuv package is available at both the standard search path and a nonstandard search path, so a mistake might mean that my code uses the wrong package!)

But what about other Linux distributions, or future releases of my current distribution, where libuv:1.45 may be available in the standard search path, or available to a package-manager (which presumably installs to a standard search path)? In this case, presumably, there would be no need to specify any -I or -L flag.

So my question is: given the possible scenarios described above, how do you (cleanly) handle compiler and linker flags to specify nonstandard search paths when you can't be certain whether packages, or specific versions of packages, are available in the standard search paths in the specific Linux distribution/release that you're working in? Is there a prevailing convention/idiom to handle this so that one set of build system files (like Makefiles) or build system generator files (like CMakeLists.txt) works across multiple Linux distributions/releases? Or can this only be handled by the compiler/linker flags (or their governing build system files/build system generator files) implicitly being tightly coupled to the development environment in which they're run?

1

There are 1 best solutions below

0
On

how do you (cleanly) handle compiler and linker flags to specify nonstandard search paths when you can't be certain whether packages, or specific versions of packages, are available in the standard search paths in the specific Linux distribution/release that you're working in?

Old School solution

The builder is expected to customize the Makefile, typically by setting appropriate values for a few well-named variables appearing at the very top. There are probably some defaults that work under favorable circumstances, and maybe multiple sets of them for different kinds of environments.

New School solution

Before the build proper, there is some kind of one-time configuration step wherein the local environment is evaluated, possibly in light of user input, to locate needed dependencies and otherwise characterize the flags needed for a successful build. That information is recorded in a form that make or some other build tool can subsequently consume.

This is the domain of the Autotools and CMake, among others. It is usually easier on people building the software, but it is much more complicated for the project maintainer.

Bundling

For open source dependencies, one option to consider is bundling source code with the package that is going to use them. Then you don't need to look for them at all. If you want to allow the option of using the system's versions of some or all of the dependencies then you can combine this with either of the approaches above. That can simplify things to a choice between using whatever is found in the standard search paths (which you can check, if you wish, for meeting your particular version etc requirements), or using the known-good versions bundled with the main package.