embed_resource/lib.rs
1//! A [`Cargo` build script](http://doc.crates.io/build-script.html) library to handle compilation and inclusion of Windows
2//! resources in the most resilient fashion imaginable
3//!
4//! # Background
5//!
6//! Including Windows resources seems very easy at first, despite the build scripts' abhorrent documentation:
7//! [compile with `windres`, then make linkable with
8//! `ar`](https://github.com/nabijaczleweli/cargo-update/commit/ef4346c#diff-a7b0a2dee0126cddf994326e705a91ea).
9//!
10//! I was very happy with that solution until it was brought to my attention, that [MSVC uses something
11//! different](https://github.com/nabijaczleweli/cargo-update/commit/f57e9c3#diff-a7b0a2dee0126cddf994326e705a91ea),
12//! and now either `windres`-`ar` combo or `RC.EXE` would be used, which was OK.
13//!
14//! Later it transpired, that [MSVC is even more incompatible with everything
15//! else](https://github.com/nabijaczleweli/cargo-update/commit/39fa758#diff-a7b0a2dee0126cddf994326e705a91ea)
16//! by way of not having `RC.EXE` in `$PATH` (because it would only be reasonable to do so),
17//! so another MSVC artisan made the script [find the most likely places for `RC.EXE` to
18//! be](https://github.com/nabijaczleweli/cargo-update/pull/22), and the script grew yet again,
19//! now standing at 100 lines and 3.2 kB.
20//!
21//! After [copying the build script in its
22//! entirety](https://github.com/thecoshman/http/commit/98205a4#diff-a7b0a2dee0126cddf994326e705a91ea)
23//! and realising how error-prone that was, then being [nudged by
24//! Shepmaster](https://chat.stackoverflow.com/transcript/message/35378953#35378953)
25//! to extract it to a crate, here we are.
26//!
27//! # Usage
28//!
29//! For the purposes of the demonstration we will assume that the resource file's name
30//! is "checksums.rc", but it can be any name relative to the crate root.
31//!
32//! In `Cargo.toml`:
33//!
34//! ```toml
35//! # The general section with crate name, license, etc.
36//! build = "build.rs"
37//!
38//! [build-dependencies]
39//! embed-resource = "3.0"
40//! ```
41//!
42//! In `build.rs`:
43//!
44//! ```rust,no_run
45//! extern crate embed_resource;
46//!
47//! fn main() {
48//! embed_resource::compile("checksums.rc", embed_resource::NONE).manifest_optional().unwrap();
49//! // or
50//! embed_resource::compile("checksums.rc", &["VERSION=000901"]).manifest_required().unwrap();
51//! // or
52//! embed_resource::compile("checksums.rc", embed_resource::ParamsMacrosAndIncludeDirs(
53//! &["VERSION=000901"], &["src/include"])).manifest_required().unwrap();
54//! // or
55//! embed_resource::compile("checksums.rc", embed_resource::ParamsIncludeDirs(
56//! &["src/include"])).manifest_required().unwrap();
57//! }
58//! ```
59//!
60//! Use `.manifest_optional().unwrap()` if the manifest is cosmetic (like an icon).<br />
61//! Use `.manifest_required().unwrap()` if the manifest is required (security, entry point, &c.).
62//!
63//! Parameters that look like `&["string"]` or `embed_resource::NONE` in the example above
64//! can be anything that satisfies `IntoIterator<AsRef<OsStr>>`:
65//! `&[&str]`, of course, but also `Option<PathBuf>`, `Vec<OsString>`, `BTreeSet<&Path>`, &c.
66//!
67//! ## Errata
68//!
69//! If no `cargo:rerun-if-changed` annotations are generated, Cargo scans the entire build root by default.
70//! Because the first step in building a manifest is an unspecified C preprocessor step with-out the ability to generate the
71//! equivalent of `cc -MD`, we do *not* output said annotation.
72//!
73//! If scanning is prohibitively expensive, or you have something else that generates the annotations, you may want to spec the
74//! full non-system dependency list for your manifest manually, so:
75//! ```rust,no_run
76//! println!("cargo:rerun-if-changed=app-name-manifest.rc");
77//! embed_resource::compile("app-name-manifest.rc", embed_resource::NONE);
78//! ```
79//! for the above example (cf. [#41](https://github.com/nabijaczleweli/rust-embed-resource/issues/41)).
80//!
81//! # Cross-compilation
82//!
83//! It is possible to embed resources in Windows executables built on non-Windows hosts. There are two ways to do this:
84//!
85//! When targetting `*-pc-windows-gnu`, `*-w64-mingw32-windres` is attempted by default, for `*-pc-windows-msvc` it's `llvm-rc`,
86//! this can be overriden by setting `RC_$TARGET`, `RC_${TARGET//-/_}`, or `RC` environment variables.
87//!
88//! When compiling with LLVM-RC, an external C compiler is used to preprocess the resource,
89//! preloaded with configuration from
90//! [`cc`](https://github.com/alexcrichton/cc-rs#external-configuration-via-environment-variables).
91//!
92//! ## Migration
93//! ### 2.x
94//!
95//! Add `embed_resource::NONE` as the last argument to `embed_resource::compile()` and `embed_resource::compile_for()`.
96//!
97//! ### 3.x
98//!
99//! Add `.manifest_optional().unwrap()` or `.manifest_required().unwrap()` to all [`compile()`] and `compile_for*()` calls.
100//! `CompilationResult` is `#[must_use]` so should be highlighted automatically.
101//!
102//! Embed-resource <3.x always behaves like `.manifest_optional().unwrap()`.
103//!
104//! # Credit
105//!
106//! In chronological order:
107//!
108//! [@liigo](https://github.com/liigo) -- persistency in pestering me and investigating problems where I have failed
109//!
110//! [@mzji](https://github.com/mzji) -- MSVC lab rat
111//!
112//! [@TheCatPlusPlus](https://github.com/TheCatPlusPlus) -- knowledge and providing first iteration of manifest-embedding code
113//!
114//! [@azyobuzin](https://github.com/azyobuzin) -- providing code for finding places where RC.EXE could hide
115//!
116//! [@retep998](https://github.com/retep998) -- fixing MSVC support
117//!
118//! [@SonnyX](https://github.com/SonnyX) -- Windows cross-compilation support and testing
119//!
120//! [@MSxDOS](https://github.com/MSxDOS) -- finding and supplying RC.EXE its esoteric header include paths
121//!
122//! [@roblabla](https://github.com/roblabla) -- cross-compilation to Windows MSVC via LLVM-RC
123//!
124//! # Special thanks
125//!
126//! To all who support further development on [Patreon](https://patreon.com/nabijaczleweli), in particular:
127//!
128//! * ThePhD
129//! * Embark Studios
130//! * Lars Strojny
131//! * EvModder
132
133#![allow(private_bounds)]
134
135
136#[cfg(any(not(target_os = "windows"), all(target_os = "windows", target_env = "msvc")))]
137extern crate cc;
138#[cfg(not(target_os = "windows"))]
139extern crate memchr;
140#[cfg(all(target_os = "windows", target_env = "msvc"))]
141extern crate vswhom;
142#[cfg(all(target_os = "windows", target_env = "msvc"))]
143extern crate winreg;
144extern crate rustc_version;
145extern crate toml;
146
147#[cfg(not(target_os = "windows"))]
148mod non_windows;
149#[cfg(all(target_os = "windows", target_env = "msvc"))]
150mod windows_msvc;
151#[cfg(all(target_os = "windows", not(target_env = "msvc")))]
152mod windows_not_msvc;
153
154#[cfg(not(target_os = "windows"))]
155use self::non_windows::*;
156#[cfg(all(target_os = "windows", target_env = "msvc"))]
157use self::windows_msvc::*;
158#[cfg(all(target_os = "windows", not(target_env = "msvc")))]
159use self::windows_not_msvc::*;
160
161use std::{env, fs};
162use std::ffi::OsStr;
163use std::borrow::Cow;
164use std::process::Command;
165use toml::Table as TomlTable;
166use std::fmt::{self, Display};
167use std::path::{Path, PathBuf};
168
169
170/// Empty slice, properly-typed for [`compile()`] and `compile_for*()` to mean "no additional parameters".
171///
172/// Rust helpfully forbids default type parameters on functions, so just passing `[]` doesn't work :)
173pub const NONE: &[&OsStr] = &[];
174
175
176// This is all of the parameters and it's non-public:
177// the only way users can construct this is via From<Mi> (same as From<ParamsMacros>), From<ParamsIncludeDirs>,
178// and From<ParamsMacrosAndIncludeDirs>
179#[derive(PartialEq, Eq, Debug)] // only for tests
180struct ParameterBundle<Ms: AsRef<OsStr>, Mi: IntoIterator<Item = Ms>, Is: AsRef<OsStr>, Ii: IntoIterator<Item = Is>> {
181 macros: Mi,
182 include_dirs: Ii,
183}
184
185impl<Ms: AsRef<OsStr>, Mi: IntoIterator<Item = Ms>> From<Mi> for ParameterBundle<Ms, Mi, &'static &'static OsStr, &'static [&'static OsStr]> {
186 fn from(macros: Mi) -> Self {
187 ParamsMacros(macros).into()
188 }
189}
190
191/// Give this to [`compile()`] or `compile_for*()` to add some macro definitions (`-D`/`/D`).
192///
193/// Every value must be in the form `MACRO=value` or `MACRO`. An empty iterator is a no-op.
194pub struct ParamsMacros<Ms: AsRef<OsStr>, Mi: IntoIterator<Item = Ms>>(pub Mi);
195impl<Ms: AsRef<OsStr>, Mi: IntoIterator<Item = Ms>> From<ParamsMacros<Ms, Mi>> for ParameterBundle<Ms, Mi, &'static &'static OsStr, &'static [&'static OsStr]> {
196 fn from(macros: ParamsMacros<Ms, Mi>) -> Self {
197 ParamsMacrosAndIncludeDirs(macros.0, NONE).into()
198 }
199}
200
201/// Give this to [`compile()`] or `compile_for*()` to add include directories (`-I`/`/I`).
202///
203/// An empty iterator is a no-op.
204pub struct ParamsIncludeDirs<Is: AsRef<OsStr>, Ii: IntoIterator<Item = Is>>(pub Ii);
205impl<Is: AsRef<OsStr>, Ii: IntoIterator<Item = Is>> From<ParamsIncludeDirs<Is, Ii>>
206 for ParameterBundle<&'static &'static OsStr, &'static [&'static OsStr], Is, Ii> {
207 fn from(include_dirs: ParamsIncludeDirs<Is, Ii>) -> Self {
208 ParamsMacrosAndIncludeDirs(NONE, include_dirs.0).into()
209 }
210}
211
212/// Give this to [`compile()`] or `compile_for*()` to add some macro definitions (`-D`/`/D`) and include directories
213/// (`-I`/`/I`).
214///
215/// Every macro value must be in the form `MACRO=value` or `MACRO`.
216///
217/// Empty iterators are no-ops.
218pub struct ParamsMacrosAndIncludeDirs<Ms: AsRef<OsStr>, Mi: IntoIterator<Item = Ms>, Is: AsRef<OsStr>, Ii: IntoIterator<Item = Is>>(pub Mi, pub Ii);
219impl<Ms: AsRef<OsStr>, Mi: IntoIterator<Item = Ms>, Is: AsRef<OsStr>, Ii: IntoIterator<Item = Is>> From<ParamsMacrosAndIncludeDirs<Ms, Mi, Is, Ii>>
220 for ParameterBundle<Ms, Mi, Is, Ii> {
221 fn from(maid: ParamsMacrosAndIncludeDirs<Ms, Mi, Is, Ii>) -> Self {
222 Self {
223 macros: maid.0,
224 include_dirs: maid.1,
225 }
226 }
227}
228
229
230/// https://101010.pl/@nabijaczleweli/115226665478478763
231#[cfg(test)]
232#[allow(dead_code)]
233fn compat_3_0_5() {
234 use std::collections::BTreeSet;
235
236 // these spellings of the macros argument taken from GitHub "embed_resource::" search
237 // and https://crates.io/crates/embed-resource/reverse_dependencies on 2025-09-18
238 let _ = compile("", std::iter::empty::<&str>());
239 let _ = compile("", None::<&str>);
240 let marcos = &[format!("VERSION_PATCH={}", env!("CARGO_PKG_VERSION_PATCH"))];
241 let _ = compile("", marcos);
242 let marcos = vec![format!("VERSION_PATCH={}", env!("CARGO_PKG_VERSION_PATCH"))];
243 let _ = compile("", marcos);
244
245 // these weren't
246 let _ = compile("", [""]);
247 let _ = compile("", &[""]);
248 let _ = compile("", vec![""]);
249 let _ = compile("", vec![Path::new("gaming=baming")].into_iter().collect::<BTreeSet<_>>());
250 let _ = compile("", vec![Path::new("gaming=baming").to_owned()].into_iter().collect::<BTreeSet<_>>());
251 let _ = compile("", [PathBuf::from("gaming=baming")].iter());
252 let _ = compile("", [PathBuf::from("gaming=baming")].iter().collect::<BTreeSet<_>>());
253
254 // this is new
255 let _ = compile("", ParamsIncludeDirs(&[Path::new("include_dir")]));
256 let _ = compile("", ParamsIncludeDirs([PathBuf::from("include_dir")]));
257 let _ = compile("", ParamsIncludeDirs(vec![Path::new("include_dir1"), Path::new("include_dir2")]));
258
259 let _ = compile("", ParamsMacrosAndIncludeDirs(NONE, NONE));
260 let _ = compile("", ParamsMacrosAndIncludeDirs([""], [""]));
261}
262
263#[test]
264fn argument_bundle_into() {
265 assert_eq!(ParameterBundle::from(NONE),
266 ParameterBundle {
267 macros: NONE,
268 include_dirs: NONE,
269 });
270 assert_eq!(ParameterBundle::from([""]),
271 ParameterBundle {
272 macros: [""],
273 include_dirs: NONE,
274 });
275
276 assert_eq!(ParameterBundle::from(ParamsMacros(NONE)),
277 ParameterBundle {
278 macros: NONE,
279 include_dirs: NONE,
280 });
281 assert_eq!(ParameterBundle::from(ParamsMacros([""])),
282 ParameterBundle {
283 macros: [""],
284 include_dirs: NONE,
285 });
286
287 assert_eq!(ParameterBundle::from(ParamsIncludeDirs(NONE)),
288 ParameterBundle {
289 macros: NONE,
290 include_dirs: NONE,
291 });
292 assert_eq!(ParameterBundle::from(ParamsIncludeDirs([""])),
293 ParameterBundle {
294 macros: NONE,
295 include_dirs: [""],
296 });
297
298 assert_eq!(ParameterBundle::from(ParamsMacrosAndIncludeDirs(NONE, NONE)),
299 ParameterBundle {
300 macros: NONE,
301 include_dirs: NONE,
302 });
303 assert_eq!(ParameterBundle::from(ParamsMacrosAndIncludeDirs([""], NONE)),
304 ParameterBundle {
305 macros: [""],
306 include_dirs: NONE,
307 });
308 assert_eq!(ParameterBundle::from(ParamsMacrosAndIncludeDirs(NONE, [""])),
309 ParameterBundle {
310 macros: NONE,
311 include_dirs: [""],
312 });
313 assert_eq!(ParameterBundle::from(ParamsMacrosAndIncludeDirs([""], [""])),
314 ParameterBundle {
315 macros: [""],
316 include_dirs: [""],
317 });
318}
319
320
321/// Result of [`compile()`] and `compile_for*()`
322///
323/// Turn this into a `Result` with `manifest_optional()` if the manifest is nice, but isn't required, like when embedding an
324/// icon or some other cosmetic.
325///
326/// Turn this into a `Result` with `manifest_required()` if the manifest is mandatory, like when configuring entry points or
327/// security.
328#[must_use]
329#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
330pub enum CompilationResult {
331 /// not building for windows
332 NotWindows,
333 /// built, linked
334 Ok,
335 /// building for windows, but the environment can't compile a resource (most likely due to a missing compiler)
336 NotAttempted(Cow<'static, str>),
337 /// environment can compile a resource, but has failed to do so
338 Failed(Cow<'static, str>),
339}
340impl CompilationResult {
341 /// `Ok(())` if `NotWindows`, `Ok`, or `NotAttempted`; `Err(self)` if `Failed`
342 pub fn manifest_optional(self) -> Result<(), CompilationResult> {
343 match self {
344 CompilationResult::NotWindows |
345 CompilationResult::Ok |
346 CompilationResult::NotAttempted(..) => Ok(()),
347 err @ CompilationResult::Failed(..) => Err(err),
348 }
349 }
350
351 /// `Ok(())` if `NotWindows`, `Ok`; `Err(self)` if `NotAttempted` or `Failed`
352 pub fn manifest_required(self) -> Result<(), CompilationResult> {
353 match self {
354 CompilationResult::NotWindows |
355 CompilationResult::Ok => Ok(()),
356 err @ CompilationResult::NotAttempted(..) |
357 err @ CompilationResult::Failed(..) => Err(err),
358 }
359 }
360}
361impl Display for CompilationResult {
362 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
363 f.write_str("embed-resource: ")?;
364 match self {
365 CompilationResult::NotWindows => f.write_str("not building for windows"),
366 CompilationResult::Ok => f.write_str("OK"),
367 CompilationResult::NotAttempted(why) => {
368 f.write_str("compilation not attempted: ")?;
369 if !why.contains(' ') {
370 f.write_str("missing compiler: ")?;
371 }
372 f.write_str(why)
373 }
374 CompilationResult::Failed(err) => f.write_str(err),
375 }
376 }
377}
378impl std::error::Error for CompilationResult {}
379
380macro_rules! try_compile_impl {
381 ($expr:expr) => {
382 match $expr {
383 Result::Ok(val) => val,
384 Result::Err(err) => return err,
385 }
386 };
387}
388
389
390/// Compile the Windows resource file and update the cargo search path if building for Windows.
391///
392/// On non-Windows non-Windows-cross-compile-target this does nothing, on non-MSVC Windows and Windows cross-compile targets,
393/// this chains `windres` with `ar`,
394/// but on MSVC Windows, this will try its hardest to find `RC.EXE` in Windows Kits and/or SDK directories,
395/// falling back to [Jon Blow's VS discovery script](https://pastebin.com/3YvWQa5c),
396/// and on Windows 10 `%INCLUDE%` will be updated to help `RC.EXE` find `windows.h` and friends.
397///
398/// `$OUT_DIR` is added to the include search path.
399///
400/// Note that this does *nothing* if building with rustc before 1.50.0 and there's a library in the crate,
401/// since the resource is linked to the library, if any, instead of the binaries.
402///
403/// Since rustc 1.50.0, the resource is linked only to the binaries
404/// (unless there are none, in which case it's also linked to the library).
405///
406/// `parameters` are a list of macros to define (directly or via [`ParamsMacros`]), in standard `NAME`/`NAME=VALUE` format,
407/// [`ParamsIncludeDirs`], or [`ParamsMacrosAndIncludeDirs`].
408///
409/// # Examples
410///
411/// In your build script, assuming the crate's name is "checksums":
412///
413/// ```rust,no_run
414/// extern crate embed_resource;
415///
416/// fn main() {
417/// // Compile and link checksums.rc
418/// embed_resource::compile("checksums.rc", embed_resource::NONE);
419/// }
420/// ```
421pub fn compile<T: AsRef<Path>,
422 Ms: AsRef<OsStr>,
423 Mi: IntoIterator<Item = Ms>,
424 Is: AsRef<OsStr>,
425 Ii: IntoIterator<Item = Is>,
426 P: Into<ParameterBundle<Ms, Mi, Is, Ii>>>(
427 resource_file: T, parameters: P)
428 -> CompilationResult {
429 let (prefix, out_dir, out_file) = try_compile_impl!(compile_impl(resource_file.as_ref(), parameters.into()));
430 let hasbins = fs::read_to_string("Cargo.toml")
431 .unwrap_or_else(|err| {
432 eprintln!("Couldn't read Cargo.toml: {}; assuming src/main.rs or S_ISDIR(src/bin/)", err);
433 String::new()
434 })
435 .parse::<TomlTable>()
436 .unwrap_or_else(|err| {
437 eprintln!("Couldn't parse Cargo.toml: {}; assuming src/main.rs or S_ISDIR(src/bin/)", err);
438 TomlTable::new()
439 })
440 .contains_key("bin") || (Path::new("src/main.rs").exists() || Path::new("src/bin").is_dir());
441 eprintln!("Final verdict: crate has binaries: {}", hasbins);
442
443 if hasbins && rustc_version::version().expect("couldn't get rustc version") >= rustc_version::Version::new(1, 50, 0) {
444 println!("cargo:rustc-link-arg-bins={}", out_file);
445 } else {
446 // Cargo pre-0.51.0 (rustc pre-1.50.0) compat
447 // Only links to the calling crate's library
448 println!("cargo:rustc-link-search=native={}", out_dir);
449 println!("cargo:rustc-link-lib=dylib={}", prefix);
450 }
451 CompilationResult::Ok
452}
453
454/// Likewise, but only for select binaries.
455///
456/// Only available since rustc 1.55.0, does nothing before.
457///
458/// # Examples
459///
460/// ```rust,no_run
461/// extern crate embed_resource;
462///
463/// fn main() {
464/// embed_resource::compile_for("assets/poke-a-mango.rc", &["poke-a-mango", "poke-a-mango-installer"],
465/// &["VERSION=\"0.5.0\""]);
466/// embed_resource::compile_for("assets/uninstaller.rc", &["unins001"], embed_resource::NONE);
467/// }
468/// ```
469pub fn compile_for<T: AsRef<Path>,
470 J: Display,
471 I: IntoIterator<Item = J>,
472 Ms: AsRef<OsStr>,
473 Mi: IntoIterator<Item = Ms>,
474 Is: AsRef<OsStr>,
475 Ii: IntoIterator<Item = Is>,
476 P: Into<ParameterBundle<Ms, Mi, Is, Ii>>>(
477 resource_file: T, for_bins: I, parameters: P)
478 -> CompilationResult {
479 let (_, _, out_file) = try_compile_impl!(compile_impl(resource_file.as_ref(), parameters.into()));
480 for bin in for_bins {
481 println!("cargo:rustc-link-arg-bin={}={}", bin, out_file);
482 }
483 CompilationResult::Ok
484}
485
486/// Likewise, but only link the resource to test binaries (select types only. unclear which (and likely to change). you may
487/// prefer [`compile_for_everything()`]).
488///
489/// Only available since rustc 1.60.0, does nothing before.
490pub fn compile_for_tests<T: AsRef<Path>,
491 Ms: AsRef<OsStr>,
492 Mi: IntoIterator<Item = Ms>,
493 Is: AsRef<OsStr>,
494 Ii: IntoIterator<Item = Is>,
495 P: Into<ParameterBundle<Ms, Mi, Is, Ii>>>(
496 resource_file: T, parameters: P)
497 -> CompilationResult {
498 let (_, _, out_file) = try_compile_impl!(compile_impl(resource_file.as_ref(), parameters.into()));
499 println!("cargo:rustc-link-arg-tests={}", out_file);
500 CompilationResult::Ok
501}
502
503/// Likewise, but only link the resource to benchmarks.
504///
505/// Only available since rustc 1.60.0, does nothing before.
506pub fn compile_for_benchmarks<T: AsRef<Path>,
507 Ms: AsRef<OsStr>,
508 Mi: IntoIterator<Item = Ms>,
509 Is: AsRef<OsStr>,
510 Ii: IntoIterator<Item = Is>,
511 P: Into<ParameterBundle<Ms, Mi, Is, Ii>>>(
512 resource_file: T, parameters: P)
513 -> CompilationResult {
514 let (_, _, out_file) = try_compile_impl!(compile_impl(resource_file.as_ref(), parameters.into()));
515 println!("cargo:rustc-link-arg-benches={}", out_file);
516 CompilationResult::Ok
517}
518
519/// Likewise, but only link the resource to examples.
520///
521/// Only available since rustc 1.60.0, does nothing before.
522pub fn compile_for_examples<T: AsRef<Path>,
523 Ms: AsRef<OsStr>,
524 Mi: IntoIterator<Item = Ms>,
525 Is: AsRef<OsStr>,
526 Ii: IntoIterator<Item = Is>,
527 P: Into<ParameterBundle<Ms, Mi, Is, Ii>>>(
528 resource_file: T, parameters: P)
529 -> CompilationResult {
530 let (_, _, out_file) = try_compile_impl!(compile_impl(resource_file.as_ref(), parameters.into()));
531 println!("cargo:rustc-link-arg-examples={}", out_file);
532 CompilationResult::Ok
533}
534
535/// Likewise, but link the resource into *every* artifact: binaries, cdylibs, examples, tests (`[[test]]`/`#[test]`/doctest),
536/// benchmarks, &c.
537///
538/// Only available since rustc 1.50.0, does nothing before.
539pub fn compile_for_everything<T: AsRef<Path>,
540 Ms: AsRef<OsStr>,
541 Mi: IntoIterator<Item = Ms>,
542 Is: AsRef<OsStr>,
543 Ii: IntoIterator<Item = Is>,
544 P: Into<ParameterBundle<Ms, Mi, Is, Ii>>>(
545 resource_file: T, parameters: P)
546 -> CompilationResult {
547 let (_, _, out_file) = try_compile_impl!(compile_impl(resource_file.as_ref(), parameters.into()));
548 println!("cargo:rustc-link-arg={}", out_file);
549 CompilationResult::Ok
550}
551
552fn compile_impl<Ms: AsRef<OsStr>, Mi: IntoIterator<Item = Ms>, Is: AsRef<OsStr>, Ii: IntoIterator<Item = Is>, P: Into<ParameterBundle<Ms, Mi, Is, Ii>>>(
553 resource_file: &Path, parameters: P)
554 -> Result<(&str, String, String), CompilationResult> {
555 let mut comp = ResourceCompiler::new();
556 if let Some(missing) = comp.is_supported() {
557 if missing.is_empty() {
558 Err(CompilationResult::NotWindows)
559 } else {
560 Err(CompilationResult::NotAttempted(missing))
561 }
562 } else {
563 let prefix = &resource_file.file_stem().expect("resource_file has no stem").to_str().expect("resource_file's stem not UTF-8");
564 let out_dir = env::var("OUT_DIR").expect("No OUT_DIR env var");
565
566 let out_file = comp.compile_resource(&out_dir, &prefix, resource_file.to_str().expect("resource_file not UTF-8"), parameters.into())
567 .map_err(CompilationResult::Failed)?;
568 Ok((prefix, out_dir, out_file))
569 }
570}
571
572fn apply_parameters<'t, Ms: AsRef<OsStr>, Mi: IntoIterator<Item = Ms>, Is: AsRef<OsStr>, Ii: IntoIterator<Item = Is>>(to: &'t mut Command, macro_pref: &str,
573 include_dir_pref: &str,
574 parameters: ParameterBundle<Ms,
575 Mi,
576 Is,
577 Ii>)
578 -> &'t mut Command {
579 for m in parameters.macros {
580 to.arg(macro_pref).arg(m);
581 }
582 for id in parameters.include_dirs {
583 to.arg(include_dir_pref).arg(id);
584 }
585 to
586}
587
588
589/// Find MSVC build tools other than the compiler and linker
590///
591/// On Windows + MSVC this can be used try to find tools such as `MIDL.EXE` in Windows Kits and/or SDK directories.
592///
593/// The compilers and linkers can be better found with the `cc` or `vswhom` crates.
594/// This always returns `None` on non-MSVC targets.
595///
596/// # Examples
597///
598/// In your build script, find `midl.exe` and use it to compile an IDL file:
599///
600/// ```rust,no_run
601/// # #[cfg(all(target_os = "windows", target_env = "msvc"))]
602/// # {
603/// extern crate embed_resource;
604/// extern crate vswhom;
605/// # use std::env;
606/// # use std::process::Command;
607///
608/// let midl = embed_resource::find_windows_sdk_tool("midl.exe").unwrap();
609///
610/// // midl.exe uses cl.exe as a preprocessor, so it needs to be in PATH
611/// let vs_locations = vswhom::VsFindResult::search().unwrap();
612/// let output = Command::new(midl)
613/// .env("PATH", vs_locations.vs_exe_path.unwrap())
614/// .args(&["/out", &env::var("OUT_DIR").unwrap()])
615/// .arg("haka.pfx.idl").output().unwrap();
616///
617/// assert!(output.status.success());
618/// # }
619/// ```
620pub fn find_windows_sdk_tool<T: AsRef<str>>(tool: T) -> Option<PathBuf> {
621 find_windows_sdk_tool_impl(tool.as_ref())
622}