1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
//! A [`Cargo` build script](http://doc.crates.io/build-script.html) library to handle compilation and inclusion of Windows //! resources in the most resilient fashion imaginable //! //! # Background //! //! Including Windows resources seems very easy at first, despite the build scripts' abhorrent documentation: //! [compile with `windres`, then make linkable with `ar`] //! (https://github.com/nabijaczleweli/cargo-update/commit/ef4346c#diff-a7b0a2dee0126cddf994326e705a91ea). //! //! I was very happy with that solution until it was brought to my attention, that [MSVC uses something different] //! (https://github.com/nabijaczleweli/cargo-update/commit/f57e9c3#diff-a7b0a2dee0126cddf994326e705a91ea), //! and now either `windres`-`ar` combo or `RC.EXE` would be used, which was OK. //! //! Later it transpired, that [MSVC is even more incompatible with everything else] //! (https://github.com/nabijaczleweli/cargo-update/commit/39fa758#diff-a7b0a2dee0126cddf994326e705a91ea) //! by way of not having `RC.EXE` in `$PATH` (because it would only be reasonable to do so), //! so another MSVC artisan made the script [find the most likely places for `RC.EXE` to be] //! (https://github.com/nabijaczleweli/cargo-update/pull/22), and the script grew yet again, //! now standing at 100 lines and 3.2 kB. //! //! After [copying the build script in its entirety] //! (https://github.com/thecoshman/http/commit/98205a4#diff-a7b0a2dee0126cddf994326e705a91ea) //! and realising how error-prone that was, then being [nudged by Shepmaster] //! (https://chat.stackoverflow.com/transcript/message/35378953#35378953) //! to extract it to a crate, here we are. //! //! # Usage (overview) //! //! Since the [build script](http://doc.crates.io/build-script.html) documentation is trash and //! [build script handling is even more trash] //! (https://github.com/nabijaczleweli/cargo-update/commit/ef4346c#diff-639fbc4ef05b315af92b4d836c31b023), //! we can't print a build script line to link to the compiled resource file. Instead, you need to use and `extern` //! block in your `main.rs` file like so: //! //! ```rust,ignore //! #[cfg(target_os="windows")] //! #[link(name="checksums-manifest", kind="static")] //! extern "C" {} //! //! // Your main() and w/e //! ``` //! //! Since the manifest is only generated on Windows, the `cfg` attribute takes care of that. //! //! The `name` attribute argument is either: //! //! * The name of the crate + "-manifest" by default, or //! * The second argument to `compile()`. //! //! # Usage (detailed) //! //! For the purposes of the demonstration we will assume that the crate's name is "checksums" and that the resource file's name //! is "checksums.rc". //! //! In `Cargo.toml`: //! //! ```toml //! # The general section with crate name, license, etc. //! build = "build.rs" //! //! [build-dependencies] //! embed-resource = "0.1" //! ``` //! //! In `build.rs`: //! //! ```rust,no-run //! extern crate embed_resource; //! //! fn main() { //! embed_resource::compile("checksums.rc", None, None); //! } //! ``` //! //! In `main.rs`: //! //! ```rust,ignore //! #[cfg(target_os="windows")] //! #[link(name="checksums-manifest", kind="static")] //! extern "C" {} //! ``` //! //! If, however, you want to use a different manifest link name (here: "chksum-rc"): //! //! In `build.rs`: //! //! ```rust,no-run //! extern crate embed_resource; //! //! fn main() { //! embed_resource::compile("checksums.rc", Some("chksum-rc"), None); //! } //! ``` //! //! In `main.rs`: //! //! ```rust,ignore //! #[cfg(target_os="windows")] //! #[link(name="chksum-rc", kind="static")] //! extern "C" {} //! ``` //! //! If, for an unfathomable reason, you want to create the resource archive in a different location: //! //! In `build.rs`: //! //! ```rust,no-run //! extern crate embed_resource; //! //! fn main() { //! embed_resource::compile("checksums.rc", None, Some("C:/Rast/build-files-output")); //! } //! ``` //! //! I souldn't recommend this, but hey. //! //! # Credit //! //! In chronological order: //! //! [@liigo](https://github.com/liigo) -- persistency in pestering me and investigating problems where I have failed //! //! [@mzji](https://github.com/mzji) -- MSVC lab rat //! //! [@TheCatPlusPlus](https://github.com/TheCatPlusPlus) -- knowledge and providing first iteration of manifest-embedding code //! //! [@azyobuzin](https://github.com/azyobuzin) -- providing code for finding places where RC.EXE could hide #[cfg(all(windows, target_env = "msvc"))] extern crate winreg; #[cfg(not(windows))] mod non_windows; #[cfg(all(windows, target_env = "msvc"))] mod windows_msvc; #[cfg(all(windows, not(target_env = "msvc")))] mod windows_not_msvc; #[cfg(not(windows))] use self::non_windows::*; #[cfg(all(windows, target_env = "msvc"))] use self::windows_msvc::*; #[cfg(all(windows, not(target_env = "msvc")))] use self::windows_not_msvc::*; use std::env; /// Compile the Windows resource file and update the cargo search path if we're on Windows. /// /// `prefix`, if `None`, defaults to `"$CARGO_PKG_NAME-manifest"`, this is the name you'll link to in `main.rs`. /// /// `out_dir`, if `None`, defaults to `$OUT_DIR`, which you probably don't want to change. /// /// On non-Windows this does nothing, on non-MSVC Windows, this chains `windres` with `ar`, /// but on MSVC Windows, this will try its hardest to find `RC.EXE` in Windows Kits and/or SDK directories /// (because someone thought not putting it in `$PATH` was a great idea). /// /// # Examples /// /// In your build script, assuming the crate's name is "checksums": /// /// ```rust,no-run /// extern crate embed_resource; /// /// fn main() { /// // Compile file checksums.rc to be linkable as checksums-manifest in $OUT_DIR /// embed_resource::compile("checksums.rc", None, None); /// } /// ``` /// /// If you want to link as `chksum-rc`: /// /// ```rust,no-run /// extern crate embed_resource; /// /// fn main() { /// // Compile file checksums.rc to be linkable as chksum-rc in $OUT_DIR /// embed_resource::compile("checksums.rc", Some("chksum-rc"), None); /// } /// ``` pub fn compile(resource_file: &str, prefix: Option<&str>, out_dir: Option<&str>) { if SUPPORTED { let prefix = prefix.map_or_else(|| env::var("CARGO_PKG_NAME").unwrap() + "-manifest", str::to_string); let out_dir = out_dir.map_or_else(|| env::var("OUT_DIR").unwrap(), str::to_string); compile_resource(&out_dir, &prefix, resource_file); println!("cargo:rustc-link-search=native={}", out_dir); } }