Hi, I am asking for advice how to deploy a rust application that needs shared libraries. My workflow often is as following:
write rust sys crate with binding for a given shared library, e.g. a *.so file. This file is contained in the git repo
write application that uses this sys crate as dependency
deploy application as debian package with cargo-deb
What is a good way to deploy the shared library? If I have the file, I can deploy it with the debian package but I do not see how I can access the file that is contained in the sys crate. I could copy it to the application directory, but whenever I update the shared library in the syscrate I would have to update the .so file in the application. Is there a way to access the dependency repos folder in the application build.rs, so I can always deploy the file from the sys crate?
Unfortunately, .so files aren't handled nicely in Cargo, and there's no good solution to this.
Sys crates are not supposed to install anything outside of the build. Usually they find pre-installed libraries on the system, so the distribution problem depends on where your system gets libraries from. In Debian, that would be simply a completely separate deb package, and cargo deb should be able to guess Depends directive for it.
If you're building a custom .so file in the -sys crate, then unfortunately Cargo doesn't fit that use-case. Cargo would like you to place built files in OUT_DIR location, but that location doesn't have a stable path, and is a pain to discover programmatically. Build scripts don't have a reliable information about the target-dir either, so if you try to place the .so file yourself in target/, it may fail when there's a custom target-dir config, or the dependency is built as part of a workspace.
To expose an .so file "nicely" from a sys crate, you could add links attribute, which despite the name, doesn't link anything and emit cargo::metadata=path={}", OUT_DIR.join("file.so")), which would set DEP_<links name>_path for other parent dependencies allowing them to find the path file and copy it somewhere. But the DEP_ env vars are still only visible inside Cargo's build scripts, so this only pushes the problem higher up the dependency tree, and some crate will have to have a custom hacky solution where it puts the .so file.
You may also need to adjust rpath of the .so file when you package it up, and then add it to assets in cargo-deb's config.
Thanks for sharing your experience and for providing cargo deb. Since the application is a leaf in the dependency tree and is already a workspace (the sys-crate is not a sub crate), saving outside of OUT_DIR could be acceptable.
The thing is, that updates of those shared libraries are quite rare. It could be ok to do it by hand and only check / error if the library is not the expected one. Is there an elegent way to do at least that? I was thinking something like this:
calculate hash and generate check function in build script of sys crate
have a test in application project that fails if library has different hash
it would be nice to have a test at runtime as well, is that possible?
If it rarely changes, maybe you could treat it like a 3rd party library, and make a separate .deb package for it?
Hashing and comparison are doable, but you'd have to do it entirely in custom code in build scripts and tests. This may be complicated by the fact that so lib can be in different locations while in a workspace vs after .deb install.
Cargo doesn't provide such checks for you (it tracks versions and checksums of Rust crates, but that goes back to all the problems of having a so lib in a crate).
I can make it a debian package and probably it is a good idea in general. Would you then provide the debian package in the sys crate or have it completely seperate from rust? Is there a simple tool that helps with this packaging of installing file similar to what cargo deb does?
But I don’t see how that helps with the problem of having the correct version of shared library in the application after an update. I would need to update the `depends` in the application cargo.toml by hand right?
I think I know all options by now and probably no option is really the best. I simply try and see how it goes.