How should Windows DLLs be handled in build.rs?

Hey folks,

I'm a Linux developer with very little Windows development experience, trying to upstream some changes to a Windows-only crate. The author currently has users copying .lib files into Rust's system-wide library path, then copying DLLs to the location of Cargo.toml. Since the sys library and its dependent are very small, I've set about using cc and building the sources directly.

Things seem to build, but I have to manually place DLL files in the directory alongside Cargo.toml. These DLLs are small, unlikely to be installed system-wide, shipped with the dependent library, and most consumers of the library just include them alongside their executable.

I'm wondering if, or how, build.rs should handle these DLLs? My understanding of DLLs is that they can't be statically linked into binaries. Is that so? The dependencies are shipped as both 32 and 64-bit, so I can probably copy them alongside whatever executable gets built based on the build target, but I'm wondering if that's how to go about it, or if there is a better way? I also want to ensure that they appear where they need to regardless of what type of executable is being built--main binary, an example, etc.

Thanks for any help.

I'm not sure how much help I can be but here's some quick thoughts.

Yes DLLs are dynamic libraries that must be shipped with the binary and are loaded at runtime. Static libraries in Windows are .lib files but DLLs usually come with a minimal .lib file called an "import library" that merely contains code for dynamically loading the symbols from the DLL and aren't full static libraries themselves. This sometimes confuses people.

The alternative to DLLs, if you're building the libraries from source, is to build them as static libraries and then link those instead of copying DLLs. Hopefully the libraries will provide documentation or a build option for creating static libraries for Windows. If not it still could be done (licenses allowing) but the details may vary depending on the build environment.

To add to what chrisd said, it is extremely common for Windows programs of any significant size to provide copies of the DLLs that the executable relies on bundled into the directory structure of the program. Indeed, it seems to me that many executables (even for notable non-free software) are little more than a fairly thin wrapper around one or more DLLs.

So, it should be fine to have DLLs copied into a particular directory - though if at all possible I would recommend looking at automating the process in some fashion.

GOt it, thanks. Here are some additional details.

  • Here is the library I'm trying to
    build
    .
    These are the DLLs I
    need available to any Rust code using that library.

  • I don't know that I have the capacity to link those in statically,
    but I'd appreciate feedback from anyone more familiar with how Windows
    binaries work.

  • I have to place those DLLs alongside examples in
    target/debug/examples to make them work with my example. Assuming I
    vendor DLLs alongside the source in my sys crate, is there any way to
    copy them to a final location where they'll transparently work alongside
    executables and binaries using this crate? Can I do that in build.rs? I
    don't think so but thought I'd ask.

My goal is a tolk-sys crate that compiles the code and makes the
required DLLs available to any libraries/executables that need it. Thus
far I have the first bit working, but am unsure how to correctly and
automatically place the DLLs.

Thanks for any help.

I think cargo should be able to run binaries depending on dynamic libraries generated by a build script, as long as it supplies the rustc-link-search key. Does this work for you?

Deploying the app (i.e. copying all the required libraries and binaries in a separate place) should be a separate step, as it's not the job of the build script (e.g. build script doesn't know the path to the binary that will be produced).

I'm not sure that it is possible to link them in statically. I don't know how you would make that happen, at least.

As for distribution, this isn't something I know too much about (I have never had to deal with distributing a non-trivial program for Windows), but I agree with Riateche that supplying the DLLs is probably outside the scope of the build process.

My best suggestion would be to decide to have a folder in a fixed location relative to the position of your executable (probably in a new folder one layer down from the top directory), and look there no matter what. When you come to distribute the program, the approach you want to take towards ensuring the DLLs end up in the right place depends on your target audience:

  • If they're tech-savvy, e.g. developers, you can probably just tell them that they need to ensure DLLs X and Y are in folder Z.
  • If they're not tech-savvy, then you're probably best off either looking to create a .msi Windows installer file. Or, failing that a Powershell script that puts things into the right places (you perhaps could about write it as a Bash script, and then plonk it in a .ps1 file, since Powershell aliases a lot of the common Bash commands to Powershell commands).

Did that help any, or am I misunderstanding what you are asking?

1 Like

Thanks folks. I don't know if this is a horrible idea--time will
tell--but for the moment I'm copying the vendored DLLs into, for example
in debug builds, target/debug/ and target/debug/examples. The script
should correctly account for release builds as well. My expectation is
that folks wanting to install the app/libraries need only copy *.exe and
*.dll from target/release, though in all likelihood they'll have their
installer generator scripts do it for them.

My intent is mainly to ease the creation of installers, GitHub/GitLab
release archives, etc. To that end, this should just create a directory
with binaries that link statically when possible, or colocate DLLs where
not. As of now, rm target/ and cargo run --example hello_world
builds my example, places the DLLs correctly, and runs it without issue.

Thanks again, everyone.

1 Like