/u-config

a smaller, simpler, portable pkg-config clone

Primary LanguageCThe UnlicenseUnlicense

u-config: a small, simple, portable pkg-config clone

u-config ("micro-config") is a small, highly portable pkg-config and pkgconf clone. It was written primarily for w64devkit and Windows, but can also serve as a reliable drop-in replacement on other platforms. Notable features:

  • A fraction of the size while retaining the core, user-level features of pkg-config and pkgconf.

  • Windows as a first-class supported platform.

  • Highly portable to any machine. Supports a variety of compilers and operating systems, ancient and new. Can be built without libc, which is handy for bootstrapping.

  • Trivial, fast build. No messing around with GNU Autotools, or any build system for that matter.

Each pkg-config is specialized to its surrounding software distribution, and so u-config is probably only useful to distribution maintainers.

As noted, u-config focuses on user-level features but deliberately lacks developer-level features. The goal is to support existing pkg-config based builds, not make more of them. In summary:

  • Omits most .pc debugging features (--print-…)
  • No special handling of "uninstalled" packages, and no attendant knobs
  • Skips checks unimplemented by pkg-config (i.e. Conflicts:)
  • Less strict .pc syntax

It still supports the important pkg-config run-time environment variables:

  • PKG_CONFIG_PATH
  • PKG_CONFIG_LIBDIR
  • PKG_CONFIG_TOP_BUILD_DIR
  • PKG_CONFIG_SYSTEM_INCLUDE_PATH
  • PKG_CONFIG_SYSTEM_LIBRARY_PATH
  • PKG_CONFIG_ALLOW_SYSTEM_CFLAGS
  • PKG_CONFIG_ALLOW_SYSTEM_LIBS

See the pkg-config documentation for details. It also closely follows pkg-config's idiosyncratic argument parsing — positional arguments are concatenated then retokenzied — and its undocumented .pc quote and backslash syntax.

Features unique to u-config

  • --newlines: separates arguments by line feeds instead of spaces, which is sometimes useful like in the fish shell or when manually examining output.

  • Handles spaces in prefix: Especially important on Windows where spaces in paths are common. Libraries will work correctly even when installed under such a path. (Note: Despite popular belief, and the examples in its own documentation, pkg-config was never designed for use in command substitution. It was designed for eval, and spaces in prefix will require implicit or explicit eval.)

Build

u-config compiles as one translation unit. Choose an appropriate platform layer (*_main.c) for your target then invoke your C compiler only that source file. The "generic" platform is libc so it works everywhere, but inherits the target's libc limitations (restricted path and environment variable access, no automatic self-configuration, etc.).

$ cc -Os -o pkg-config generic_main.c

However, one of core goals is to be a reliable, native pkg-config for Windows, so it has a dedicated platform layer. This layer understands Unicode paths and environment variables — though keep in mind that it's probably interacting with tools that do not. It outputs arguments encoded in UTF-8 regardless of the system code page. Do not link a C runtime (CRT) in this configuration.

$ cc -Os -nostartfiles -o pkg-config win32_main.c

Or with MSVC:

$ cl /GS- /O2 /Os /Fe:pkg-config msvc_main.c

The Makefile documents compiler options for a more aggressively optimized GCC-based build.

Configuration

The default search path tries to be useful, but the pkgconfig directory locations are unpredictable and largely distribution-specific. Once known, use the macros below to hard code the correct paths into the binary. To double check your configuration, examine pc_path:

$ ./pkg-config --variable pc_path pkg-config

The pkg-config package is virtual and need not exist as an actual .pc file installed in the system. The pkg-config make target automatically grabs the search path from the system pkg-config, and so could then be a drop-in replacement for it.

$ make pkg-config   # grabs system's pkg-config search path

Generic configuration options

The "generic" platform has several compile time configuration parameters. Each must be formatted as a C string with quotes. Relative paths will be relative to the run-time working directory, not the installation prefix, which is usually not useful.

  • PKG_CONFIG_PATH: Like the run-time environment variable, but places these additional paths ahead of the standard search locations. If the standard search locations are insufficient, this is probably what you want.

  • PKG_CONFIG_PREFIX (default "/usr"): The default prefix for the four standard search locations.

  • PKG_CONFIG_LIBDIR: Like the run-time environment variable, but fully configures the fixed, static search path. If set, PKG_CONFIG_PREFIX is not used. If the environment variable is set at run time, it replaces this path.

  • PKG_CONFIG_DEFINE_PREFIX: Sets the default for --define-prefix / --dont-define-prefix. Defaults to true on Windows, false otherwise. You probably want the default.

  • PKG_CONFIG_SYSTEM_INCLUDE_PATH: Like the run-time environment variable but sets the static default.

  • PKG_CONFIG_SYSTEM_LIBRARY_PATH: Like the run-time environment variable but sets the static default.

Examples:

$ cc -DPKG_CONFIG_PREFIX="\"$HOME/.local\"" ...
$ cc -DPKG_CONFIG_PATH="\"$HOME/.local/lib/pkgconfig\"" ...

Windows configuration options

The "win32" platform always searches relative to pkg-config.exe since it's essentially the only useful behavior. It is rare for anything aside from system components to be at compile-time-known absolute paths. The default configuration assumes pkg-config resides in bin/ adjacent to lib/ and share/ — a typical sysroot.

  • PKG_CONFIG_PREFIX: A relative component placed before the "standard" search paths (lib/pkgconfig, share/pkgconfig). Either slash or backslash may be used to separate path components, but slash reduces problems.

Example, if just outside a sysroot which identifies the architecture:

$ gcc -DPKG_CONFIG_PREFIX=\"/$(gcc -dumpmachine)\" ...

While both are supported, u-config prefers slashes over backslashes in order to reduce issues involving backslash as a shell metacharacter.

libc-free Linux configuration options

linux_*_main.c makes direct Linux system calls using assembly and does not require libc. It compiles to a ~20kB static executable that will work on any Linux distribution. It supports these configuration macros with the same behavior as the generic platform.

  • PKG_CONFIG_LIBDIR
  • PKG_CONFIG_SYSTEM_INCLUDE_PATH
  • PKG_CONFIG_SYSTEM_LIBRARY_PATH

Debugging

Suggested debug build, intended to be run under a debugger:

$ cc -g3 -Wall -Wextra -Wconversion -Wno-sign-conversion \
     -fsanitize=undefined -fsanitize-trap PLATFORM_main.c

Enabling Undefined Behavior Sanitizer also enables assertions.

Test suite

The test suite is a libc-based platform layer and runs u-config through its entry point in various configurations on a virtual file system. Either build and run test_main.c as a platform, or use the suggested test configuration in the Makefile (set EXE=.exe on Windows):

$ make check

Fuzz testing

fuzz_main.c is a platform layer implemented on top of AFL++. Fuzzer input is supplied through a virtual file, and it exercises .pc parsing, variable expansion, and output processing. Its header documents suggested usage.