dcreager.net

Build script links

2023-11-15

You have an “upstream” crate that wraps a C library and provides Rust bindings for it. You also have a “downstream” crate that wraps a different C library. The downstream C library depends on the upstream C library — for example, to compile the downstream C code, you need access to the header files of the upstream C code.

The problem: How do you tell the compiler where to find the upstream crate's header files when compiling the downstream library?

The answer: the ‘package.links’ field and “build script metadata”. The upstream library needs to set ‘package.links’ in its ‘Cargo.toml’. You can choose any value (which I'll call ‘LINK_NAME’), but convention is that it's the name of the library that the Rust crate is providing bindings for. In the upstream library's ‘build.rs’, you output additional cargo metadata:

cargo:VAR_NAME=VALUE

Then, in your downstream library, this custom metadata will be available in an environment variable that you can read: ‘DEP_${LINK_NAME}_${VAR_NAME}’.

For this compilation example, you output a carefully named ‘include’ metadata variable, whose value is the actual location of the upstream header files. In your downstream ‘build.rs’, you read the ‘DEP_${LINK_NAME}_INCLUDE’ env var, and add that to the compiler's include search path.

The ‘links’ manifest key [The Cargo book]

An example

I encountered this situation specifically with the upstream ‘lua’ crate, which provides Rust bindings for the Lua programming language, and the downstream ‘ltreesitter’ crate, which provides Lua bindings to tree-sitter, which I wanted to make available to a Lua environment managed by Rust code.

‘lua’ crate

‘ltreesitter’ Lua package

My ‘ltreesitter’ fork with Rust bindings

I had to patch the ‘lua’ crate to output the custom metadata, and then use the resulting env var in the ‘ltreesitter’ build script.

Output the custom metadata upstream

Use the custom metadata downstream

..