probe-c-api 0.1.0

Library to probe C interface types and constants
Documentation

probe-c-api

Build Status

The purpose of probe-c-api is to deal with cases where the API of a C library is known well enough to write C code using the library, but not enough is known about the interface to write bindings in another language, such as Rust.

In such cases, there are three possible approaches:

  1. Acquire the library and write bindings manually for that specific library. This works best if one knows a priori that the ABI for a library is very stable, whether that is true by design, or simply because the users of a set of bindings are likely to all use one particular version in practice.

    This approach generally fails if the bindings must have many variants, e.g. to account for different platforms, C compilers, configuration options, and/or library versions.

  2. Parse the C header file(s) and extract any information needed. This is the approach used by bindgen, for instance. This is highly effective for getting the correct information for a particular machine that will be running software using the target C library.

    One potential problem with this approach is that it tends to be difficult to implement. To be both generic and robust, such a system has to include much of the functionality of a full C compiler, including the preprocessor, parser, and some pieces of the type checker. Perhaps more seriously, such a system has to replicate non-portable properties of a C compiler, e.g. platform-specific integer sizes, or non-standard attributes and other syntax extensions.

    Finally, a naive analysis of header files may include macros, types, or objects in the generated bindings that are not actually in the documented API, leading to unstable items and quasi-private implementation details being exposed in the output.

  3. Use the library to compile, then optionally link and run, short test functions written in C. By monitoring the exit status and output of each step, details of the C library can be probed. This is the approach of probe-c-api, as well as of many existing build system toolkits, such as autotools and CMake.

    The advantage of this approach is that it checks the actual behavior of programs compiled against the target library with a C compiler, but in a way that's guided by knowledge of the documented API, which is input by the developer of the bindings. This results in a "best of both worlds" situation where the ability to create correct bindings is not dependent on hard-coded knowledge about the library, platform, or C compiler.

    Perhaps the biggest disadvantage of this approach is that it does not work well for cross-compilation. In order to probe a C library, it is usually necessary to be able to run programs linked against it locally.

    Another disadvantage of this approach is that the probe has to rely on many "moving parts". Specifically, it has to have a C compiler available, be able to invoke that compiler with the correct flags to build a running program, be able to run that program in a compatible environment (e.g. dynamically linking against the correct libraries), and get the output and return status of child processes from the OS.

    While this may not seem like such a high bar to overcome, it is not unheard of for such testing to fail on unusual or novel platforms, or with rare C compilers. For this reason, probe-c-api does not invoke a C compiler directly, but rather requires the user to provide a method to convert a C source file into an object file. Similarly, it does not invoke the program produced directly, but rather requires the user to provide a method that can invoke the program and which returns the output.

    This will hopefully produce a system that's more flexible and modular than systems like autotools are (the way most people use them). A library that wraps a C compiler can handle compilation, probe-c-api can handle auto-generating test programs and interpreting the results, standard libraries can handle interaction with the OS, and the user can handle any quirks relevant to their particular case. In particular, the user can handle cross-compilation by running test programs outside of the build platform, by sending the executable over the network, to a machine attached as a peripheral device, or to an emulator.

    Not coincidentally, by requiring the user to specify how the probe should interact with the C compiler and target platform, this design reduces the scope of probe-c-api, making it much simpler to implement as a standalone crate.