static_vcruntime/lib.rs
1//! Statically link the VCRuntime while dynamically linking the UCRT.
2//! This only applies when using the MSVC toolchain.
3//!
4//! By default, Rust requires programs to deploy `vcruntime140.dll`
5//! (or equivalent) when redistributing binaries.
6//! You can use the `-C target-feature=+crt-static` rustc flag to statically link it
7//! but that also statically links the Universal CRT.
8//! The Universal CRT is a component of Windows so can always be dynamically linked.
9//!
10//! See [Details] for more information.
11//!
12//! # Usage
13//!
14//! Add this to your `Cargo.toml`:
15//!
16//! ```toml
17//! [build-dependencies]
18//! static_vcruntime = "3.0"
19//! ```
20//!
21//! And then in your [build script] you can call [`static_vcruntime::metabuild`] like so:
22//!
23//! ```rust,ignore
24//! fn main() {
25//! static_vcruntime::metabuild();
26//! }
27//! ```
28//!
29//! For the best compatibility it is recommended that you also set the `target-feature` flag to `+crt-static`.
30//! In the same directory as your Cargo.toml, create a folder called `.cargo`. In that folder create the file `config.toml` and add the following:
31//!
32//! ```toml
33//! # In .cargo/config.toml
34//! [target.'cfg(all(windows, target_env = "msvc"))']
35//! rustflags = ["-C", "target-feature=+crt-static"]
36//! ```
37//!
38//! # Details
39//!
40//! What we call the "CRT" is actually three components:
41//! - The C startup files. This provide startup/shutdown code.
42//! - The VC runtime. In rust this is mainly used for panic handling but also provides some fundamental functions such as `memcpy`.
43//! - The Universal CRT (aka UCRT). This is where most of the C standard library lives.
44//!
45//! Each of these can be linked either dynamically or statically, however it is usually required that if one is linked statically then they are all linked statically (and ditto for dynamica linking).
46//! There is one exceptions.
47//! If the VC runtime and C startup files are linked statically then the UCRT can be linked dynamically.
48//!
49//! The following table summarises these options:
50//!
51//! | | C startup | VC runtime | Universal CRT |
52//! | ------------------ | ---------- | ---------- | ------------- |
53//! | Default | dynamic | dynamic | dynamic |
54//! | `+crt-static` | static | static | static |
55//! | `static_vcruntime` | static | static | dynamic |
56//!
57//! # Issues
58//!
59//! If you have problems then you may need to clean the build directory before rebuilding:
60//!
61//! ```text
62//! cargo clean
63//! ```
64//!
65//! It is possible that some C/C++ dependencies may not work in this configuration.
66//!
67//! Note that Microsoft recommends all the runtime libraries be dynamically linked (which is the default).
68//!
69//! [Details]: #details
70//! [build script]: https://doc.rust-lang.org/cargo/reference/build-scripts.html
71//! [`static_vcruntime::metabuild`]: metabuild
72
73use std::env;
74
75/// Use dynamically linked ucrt with a statically linked vcruntime.
76///
77/// This must be called from a [build script], like so:
78///
79/// ```rust,ignore
80/// // build.rs
81/// fn main() {
82/// static_vcruntime::metabuild();
83/// }
84/// ```
85///
86/// You can restrict it to only be enabled on specific profiles, such as `release`.
87///
88/// ```rust,ignore
89/// fn main() {
90/// if std::env::var("PROFILE").as_deref() == Ok("release") {
91/// static_vcruntime::metabuild();
92/// }
93/// }
94/// ```
95///
96/// [build script]: https://doc.rust-lang.org/cargo/reference/build-scripts.html
97pub fn metabuild() {
98 // Early exit if not msvc
99 let target = env::var("CARGO_CFG_TARGET_ENV").expect("`CARGO_CFG_TARGET_ENV` environment variable is missing. Ensure you're using `static_vcruntime` in a build script");
100 if target != "msvc" {
101 return;
102 }
103
104 // Disable conflicting libraries
105 println!("cargo:rustc-link-arg=/NODEFAULTLIB:libvcruntimed.lib");
106 println!("cargo:rustc-link-arg=/NODEFAULTLIB:vcruntime.lib");
107 println!("cargo:rustc-link-arg=/NODEFAULTLIB:vcruntimed.lib");
108 println!("cargo:rustc-link-arg=/NODEFAULTLIB:libcmtd.lib");
109 println!("cargo:rustc-link-arg=/NODEFAULTLIB:msvcrt.lib");
110 println!("cargo:rustc-link-arg=/NODEFAULTLIB:msvcrtd.lib");
111 println!("cargo:rustc-link-arg=/NODEFAULTLIB:libucrt.lib");
112 println!("cargo:rustc-link-arg=/NODEFAULTLIB:libucrtd.lib");
113 // Set the libraries we want.
114 println!("cargo:rustc-link-arg=/DEFAULTLIB:libcmt.lib");
115 println!("cargo:rustc-link-arg=/DEFAULTLIB:libvcruntime.lib");
116 println!("cargo:rustc-link-arg=/DEFAULTLIB:ucrt.lib");
117}