Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,24 @@ jobs:
- name: Test
run: cargo xtask test

- name: Check embedded executable icon
shell: pwsh
run: |
cargo build --release --locked -p disktree-app
Add-Type @'
using System;
using System.Runtime.InteropServices;
public static class ExecutableIcons {
[DllImport("shell32.dll", CharSet = CharSet.Unicode)]
public static extern uint ExtractIconEx(
string file, int index, IntPtr[] large, IntPtr[] small, uint count);
}
'@
$exe = (Resolve-Path target/release/disktree.exe).Path
if ([ExecutableIcons]::ExtractIconEx($exe, -1, $null, $null, 0) -lt 1) {
throw "disktree.exe has no embedded icon"
}

# The other half of each release: the same tests on the architectures
# the release builds for but the jobs above do not run on.
other-architectures:
Expand Down
Binary file added assets/disktree.ico
Binary file not shown.
65 changes: 65 additions & 0 deletions crates/disktree-app/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#[cfg(windows)]
fn main() {
use std::{env, fs, path::PathBuf, process::Command};

let icon = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap())
.join("../../assets/disktree.ico")
.canonicalize()
.expect("Windows icon asset is missing");
let out = PathBuf::from(env::var_os("OUT_DIR").unwrap());
let rc = out.join("disktree.rc");
let res = out.join("disktree.res");

// rc.exe treats backslashes inside quoted paths as separators, not escapes.
let icon_path = icon.to_string_lossy().replace('\\', "/");
fs::write(&rc, format!("1 ICON \"{icon_path}\"\n"))
.expect("could not write Windows icon resource script");

// Cargo can run outside a Visual Studio prompt, where the SDK bin is not
// on PATH even though the MSVC linker and Windows SDK are installed.
let rc_exe = env::var_os("DISKTREE_RC").map_or_else(
|| {
let sdk_bin = env::var_os("WindowsSdkDir")
.map(PathBuf::from)
.or_else(|| {
env::var_os("ProgramFiles(x86)")
.map(|root| PathBuf::from(root).join("Windows Kits/10"))
})
.expect("Windows SDK is required to embed the icon")
.join("bin");
let host = match env::consts::ARCH {
"x86_64" => "x64",
"aarch64" => "arm64",
"x86" => "x86",
other => panic!("unsupported Windows build host: {other}"),
};
let mut versions: Vec<_> = fs::read_dir(&sdk_bin)
.expect("Windows SDK bin directory is missing")
.filter_map(Result::ok)
.map(|entry| entry.path().join(host).join("rc.exe"))
.filter(|path| path.is_file())
.collect();
versions.sort();
versions
.pop()
.expect("rc.exe is missing from the Windows SDK")
},
PathBuf::from,
);
let status = Command::new(rc_exe)
.arg("/nologo")
.arg(format!("/fo{}", res.display()))
.arg(&rc)
.status()
.expect("could not start rc.exe to embed the Windows icon");
assert!(
status.success(),
"rc.exe failed to compile the Windows icon"
);

println!("cargo:rerun-if-changed={}", icon.display());
println!("cargo:rustc-link-arg-bin=disktree={}", res.display());
}

#[cfg(not(windows))]
fn main() {}