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
12 changes: 7 additions & 5 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ on:
workflow_dispatch:

env:
MCPP_VERSION: "0.0.68"
# Bumped to 0.0.73: carries Windows runtime-DLL deployment, required by the
# compat.openblas Windows recipe (bin/libopenblas.dll staged beside the .exe).
MCPP_VERSION: "0.0.73"

jobs:
lint:
Expand Down Expand Up @@ -238,14 +240,14 @@ jobs:
include:
- platform: macos
os: macos-15
archive: mcpp-0.0.68-macosx-arm64.tar.gz
root: mcpp-0.0.68-macosx-arm64
archive: mcpp-0.0.73-macosx-arm64.tar.gz
root: mcpp-0.0.73-macosx-arm64
mcpp: bin/mcpp
xlings: registry/bin/xlings
- platform: windows
os: windows-latest
archive: mcpp-0.0.68-windows-x86_64.zip
root: mcpp-0.0.68-windows-x86_64
archive: mcpp-0.0.73-windows-x86_64.zip
root: mcpp-0.0.73-windows-x86_64
mcpp: bin/mcpp.exe
xlings: registry/bin/xlings.exe
steps:
Expand Down
64 changes: 58 additions & 6 deletions pkgs/c/compat.openblas.lua
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,18 @@
-- headers, and emits the anchor — then compiles the anchor and links the lib
-- (`-Llib -lopenblas`, with `-Llib` rewritten to <verdir>/lib).
--
-- Platforms: linux/macosx build from source via Make. Windows (no upstream Make
-- path; OpenBLAS ships prebuilt zips) is a follow-up.
-- Platforms:
-- * linux/macosx — build a fully static libopenblas.a from source via Make
-- (no runtime artifact; install() emits the anchor → triggers the build).
-- * windows — no upstream Make path with the MSVC-ABI Clang mcpp links with, so
-- use OpenBLAS's prebuilt x64 zip: link the MSVC import lib (lib/libopenblas.lib)
-- and ship the runtime DLL (bin/libopenblas.dll) beside the consumer's .exe.
-- mcpp >= 0.0.73 deploys a dependency's [runtime] library_dirs *.dll next to
-- the executable (PE has no RPATH); see
-- mcpp/.agents/docs/2026-06-29-windows-runtime-dll-deployment-and-openblas.md.
-- The Windows anchor is a generated_files TU (no Make build), so install()
-- returns immediately on windows. The asymmetry is intentional: only Windows
-- declares a [runtime] library_dir because only Windows ships a runtime DLL.
package = {
spec = "1",
namespace = "compat",
Expand Down Expand Up @@ -50,21 +60,57 @@ package = {
sha256 = "6761af1d9f5d353ab4f0b7497be2643313b36c8f31caec0144bfef198e71e6ab",
},
},
windows = {
-- Prebuilt x64 zip (no Make build). Unpacks to bin/ lib/ include/ with
-- no wrapper dir: bin/libopenblas.dll, lib/libopenblas.lib (MSVC import
-- lib), include/cblas.h.
["0.3.33"] = {
url = {
GLOBAL = "https://github.com/OpenMathLib/OpenBLAS/releases/download/v0.3.33/OpenBLAS-0.3.33-x64.zip",
CN = "https://gitcode.com/mcpp-res/openblas/releases/download/0.3.33/OpenBLAS-0.3.33-x64.zip",
},
sha256 = "7ad797ef0c9a5c42e28903bf726eaaaade307dafe187ff0e923d90cd4002780c",
},
},
},

mcpp = {
language = "c++23",
import_std = false,
c_standard = "c11",
-- The anchor is NOT a generated_files entry: it is emitted by install()
-- so mcpp must run install() (which also builds the lib) before it can
-- compile this source. include/ + lib/ are produced by `make install`.
-- On linux/macosx the anchor is NOT a generated_files entry: it is emitted
-- by install() so mcpp must run install() (which also builds the lib)
-- before it can compile this source. include/ + lib/ are produced by
-- `make install`. On windows there is no Make build, so the anchor is a
-- generated_files TU (see the windows block) and install() is a no-op.
sources = { "mcpp_openblas_anchor.c" },
targets = { ["openblas"] = { kind = "lib" } },
include_dirs = { "include" },
ldflags = { "-Llib", "-lopenblas" },
provides = { "blas" },
deps = { },

-- ldflags are per-OS: the synthesized-manifest parser APPENDS ldflags, so
-- a global `-lopenblas` would also reach the Windows link (wrong: there
-- the lib is the import lib libopenblas.lib). linux/macosx link the static
-- archive; the per-OS merge picks exactly one of these blocks by host.
linux = { ldflags = { "-Llib", "-lopenblas" } },
macosx = { ldflags = { "-Llib", "-lopenblas" } },
windows = {
-- Link the MSVC import lib lib/libopenblas.lib (clang MSVC driver maps
-- `-llibopenblas` → libopenblas.lib). `-Llib` is rewritten to
-- <verdir>/lib by mcpp.
ldflags = { "-Llib", "-llibopenblas" },
-- bin/libopenblas.dll is staged beside the consumer .exe by mcpp's
-- runtime-DLL deployment (mcpp >= 0.0.73). On non-Windows the *.dll
-- filter makes this declaration inert.
runtime = { library_dirs = { "bin" } },
-- No Make build on Windows: provide the anchor TU directly so mcpp is
-- self-sufficient and never triggers install() here. (linux/macosx get
-- their anchor from install(), which is what triggers the Make build.)
generated_files = {
["mcpp_openblas_anchor.c"] = "int mcpp_compat_openblas_anchor(void) { return 0; }\n",
},
},
},
}

Expand Down Expand Up @@ -155,6 +201,12 @@ local function _install_impl()
end

function install()
-- Windows ships a prebuilt zip (import lib + DLL); nothing to build. The
-- anchor TU comes from mcpp `generated_files`, so mcpp never needs install()
-- to produce a source here — this is a no-op success.
if os.host() == "windows" then
return true
end
local ok, err = pcall(_install_impl)
if not ok then
log.error("compat.openblas install() failed: %s", tostring(err))
Expand Down
53 changes: 53 additions & 0 deletions tests/smoke_compat_portable.sh
Original file line number Diff line number Diff line change
Expand Up @@ -414,4 +414,57 @@ EOF
"$MCPP_BIN_POSIX" build
"$MCPP_BIN_POSIX" run

# compat.openblas — Windows-only build-AND-run. This is the only place that
# exercises the mcpp >= 0.0.73 runtime-DLL deployment end to end: the consumer
# links the MSVC import lib (lib/libopenblas.lib) and, at `mcpp run`, loads the
# bin/libopenblas.dll that mcpp staged beside the .exe (a build-only check would
# pass even if the DLL were never deployed). cblas_dgemm computes a 2x2 GEMM and
# the program returns nonzero unless the result is exactly [19 22; 43 50], so a
# wrong/missing BLAS fails the run. Scoped to Windows: linux/macosx link a static
# libopenblas.a (no DLL, nothing new to validate here) and macosx would add a
# multi-minute source build to every portable run.
if [[ "$platform" == "Windows_NT" ]]; then
make_project "compat-portable-openblas-smoke"
cat >> mcpp.toml <<'EOF'

[dependencies.compat]
openblas = "0.3.33"
EOF
cat > src/main.cpp <<'EOF'
#include <cblas.h>

int main() {
// A (2x2, row-major) * B (2x2) = C; expect [[19,22],[43,50]].
const double A[4] = {1, 2, 3, 4};
const double B[4] = {5, 6, 7, 8};
double C[4] = {0, 0, 0, 0};
cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans,
2, 2, 2, 1.0, A, 2, B, 2, 0.0, C, 2);
const double expected[4] = {19, 22, 43, 50};
for (int i = 0; i < 4; ++i) {
if (C[i] != expected[i]) return 10 + i; // wrong/missing BLAS
}
return 0;
}
EOF
"$MCPP_BIN_POSIX" build
# `mcpp run` also prepends the source bin/ to PATH, so it alone would not
# prove the DLL was deployed. Run the produced .exe DIRECTLY from a neutral
# CWD: Windows searches the executable's own directory first, so a clean exit
# proves bin/libopenblas.dll was actually staged beside app.exe (the deploy).
"$MCPP_BIN_POSIX" run
exe="$(find target -type f -name '*.exe' | head -1)"
[ -n "$exe" ] || { echo "FAIL: openblas smoke produced no .exe"; exit 1; }
# Absolutise: `find` yields a path relative to the project dir, but the launch
# runs from a neutral CWD (so the DLL can only be found beside the .exe, not
# via CWD). pwd makes exedir absolute; build an absolute native exe path.
exedir="$(cd "$(dirname "$exe")" && pwd)"
exeabs="$exedir/$(basename "$exe")"
[ -f "$exedir/libopenblas.dll" ] || {
echo "FAIL: libopenblas.dll was not deployed beside the .exe ($exedir)"
ls -la "$exedir"; exit 1; }
( cd / && "$(to_native_path "$exeabs")" ) || {
echo "FAIL: direct .exe launch failed — deployed DLL not loadable"; exit 1; }
fi

echo "OK"
Loading