Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit fefce3c

Browse files
committed
Auto merge of #146018 - lambdageek:add-winres-version, r=wesleywiser
compiler: Add Windows resources to rustc-main and rustc_driver Adds Windows resources with the rust version information to rustc-main.exe and rustc_driver.dll Invokes `rc.exe` directly, rather than using one of the crates from the ecosystem to avoid adding dependencies. A new internal `rustc_windows_rc` crate has the common build script machinery for locating `rc.exe` and constructing the resource script
2 parents e9b6085 + 095fa86 commit fefce3c

File tree

11 files changed

+271
-8
lines changed

11 files changed

+271
-8
lines changed

‎Cargo.lock

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3248,6 +3248,7 @@ dependencies = [
32483248
"rustc_driver_impl",
32493249
"rustc_public",
32503250
"rustc_public_bridge",
3251+
"rustc_windows_rc",
32513252
"tikv-jemalloc-sys",
32523253
]
32533254

@@ -3623,6 +3624,7 @@ name = "rustc_driver"
36233624
version = "0.0.0"
36243625
dependencies = [
36253626
"rustc_driver_impl",
3627+
"rustc_windows_rc",
36263628
]
36273629

36283630
[[package]]
@@ -4719,6 +4721,13 @@ dependencies = [
47194721
"semver",
47204722
]
47214723

4724+
[[package]]
4725+
name = "rustc_windows_rc"
4726+
version = "0.0.0"
4727+
dependencies = [
4728+
"cc",
4729+
]
4730+
47224731
[[package]]
47234732
name = "rustdoc"
47244733
version = "0.0.0"

‎compiler/rustc/Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,8 @@ llvm = ['rustc_driver_impl/llvm']
3333
max_level_info = ['rustc_driver_impl/max_level_info']
3434
rustc_randomized_layouts = ['rustc_driver_impl/rustc_randomized_layouts']
3535
# tidy-alphabetical-end
36+
37+
[build-dependencies]
38+
# tidy-alphabetical-start
39+
rustc_windows_rc = { path = "../rustc_windows_rc" }
40+
# tidy-alphabetical-end

‎compiler/rustc/build.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
use std::env;
1+
use std::{env, path};
2+
3+
use rustc_windows_rc::{VersionInfoFileType, compile_windows_resource_file};
24

35
fn main() {
46
let target_os = env::var("CARGO_CFG_TARGET_OS");
@@ -13,6 +15,18 @@ fn main() {
1315

1416
// Add a manifest file to rustc.exe.
1517
fn set_windows_exe_options() {
18+
set_windows_resource();
19+
set_windows_manifest();
20+
}
21+
22+
fn set_windows_resource() {
23+
let stem = path::PathBuf::from("rustc_main_resource");
24+
let file_description = "rustc";
25+
let res_file = compile_windows_resource_file(&stem, file_description, VersionInfoFileType::App);
26+
println!("cargo:rustc-link-arg={}", res_file.display());
27+
}
28+
29+
fn set_windows_manifest() {
1630
static WINDOWS_MANIFEST_FILE: &str = "Windows Manifest.xml";
1731

1832
let mut manifest = env::current_dir().unwrap();

‎compiler/rustc_codegen_ssa/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ ar_archive_writer = "0.5"
99
bitflags = "2.4.1"
1010
bstr = "1.11.3"
1111
# `cc` updates often break things, so we pin it here. Cargo enforces "max 1 semver-compat version
12-
# per crate", so if you change this, you need to also change it in `rustc_llvm`.
12+
# per crate", so if you change this, you need to also change it in `rustc_llvm` and `rustc_windows_rc`.
1313
cc = "=1.2.16"
1414
itertools = "0.12"
1515
pathdiff = "0.2.0"

‎compiler/rustc_driver/Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,8 @@ crate-type = ["dylib"]
1010
# tidy-alphabetical-start
1111
rustc_driver_impl = { path = "../rustc_driver_impl" }
1212
# tidy-alphabetical-end
13+
14+
[build-dependencies]
15+
# tidy-alphabetical-start
16+
rustc_windows_rc = { path = "../rustc_windows_rc" }
17+
# tidy-alphabetical-end

‎compiler/rustc_driver/build.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
use std::{env, path};
2+
3+
use rustc_windows_rc::{VersionInfoFileType, compile_windows_resource_file};
4+
5+
fn main() {
6+
let target_os = env::var("CARGO_CFG_TARGET_OS");
7+
let target_env = env::var("CARGO_CFG_TARGET_ENV");
8+
if Ok("windows") == target_os.as_deref() && Ok("msvc") == target_env.as_deref() {
9+
set_windows_dll_options();
10+
} else {
11+
// Avoid rerunning the build script every time.
12+
println!("cargo:rerun-if-changed=build.rs");
13+
}
14+
}
15+
16+
fn set_windows_dll_options() {
17+
let stem = path::PathBuf::from("rustc_driver_resource");
18+
let file_description = "rustc_driver";
19+
let res_file = compile_windows_resource_file(&stem, file_description, VersionInfoFileType::Dll);
20+
println!("cargo:rustc-link-arg={}", res_file.display());
21+
}

‎compiler/rustc_llvm/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ libc = "0.2.73"
1111
[build-dependencies]
1212
# tidy-alphabetical-start
1313
# `cc` updates often break things, so we pin it here. Cargo enforces "max 1 semver-compat version
14-
# per crate", so if you change this, you need to also change it in `rustc_codegen_ssa`.
14+
# per crate", so if you change this, you need to also change it in `rustc_codegen_ssa` and `rustc_windows_rc`.
1515
cc = "=1.2.16"
1616
# tidy-alphabetical-end
1717

‎compiler/rustc_windows_rc/Cargo.toml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[package]
2+
name = "rustc_windows_rc"
3+
version = "0.0.0"
4+
edition = "2024"
5+
6+
[dependencies]
7+
#tidy-alphabetical-start
8+
# `cc` updates often break things, so we pin it here. Cargo enforces "max 1 semver-compat version
9+
# per crate", so if you change this, you need to also change it in `rustc_llvm` and `rustc_codegen_ssa`.
10+
cc = "=1.2.16"
11+
#tidy-alphabetical-end

‎compiler/rustc_windows_rc/rustc.rc.in

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// A template for the rustc_driver and rustc Windows resource files.
2+
// This file is processed by the build script to produce rustc.rc and rustc_driver.rc
3+
// with the appropriate version information filled in.
4+
5+
// All the strings are in UTF-8
6+
#pragma code_page(65001)
7+
8+
#define RUSTC_FILEDESCRIPTION_STR "@RUSTC_FILEDESCRIPTION_STR@"
9+
#define RUSTC_FILETYPE @RUSTC_FILETYPE@
10+
#define RUSTC_FILEVERSION_QUAD @RUSTC_FILEVERSION_QUAD@
11+
#define RUSTC_FILEVERSION_STR "@RUSTC_FILEVERSION_STR@"
12+
13+
#define RUSTC_PRODUCTNAME_STR "@RUSTC_PRODUCTNAME_STR@"
14+
#define RUSTC_PRODUCTVERSION_QUAD @RUSTC_PRODUCTVERSION_QUAD@
15+
#define RUSTC_PRODUCTVERSION_STR "@RUSTC_PRODUCTVERSION_STR@"
16+
17+
18+
1 VERSIONINFO
19+
FILEVERSION RUSTC_FILEVERSION_QUAD
20+
PRODUCTVERSION RUSTC_PRODUCTVERSION_QUAD
21+
FILEOS 0x00040004
22+
FILETYPE RUSTC_FILETYPE
23+
FILESUBTYPE 0
24+
FILEFLAGSMASK 0x3f
25+
FILEFLAGS 0x0
26+
{
27+
BLOCK "StringFileInfo"
28+
{
29+
BLOCK "000004b0"
30+
{
31+
VALUE "FileDescription", RUSTC_FILEDESCRIPTION_STR
32+
VALUE "FileVersion", RUSTC_FILEVERSION_STR
33+
VALUE "ProductVersion", RUSTC_PRODUCTVERSION_STR
34+
VALUE "ProductName", RUSTC_PRODUCTNAME_STR
35+
}
36+
}
37+
BLOCK "VarFileInfo" {
38+
VALUE "Translation", 0x0, 0x04b0
39+
}
40+
}

‎compiler/rustc_windows_rc/src/lib.rs

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
//! A build script dependency to create a Windows resource file for the compiler
2+
//!
3+
//! Uses values from the `CFG_VERSION` and `CFG_RELEASE` environment variables
4+
//! to set the product and file version information in the Windows resource file.
5+
use std::{env, ffi, fs, path, process};
6+
7+
use cc::windows_registry;
8+
9+
/// The template for the Windows resource file.
10+
const RESOURCE_TEMPLATE: &str = include_str!("../rustc.rc.in");
11+
12+
/// A subset of the possible values for the `FILETYPE` field in a Windows resource file
13+
///
14+
/// See the `dwFileType` member of [VS_FIXEDFILEINFO](https://learn.microsoft.com/en-us/windows/win32/api/verrsrc/ns-verrsrc-vs_fixedfileinfo#members)
15+
#[derive(Debug, Clone, Copy)]
16+
#[repr(u32)]
17+
pub enum VersionInfoFileType {
18+
/// `VFT_APP` - The file is an application.
19+
App = 0x00000001,
20+
/// `VFT_DLL` - The file is a dynamic link library.
21+
Dll = 0x00000002,
22+
}
23+
24+
/// Create and compile a Windows resource file with the product and file version information for the rust compiler.
25+
///
26+
/// Returns the path to the compiled resource file
27+
///
28+
/// Does not emit any cargo directives, the caller is responsible for that.
29+
pub fn compile_windows_resource_file(
30+
file_stem: &path::Path,
31+
file_description: &str,
32+
filetype: VersionInfoFileType,
33+
) -> path::PathBuf {
34+
let mut resources_dir = path::PathBuf::from(env::var_os("OUT_DIR").unwrap());
35+
resources_dir.push("resources");
36+
fs::create_dir_all(&resources_dir).unwrap();
37+
38+
let resource_compiler =
39+
find_resource_compiler(&env::var("CARGO_CFG_TARGET_ARCH").unwrap()).expect("found rc.exe");
40+
41+
let rc_path = resources_dir.join(file_stem.with_extension("rc"));
42+
43+
write_resource_script_file(&rc_path, file_description, filetype);
44+
45+
let res_path = resources_dir.join(file_stem.with_extension("res"));
46+
47+
let status = process::Command::new(resource_compiler)
48+
.arg("/fo")
49+
.arg(&res_path)
50+
.arg(&rc_path)
51+
.status()
52+
.expect("can execute resource compiler");
53+
assert!(status.success(), "rc.exe failed with status {}", status);
54+
assert!(
55+
res_path.try_exists().unwrap_or(false),
56+
"resource file {} was not created",
57+
res_path.display()
58+
);
59+
res_path
60+
}
61+
62+
/// Writes a Windows resource script file for the rust compiler with the product and file version information
63+
/// into `rc_path`
64+
fn write_resource_script_file(
65+
rc_path: &path::Path,
66+
file_description: &str,
67+
filetype: VersionInfoFileType,
68+
) {
69+
let mut resource_script = RESOURCE_TEMPLATE.to_string();
70+
71+
// Set the string product and file version to the same thing as `rustc --version`
72+
let descriptive_version = env::var("CFG_VERSION").unwrap_or("unknown".to_string());
73+
74+
// Set the product name to "Rust Compiler" or "Rust Compiler (nightly)" etc
75+
let product_name = product_name(env::var("CFG_RELEASE_CHANNEL").unwrap());
76+
77+
// For the numeric version we need `major,minor,patch,build`.
78+
// Extract them from `CFG_RELEASE` which is "major.minor.patch" and a "-dev", "-nightly" or similar suffix
79+
let cfg_release = env::var("CFG_RELEASE").unwrap();
80+
// remove the suffix, if present and parse into [`ResourceVersion`]
81+
let version = parse_version(cfg_release.split("-").next().unwrap_or("0.0.0"))
82+
.expect("valid CFG_RELEASE version");
83+
84+
resource_script = resource_script
85+
.replace("@RUSTC_FILEDESCRIPTION_STR@", file_description)
86+
.replace("@RUSTC_FILETYPE@", &format!("{}", filetype as u32))
87+
.replace("@RUSTC_FILEVERSION_QUAD@", &version.to_quad_string())
88+
.replace("@RUSTC_FILEVERSION_STR@", &descriptive_version)
89+
.replace("@RUSTC_PRODUCTNAME_STR@", &product_name)
90+
.replace("@RUSTC_PRODUCTVERSION_QUAD@", &version.to_quad_string())
91+
.replace("@RUSTC_PRODUCTVERSION_STR@", &descriptive_version);
92+
93+
fs::write(&rc_path, resource_script)
94+
.unwrap_or_else(|_| panic!("failed to write resource file {}", rc_path.display()));
95+
}
96+
97+
fn product_name(channel: String) -> String {
98+
format!(
99+
"Rust Compiler{}",
100+
if channel == "stable" { "".to_string() } else { format!(" ({})", channel) }
101+
)
102+
}
103+
104+
/// Windows resources store versions as four 16-bit integers.
105+
struct ResourceVersion {
106+
major: u16,
107+
minor: u16,
108+
patch: u16,
109+
build: u16,
110+
}
111+
112+
impl ResourceVersion {
113+
/// Format the version as a comma-separated string of four integers
114+
/// as expected by Windows resource scripts for the `FILEVERSION` and `PRODUCTVERSION` fields.
115+
fn to_quad_string(&self) -> String {
116+
format!("{},{},{},{}", self.major, self.minor, self.patch, self.build)
117+
}
118+
}
119+
120+
/// Parse a string in the format "major.minor.patch" into a [`ResourceVersion`].
121+
/// The build is set to 0.
122+
/// Returns `None` if the version string is not in the expected format.
123+
fn parse_version(version: &str) -> Option<ResourceVersion> {
124+
let mut parts = version.split('.');
125+
let major = parts.next()?.parse::<u16>().ok()?;
126+
let minor = parts.next()?.parse::<u16>().ok()?;
127+
let patch = parts.next()?.parse::<u16>().ok()?;
128+
if parts.next().is_some() {
129+
None
130+
} else {
131+
Some(ResourceVersion { major, minor, patch, build: 0 })
132+
}
133+
}
134+
135+
/// Find the Windows SDK resource compiler `rc.exe` for the given architecture or target triple.
136+
/// Returns `None` if the tool could not be found.
137+
fn find_resource_compiler(arch_or_target: &str) -> Option<path::PathBuf> {
138+
find_windows_sdk_tool(arch_or_target, "rc.exe")
139+
}
140+
141+
/// Find a Windows SDK tool for the given architecture or target triple.
142+
/// Returns `None` if the tool could not be found.
143+
fn find_windows_sdk_tool(arch_or_target: &str, tool_name: &str) -> Option<path::PathBuf> {
144+
// windows_registry::find_tool can only find MSVC tools, not Windows SDK tools, but
145+
// cc does include the Windows SDK tools in the PATH environment of MSVC tools.
146+
147+
let msvc_linker = windows_registry::find_tool(arch_or_target, "link.exe")?;
148+
let path = &msvc_linker.env().iter().find(|(k, _)| k == "PATH")?.1;
149+
find_tool_in_path(tool_name, path)
150+
}
151+
152+
/// Find a tool in the directories in a given PATH-like string.
153+
fn find_tool_in_path<P: AsRef<ffi::OsStr>>(tool_name: &str, path: P) -> Option<path::PathBuf> {
154+
env::split_paths(path.as_ref()).find_map(|p| {
155+
let tool_path = p.join(tool_name);
156+
if tool_path.try_exists().unwrap_or(false) { Some(tool_path) } else { None }
157+
})
158+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /