From d69d70c3fc3c79998faccaf40fe46f03f84c2dba Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Sun, 4 Oct 2026 10:44:20 +0000 Subject: [PATCH] Windows exes carry the coin icon and a version block (the project lead's rule, 4 Oct 2026) Every Windows executable now ships with the coin icon Explorer shows and the version block Properties shows (CompanyName Igneum, ProductName Igneum Miner, FileDescription per exe, 0.3.0, LegalCopyright Igneum contributors), like the Mac app and DMG already do. - igneumd.exe, igneum-miner.exe: packaging/windows/embed-resources.sh now takes the worktree and target dir (defaults vendor/igneum-node-v4 and vendor/igneum-node/target-integration) and relinks with a linker shim first in PATH instead of `cargo rustc -- -C link-arg`: cargo rustc takes one package and unifies features differently from the two-package cross-build (300 crates differ), and a configured linker is fingerprinted and rebuilds everything; the PATH shim changes neither. .rc files moved to 0.3.0. - igneum-worker-cuda.exe, igneum-worker-opencl.exe: proto-cuda/nvrtc/build-windows.sh compiles the two new .rc files with windres and links the objects; the icon is made with make-icons.py if missing. - igneum-app.exe: app/igneum-app/build.rs runs windres on resources/igneum-app.rc for Windows targets and links it (cargo:rustc-link-arg-bins, no crate dependency); it fails the build if the .rc version drifts from Cargo.toml. - packaging/windows/resources/verify-exe.py: small PE parser that checks the .rsrc section, icon group and version strings on the Mac; both build scripts call it, and it checks every exe inside the packages. - proto-opencl/cl_dynamic.h: clGetEventInfo added to the run-time loader table (4714b51 added the call in host.c without it, so the one-click OpenCL worker no longer linked under IGNEUM_CL_DYNAMIC). Co-Authored-By: Claude Fable 5.1 --- app/igneum-app/Cargo.toml | 1 + app/igneum-app/build.rs | 80 +++++++++++ app/igneum-app/resources/igneum-app.rc | 35 +++++ packaging/windows/embed-resources.sh | 104 ++++++++++---- packaging/windows/resources/igneum-miner.rc | 69 ++++----- packaging/windows/resources/igneumd.rc | 69 ++++----- packaging/windows/resources/verify-exe.py | 147 ++++++++++++++++++++ proto-cuda/nvrtc/build-windows.sh | 20 ++- proto-cuda/nvrtc/igneum-worker-cuda.rc | 35 +++++ proto-cuda/nvrtc/igneum-worker-opencl.rc | 35 +++++ proto-opencl/cl_dynamic.h | 3 +- 11 files changed, 498 insertions(+), 100 deletions(-) create mode 100644 app/igneum-app/build.rs create mode 100644 app/igneum-app/resources/igneum-app.rc create mode 100755 packaging/windows/resources/verify-exe.py create mode 100644 proto-cuda/nvrtc/igneum-worker-cuda.rc create mode 100644 proto-cuda/nvrtc/igneum-worker-opencl.rc diff --git a/app/igneum-app/Cargo.toml b/app/igneum-app/Cargo.toml index cc6803fd5..b8fd8004e 100644 --- a/app/igneum-app/Cargo.toml +++ b/app/igneum-app/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" description = "Igneum Miner engine: supervises the node, the miner and the GPU workers, and serves the dashboard on 127.0.0.1" license = "MIT" publish = false +build = "build.rs" # Windows targets: links resources/igneum-app.rc (coin icon, version block) into the exe [[bin]] name = "igneum-app" diff --git a/app/igneum-app/build.rs b/app/igneum-app/build.rs new file mode 100644 index 000000000..e2661b97a --- /dev/null +++ b/app/igneum-app/build.rs @@ -0,0 +1,80 @@ +// Build script for igneum-app. On a Windows target it compiles resources/igneum-app.rc (the coin icon Explorer shows +// and the version block under Properties > Details) with windres and links the object into igneum-app.exe. Other +// targets: nothing. the project lead's rule (4 October 2026): every shipped exe carries the coin icon and a version block, like the +// Mac app and DMG. No crate dependency: windres is called directly (x86_64-w64-mingw32-windres from Homebrew mingw-w64 +// on the Mac, windres from MSYS2 on a PC; IGNEUM_WINDRES names another one). +use std::env; +use std::fs; +use std::path::PathBuf; +use std::process::Command; + +fn main() { + let manifest = PathBuf::from(env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR")); + let rc = manifest.join("resources").join("igneum-app.rc"); + let icons = manifest.join("..").join("..").join("brand").join("icons"); + let ico = icons.join("igneum.ico"); + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed={}", rc.display()); + println!("cargo:rerun-if-changed={}", ico.display()); + println!("cargo:rerun-if-env-changed=IGNEUM_WINDRES"); + if env::var("CARGO_CFG_TARGET_OS").as_deref() != Ok("windows") { + return; + } + // The .rc carries the version by hand; it must agree with Cargo.toml so Properties shows the number the app reports. + let version = env::var("CARGO_PKG_VERSION").expect("CARGO_PKG_VERSION"); + let text = fs::read_to_string(&rc).unwrap_or_else(|e| panic!("cannot read {}: {e}", rc.display())); + let numeric = format!("{},0", version.replace('.', ",")); + if !text.contains(&format!("\"{version}\"")) || !text.contains(&numeric) { + panic!( + "{} must carry version {version} (FILEVERSION/PRODUCTVERSION {numeric}, FileVersion/ProductVersion \"{version}\")", + rc.display() + ); + } + if !ico.is_file() { + panic!("no {}; run python3 brand/icons/make-icons.py first", ico.display()); + } + let windres = match find_windres() { + Some(w) => w, + None => { + if env::var("CARGO_CFG_TARGET_ENV").as_deref() == Ok("msvc") { + println!("cargo:warning=no windres found: igneum-app.exe is built without the coin icon and version block (set IGNEUM_WINDRES)"); + return; + } + panic!("no windres found (brew install mingw-w64, or set IGNEUM_WINDRES): igneum-app.exe must carry the coin icon and version block"); + } + }; + let out = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR")).join("igneum-app.res.o"); + let status = Command::new(&windres) + .arg("-I").arg(&icons) + .arg("-i").arg(&rc) + .arg("-O").arg("coff") + .arg("-o").arg(&out) + .status() + .unwrap_or_else(|e| panic!("cannot run {windres}: {e}")); + if !status.success() { + panic!("{windres} failed on {} ({status})", rc.display()); + } + println!("cargo:rustc-link-arg-bins={}", out.display()); +} + +fn find_windres() -> Option { + let mut candidates = Vec::new(); + if let Ok(w) = env::var("IGNEUM_WINDRES") { + if !w.is_empty() { + candidates.push(w); + } + } + let prefix = match env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_default().as_str() { + "x86_64" => Some("x86_64-w64-mingw32"), + "x86" => Some("i686-w64-mingw32"), + "aarch64" => Some("aarch64-w64-mingw32"), + _ => None, + }; + if let Some(p) = prefix { + candidates.push(format!("{p}-windres")); + } + candidates.push("windres".to_string()); + candidates + .into_iter() + .find(|c| Command::new(c).arg("--version").output().map(|o| o.status.success()).unwrap_or(false)) +} diff --git a/app/igneum-app/resources/igneum-app.rc b/app/igneum-app/resources/igneum-app.rc new file mode 100644 index 000000000..5544a84a8 --- /dev/null +++ b/app/igneum-app/resources/igneum-app.rc @@ -0,0 +1,35 @@ +// Windows resources for igneum-app.exe: the coin icon Explorer shows and the version block under Properties > Details. +// Compiled with x86_64-w64-mingw32-windres (the icon path is relative to brand/icons, passed with -I). +// the project lead's rule (4 October 2026): every shipped exe carries the coin icon and a version block, like the Mac app and DMG. +#include + +1 ICON "igneum.ico" + +1 VERSIONINFO +FILEVERSION 0,3,0,0 +PRODUCTVERSION 0,3,0,0 +FILEFLAGSMASK 0x3fL +FILEFLAGS 0x0L +FILEOS VOS_NT_WINDOWS32 +FILETYPE VFT_APP +FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + VALUE "CompanyName", "Igneum" + VALUE "FileDescription", "Igneum Miner engine" + VALUE "FileVersion", "0.3.0" + VALUE "InternalName", "igneum-app" + VALUE "LegalCopyright", "Igneum contributors" + VALUE "OriginalFilename", "igneum-app.exe" + VALUE "ProductName", "Igneum Miner" + VALUE "ProductVersion", "0.3.0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/packaging/windows/embed-resources.sh b/packaging/windows/embed-resources.sh index 5544c9aef..13ab3f392 100755 --- a/packaging/windows/embed-resources.sh +++ b/packaging/windows/embed-resources.sh @@ -1,61 +1,107 @@ #!/usr/bin/env bash -# Gives igneumd.exe and igneum-miner.exe the coin icon and a version block (CompanyName Igneum, ProductName -# Igneum Miner, 0.1.0). Two steps, both on the Mac: +# Gives igneumd.exe and igneum-miner.exe the coin icon and the version block Explorer shows under Properties > Details +# (CompanyName Igneum, ProductName Igneum Miner, 0.3.0). the project lead's rule (4 October 2026): every shipped exe carries the +# coin icon and a version block, like the Mac app and DMG. Two steps, both on the Mac: # 1. windres compiles resources/igneumd.rc and resources/igneum-miner.rc into COFF objects (cheap, always done). -# 2. --relink runs `cargo rustc` for each binary with `-C link-arg=`: only the final crate is recompiled and -# relinked, every dependency comes from the cache, so it costs a few minutes after a normal cross-build. The -# objects are linked in as extra inputs, nothing in vendor/ changes. Without --relink the exact commands are -# printed, and build-installer.ps1 stamps the resources with rcedit on the PC instead. -# The resulting exes land where proto-cuda/windows-app/make-package.sh already picks them up -# (vendor/igneum-node-win/target/x86_64-pc-windows-gnu/release/). +# 2. --relink runs the same cargo build as proto-cuda/windows-node/cross-build.sh (same packages, features and +# environment, so every cached dependency is reused) with a linker shim first in PATH: a script named +# x86_64-w64-mingw32-gcc (the linker rustc picks for x86_64-pc-windows-gnu) that runs the real one and appends +# the matching object whenever the output being linked is igneumd-.exe or igneum-miner-.exe. The two +# exes are removed from deps/ first so cargo relinks them even when nothing changed. Nothing in vendor/ changes. +# Why not `cargo rustc -- -C link-arg=`: it takes one package, and a one-package selection unifies features +# differently from the two-package cross-build (300 crates differ, checked 4 Oct 2026), so it would rebuild half +# the tree and ship a differently-featured miner. Why PATH and not CARGO_TARGET_..._LINKER: cargo puts the +# configured linker in its fingerprints, so that rebuilds every crate (checked on app/igneum-app the same day). +# The exes land in /x86_64-pc-windows-gnu/release/, where proto-cuda/windows-app/make-package.sh and +# packaging/windows/make-payload.sh pick them up. Without --relink only the objects are compiled and the command is +# printed (build-installer.ps1 can stamp the resources with rcedit on the PC instead). # -# packaging/windows/embed-resources.sh compile the objects, print the relink commands -# packaging/windows/embed-resources.sh --relink also relink both exes (needs a completed cross-build in the cache) +# Usage: packaging/windows/embed-resources.sh [--relink] [worktree] [target dir] +# worktree the node checkout to build, default vendor/igneum-node-v4 (or NODE_WORKTREE) +# target dir CARGO_TARGET_DIR, default vendor/igneum-node/target-integration (or CARGO_TARGET_DIR); relative paths +# are taken from the repository root +# JOBS=n cargo jobs, default 4 set -euo pipefail HERE="$(cd "$(dirname "$0")" && pwd)" ROOT="$(cd "$HERE/../.." && pwd)" ICONS="$ROOT/brand/icons" OUT="$HERE/build/res" -NODE="${NODE_WORKTREE:-$ROOT/vendor/igneum-node-win}" +VERIFY="$HERE/resources/verify-exe.py" MINGW="/opt/homebrew/opt/mingw-w64/toolchain-x86_64/x86_64-w64-mingw32" +RELINK=0 +if [ "${1:-}" = "--relink" ]; then RELINK=1; shift; fi +NODE="${1:-${NODE_WORKTREE:-$ROOT/vendor/igneum-node-v4}}" +TD="${2:-${CARGO_TARGET_DIR:-$ROOT/vendor/igneum-node/target-integration}}" +case "$NODE" in /*) ;; *) NODE="$ROOT/$NODE" ;; esac +case "$TD" in /*) ;; *) TD="$ROOT/$TD" ;; esac +JOBS="${JOBS:-4}" command -v x86_64-w64-mingw32-windres >/dev/null || { echo "x86_64-w64-mingw32-windres not found (brew install mingw-w64)" >&2; exit 1; } -[ -f "$ICONS/igneum.ico" ] || { echo "no $ICONS/igneum.ico; run python3 brand/icons/make-icons.py first" >&2; exit 1; } +[ -f "$ICONS/igneum.ico" ] || { echo "no $ICONS/igneum.ico, making the icons"; python3 "$ICONS/make-icons.py"; } +[ -d "$NODE" ] || { echo "no worktree at $NODE" >&2; exit 1; } mkdir -p "$OUT" for name in igneumd igneum-miner; do x86_64-w64-mingw32-windres -I "$ICONS" -i "$HERE/resources/$name.rc" -O coff -o "$OUT/$name.res.o" echo "compiled $OUT/$name.res.o ($(stat -f %z "$OUT/$name.res.o") bytes)" done +# The linker shim: the real x86_64-w64-mingw32-gcc plus the resource object for the exe being linked +REAL_GCC="$(command -v x86_64-w64-mingw32-gcc)" || { echo "x86_64-w64-mingw32-gcc not found" >&2; exit 1; } +SHIM="$HERE/build/linker-shim" +rm -rf "$SHIM" && mkdir -p "$SHIM" +cat > "$SHIM/x86_64-w64-mingw32-gcc" <.exe or igneum-miner-.exe, found after -o, also inside a @response file). +REAL="$REAL_GCC" +RES="$OUT" +out="" +prev="" +for a in "\$@"; do + case "\$a" in @*) if [ -f "\${a#@}" ]; then + p2=""; for w in \$(cat "\${a#@}"); do if [ "\$p2" = "-o" ]; then out="\$w"; fi; p2="\$w"; done + fi ;; esac + if [ "\$prev" = "-o" ]; then out="\$a"; fi + prev="\$a" +done +case "\$(basename "\$out")" in + igneumd-*.exe|igneumd.exe) exec "\$REAL" "\$@" "\$RES/igneumd.res.o" ;; + igneum-miner-*.exe|igneum_miner-*.exe|igneum-miner.exe) exec "\$REAL" "\$@" "\$RES/igneum-miner.res.o" ;; +esac +exec "\$REAL" "\$@" +SHIM_SH +chmod +x "$SHIM/x86_64-w64-mingw32-gcc" + # The same environment as proto-cuda/windows-node/cross-build.sh, so the cached dependencies are reused. export PATH="$HOME/.cargo/bin:/opt/homebrew/bin:$PATH" -export CARGO_TARGET_DIR="${CARGO_TARGET_DIR:-target}" +export CARGO_TARGET_DIR="$TD" export CC_x86_64_pc_windows_gnu=x86_64-w64-mingw32-gcc export CXX_x86_64_pc_windows_gnu=x86_64-w64-mingw32-g++ export AR_x86_64_pc_windows_gnu=x86_64-w64-mingw32-ar export LIBCLANG_PATH=/opt/homebrew/opt/llvm/lib export BINDGEN_EXTRA_CLANG_ARGS_x86_64_pc_windows_gnu="--target=x86_64-w64-mingw32 --sysroot=$MINGW -I$MINGW/include" export CARGO_TARGET_X86_64_PC_WINDOWS_GNU_RUSTFLAGS="-C link-arg=-static -C link-arg=-static-libgcc" +REL="$TD/x86_64-pc-windows-gnu/release" +BUILD="nice -n 19 cargo build --release -j $JOBS -p kaspad -p igneum-miner --features igneum-pow --target x86_64-pc-windows-gnu" -relink_cmd() { # $1 package, $2 bin, $3 object - printf 'nice -n 19 cargo rustc --release -j 2 -p %s --bin %s --features igneum-pow --target x86_64-pc-windows-gnu -- -C link-arg=%q\n' "$1" "$2" "$3" -} -if [ "${1:-}" = "--relink" ]; then +if [ "$RELINK" = 1 ]; then cd "$NODE" - for spec in "kaspad igneumd" "igneum-miner igneum-miner"; do - set -- $spec - echo "relinking $2.exe" - eval "$(relink_cmd "$1" "$2" "$OUT/$2.res.o")" - exe="$CARGO_TARGET_DIR/x86_64-pc-windows-gnu/release/$2.exe" - x86_64-w64-mingw32-objdump -h "$exe" | grep -q '\.rsrc' && echo "$exe: .rsrc section present ($(stat -f %z "$exe") bytes)" || { echo "$exe has no .rsrc section" >&2; exit 1; } + echo "worktree $NODE at $(git rev-parse --short HEAD) ($(git status --porcelain | grep -v '^??' | wc -l | tr -d ' ') modified files), target dir $TD" + rm -f "$REL"/deps/igneumd-*.exe "$REL"/deps/igneum-miner-*.exe "$REL"/deps/igneum_miner-*.exe + PATH="$SHIM:$PATH" $BUILD + for exe in igneumd igneum-miner; do + f="$REL/$exe.exe" + x86_64-w64-mingw32-objdump -h "$f" | grep -q '\.rsrc' || { echo "$f has no .rsrc section" >&2; exit 1; } + echo "$f: $(stat -f %z "$f") bytes; DLLs: $(x86_64-w64-mingw32-objdump -p "$f" | grep 'DLL Name' | awk '{print $3}' | sort -u | tr '\n' ' ')" done + python3 "$VERIFY" --version 0.3.0 "$REL/igneumd.exe" "$REL/igneum-miner.exe" else cat < Details. -// Compiled by embed-resources.sh with x86_64-w64-mingw32-windres (icon path relative to brand/icons, passed with -I). -#include - -1 ICON "igneum.ico" - -1 VERSIONINFO -FILEVERSION 0,1,0,0 -PRODUCTVERSION 0,1,0,0 -FILEFLAGSMASK 0x3fL -FILEFLAGS 0x0L -FILEOS VOS_NT_WINDOWS32 -FILETYPE VFT_APP -FILESUBTYPE VFT2_UNKNOWN -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904B0" - BEGIN - VALUE "CompanyName", "Igneum" - VALUE "FileDescription", "Igneum Miner" - VALUE "FileVersion", "0.1.0" - VALUE "InternalName", "igneum-miner" - VALUE "LegalCopyright", "Igneum. Nothing is bought or sold." - VALUE "OriginalFilename", "igneum-miner.exe" - VALUE "ProductName", "Igneum Miner" - VALUE "ProductVersion", "0.1.0" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1200 - END -END +// Windows resources for igneum-miner.exe: the coin icon Explorer shows and the version block under Properties > Details. +// Compiled with x86_64-w64-mingw32-windres (the icon path is relative to brand/icons, passed with -I). +// the project lead's rule (4 October 2026): every shipped exe carries the coin icon and a version block, like the Mac app and DMG. +#include + +1 ICON "igneum.ico" + +1 VERSIONINFO +FILEVERSION 0,3,0,0 +PRODUCTVERSION 0,3,0,0 +FILEFLAGSMASK 0x3fL +FILEFLAGS 0x0L +FILEOS VOS_NT_WINDOWS32 +FILETYPE VFT_APP +FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + VALUE "CompanyName", "Igneum" + VALUE "FileDescription", "Igneum Miner lottery miner" + VALUE "FileVersion", "0.3.0" + VALUE "InternalName", "igneum-miner" + VALUE "LegalCopyright", "Igneum contributors" + VALUE "OriginalFilename", "igneum-miner.exe" + VALUE "ProductName", "Igneum Miner" + VALUE "ProductVersion", "0.3.0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/packaging/windows/resources/igneumd.rc b/packaging/windows/resources/igneumd.rc index 08fe8e9ab..2ea1c5f41 100644 --- a/packaging/windows/resources/igneumd.rc +++ b/packaging/windows/resources/igneumd.rc @@ -1,34 +1,35 @@ -// Windows resources for igneumd.exe: the coin icon and the version block Explorer shows under Properties > Details. -// Compiled by embed-resources.sh with x86_64-w64-mingw32-windres (icon path relative to brand/icons, passed with -I). -#include - -1 ICON "igneum.ico" - -1 VERSIONINFO -FILEVERSION 0,1,0,0 -PRODUCTVERSION 0,1,0,0 -FILEFLAGSMASK 0x3fL -FILEFLAGS 0x0L -FILEOS VOS_NT_WINDOWS32 -FILETYPE VFT_APP -FILESUBTYPE VFT2_UNKNOWN -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904B0" - BEGIN - VALUE "CompanyName", "Igneum" - VALUE "FileDescription", "Igneum Miner node" - VALUE "FileVersion", "0.1.0" - VALUE "InternalName", "igneumd" - VALUE "LegalCopyright", "Igneum. Nothing is bought or sold." - VALUE "OriginalFilename", "igneumd.exe" - VALUE "ProductName", "Igneum Miner" - VALUE "ProductVersion", "0.1.0" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1200 - END -END +// Windows resources for igneumd.exe: the coin icon Explorer shows and the version block under Properties > Details. +// Compiled with x86_64-w64-mingw32-windres (the icon path is relative to brand/icons, passed with -I). +// the project lead's rule (4 October 2026): every shipped exe carries the coin icon and a version block, like the Mac app and DMG. +#include + +1 ICON "igneum.ico" + +1 VERSIONINFO +FILEVERSION 0,3,0,0 +PRODUCTVERSION 0,3,0,0 +FILEFLAGSMASK 0x3fL +FILEFLAGS 0x0L +FILEOS VOS_NT_WINDOWS32 +FILETYPE VFT_APP +FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + VALUE "CompanyName", "Igneum" + VALUE "FileDescription", "Igneum Miner node" + VALUE "FileVersion", "0.3.0" + VALUE "InternalName", "igneumd" + VALUE "LegalCopyright", "Igneum contributors" + VALUE "OriginalFilename", "igneumd.exe" + VALUE "ProductName", "Igneum Miner" + VALUE "ProductVersion", "0.3.0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/packaging/windows/resources/verify-exe.py b/packaging/windows/resources/verify-exe.py new file mode 100755 index 000000000..feb7057b2 --- /dev/null +++ b/packaging/windows/resources/verify-exe.py @@ -0,0 +1,147 @@ +#!/usr/bin/env python3 +"""Checks that a Windows exe carries the Igneum resources: a .rsrc section with an icon group and a version block whose +CompanyName is Igneum and ProductName is Igneum Miner. Prints what it finds. Small PE parser, no dependencies, runs on +the Mac where rcedit and Explorer are not available. + python3 verify-exe.py [--version 0.3.0] exe [exe ...] exit 1 if any exe fails""" +import struct, sys + +RT_ICON, RT_GROUP_ICON, RT_VERSION = 3, 14, 16 +REQUIRED = ("CompanyName", "ProductName", "FileDescription", "FileVersion", "ProductVersion", "LegalCopyright") + + +def sections_of(data): + pe = struct.unpack_from("= 52 and struct.unpack_from("> 16, ms & 0xFFFF, ls >> 16, ls & 0xFFFF, pms >> 16, pms & 0xFFFF, pls >> 16, pls & 0xFFFF) + pos = (val + vlen + 3) & ~3 + while pos < off + length: + clen, _, _, ckey, cval = block(pos) + if ckey == "StringFileInfo": + tpos = cval + while tpos < pos + clen: + tlen, _, _, _, tval = block(tpos) + spos = tval + while spos < tpos + tlen: + slen, svlen, _, skey, sval = block(spos) + if slen == 0: + break + raw = data[sval:sval + 2 * svlen] + strings[skey] = raw.decode("utf-16-le").rstrip("\0") + spos = (spos + slen + 3) & ~3 + tpos = (tpos + tlen + 3) & ~3 + if clen == 0: + break + pos = (pos + clen + 3) & ~3 + return fixed, strings + + +def check(path, want_version): + data = open(path, "rb").read() + secs, res_rva, res_size = sections_of(data) + names = [s[0] for s in secs] + problems = [] + if ".rsrc" not in names: + problems.append("no .rsrc section") + if not res_rva: + problems.append("resource table empty in the PE data directory") + icons = groups = 0 + fixed, strings = None, {} + if res_rva: + base = rva_to_off(secs, res_rva) + found = [] + walk(data, base, 0, 0, found) + for p, rva, size in found: + if p[0] == RT_ICON: + icons += 1 + elif p[0] == RT_GROUP_ICON: + groups += 1 + elif p[0] == RT_VERSION and fixed is None: + fixed, strings = version_strings(data, rva_to_off(secs, rva), size) + if not groups: + problems.append("no icon group (RT_GROUP_ICON)") + if fixed is None: + problems.append("no version block (RT_VERSION)") + for k in REQUIRED: + if not strings.get(k): + problems.append("missing " + k) + if strings.get("CompanyName") not in (None, "Igneum"): + problems.append("CompanyName is %r, not Igneum" % strings["CompanyName"]) + if strings.get("ProductName") not in (None, "Igneum Miner"): + problems.append("ProductName is %r, not Igneum Miner" % strings["ProductName"]) + if want_version: + for k in ("FileVersion", "ProductVersion"): + if strings.get(k) and strings[k] != want_version: + problems.append("%s is %s, not %s" % (k, strings[k], want_version)) + if fixed and ".".join(map(str, fixed[:3])) != want_version: + problems.append("FILEVERSION is %s, not %s" % (".".join(map(str, fixed[:4])), want_version)) + print("%s: %d bytes, sections %s" % (path, len(data), " ".join(names))) + print(" icon images %d, icon groups %d, FILEVERSION %s" % (icons, groups, ".".join(map(str, fixed[:4])) if fixed else "none")) + for k in REQUIRED + ("InternalName", "OriginalFilename"): + if k in strings: + print(" %-17s %s" % (k, strings[k])) + for p in problems: + print(" FAIL: " + p) + return not problems + + +if __name__ == "__main__": + args = sys.argv[1:] + want = None + if args[:1] == ["--version"]: + want, args = args[1], args[2:] + if not args: + sys.exit(__doc__) + ok = all([check(p, want) for p in args]) + print("all %d exe(s) carry the Igneum resources" % len(args) if ok else "FAILED") + sys.exit(0 if ok else 1) diff --git a/proto-cuda/nvrtc/build-windows.sh b/proto-cuda/nvrtc/build-windows.sh index 0cad55641..278fa41e2 100755 --- a/proto-cuda/nvrtc/build-windows.sh +++ b/proto-cuda/nvrtc/build-windows.sh @@ -5,27 +5,43 @@ # proto-opencl/igneum-worker-opencl.exe host.c with IGNEUM_CL_DYNAMIC (OpenCL.dll loaded at run time) and the generic # --pack serve mode; the pack it is built against is only a placeholder # Static libgcc/libstdc++/winpthread, so each exe needs only KERNEL32 and the Universal CRT (Windows 10 and 11 have it). +# Each exe carries the coin icon and a version block (igneum-worker-cuda.rc, igneum-worker-opencl.rc, compiled with windres +# and linked in; the project lead's rule, 4 October 2026: every shipped exe shows the coin in Explorer and a publisher in Properties). # Usage: build-windows.sh (then windows-app/make-package.sh ships them with the NVRTC DLLs) set -euo pipefail HERE="$(cd "$(dirname "$0")" && pwd)" ROOT="$(cd "$HERE/../.." && pwd)" RED="$HERE/redist" +ICONS="$ROOT/brand/icons" [ -f "$RED/include/cuda.h" ] && [ -f "$RED/include/nvrtc.h" ] && [ -f "$RED/include/CL/cl.h" ] || { echo "run $HERE/fetch-redist.sh first" >&2; exit 1; } CXX=x86_64-w64-mingw32-g++ CC=x86_64-w64-mingw32-gcc STRIP=x86_64-w64-mingw32-strip +WINDRES=x86_64-w64-mingw32-windres +command -v "$WINDRES" >/dev/null || { echo "$WINDRES not found (brew install mingw-w64)" >&2; exit 1; } PLACEHOLDER="$ROOT/proto-cuda/packs/igneum-devnet-v4-epoch0" +VERIFY="$ROOT/packaging/windows/resources/verify-exe.py" + +# The coin icon and the version blocks, as COFF objects the linker takes like any other input +[ -f "$ICONS/igneum.ico" ] || { echo "== no $ICONS/igneum.ico, making the icons"; python3 "$ICONS/make-icons.py"; } +RES="$(mktemp -d)" +for w in cuda opencl; do + "$WINDRES" -I "$ICONS" -i "$HERE/igneum-worker-$w.rc" -O coff -o "$RES/igneum-worker-$w.res.o" +done echo "== igneum-worker-cuda.exe" -"$CXX" -std=c++17 -O2 -Wall -Wextra -static -I "$HERE" -I "$RED/include" -o "$HERE/igneum-worker-cuda.exe" "$HERE/worker.cpp" +"$CXX" -std=c++17 -O2 -Wall -Wextra -static -I "$HERE" -I "$RED/include" -o "$HERE/igneum-worker-cuda.exe" "$HERE/worker.cpp" "$RES/igneum-worker-cuda.res.o" "$STRIP" "$HERE/igneum-worker-cuda.exe" echo "== igneum-worker-opencl.exe" "$CC" -std=c99 -O2 -Wall -Wextra -Wno-stringop-truncation -Wno-format-truncation -static -DIGNEUM_CL_DYNAMIC -DCL_TARGET_OPENCL_VERSION=120 \ -I "$RED/include" -I "$PLACEHOLDER" -DIGNEUM_KERNEL_PATH='"kernel_bound.cl"' \ - -o "$ROOT/proto-opencl/igneum-worker-opencl.exe" "$ROOT/proto-opencl/host.c" + -o "$ROOT/proto-opencl/igneum-worker-opencl.exe" "$ROOT/proto-opencl/host.c" "$RES/igneum-worker-opencl.res.o" "$STRIP" "$ROOT/proto-opencl/igneum-worker-opencl.exe" +rm -rf "$RES" for exe in "$HERE/igneum-worker-cuda.exe" "$ROOT/proto-opencl/igneum-worker-opencl.exe"; do printf '%s: %d bytes, imports:' "$(basename "$exe")" "$(stat -f %z "$exe")" x86_64-w64-mingw32-objdump -p "$exe" | sed -n 's/^[[:space:]]*DLL Name: //p' | tr '\n' ' ' echo done +# the icon and version block survived the strip (strip keeps .rsrc; this proves it) +python3 "$VERIFY" --version 0.3.0 "$HERE/igneum-worker-cuda.exe" "$ROOT/proto-opencl/igneum-worker-opencl.exe" diff --git a/proto-cuda/nvrtc/igneum-worker-cuda.rc b/proto-cuda/nvrtc/igneum-worker-cuda.rc new file mode 100644 index 000000000..497b08b48 --- /dev/null +++ b/proto-cuda/nvrtc/igneum-worker-cuda.rc @@ -0,0 +1,35 @@ +// Windows resources for igneum-worker-cuda.exe: the coin icon Explorer shows and the version block under Properties > Details. +// Compiled with x86_64-w64-mingw32-windres (the icon path is relative to brand/icons, passed with -I). +// the project lead's rule (4 October 2026): every shipped exe carries the coin icon and a version block, like the Mac app and DMG. +#include + +1 ICON "igneum.ico" + +1 VERSIONINFO +FILEVERSION 0,3,0,0 +PRODUCTVERSION 0,3,0,0 +FILEFLAGSMASK 0x3fL +FILEFLAGS 0x0L +FILEOS VOS_NT_WINDOWS32 +FILETYPE VFT_APP +FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + VALUE "CompanyName", "Igneum" + VALUE "FileDescription", "Igneum Miner CUDA worker" + VALUE "FileVersion", "0.3.0" + VALUE "InternalName", "igneum-worker-cuda" + VALUE "LegalCopyright", "Igneum contributors" + VALUE "OriginalFilename", "igneum-worker-cuda.exe" + VALUE "ProductName", "Igneum Miner" + VALUE "ProductVersion", "0.3.0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/proto-cuda/nvrtc/igneum-worker-opencl.rc b/proto-cuda/nvrtc/igneum-worker-opencl.rc new file mode 100644 index 000000000..c6fec7595 --- /dev/null +++ b/proto-cuda/nvrtc/igneum-worker-opencl.rc @@ -0,0 +1,35 @@ +// Windows resources for igneum-worker-opencl.exe: the coin icon Explorer shows and the version block under Properties > Details. +// Compiled with x86_64-w64-mingw32-windres (the icon path is relative to brand/icons, passed with -I). +// the project lead's rule (4 October 2026): every shipped exe carries the coin icon and a version block, like the Mac app and DMG. +#include + +1 ICON "igneum.ico" + +1 VERSIONINFO +FILEVERSION 0,3,0,0 +PRODUCTVERSION 0,3,0,0 +FILEFLAGSMASK 0x3fL +FILEFLAGS 0x0L +FILEOS VOS_NT_WINDOWS32 +FILETYPE VFT_APP +FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + VALUE "CompanyName", "Igneum" + VALUE "FileDescription", "Igneum Miner OpenCL worker" + VALUE "FileVersion", "0.3.0" + VALUE "InternalName", "igneum-worker-opencl" + VALUE "LegalCopyright", "Igneum contributors" + VALUE "OriginalFilename", "igneum-worker-opencl.exe" + VALUE "ProductName", "Igneum Miner" + VALUE "ProductVersion", "0.3.0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/proto-opencl/cl_dynamic.h b/proto-opencl/cl_dynamic.h index c146499fc..7544326f9 100644 --- a/proto-opencl/cl_dynamic.h +++ b/proto-opencl/cl_dynamic.h @@ -19,7 +19,7 @@ X(clCreateCommandQueue) X(clCreateProgramWithSource) X(clBuildProgram) X(clGetProgramBuildInfo) X(clCreateKernel) \ X(clSetKernelArg) X(clEnqueueNDRangeKernel) X(clEnqueueReadBuffer) X(clEnqueueWriteBuffer) X(clCreateBuffer) \ X(clReleaseMemObject) X(clReleaseKernel) X(clReleaseProgram) X(clReleaseCommandQueue) X(clReleaseContext) \ - X(clFinish) X(clWaitForEvents) X(clReleaseEvent) X(clGetEventProfilingInfo) X(clGetKernelWorkGroupInfo) \ + X(clFinish) X(clWaitForEvents) X(clReleaseEvent) X(clGetEventInfo) X(clGetEventProfilingInfo) X(clGetKernelWorkGroupInfo) \ X(clGetExtensionFunctionAddressForPlatform) /* One pointer per entry point, typed from the header's declaration (so a signature drift is a compile error). */ @@ -72,6 +72,7 @@ static int ig_cl_load(void) { #define clFinish ig_clFinish #define clWaitForEvents ig_clWaitForEvents #define clReleaseEvent ig_clReleaseEvent +#define clGetEventInfo ig_clGetEventInfo #define clGetEventProfilingInfo ig_clGetEventProfilingInfo #define clGetKernelWorkGroupInfo ig_clGetKernelWorkGroupInfo #define clGetExtensionFunctionAddressForPlatform ig_clGetExtensionFunctionAddressForPlatform