diff --git a/Cargo.lock b/Cargo.lock index 4c95e43eadb..57bbd15efc8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1268,7 +1268,7 @@ dependencies = [ "simplelog", "tempfile", "windows 0.61.3", - "winresource", + "windows_resources", ] [[package]] @@ -2946,6 +2946,7 @@ dependencies = [ "util", "walkdir", "windows 0.61.3", + "windows_resources", ] [[package]] @@ -21417,6 +21418,13 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" +[[package]] +name = "windows_resources" +version = "0.1.0" +dependencies = [ + "embed-resource", +] + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -22512,7 +22520,7 @@ dependencies = [ "web_search_providers", "which_key", "windows 0.61.3", - "winresource", + "windows_resources", "workspace", "zed-reqwest", "zed_actions", diff --git a/Cargo.toml b/Cargo.toml index a8c68c01ed6..2fac513d4ee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -225,6 +225,7 @@ members = [ "crates/x_ai", "crates/zed", "crates/zed_actions", + "crates/windows_resources", "crates/zed_credentials_provider", "crates/zed_env_vars", "crates/zeta_prompt", diff --git a/crates/auto_update_helper/Cargo.toml b/crates/auto_update_helper/Cargo.toml index aa5bf6ac40b..81c31a63c36 100644 --- a/crates/auto_update_helper/Cargo.toml +++ b/crates/auto_update_helper/Cargo.toml @@ -25,8 +25,8 @@ windows.workspace = true [target.'cfg(target_os = "windows")'.dev-dependencies] tempfile.workspace = true -[target.'cfg(target_os = "windows")'.build-dependencies] -winresource = "0.1" +[build-dependencies] +windows_resources = { path = "../windows_resources" } [package.metadata.docs.rs] targets = ["x86_64-pc-windows-msvc"] diff --git a/crates/auto_update_helper/build.rs b/crates/auto_update_helper/build.rs index 2910632c7ff..b91bbcb4bf5 100644 --- a/crates/auto_update_helper/build.rs +++ b/crates/auto_update_helper/build.rs @@ -1,15 +1,9 @@ fn main() { #[cfg(target_os = "windows")] { - println!("cargo:rerun-if-changed=manifest.xml"); + println!("cargo:rerun-if-env-changed=RELEASE_CHANNEL"); + println!("cargo:rerun-if-env-changed=GITHUB_RUN_NUMBER"); - let mut res = winresource::WindowsResource::new(); - res.set_manifest_file("manifest.xml"); - res.set_icon("app-icon.ico"); - - if let Err(e) = res.compile() { - eprintln!("{}", e); - std::process::exit(1); - } + windows_resources::compile(true).expect("failed to compile Windows resources"); } } diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index cfd807c0356..e8667c60875 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -52,3 +52,6 @@ plist = "1.3" [target.'cfg(target_os = "windows")'.dependencies] windows.workspace = true + +[build-dependencies] +windows_resources = { path = "../windows_resources" } diff --git a/crates/cli/build.rs b/crates/cli/build.rs index a3c4bc64373..8bda0576a07 100644 --- a/crates/cli/build.rs +++ b/crates/cli/build.rs @@ -26,4 +26,12 @@ fn main() { if let Some(build_identifier) = option_env!("GITHUB_RUN_NUMBER") { println!("cargo:rustc-env=ZED_BUILD_ID={build_identifier}"); } + + #[cfg(windows)] + { + println!("cargo:rerun-if-env-changed=RELEASE_CHANNEL"); + println!("cargo:rerun-if-env-changed=GITHUB_RUN_NUMBER"); + + windows_resources::compile(false).expect("failed to compile Windows resources"); + } } diff --git a/crates/windows_resources/Cargo.toml b/crates/windows_resources/Cargo.toml new file mode 100644 index 00000000000..4344040660c --- /dev/null +++ b/crates/windows_resources/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "windows_resources" +version = "0.1.0" +edition.workspace = true +publish.workspace = true +license = "GPL-3.0-or-later" + +[lints] +workspace = true + +[lib] +path = "src/windows_resources.rs" +doctest = false + +[target.'cfg(target_os = "windows")'.dependencies] +embed-resource = "3.0" diff --git a/crates/windows_resources/LICENSE-GPL b/crates/windows_resources/LICENSE-GPL new file mode 120000 index 00000000000..89e542f750c --- /dev/null +++ b/crates/windows_resources/LICENSE-GPL @@ -0,0 +1 @@ +../../LICENSE-GPL \ No newline at end of file diff --git a/crates/auto_update_helper/manifest.xml b/crates/windows_resources/resources/manifest.xml similarity index 100% rename from crates/auto_update_helper/manifest.xml rename to crates/windows_resources/resources/manifest.xml diff --git a/crates/windows_resources/src/windows_resources.rs b/crates/windows_resources/src/windows_resources.rs new file mode 100644 index 00000000000..fee3e7368bd --- /dev/null +++ b/crates/windows_resources/src/windows_resources.rs @@ -0,0 +1,125 @@ +#![allow( + clippy::disallowed_methods, + reason = "build helper used only from build scripts" +)] +#![cfg(target_os = "windows")] + +use std::process::Command; + +fn git_sha() -> Option { + if let Ok(sha) = std::env::var("ZED_COMMIT_SHA") { + return Some(sha); + } + + Command::new("git") + .args(["rev-parse", "HEAD"]) + .output() + .ok() + .filter(|output| output.status.success()) + .map(|output| String::from_utf8_lossy(&output.stdout).trim().to_string()) +} + +fn product_version() -> String { + let commit_sha = git_sha(); + let pkg_version = std::env::var("CARGO_PKG_VERSION").unwrap_or_default(); + let channel = std::env::var("RELEASE_CHANNEL").unwrap_or_else(|_| "dev".into()); + let build_id = std::env::var("GITHUB_RUN_NUMBER").ok(); + + let mut metadata = channel; + if let Some(build_id) = &build_id { + metadata.push('.'); + metadata.push_str(build_id); + } + if let Some(sha) = &commit_sha { + metadata.push('.'); + metadata.push_str(sha); + } + + format!("{pkg_version}+{metadata}") +} + +const ICON_DIR: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../zed/resources/windows"); +const MANIFEST_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/resources/manifest.xml"); + +pub fn compile(manifest: bool) -> Result<(), Box> { + let channel = option_env!("RELEASE_CHANNEL").unwrap_or("dev"); + let (icon_filename, product_name) = match channel { + "stable" => ("app-icon.ico", "Zed"), + "preview" => ("app-icon-preview.ico", "Zed Preview"), + "nightly" => ("app-icon-nightly.ico", "Zed Nightly"), + _ => ("app-icon-dev.ico", "Zed Dev"), + }; + let icon = std::path::PathBuf::from(ICON_DIR).join(icon_filename); + let icon_escaped = icon.to_string_lossy().replace('\\', "\\\\"); + + let manifest_line = if manifest { + let escaped = MANIFEST_PATH.replace('\\', "\\\\"); + format!("1 24 \"{escaped}\"") + } else { + String::new() + }; + + let pkg_version = std::env::var("CARGO_PKG_VERSION").unwrap_or_default(); + let product_version = product_version(); + let mut version_parts = pkg_version + .split('.') + .map(|part| part.parse::().unwrap_or(0)) + .chain(std::iter::repeat(0)); + let file_version = format!( + "{},{},{},{}", + version_parts.next().unwrap_or(0), + version_parts.next().unwrap_or(0), + version_parts.next().unwrap_or(0), + version_parts.next().unwrap_or(0), + ); + + let rc_content = format!( + r#"1 ICON "{icon_escaped}" +{manifest_line} + +1 VERSIONINFO +FILEVERSION {file_version} +PRODUCTVERSION {file_version} +FILEFLAGSMASK 0x3fL +FILEFLAGS 0x0L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "FileDescription", "{product_name}\0" + VALUE "FileVersion", "{pkg_version}\0" + VALUE "ProductName", "{product_name}\0" + VALUE "ProductVersion", "{product_version}\0" + VALUE "CompanyName", "Zed Industries, Inc.\0" + VALUE "LegalCopyright", "Copyright 2022 - 2025 Zed Industries, Inc.\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 1200 + END +END +"# + ); + + let out_dir = std::path::PathBuf::from(std::env::var("OUT_DIR")?); + let rc_path = out_dir.join("zed_resources.rc"); + std::fs::write(&rc_path, rc_content)?; + + if let Ok(toolkit_path) = std::env::var("ZED_RC_TOOLKIT_PATH") { + let rc_exe = std::path::Path::new(&toolkit_path).join("rc.exe"); + unsafe { + std::env::set_var("RC", rc_exe); + } + } + + embed_resource::compile(&rc_path, embed_resource::NONE) + .manifest_optional() + .unwrap(); + + Ok(()) +} diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 0374f6ec605..d8ac8be3369 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -233,8 +233,8 @@ gpui = { workspace = true, features = [ "windows-manifest", ] } -[target.'cfg(target_os = "windows")'.build-dependencies] -winresource = "0.1" +[build-dependencies] +windows_resources = { path = "../windows_resources" } [target.'cfg(any(target_os = "linux", target_os = "freebsd"))'.dependencies] gpui = { workspace = true, features = [ diff --git a/crates/zed/build.rs b/crates/zed/build.rs index 80bf1d8642e..b27eba36a8a 100644 --- a/crates/zed/build.rs +++ b/crates/zed/build.rs @@ -202,37 +202,12 @@ fn main() { } } - let release_channel = option_env!("RELEASE_CHANNEL").unwrap_or("dev"); - let icon = match release_channel { - "stable" => "resources/windows/app-icon.ico", - "preview" => "resources/windows/app-icon-preview.ico", - "nightly" => "resources/windows/app-icon-nightly.ico", - "dev" => "resources/windows/app-icon-dev.ico", - _ => "resources/windows/app-icon-dev.ico", - }; - let icon = std::path::Path::new(icon); - println!("cargo:rerun-if-env-changed=RELEASE_CHANNEL"); - println!("cargo:rerun-if-changed={}", icon.display()); + println!("cargo:rerun-if-env-changed=GITHUB_RUN_NUMBER"); #[cfg(windows)] { - let mut res = winresource::WindowsResource::new(); - - // Depending on the security applied to the computer, winresource might fail - // fetching the RC path. Therefore, we add a way to explicitly specify the - // toolkit path, allowing winresource to use a valid RC path. - if let Some(explicit_rc_toolkit_path) = std::env::var("ZED_RC_TOOLKIT_PATH").ok() { - res.set_toolkit_path(explicit_rc_toolkit_path.as_str()); - } - res.set_icon(icon.to_str().unwrap()); - res.set("FileDescription", "Zed"); - res.set("ProductName", "Zed"); - - if let Err(e) = res.compile() { - eprintln!("{}", e); - std::process::exit(1); - } + windows_resources::compile(false).expect("failed to compile Windows resources"); } }