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}