mirror of
https://github.com/rust-lang/rust.git
synced 2025-12-01 04:07:45 +00:00
138 lines
5.2 KiB
Rust
138 lines
5.2 KiB
Rust
//! A build script dependency to create a Windows resource file for the compiler
|
|
//!
|
|
//! Uses values from the `CFG_VERSION` and `CFG_RELEASE` environment variables
|
|
//! to set the product and file version information in the Windows resource file.
|
|
use std::{env, fs, path, process};
|
|
|
|
/// The template for the Windows resource file.
|
|
const RESOURCE_TEMPLATE: &str = include_str!("../rustc.rc.in");
|
|
|
|
/// A subset of the possible values for the `FILETYPE` field in a Windows resource file
|
|
///
|
|
/// See the `dwFileType` member of [VS_FIXEDFILEINFO](https://learn.microsoft.com/en-us/windows/win32/api/verrsrc/ns-verrsrc-vs_fixedfileinfo#members)
|
|
#[derive(Debug, Clone, Copy)]
|
|
#[repr(u32)]
|
|
pub enum VersionInfoFileType {
|
|
/// `VFT_APP` - The file is an application.
|
|
App = 0x00000001,
|
|
/// `VFT_DLL` - The file is a dynamic link library.
|
|
Dll = 0x00000002,
|
|
}
|
|
|
|
/// Create and compile a Windows resource file with the product and file version information for the rust compiler.
|
|
///
|
|
/// Returns the path to the compiled resource file
|
|
///
|
|
/// Does not emit any cargo directives, the caller is responsible for that.
|
|
pub fn compile_windows_resource_file(
|
|
file_stem: &path::Path,
|
|
file_description: &str,
|
|
filetype: VersionInfoFileType,
|
|
) -> path::PathBuf {
|
|
let mut resources_dir = path::PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
|
resources_dir.push("resources");
|
|
fs::create_dir_all(&resources_dir).unwrap();
|
|
|
|
let resource_compiler = if let Ok(path) = env::var("RUSTC_WINDOWS_RC") {
|
|
path.into()
|
|
} else {
|
|
find_msvc_tools::find_tool(&env::var("CARGO_CFG_TARGET_ARCH").unwrap(), "rc.exe")
|
|
.expect("found rc.exe")
|
|
.path()
|
|
.to_owned()
|
|
};
|
|
|
|
let rc_path = resources_dir.join(file_stem.with_extension("rc"));
|
|
|
|
write_resource_script_file(&rc_path, file_description, filetype);
|
|
|
|
let res_path = resources_dir.join(file_stem.with_extension("res"));
|
|
|
|
let status = process::Command::new(resource_compiler)
|
|
.arg("/fo")
|
|
.arg(&res_path)
|
|
.arg(&rc_path)
|
|
.status()
|
|
.expect("can execute resource compiler");
|
|
assert!(status.success(), "rc.exe failed with status {}", status);
|
|
assert!(
|
|
res_path.try_exists().unwrap_or(false),
|
|
"resource file {} was not created",
|
|
res_path.display()
|
|
);
|
|
res_path
|
|
}
|
|
|
|
/// Writes a Windows resource script file for the rust compiler with the product and file version information
|
|
/// into `rc_path`
|
|
fn write_resource_script_file(
|
|
rc_path: &path::Path,
|
|
file_description: &str,
|
|
filetype: VersionInfoFileType,
|
|
) {
|
|
let mut resource_script = RESOURCE_TEMPLATE.to_string();
|
|
|
|
// Set the string product and file version to the same thing as `rustc --version`
|
|
let descriptive_version = env::var("CFG_VERSION").unwrap_or("unknown".to_string());
|
|
|
|
// Set the product name to "Rust Compiler" or "Rust Compiler (nightly)" etc
|
|
let product_name = product_name(env::var("CFG_RELEASE_CHANNEL").unwrap());
|
|
|
|
// For the numeric version we need `major,minor,patch,build`.
|
|
// Extract them from `CFG_RELEASE` which is "major.minor.patch" and a "-dev", "-nightly" or similar suffix
|
|
let cfg_release = env::var("CFG_RELEASE").unwrap();
|
|
// remove the suffix, if present and parse into [`ResourceVersion`]
|
|
let version = parse_version(cfg_release.split("-").next().unwrap_or("0.0.0"))
|
|
.expect("valid CFG_RELEASE version");
|
|
|
|
resource_script = resource_script
|
|
.replace("@RUSTC_FILEDESCRIPTION_STR@", file_description)
|
|
.replace("@RUSTC_FILETYPE@", &format!("{}", filetype as u32))
|
|
.replace("@RUSTC_FILEVERSION_QUAD@", &version.to_quad_string())
|
|
.replace("@RUSTC_FILEVERSION_STR@", &descriptive_version)
|
|
.replace("@RUSTC_PRODUCTNAME_STR@", &product_name)
|
|
.replace("@RUSTC_PRODUCTVERSION_QUAD@", &version.to_quad_string())
|
|
.replace("@RUSTC_PRODUCTVERSION_STR@", &descriptive_version);
|
|
|
|
fs::write(&rc_path, resource_script)
|
|
.unwrap_or_else(|_| panic!("failed to write resource file {}", rc_path.display()));
|
|
}
|
|
|
|
fn product_name(channel: String) -> String {
|
|
format!(
|
|
"Rust Compiler{}",
|
|
if channel == "stable" { "".to_string() } else { format!(" ({})", channel) }
|
|
)
|
|
}
|
|
|
|
/// Windows resources store versions as four 16-bit integers.
|
|
struct ResourceVersion {
|
|
major: u16,
|
|
minor: u16,
|
|
patch: u16,
|
|
build: u16,
|
|
}
|
|
|
|
impl ResourceVersion {
|
|
/// Format the version as a comma-separated string of four integers
|
|
/// as expected by Windows resource scripts for the `FILEVERSION` and `PRODUCTVERSION` fields.
|
|
fn to_quad_string(&self) -> String {
|
|
format!("{},{},{},{}", self.major, self.minor, self.patch, self.build)
|
|
}
|
|
}
|
|
|
|
/// Parse a string in the format "major.minor.patch" into a [`ResourceVersion`].
|
|
/// The build is set to 0.
|
|
/// Returns `None` if the version string is not in the expected format.
|
|
fn parse_version(version: &str) -> Option<ResourceVersion> {
|
|
let mut parts = version.split('.');
|
|
let major = parts.next()?.parse::<u16>().ok()?;
|
|
let minor = parts.next()?.parse::<u16>().ok()?;
|
|
let patch = parts.next()?.parse::<u16>().ok()?;
|
|
if parts.next().is_some() {
|
|
None
|
|
} else {
|
|
Some(ResourceVersion { major, minor, patch, build: 0 })
|
|
}
|
|
}
|