Windows build: generate the OpenCL import library, drop the SDK dependency

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) <noreply@anthropic.com>
This commit is contained in:
jackpotincorporated
2026-06-06 12:38:15 -04:00
parent 8a9d98a21d
commit 00531fb591
6 changed files with 335 additions and 0 deletions
+119
View File
@@ -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
isnt 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 dont 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 cant 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.
Generated
+1
View File
@@ -2230,6 +2230,7 @@ version = "0.1.0"
dependencies = [
"anyhow",
"blake2b_simd",
"cc",
"clap",
"core_affinity",
"ctrlc",
+5
View File
@@ -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
+7
View File
@@ -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
+79
View File
@@ -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."
);
}
}
}
+124
View File
@@ -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