Home > Enterprise >  is it possible to make cargo install installing dependent cdylib dlls
is it possible to make cargo install installing dependent cdylib dlls

Time:12-13

I'm on windows and have

a Rust cdylib

[package]
name = "test1dll"
[lib]
crate-type = ["cdylib"]

and a Rust binary which depends on that dll

[package]
name = "test1"
[dependencies]
test1dll = { path = "..." }

and am looking for a way to make

cargo install

install both, the test1.exe and test1dll.dll, or, if it's not possible with cargo, for a not too complicated alternative.

The library needs to be a dll so i can LoadLibrary it. A static library won't work.

I can see the binary and the dll in the target/*/deps directory but cargo install only installs the executable.

Running cargo install from the dll project itself gives a

error: no packages found with binaries or examples.

which matches with the documentation

This command manages Cargo's local set of installed binary crates. Only packages which have executable [[bin]] or [[example]] targets can be installed

but since it's a useful scenario to deploy a binary together with a dll on windows and Rust even offers the possibility to compile cdylib targets into dlls i'm wondering if there's a way doing this with cargo. Unfortunately i'm new to Rust and may be searching using the wrong keywords.

I thought this might come close but the runtime is explicitly excluded:

Note that cargo only supplies these dependencies when building your crate. If your program or library requires artifacts at runtime, you will still need to handle that yourself by some other means.

And either i use it wrong or cdylib-plugin also doesn't help with installing the dll.

CodePudding user response:

Rust is meant to compile to a single program, which is one of the arguments that C users use against Rust. Rust binaries are much larger because all the libraries are statically linked instead of dynamically linked. Is there a specific reason that you need to dynamically link instead of statically link?

The target/*/deps folder contains the compiled libraries that are statically linked to your binary.

CodePudding user response:

Installing a dll together with a exe with cargo is indeed not possible. But it's possible to add the dll binary stream as a blob to the exe and recreate the dll on first run. The are two drawbacks:

  1. cargo uninstall won't uninstall the dll
  2. when using a sub-package to create the dll during build, the package cannot be published because

Regardless of whether exclude or include is specified, the following files are always excluded: Any sub-packages will be skipped (any subdirectory that contains a Cargo.toml file).

This could be solved by creating the Cargo.toml of the sub-package during build.rs of the main package.

The basic process is like this:

  1. create a sub-package defining the cdylib
  2. create a build.rs in the main package which
  • invokes cargo build for the sub-package

:

std::process::Command::new("cargo")
    .stdout(std::process::Stdio::inherit())
    .current_dir(sub_package_dir)
    .arg("build")
    .arg("--release")
    .arg("-vv")
    .status()
    .unwrap();
  • and then creates a rust source on the fly containing the dll byte stream and a function to access it

:

let dll_src_str = format!(
    "const DLL_BIN: &[u8] = include_bytes!(r\"{}\");
    pub fn dll_bin() -> &'static [u8] {{ DLL_BIN }}",
    dll_path);
let mut dll_src_file =
    std::fs::File::create(dll_src_path).unwrap();
use std::io::prelude::*;
let write_ok = dll_src_file.write_all(dll_src_str.as_bytes());

After the build.rs has finished executing, the build process will find and compile the created additional source.

  1. create the dll file during program execution

:

pub fn create_dll() -> std::io::Result<()> {
    let dll_bytes = dll_contents::dll_bytes();
    let mut dll_file = std::fs::File::create(dll_path).unwrap();
    use std::io::prelude::*;
    return dll_file.write_all(dll_bytes);
}
  • Related