-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathcompat.openblas.lua
More file actions
216 lines (204 loc) · 10.4 KB
/
Copy pathcompat.openblas.lua
File metadata and controls
216 lines (204 loc) · 10.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
-- compat.openblas — OpenBLAS built from source as a portable, BLAS-only static
-- library that satisfies the `blas` capability for consumers (e.g. Eigen's
-- `use_blas` feature, which defines EIGEN_USE_BLAS and links the Fortran-ABI
-- BLAS symbols sgemm_/dgemm_/ddot_/…) AND is usable standalone via <cblas.h>.
--
-- OpenBLAS builds through its own GNU Make system (getarch config generation +
-- per-arch kernels), which does not fit mcpp's "list the .c files" model. The
-- xpkg install() hook runs that Make build (build-dep `xim:make`) and lays the
-- lib + headers under the install dir. Built BLAS-ONLY (NO_LAPACK — LAPACK
-- needs Fortran) with TARGET=GENERIC (portable C kernels), static (NO_SHARED).
--
-- How mcpp is made to run install(): mcpp runs an xpkg's install() hook when its
-- build needs a source that install() PRODUCES (the same way compat.xcb's
-- c_client.py generates xproto.c, which mcpp then compiles). So instead of a
-- mcpp `generated_files` anchor (which would make mcpp self-sufficient and skip
-- install()), install() WRITES the anchor TU itself. mcpp extracts the tarball,
-- finds the anchor missing, runs install() — which builds the lib, writes the
-- 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 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",
name = "compat.openblas",
description = "OpenBLAS — optimized BLAS, built from source (BLAS-only, no Fortran/LAPACK)",
licenses = {"BSD-3-Clause"},
repo = "https://github.com/OpenMathLib/OpenBLAS",
type = "package",
xpm = {
linux = {
deps = { "xim:make@latest" },
["0.3.33"] = {
url = {
GLOBAL = "https://github.com/OpenMathLib/OpenBLAS/releases/download/v0.3.33/OpenBLAS-0.3.33.tar.gz",
CN = "https://gitcode.com/mcpp-res/openblas/releases/download/0.3.33/OpenBLAS-0.3.33.tar.gz",
},
sha256 = "6761af1d9f5d353ab4f0b7497be2643313b36c8f31caec0144bfef198e71e6ab",
},
},
macosx = {
deps = { "xim:make@latest" },
["0.3.33"] = {
url = {
GLOBAL = "https://github.com/OpenMathLib/OpenBLAS/releases/download/v0.3.33/OpenBLAS-0.3.33.tar.gz",
CN = "https://gitcode.com/mcpp-res/openblas/releases/download/0.3.33/OpenBLAS-0.3.33.tar.gz",
},
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",
-- 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" },
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",
},
},
},
}
import("xim.libxpkg.pkginfo")
import("xim.libxpkg.log")
local function sh_quote(value)
return "'" .. tostring(value):gsub("'", "'\\''") .. "'"
end
local function resolve_make()
local mk = pkginfo.build_dep("xim:make") or pkginfo.build_dep("make")
if mk and mk.bin then
local cand = path.join(mk.bin, "make")
if os.isfile(cand) then return cand end
end
return "make"
end
-- The Make build runs inside the install dir and writes a log there (xim's
-- interface mode suppresses subprocess stdout, so an on-disk log is the only way
-- to inspect a failed compile after the fact).
local function _install_impl()
-- The fetched tarball unpacks to OpenBLAS-<ver>/ beside the archive.
local ifile = pkginfo.install_file()
local srcroot = ifile and tostring(ifile):replace(".tar.gz", "")
or ("OpenBLAS-" .. pkginfo.version())
if not os.isdir(srcroot) then
srcroot = "OpenBLAS-" .. pkginfo.version()
end
-- Move the unpacked source into the install dir and build in place (the
-- compat.xcb pattern — the extracted srcroot is transient). `os.cd` is the
-- only directory primitive xim's restricted Lua exposes here (no os.curdir
-- / os.files / os.trymkdir), so paths are formed explicitly via path.join.
local prefix = pkginfo.install_dir()
os.tryrm(prefix)
os.mv(srcroot, prefix)
os.cd(prefix)
-- BLAS-only, portable C kernels, static, single-threaded — no Fortran/LAPACK
-- (those need a Fortran compiler). CC is pinned to gcc for a stable C ABI
-- with the consumer's toolchain. The Make build-dep (`xim:make`) provides a
-- musl-static GNU Make; fall back to PATH `make` if unresolved.
local make = resolve_make()
local jobs = (os.default_njob and os.default_njob()) or 4
local flags = "TARGET=GENERIC NO_FORTRAN=1 NO_LAPACK=1 NO_SHARED=1 "
.. "USE_THREAD=0 USE_OPENMP=0 CC=gcc"
local logf = path.join(prefix, "mcpp_openblas_build.log")
os.exec(string.format("bash -c %s", sh_quote(string.format(
"cd %s && %s -j%d %s libs > %s 2>&1",
sh_quote(prefix), make, jobs, flags, sh_quote(logf)))))
os.exec(string.format("bash -c %s", sh_quote(string.format(
"cd %s && %s %s PREFIX=%s install >> %s 2>&1",
sh_quote(prefix), make, flags, sh_quote(prefix), sh_quote(logf)))))
-- Materialise lib/libopenblas.a. `make install` lays a versioned archive +
-- a `libopenblas.a` symlink under lib/; if the symlink is absent, copy the
-- (root- or lib-) built archive there. xim's Lua has no os.files glob, so
-- the candidate names are enumerated explicitly.
local libdir = path.join(prefix, "lib")
local target_a = path.join(libdir, "libopenblas.a")
if not os.isfile(target_a) then
local versioned = "libopenblas_generic-r" .. pkginfo.version() .. ".a"
local candidates = {
path.join(prefix, "libopenblas.a"),
path.join(libdir, versioned),
path.join(prefix, versioned),
}
local picked
for _, c in ipairs(candidates) do
if os.isfile(c) then picked = c; break end
end
if not picked then
log.error("compat.openblas: build produced no libopenblas archive "
.. "(see %s)", logf)
return false
end
os.cp(picked, target_a)
end
-- Emit the anchor TU mcpp compiles. Its absence after extraction is what
-- makes mcpp run this install() before the build (same trigger as
-- compat.xcb's generated xproto.c); building it here also produces the lib.
io.writefile(path.join(prefix, "mcpp_openblas_anchor.c"),
"int mcpp_compat_openblas_anchor(void) { return 0; }\n")
return true
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))
return false
end
return true
end