crates/memospot-build-internals/src/windows_test_manifest.rs
Rama main · réplica de lectura del repositorio original.
//! Windows application manifest reuse for test binaries.
//!
//! Test binaries import GUI symbols such as `TaskDialogIndirect` from
//! `comctl32.dll`, which exist only in Common Controls v6. The Windows
//! loader binds v6 solely when the executable carries an application
//! manifest declaring it; otherwise the process dies before `main` with
//! `STATUS_ENTRYPOINT_NOT_FOUND` (`0xc0000139`). Tauri links its manifest
//! into the shipped binary alone, leaving the lib unit-test binary bare.
//! This module extracts Tauri's already generated manifest and directs the
//! linker to embed it into every artifact, including the test binaries.
//!
//! See <https://github.com/tauri-apps/tauri/issues/13419>.
use std::{env, fs, path::PathBuf};
/// Dependency name identifying Common Controls v6 in manifest text.
const COMCTL_V6_NAME: &str = "Microsoft.Windows.Common-Controls";
/// Common Controls version carrying the symbols test binaries import.
const COMCTL_V6_VERSION: &str = "6.0.0.0";
/// Dependency stanza merged into manifests missing Common Controls v6.
const COMCTL_V6_STANZA: &str = concat!(
" <dependency>\n",
" <dependentAssembly>\n",
" <assemblyIdentity\n",
" type=\"win32\"\n",
" name=\"Microsoft.Windows.Common-Controls\"\n",
" version=\"6.0.0.0\"\n",
" processorArchitecture=\"*\"\n",
" publicKeyToken=\"6595b64144ccf1df\"\n",
" language=\"*\"\n",
" />\n",
" </dependentAssembly>\n",
" </dependency>\n",
);
/// Standalone manifest used when Tauri's generated manifest cannot be reused.
const MINIMAL_V6_MANIFEST: &str = "\
<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n\
<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\">\n\
<dependency>\n\
<dependentAssembly>\n\
<assemblyIdentity\n\
type=\"win32\"\n\
name=\"Microsoft.Windows.Common-Controls\"\n\
version=\"6.0.0.0\"\n\
processorArchitecture=\"*\"\n\
publicKeyToken=\"6595b64144ccf1df\"\n\
language=\"*\"\n\
/>\n\
</dependentAssembly>\n\
</dependency>\n\
</assembly>\n";
/// Reuse Tauri's generated manifest for binaries the linker leaves bare.
///
/// Reads the `resource.rc` that `tauri-build` generated earlier in the same
/// `build.rs` invocation, extracts its manifest, patches in the Common
/// Controls v6 dependency when absent, and directs the linker to embed the
/// result into every artifact, including the lib unit-test binary. The
/// shipped binary merges the identical manifest it already carries, so its
/// resources are unchanged.
pub fn ensure_test_manifest() {
let out_dir = PathBuf::from(
env::var("OUT_DIR").expect("OUT_DIR is required while running the build script"),
);
let manifest = fs::read_to_string(out_dir.join("resource.rc"))
.ok()
.and_then(|rc| extract_manifest_from_rc(&rc))
.map(|manifest| ensure_comctl_v6(&manifest))
.unwrap_or_else(|| {
println!(
"cargo:warning=failed to reuse Tauri's Windows manifest for test binaries; embedding a minimal Common Controls v6 manifest instead"
);
MINIMAL_V6_MANIFEST.to_string()
});
let manifest_path = out_dir.join("test_manifest.xml");
fs::write(&manifest_path, manifest).expect("failed to write test manifest");
println!("cargo:rustc-link-arg=/MANIFEST:EMBED");
println!(
"cargo:rustc-link-arg=/MANIFESTINPUT:{}",
manifest_path.display()
);
// Turn linker warnings into errors so a skipped manifest fails loudly
// instead of resurfacing as STATUS_ENTRYPOINT_NOT_FOUND at test time.
println!("cargo:rustc-link-arg=/WX");
}
/// Extract the inline application manifest from a generated `.rc` file.
///
/// Returns [`None`] when no `1 24` manifest block is present, for example
/// when a custom Tauri configuration references a manifest file by path
/// instead of embedding manifest text.
pub fn extract_manifest_from_rc(rc: &str) -> Option<String> {
let mut lines = rc.lines().map(str::trim).skip_while(|line| *line != "1 24");
lines.next()?;
if lines.next()? != "{" {
return None;
}
let mut manifest = String::new();
for line in lines {
if line == "}" {
break;
}
let inner = line.strip_prefix('"')?.strip_suffix('"')?;
let inner = inner.strip_prefix(' ').unwrap_or(inner);
let inner = inner.strip_suffix(' ').unwrap_or(inner);
manifest.push_str(&unescape_rc_string(inner));
manifest.push('\n');
}
Some(manifest)
}
/// Whether a manifest is missing the Common Controls v6 dependency.
pub fn manifest_needs_comctl_v6(manifest: &str) -> bool {
!(manifest.contains(COMCTL_V6_NAME) && manifest.contains(COMCTL_V6_VERSION))
}
/// Insert the Common Controls v6 dependency into a manifest lacking it.
///
/// Falls back to a minimal standalone manifest when the input has no
/// `</assembly>` root to attach the stanza to.
pub fn ensure_comctl_v6(manifest: &str) -> String {
if !manifest_needs_comctl_v6(manifest) {
return manifest.to_string();
}
match manifest.rfind("</assembly>") {
Some(index) => {
let (head, tail) = manifest.split_at(index);
format!("{head}{COMCTL_V6_STANZA}{tail}")
}
None => MINIMAL_V6_MANIFEST.to_string(),
}
}
/// Inverse of the `escape_string` helper in `tauri-winres`.
fn unescape_rc_string(escaped: &str) -> String {
let mut unescaped = String::with_capacity(escaped.len());
let mut chars = escaped.chars();
while let Some(char_) = chars.next() {
if char_ != '\\' && char_ != '"' {
unescaped.push(char_);
continue;
}
match chars.next() {
Some('"') if char_ == '"' => unescaped.push('"'),
Some('\\') if char_ == '\\' => unescaped.push('\\'),
Some('\'') if char_ == '\\' => unescaped.push('\''),
Some('n') if char_ == '\\' => unescaped.push('\n'),
Some('t') if char_ == '\\' => unescaped.push('\t'),
Some('r') if char_ == '\\' => unescaped.push('\r'),
Some(other) => {
unescaped.push(char_);
unescaped.push(other);
}
None => unescaped.push(char_),
}
}
unescaped
}
#[cfg(test)]
mod tests {
use super::*;
const SAMPLE_RC: &str = r#"#pragma code_page(65001)
1 VERSIONINFO
FILEVERSION 1, 0, 4, 0
{
BLOCK "StringFileInfo"
{
BLOCK "040004b0"
{
VALUE "ProductName", "memospot"
}
}
}
2 ICON "C:\icons\icon.ico"
1 24
{
" <assembly manifestVersion=""1.0""> "
" <dependency name=""Common-Controls"" version=""6.0.0.0""/> "
" </assembly> "
}
"#;
#[test]
fn extracts_manifest_from_generated_resource_file() {
// GIVEN a resource file in the shape `tauri-winres` writes
// WHEN the manifest is extracted
let manifest = extract_manifest_from_rc(SAMPLE_RC).expect("manifest should extract");
// THEN the manifest XML is recovered with resource escaping undone.
assert_eq!(
manifest,
"<assembly manifestVersion=\"1.0\">\n\
<dependency name=\"Common-Controls\" version=\"6.0.0.0\"/>\n\
</assembly>\n"
);
}
#[test]
fn extraction_fails_without_manifest_block() {
// GIVEN a resource file with no `1 24` manifest block
let rc = "1 VERSIONINFO\nFILEVERSION 1, 0, 4, 0\n";
// WHEN the manifest is extracted THEN no manifest is reported.
assert_eq!(extract_manifest_from_rc(rc), None);
}
#[test]
fn detects_missing_comctl_v6_dependency() {
// GIVEN manifests with and without the v6 dependency
let complete = format!("<assembly>\n{COMCTL_V6_STANZA}</assembly>\n");
let missing_stanza = "<assembly><assemblyIdentity name=\"Other\"/></assembly>";
let stale_version =
format!("<assembly name=\"{COMCTL_V6_NAME}\" version=\"5.82.0.0\"/>");
// WHEN inspected THEN only the complete manifest passes.
assert!(!manifest_needs_comctl_v6(&complete));
// AND a missing stanza or a stale version both need the patch.
assert!(manifest_needs_comctl_v6(missing_stanza));
assert!(manifest_needs_comctl_v6(&stale_version));
}
#[test]
fn keeps_complete_manifest_untouched() {
// GIVEN a manifest already declaring Common Controls v6
let manifest = format!("<assembly>\n{COMCTL_V6_STANZA}</assembly>\n");
// WHEN ensured THEN the manifest is returned unchanged.
assert_eq!(ensure_comctl_v6(&manifest), manifest);
}
#[test]
fn inserts_stanza_before_assembly_close() {
// GIVEN a manifest missing the v6 dependency
let manifest = "<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\">\n</assembly>\n";
// WHEN ensured THEN the stanza lands inside the assembly root.
let patched = ensure_comctl_v6(manifest);
assert!(patched.contains(COMCTL_V6_NAME));
assert!(patched.contains(COMCTL_V6_VERSION));
assert!(patched.ends_with("</assembly>\n"));
assert!(patched.starts_with("<assembly"));
}
#[test]
fn falls_back_to_minimal_manifest_without_assembly_root() {
// GIVEN text with no `</assembly>` root to attach the stanza to
// WHEN ensured THEN a standalone v6 manifest is returned.
assert_eq!(ensure_comctl_v6("not xml"), MINIMAL_V6_MANIFEST);
}
#[test]
fn unescapes_resource_string_literals() {
// GIVEN resource-escaped content
// WHEN unescaped THEN quotes and backslashes are restored literally.
assert_eq!(unescape_rc_string("a\"\"b\\\\c"), "a\"b\\c");
}
}