From 00531fb59135b9f508cff7b7dc92251d645e0aca Mon Sep 17 00:00:00 2001 From: jackpotincorporated Date: Sat, 6 Jun 2026 12:38:15 -0400 Subject: [PATCH] Windows build: generate the OpenCL import library, drop the SDK dependency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Building the `gpu` (OpenCL) backend on Windows no longer needs a vendor OpenCL SDK. `cl-sys` links `OpenCL` (#[link(name = "OpenCL")]); instead of requiring an SDK-provided OpenCL.lib, build.rs now generates a vendor-neutral import library at build time from windows/OpenCL.def (all 118 cl-sys exports) — lib.exe for MSVC (located via the cc crate), dlltool for MinGW — and puts it on the link search path. The real OpenCL.dll is supplied at runtime by the GPU driver. build.rs no-ops on non-Windows targets and when the gpu feature is off, and warns rather than panics if the toolchain tool is absent so `cargo check` still works. Combined with the runtime-loaded (dlopen) CUDA/NVML, a Windows build now needs zero external GPU libraries. Add BUILD-windows.md (toolchain, build, crt-static packaging, runtime deps, CI) and link it from the README. Verified the whole crate compiles for x86_64-pc-windows-gnu (default features and gpu,cuda); Linux is unaffected. Co-Authored-By: Claude Opus 4.8 (1M context) --- BUILD-windows.md | 119 +++++++++++++++++++++++++++++++++++++++++++ Cargo.lock | 1 + Cargo.toml | 5 ++ README.md | 7 +++ build.rs | 79 +++++++++++++++++++++++++++++ windows/OpenCL.def | 124 +++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 335 insertions(+) create mode 100644 BUILD-windows.md create mode 100644 build.rs create mode 100644 windows/OpenCL.def diff --git a/BUILD-windows.md b/BUILD-windows.md new file mode 100644 index 0000000..69da697 --- /dev/null +++ b/BUILD-windows.md @@ -0,0 +1,119 @@ +# Building jackpotminer on Windows + +The miner builds on Windows with **no external GPU SDKs**. The CUDA driver and +NVML are loaded at runtime (`dlopen`/`LoadLibrary`), and the OpenCL import +library is generated at build time from `windows/OpenCL.def` — so you don't need +the CUDA Toolkit, an OpenCL SDK, or any vendor libraries to compile. The whole +codebase (miner + the `jackpotminer-config` GUI) is verified to compile for +`x86_64-pc-windows-*`. + +## Prerequisites + +- **Rust** (https://rustup.rs). The default target is `x86_64-pc-windows-msvc`. +- **MSVC build tools** — Visual Studio 2019/2022 or the standalone *Build Tools + for Visual Studio* with the **“Desktop development with C++”** workload (gives + `link.exe`, `lib.exe`, and the Windows SDK). + - Build from a **“x64 Native Tools Command Prompt for VS”** (or any shell where + the MSVC environment is active) so `lib.exe` is found. `cargo` also locates + it automatically via the registry in most cases. +- *(Alternative toolchain)* the GNU target `x86_64-pc-windows-gnu` works too, + with **MinGW-w64** on `PATH` (provides `dlltool` and the linker). + +No CUDA Toolkit and **no OpenCL SDK** are required. + +## Build + +```powershell +:: Default: OpenCL + CUDA backends + the GUI config tool +cargo build --release + +:: Miner only (no GUI), both GPU backends +cargo build --release --no-default-features --features gpu,cuda + +:: OpenCL only (AMD / Intel / NVIDIA) +cargo build --release --no-default-features --features gpu + +:: NVIDIA only — needs nothing external at build time (CUDA is dlopen'd) +cargo build --release --no-default-features --features cuda +``` + +Outputs: `target\release\jackpotminer.exe` (and `jackpotminer-config.exe` with +the default features). + +### How the OpenCL build dependency is avoided + +`ocl`/`cl-sys` link `OpenCL` (`#[link(name = "OpenCL")]`), which normally needs +an `OpenCL.lib` from a vendor SDK. Instead, `build.rs` generates a +vendor-neutral import library from `windows/OpenCL.def`: + +- **MSVC:** `lib.exe /def:windows\OpenCL.def /out:OpenCL.lib /machine:X64` +- **GNU:** `dlltool -d windows/OpenCL.def -l libOpenCL.a -m i386:x86-64` + +and puts it on the link search path. The import library only forwards to +`OpenCL.dll`, which the GPU driver provides at runtime. If the toolchain tool +isn’t on `PATH`, `build.rs` prints a warning and the link step fails with a clear +message (compilation/`cargo check` still works). + +## Distribution + +Statically link the MSVC C runtime so users don’t need the VC++ redistributable: + +```powershell +set RUSTFLAGS=-C target-feature=+crt-static +cargo build --release +``` + +## Runtime dependencies (on the mining machine) + +- **`OpenCL.dll`** — the ICD loader, installed with any GPU driver (AMD/NVIDIA/ + Intel). Required for the OpenCL backend. +- **`nvcuda.dll`** + **`nvml.dll`** — installed with the NVIDIA driver. Loaded + on demand for the CUDA backend; absent on AMD-only machines, where the miner + simply reports no CUDA devices. +- The **VC++ runtime**, unless you built with `+crt-static`. + +A `cuda`-enabled binary still starts on a machine with no NVIDIA driver. + +## Building Windows binaries without a Windows machine + +### GitHub Actions (recommended) + +```yaml +name: windows +on: [push, workflow_dispatch] +jobs: + build: + runs-on: windows-latest # MSVC + lib.exe already on PATH + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - run: cargo build --release --features gpu,cuda + env: + RUSTFLAGS: -C target-feature=+crt-static + - uses: actions/upload-artifact@v4 + with: + name: jackpotminer-windows + path: target/release/*.exe +``` + +### Cross-compile from Linux (GNU target) + +```bash +rustup target add x86_64-pc-windows-gnu +sudo pacman -S mingw-w64 # or your distro's mingw-w64 (gives dlltool + linker) +cargo build --release --target x86_64-pc-windows-gnu --no-default-features --features gpu,cuda +``` + +The MSVC target can’t be linked from Linux. The GUI config tool (`eframe`) is +easiest to build natively on Windows. + +## Status / caveats + +- **Compilation for Windows is verified** here via `cargo check + --target x86_64-pc-windows-gnu` (default features, and `gpu,cuda`). +- The OpenCL **import-library linking** uses the standard `lib.exe`/`dlltool` + technique; validate it with an actual Windows (or MinGW cross) build, which + needs those tools present. +- `relaunch_in_terminal` (reopen-in-a-terminal on GUI launch) is Linux-only; + harmless on Windows, where double-clicking a console binary already opens a + console. diff --git a/Cargo.lock b/Cargo.lock index 7d4fdf4..bd416c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2230,6 +2230,7 @@ version = "0.1.0" dependencies = [ "anyhow", "blake2b_simd", + "cc", "clap", "core_affinity", "ctrlc", diff --git a/Cargo.toml b/Cargo.toml index 9e74463..10ea380 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,6 +45,11 @@ name = "jackpotminer-config" path = "src/config_gui.rs" required-features = ["config-gui"] +[build-dependencies] +# Locates the MSVC `lib.exe` (to generate the OpenCL import library on Windows); +# unused on other platforms. See build.rs. +cc = "1" + [profile.release] opt-level = 3 lto = true diff --git a/README.md b/README.md index 26485a4..3f8126b 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,13 @@ cargo build --release --no-default-features --features cuda # CUDA only cargo build --release --no-default-features # CPU-only (no GPU) ``` +### Windows + +Builds with no external GPU SDKs — CUDA/NVML are loaded at runtime and the +OpenCL import library is generated at build time. See +[BUILD-windows.md](BUILD-windows.md) for the toolchain, build, and packaging +steps. + ### Portable / distributable builds The miner's only runtime dependencies are the C library and the OpenCL ICD loader diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..8c121be --- /dev/null +++ b/build.rs @@ -0,0 +1,79 @@ +//! Build script — Windows OpenCL import library only. +//! +//! On Windows, the `ocl`/`cl-sys` crates link `OpenCL` (`#[link(name = +//! "OpenCL")]`), which normally requires an `OpenCL.lib` import library from a +//! vendor OpenCL SDK. To avoid that build dependency, we generate a +//! vendor-neutral import library ourselves from `windows/OpenCL.def` (the list +//! of OpenCL exports) using the toolchain's own tools — `lib.exe` for MSVC, +//! `dlltool` for the GNU (MinGW) toolchain — and put it on the link search path. +//! The real `OpenCL.dll` (the ICD loader) is supplied at runtime by the GPU +//! driver, exactly like `libOpenCL.so.1` on Linux. +//! +//! Nothing here is needed on non-Windows targets (the system `libOpenCL` is used +//! directly) or when the `gpu` (OpenCL) feature is off — the script no-ops. The +//! CUDA driver / NVML are loaded at runtime via dlopen (see `src/dylib.rs`), so +//! they need no build-script support. + +use std::env; +use std::path::PathBuf; +use std::process::Command; + +fn main() { + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed=windows/OpenCL.def"); + + // Only Windows targets with the OpenCL backend need an import library. + if env::var("CARGO_CFG_TARGET_OS").as_deref() != Ok("windows") { + return; + } + if env::var_os("CARGO_FEATURE_GPU").is_none() { + return; + } + + let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); + let def = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()).join("windows/OpenCL.def"); + let target = env::var("TARGET").unwrap_or_default(); + let is_msvc = env::var("CARGO_CFG_TARGET_ENV").as_deref() == Ok("msvc"); + + let (mut cmd, tool) = if is_msvc { + // lib.exe /def:OpenCL.def /out:OpenCL.lib /machine:X64 + let mut c = cc::windows_registry::find(&target, "lib.exe") + .unwrap_or_else(|| Command::new("lib.exe")); + c.arg("/nologo") + .arg(format!("/def:{}", def.display())) + .arg(format!("/out:{}", out_dir.join("OpenCL.lib").display())) + .arg("/machine:X64"); + (c, "lib.exe") + } else { + // GNU/MinGW: dlltool -d OpenCL.def -l libOpenCL.a -m i386:x86-64 + let dlltool = ["x86_64-w64-mingw32-dlltool", "dlltool"] + .into_iter() + .find(|t| Command::new(t).arg("--version").output().is_ok()) + .unwrap_or("dlltool"); + let mut c = Command::new(dlltool); + c.arg("-d") + .arg(&def) + .arg("-l") + .arg(out_dir.join("libOpenCL.a")) + .arg("-m") + .arg("i386:x86-64"); + (c, "dlltool") + }; + + match cmd.status() { + Ok(s) if s.success() => { + // Resolve `#[link(name = "OpenCL")]` against the generated import lib. + println!("cargo:rustc-link-search=native={}", out_dir.display()); + } + Ok(s) => panic!("{tool} failed ({s}) generating the OpenCL import library from {}", def.display()), + Err(e) => { + // Tool not found: let `cargo check` (which doesn't link) still succeed; + // a real build will fail at link with a clear "cannot find OpenCL". + println!( + "cargo:warning=could not run {tool} to generate the OpenCL import library ({e}); \ + ensure the toolchain tools are on PATH (MSVC dev prompt, or mingw-w64). \ + Linking the `gpu` feature will fail until then." + ); + } + } +} diff --git a/windows/OpenCL.def b/windows/OpenCL.def new file mode 100644 index 0000000..880c4b5 --- /dev/null +++ b/windows/OpenCL.def @@ -0,0 +1,124 @@ +; OpenCL import library definition for jackpotminer's Windows build. +; Generated from the cl-sys 0.4.3 exports so a vendor-neutral OpenCL.lib / +; libOpenCL.a can be produced at build time without an OpenCL SDK. The actual +; OpenCL.dll (ICD loader) is provided at runtime by the GPU driver. +LIBRARY OpenCL +EXPORTS + clBuildProgram + clCloneKernel + clCompileProgram + clCreateBuffer + clCreateCommandQueue + clCreateCommandQueueWithProperties + clCreateContext + clCreateContextFromType + clCreateFromGLBuffer + clCreateFromGLRenderbuffer + clCreateFromGLTexture + clCreateFromGLTexture2D + clCreateFromGLTexture3D + clCreateImage + clCreateImage2D + clCreateImage3D + clCreateKernel + clCreateKernelsInProgram + clCreatePipe + clCreateProgramWithBinary + clCreateProgramWithBuiltInKernels + clCreateProgramWithIL + clCreateProgramWithSource + clCreateSampler + clCreateSamplerWithProperties + clCreateSubBuffer + clCreateSubDevices + clCreateUserEvent + clEnqueueAcquireGLObjects + clEnqueueBarrier + clEnqueueBarrierWithWaitList + clEnqueueCopyBuffer + clEnqueueCopyBufferRect + clEnqueueCopyBufferToImage + clEnqueueCopyImage + clEnqueueCopyImageToBuffer + clEnqueueFillBuffer + clEnqueueFillImage + clEnqueueMapBuffer + clEnqueueMapImage + clEnqueueMarker + clEnqueueMarkerWithWaitList + clEnqueueMigrateMemObjects + clEnqueueNativeKernel + clEnqueueNDRangeKernel + clEnqueueReadBuffer + clEnqueueReadBufferRect + clEnqueueReadImage + clEnqueueReleaseGLObjects + clEnqueueSVMFree + clEnqueueSVMMap + clEnqueueSVMMemcpy + clEnqueueSVMMemFill + clEnqueueSVMMigrateMem + clEnqueueSVMUnmap + clEnqueueTask + clEnqueueUnmapMemObject + clEnqueueWaitForEvents + clEnqueueWriteBuffer + clEnqueueWriteBufferRect + clEnqueueWriteImage + clFinish + clFlush + clGetCommandQueueInfo + clGetContextInfo + clGetDeviceAndHostTimer + clGetDeviceIDs + clGetDeviceInfo + clGetEventInfo + clGetEventProfilingInfo + clGetExtensionFunctionAddress + clGetExtensionFunctionAddressForPlatform + clGetGLContextInfoKHR + clGetGLObjectInfo + clGetGLTextureInfo + clGetHostTimer + clGetImageInfo + clGetKernelArgInfo + clGetKernelInfo + clGetKernelSubGroupInfo + clGetKernelWorkGroupInfo + clGetMemObjectInfo + clGetPipeInfo + clGetPlatformIDs + clGetPlatformInfo + clGetProgramBuildInfo + clGetProgramInfo + clGetSamplerInfo + clGetSupportedImageFormats + clLinkProgram + clReleaseCommandQueue + clReleaseContext + clReleaseDevice + clReleaseEvent + clReleaseKernel + clReleaseMemObject + clReleaseProgram + clReleaseSampler + clRetainCommandQueue + clRetainContext + clRetainDevice + clRetainEvent + clRetainKernel + clRetainMemObject + clRetainProgram + clRetainSampler + clSetDefaultDeviceCommandQueue + clSetEventCallback + clSetKernelArg + clSetKernelArgSVMPointer + clSetKernelExecInfo + clSetMemObjectDestructorCallback + clSetUserEventStatus + clSVMAlloc + clSVMFree + clUnloadCompiler + clUnloadPlatformCompiler + clWaitForEvents