1use std::{env, fs, path, process};
6
7const RESOURCE_TEMPLATE: &str = include_str!("../rustc.rc.in");
9
10#[derive(Debug, Clone, Copy)]
14#[repr(u32)]
15pub enum VersionInfoFileType {
16 App = 0x00000001,
18 Dll = 0x00000002,
20}
21
22pub fn compile_windows_resource_file(
28 file_stem: &path::Path,
29 file_description: &str,
30 filetype: VersionInfoFileType,
31) -> path::PathBuf {
32 let mut resources_dir = path::PathBuf::from(env::var_os("OUT_DIR").unwrap());
33 resources_dir.push("resources");
34 fs::create_dir_all(&resources_dir).unwrap();
35
36 let resource_compiler = if let Ok(path) = env::var("RUSTC_WINDOWS_RC") {
37 path.into()
38 } else {
39 find_msvc_tools::find_tool(&env::var("CARGO_CFG_TARGET_ARCH").unwrap(), "rc.exe")
40 .expect("found rc.exe")
41 .path()
42 .to_owned()
43 };
44
45 let rc_path = resources_dir.join(file_stem.with_extension("rc"));
46
47 write_resource_script_file(&rc_path, file_description, filetype);
48
49 let res_path = resources_dir.join(file_stem.with_extension("res"));
50
51 let status = process::Command::new(resource_compiler)
52 .arg("/fo")
53 .arg(&res_path)
54 .arg(&rc_path)
55 .status()
56 .expect("can execute resource compiler");
57 assert!(status.success(), "rc.exe failed with status {}", status);
58 assert!(
59 res_path.try_exists().unwrap_or(false),
60 "resource file {} was not created",
61 res_path.display()
62 );
63 res_path
64}
65
66fn write_resource_script_file(
69 rc_path: &path::Path,
70 file_description: &str,
71 filetype: VersionInfoFileType,
72) {
73 let mut resource_script = RESOURCE_TEMPLATE.to_string();
74
75 let descriptive_version = env::var("CFG_VERSION").unwrap_or("unknown".to_string());
77
78 let product_name = product_name(env::var("CFG_RELEASE_CHANNEL").unwrap());
80
81 let cfg_release = env::var("CFG_RELEASE").unwrap();
84 let version = parse_version(cfg_release.split("-").next().unwrap_or("0.0.0"))
86 .expect("valid CFG_RELEASE version");
87
88 resource_script = resource_script
89 .replace("@RUSTC_FILEDESCRIPTION_STR@", file_description)
90 .replace("@RUSTC_FILETYPE@", &format!("{}", filetype as u32))
91 .replace("@RUSTC_FILEVERSION_QUAD@", &version.to_quad_string())
92 .replace("@RUSTC_FILEVERSION_STR@", &descriptive_version)
93 .replace("@RUSTC_PRODUCTNAME_STR@", &product_name)
94 .replace("@RUSTC_PRODUCTVERSION_QUAD@", &version.to_quad_string())
95 .replace("@RUSTC_PRODUCTVERSION_STR@", &descriptive_version);
96
97 fs::write(&rc_path, resource_script)
98 .unwrap_or_else(|_| panic!("failed to write resource file {}", rc_path.display()));
99}
100
101fn product_name(channel: String) -> String {
102 format!(
103 "Rust Compiler{}",
104 if channel == "stable" { "".to_string() } else { format!(" ({})", channel) }
105 )
106}
107
108struct ResourceVersion {
110 major: u16,
111 minor: u16,
112 patch: u16,
113 build: u16,
114}
115
116impl ResourceVersion {
117 fn to_quad_string(&self) -> String {
120 format!("{},{},{},{}", self.major, self.minor, self.patch, self.build)
121 }
122}
123
124fn parse_version(version: &str) -> Option<ResourceVersion> {
128 let mut parts = version.split('.');
129 let major = parts.next()?.parse::<u16>().ok()?;
130 let minor = parts.next()?.parse::<u16>().ok()?;
131 let patch = parts.next()?.parse::<u16>().ok()?;
132 if parts.next().is_some() {
133 None
134 } else {
135 Some(ResourceVersion { major, minor, patch, build: 0 })
136 }
137}