Best practices for linking with C library?


#1

I’ve got a C library (libseccomp) that I’d like to use with my bigbro crate. It works fine on my own machine, but now that I’ve introduced this dependency, anyone installing it on linux needs to run apt install libseccomp-dev or the equivalent, which is a nuisance. I know that the git2 crate builds the git2 library from source if it is not installed. Should I do something similar? Looking at the build.rs from git2 is not encouraging: it looks rather complicated. But then, libgit2 itself has dependencies, where libseccomp is basically self contained.

Any suggestions how to best handle this? I think the best plan is to build from source when it’s not available, but I feel daunted by dealing with that. So suggestions for how to write a simple build.rs (or pointers to simpler examples than that of git2) will also be appreciated!


#2

In Rust generally all system-level dependencies are handled by dedicated *-sys crates.

There is one for libseccomp already: https://crates.io/crates/seccomp-sys

If you don’t want to require users to have the library installed, then building bundled version (in the -sys crate) and linking statically is the way to go. It is a bit of a hassle.


#3

The question involves how that crate should behave. Should it simply fail to build as it currently does, if libseccomp is not installed? Or should it behave like libgit2-sys does and compile the library from source?

EDIT: @kornel I hope this having somehow failed to read the last paragraph of your reply. Sounds to me like building is the way to go, since otherwise using the create becomes a total pain. I even considered simply using seccomp-bpf without libseccomp. The raw API is not that bad and libseccomp don’t really gain me much (the portability across platforms is balanced by the fact that I use ptrace anyhow and this need to know the system call registers for any architecture I support anyhow). But it’s probably harder to use seccomp-bpf without the library than bundling the library.


#4

If the dependency is a pain, and you want users not to worry about it, then use seccomp-sys and improve that crate build static lib from source if needed.


#5

Any suggestions for the best way to build from source? As I see it, build.rs could either (a) download a source tarball, (b) git clone a repository, or © use a git subrepo (which is what the git2-sys crate does).

I lean towards downloading the tarball, since that will only require using make, rather than also requiring autoconf (and maybe automake?). So now I need build.rs to (1) download the tarball (presumably from pure rust), (2) untar it (presumably with std::process::Command and tar xzf), (3) run ./configure with the right flags keeping cross-compiling in mind, (4) run make, (5) emit the right stuff to stdout that will tell cargo where to look for the static library.

Or is there a crate that might make this easier? I’ve used the gcc crate, but so far as I know it doesn’t handle makefiles or suchlike, and I’d prefer to avoid deciphering the upstream library’s build process to translate their makefile into rust.

Any suggestions?


#6

Depends on how large and complex the dependency is.

If the dependency is small and license-compatible consider bundling a copy of the files (a git submodule will end up being packaged as regular files in your crate). This way your package will build offline, won’t need to depend on curl/tar, and you won’t have to deal with complexity of failed and partial downloads.

If you want cross-compilation and other details of the build process to work, then make alone is unlikely to work. You’ll need at least need to run ./configure with appropriate target.

OTOH if you avoid it and use gcc-rs to compile sources it will handle most of Rust-specific concerns for you.

I don’t know about libseccomp specificially, but for a few libraries I’ve packaged the build process turned out to be needlessly complicated, and just giving all C files to gcc-rs, along with a few flags (copied from make -n) worked out just fine.


#7

The source is about half a megabyte, so presumably using a git submodule wouldn’t be a bad plan. Currently I’m just downloading it, running configure and then using gcc-rs to build it. It’s more than a little ugly: