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
16 changes: 9 additions & 7 deletions .github/workflows/cibuildwheel.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,16 @@ jobs:
with:
python-version: "3.11"

- name: Install cibuildwheel and build dependencies
run: |
python -m pip install --upgrade pip
# Install these on the host so cibuildwheel can resolve the paths
python -m pip install cibuildwheel scipy-openblas delvewheel
- name: Install cibuildwheel
run: |
python -m pip install --upgrade pip
python -m pip install cibuildwheel

- name: Build wheels
run: python -m cibuildwheel --output-dir wheelhouse
- name: Install Windows build dependencies
if: runner.os == 'Windows'
run: |
# Install these on the host so cibuildwheel can resolve the paths
python -m pip install scipy-openblas64 delvewheel

- name: Build wheels
run: python -m cibuildwheel --output-dir wheelhouse
Expand Down
1 change: 1 addition & 0 deletions _codeql_detected_source_root
17 changes: 8 additions & 9 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,13 @@ environment = { EASYSBA_USE_ACCELERATE = "1", EASYSBA_LAPACK_LIBS = "" }
[tool.cibuildwheel.windows]
# We already installed these on the host, but we install them in the
# build venv as well to be safe for the repair step.
before-build = "pip install scipy-openblas delvewheel"
before-build = "pip install scipy-openblas64 delvewheel"
# Use the repair-wheel-command to bundle the DLL
repair-wheel-command = "python -c \"import scipy_openblas64, subprocess, sys; subprocess.check_call(['delvewheel', 'repair', '--add-path', scipy_openblas64.get_lib_dir(), '-w', sys.argv[1], sys.argv[2]])\" {dest_dir} {wheel}"
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The inline python -c one-liner has nested quoting and positional sys.argv usage, which is easy to break when edited and hard to troubleshoot in CI logs. A more maintainable approach is to move this logic into a small repository script (or a dedicated module entry point) and invoke that from repair-wheel-command, keeping the TOML simple and reducing quoting/escaping risk.

Copilot uses AI. Check for mistakes.

[tool.cibuildwheel.windows.environment]
EASYSBA_LAPACK_LIBS = "openblas"
# These $(...) commands will now work because scipy-openblas is on the host
INCLUDE = "$(python -c \"import scipy_openblas; print(scipy_openblas.get_include_dir())\");$INCLUDE"
LIB = "$(python -c \"import scipy_openblas; print(scipy_openblas.get_lib_dir())\");$LIB"

[tool.cibuildwheel.windows.repair-wheel-command]
# Use the repair-wheel-command to bundle the DLL
repair-wheel-command = "python -c \"import scipy_openblas, subprocess, sys; subprocess.check_call(['delvewheel', 'repair', '--add-path', scipy_openblas.get_lib_dir(), '-w', sys.argv[1], sys.argv[2]])\" {dest_dir} {wheel}"
EASYSBA_LAPACK_LIBS = "libscipy_openblas64_"
# Only add the library directory for linking. Do NOT add include directory
# to avoid conflicts with scipy-openblas64's lapack.h (has C99 complex types
# incompatible with MSVC). The project has its own lapack_compat.h header.
LIB = "$(python -c \"import scipy_openblas64; print(scipy_openblas64.get_lib_dir())\");$LIB"
33 changes: 19 additions & 14 deletions src/lapack_compat.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,48 +7,53 @@ extern "C" {

typedef int lapack_int;
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

scipy-openblas64 is an ILP64 interface on Windows, which implies LAPACK integer arguments are 64-bit. Keeping lapack_int as int (32-bit on Windows/MSVC) will mismatch the expected ABI for symbols like scipy_dgetrf64_ and can lead to stack/argument corruption. Consider conditionally defining lapack_int as a 64-bit type (e.g., int64_t) when targeting the *64_ symbols, and include the appropriate header for that type.

Copilot uses AI. Check for mistakes.

// On Windows with scipy-openblas64, symbols have scipy_ prefix and 64_ suffix
#if defined(_WIN32) || defined(_WIN64)
#define FORTRAN_WRAPPER(name) scipy_##name##64_
#else
#define FORTRAN_WRAPPER(name) name##_
Comment on lines +10 to 14
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The symbol mangling is currently keyed only on Windows (_WIN32/_WIN64), which hard-codes a single LAPACK ABI choice for all Windows builds. If the project ever links against a different LAPACK/OpenBLAS distribution on Windows (LP64 naming like dgetrf_), this will force incorrect symbol names. Consider gating the scipy_##name##64_ mapping behind an explicit build macro (e.g., EASYSBA_USE_SCIPY_OPENBLAS64 / EASYSBA_LAPACK_ILP64) so consumers can opt in/out without editing headers.

Suggested change
// On Windows with scipy-openblas64, symbols have scipy_ prefix and 64_ suffix
#if defined(_WIN32) || defined(_WIN64)
#define FORTRAN_WRAPPER(name) scipy_##name##64_
#else
#define FORTRAN_WRAPPER(name) name##_
// On Windows with scipy-openblas64, symbols have scipy_ prefix and 64_ suffix.
// Enable this mangling only when explicitly requested via build macros.
#if defined(_WIN32) || defined(_WIN64)
#if defined(EASYSBA_USE_SCIPY_OPENBLAS64) || defined(EASYSBA_LAPACK_ILP64)
#define FORTRAN_WRAPPER(name) scipy_##name##64_
#else
#define FORTRAN_WRAPPER(name) name##_
#endif
#else
#define FORTRAN_WRAPPER(name) name##_

Copilot uses AI. Check for mistakes.
#endif

void dgeqrf_(const lapack_int *m, const lapack_int *n, double *a,
void FORTRAN_WRAPPER(dgeqrf)(const lapack_int *m, const lapack_int *n, double *a,
const lapack_int *lda, double *tau, double *work,
const lapack_int *lwork, lapack_int *info);
void dorgqr_(const lapack_int *m, const lapack_int *n, const lapack_int *k,
void FORTRAN_WRAPPER(dorgqr)(const lapack_int *m, const lapack_int *n, const lapack_int *k,
double *a, const lapack_int *lda, const double *tau,
double *work, const lapack_int *lwork, lapack_int *info);
void dtrtrs_(const char *uplo, const char *trans, const char *diag,
void FORTRAN_WRAPPER(dtrtrs)(const char *uplo, const char *trans, const char *diag,
const lapack_int *n, const lapack_int *nrhs, const double *a,
const lapack_int *lda, double *b, const lapack_int *ldb,
lapack_int *info);
void dpotrf_(const char *uplo, const lapack_int *n, double *a,
void FORTRAN_WRAPPER(dpotrf)(const char *uplo, const lapack_int *n, double *a,
const lapack_int *lda, lapack_int *info);
void dpotrs_(const char *uplo, const lapack_int *n, const lapack_int *nrhs,
void FORTRAN_WRAPPER(dpotrs)(const char *uplo, const lapack_int *n, const lapack_int *nrhs,
const double *a, const lapack_int *lda, double *b,
const lapack_int *ldb, lapack_int *info);
void dgetrf_(const lapack_int *m, const lapack_int *n, double *a,
void FORTRAN_WRAPPER(dgetrf)(const lapack_int *m, const lapack_int *n, double *a,
const lapack_int *lda, lapack_int *ipiv, lapack_int *info);
void dgetrs_(const char *trans, const lapack_int *n, const lapack_int *nrhs,
void FORTRAN_WRAPPER(dgetrs)(const char *trans, const lapack_int *n, const lapack_int *nrhs,
const double *a, const lapack_int *lda, const lapack_int *ipiv,
double *b, const lapack_int *ldb, lapack_int *info);
void dgetri_(const lapack_int *n, double *a, const lapack_int *lda,
void FORTRAN_WRAPPER(dgetri)(const lapack_int *n, double *a, const lapack_int *lda,
const lapack_int *ipiv, double *work, const lapack_int *lwork,
lapack_int *info);
void dgesdd_(const char *jobz, const lapack_int *m, const lapack_int *n,
void FORTRAN_WRAPPER(dgesdd)(const char *jobz, const lapack_int *m, const lapack_int *n,
double *a, const lapack_int *lda, double *s, double *u,
const lapack_int *ldu, double *vt, const lapack_int *ldvt,
double *work, const lapack_int *lwork, lapack_int *iwork,
lapack_int *info);
void dsytrf_(const char *uplo, const lapack_int *n, double *a,
void FORTRAN_WRAPPER(dsytrf)(const char *uplo, const lapack_int *n, double *a,
const lapack_int *lda, lapack_int *ipiv, double *work,
const lapack_int *lwork, lapack_int *info);
void dsytrs_(const char *uplo, const lapack_int *n, const lapack_int *nrhs,
void FORTRAN_WRAPPER(dsytrs)(const char *uplo, const lapack_int *n, const lapack_int *nrhs,
const double *a, const lapack_int *lda, const lapack_int *ipiv,
double *b, const lapack_int *ldb, lapack_int *info);
void dsytri_(const char *uplo, const lapack_int *n, double *a,
void FORTRAN_WRAPPER(dsytri)(const char *uplo, const lapack_int *n, double *a,
const lapack_int *lda, const lapack_int *ipiv, double *work,
lapack_int *info);
void dpotf2_(const char *uplo, const lapack_int *n, double *a,
void FORTRAN_WRAPPER(dpotf2)(const char *uplo, const lapack_int *n, double *a,
const lapack_int *lda, lapack_int *info);
void dpotri_(const char *uplo, const lapack_int *n, double *a,
void FORTRAN_WRAPPER(dpotri)(const char *uplo, const lapack_int *n, double *a,
const lapack_int *lda, lapack_int *info);

#ifdef __cplusplus
Expand Down
Loading