Skip to content

Add a CMake build system beside configure/make - #966

Open
DiamonDinoia wants to merge 3 commits into
flame:masterfrom
DiamonDinoia:cmake-port
Open

DiamonDinoia wants to merge 3 commits into
flame:masterfrom
DiamonDinoia:cmake-port

Conversation

@DiamonDinoia

@DiamonDinoia DiamonDinoia commented Oct 30, 2026 •

Copy link
Copy Markdown

Hi Devin,

I would like to make contributions to blis but all my editors require cmake for linting to work. I asked AI to port all the options and features from the current build system in cmake. And I asked them to verify that the compile commands are the same flag per flag, file per file object per object.

After a few rounds of review, modularization I ended up with something I understand seems reasonable. I re-wrote the docs as they were full of em dashes. The performance on my machine is the same as other other build system. And in my tests it does what I expect. This does not need to be mainteined for others. You can mark it as experimental dev only. I only need this to be able to make other changes, mainly by hand.

Cheers,
Marco

The CMake build supports the configure options, environment variables, make target names, compiler flags and install layout of the configure/make build. It adds per-config flag tables, the testsuite, blastest and examples in dual mode, documentation in docs/BuildSystemCMake.md~~, and GitHub Actions CI for Linux, macOS and Windows~~.

Assisted-by: XiaomiMiMo/MiMo-V2.6-Pro-MOPD
Assisted-by: claude-sonnet-5-5
Assisted-by: Qwen/Qwen3.8-27B-FP8
Assisted-by: claude-opus-5-5
Assisted-by: mistralai/Mistral-Medium-3.5-128B

The CMake build supports the configure options, environment variables, make target names, compiler flags and install layout of the configure/make build. It adds per-config flag tables, the testsuite, blastest and examples in dual mode, documentation in docs/BuildSystemCMake.md, and GitHub Actions CI for Linux, macOS and Windows.

Squashed from:
- Add CMake per-config flag tables (translation of config/*/make_defs.mk)
- Add CMake build system (configs, threading, BLAS/CBLAS, install, packaging)
- Port testsuite, blastest, and examples to dual-mode CMake
- Document the CMake build (option mapping, plugin flow)
- Audit fixes: memkind link parity, sde emulator path, f2c flags, changelog guard, pc serialization
- Add GitHub Actions CI for the CMake build (parallel to CircleCI make flow)
- Link consumers of openmp builds against OpenMP::OpenMP_C for static linking
- CI: add macOS (firestorm, single/pthreads) and Windows (ucrt64) CMake legs

Assisted-by: XiaomiMiMo/MiMo-V2.6-Pro-MOPD
Assisted-by: claude-sonnet-5-5
Assisted-by: Qwen/Qwen3.8-27B-FP8
Assisted-by: claude-opus-5-5
Assisted-by: mistralai/Mistral-Medium-3.5-128B
@jeffhammond

Copy link
Copy Markdown
Member

I'm concerned that you did not use enough AI models to generate this PR 😆

@DiamonDinoia

Copy link
Copy Markdown
Author

Put them against each other so the keep themselves in check

@devinamatthews

Copy link
Copy Markdown
Member

CMake is definitely overdue, so thanks for the PR. I'll review shortly.

@devinamatthews

Copy link
Copy Markdown
Member

One quick thing I noted: I really would rather not add Github Actions, but I would be OK with moving all of the CircleCI tests to CMake. Given a full working CMake the next release would have the traditional configure script deprecated and then eventually replaced with a CMake wrapper.

@DiamonDinoia

Copy link
Copy Markdown
Author

Happy to do so.

@DiamonDinoia

Copy link
Copy Markdown
Author

I cannot see why it failed so I cannot fix :(

@devinamatthews

Copy link
Copy Markdown
Member

/usr/bin/bash: line 1: pacman: command not found

Comment thread docs/BuildSystemCMake.md Outdated
## Quick start

```sh
cmake -S . -B build -DBLIS_CONFIG=auto -DBLIS_THREADING=openmp

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

-B build will fail. build is part of blis's source. I suggest out.

Comment thread cmake/BlisLibraries.cmake
@@ -0,0 +1,193 @@
function(blis_add_objects name)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Flag pairs are deduplicated. For example:

... --diag_suppress A --diag_suppress B ...

becomes

... --diag_suppress A B ...

This will break NVHPC builds and others (e.g. -mllvm X -mllvm Y).

The CMake build matches configure/make on every checked surface.
Compile commands match make on the checked configs.
The generated blis.pc and config.mk match make apart from prefix
lines.
Every make target name exists as a CMake target.
The install layout matches make, with and without DESTDIR.

Correctness fixes close the wrong-result rows found in review.
Assembly sources compile in the assembler dialect configure uses.
The uninstall targets remove the Windows DLL and import library.
Tests fail on a nonzero exit code or on a FAILURE line in the
output.

The modules split into files with one concern each in cmake/.
The per-config flag tables are generated from the make tables.
The CMake files drop from 7,217 lines to under 3,700.
The top CMakeLists.txt is at most 300 lines.

The minimum version is CMake 3.20...4.4.
The build supports MSVC, clang-cl, the clang GNU frontend and
MinGW.
A CI workflow builds and tests Linux, Windows, MSVC and s390x.
The CMake files are formatted with gersemi.

Assisted-by: Hermes Agent
@bimalgaudel

Copy link
Copy Markdown
Collaborator

🤖 This review was written by Claude (Anthropic's AI model) at my request. I've posted it as-is. Please check the findings before acting on them.

Summary

This is good work. I found no blocking correctness problems for the common gcc/clang paths. Before merge, it needs some CI and doc cleanup and a few compiler-parity fixes.

What was checked

  • Build: configure and build succeed on macOS arm64 (firestorm config, clang 23, CMake 4.4), with no errors.
  • Flag parity: I diffed the per-file compile commands from make -n V=1 libs against ninja -t commands. Both builds compile the same 338 objects with the same flag set. The only differences are harmless: CMake adds its default -arch arm64, writes the -include path as absolute, and puts -std=c99 in a different position.
  • make_defs parser: I ran BlisMakeDefs.cmake over every config/*/make_defs.mk under the gcc, clang, aocc, icc and NVIDIA vendors. All of them parse.
  • CI: all 34 CircleCI jobs pass on the current head.
  • Tests: I did not run the testsuite or blastest locally. CI runs them.
  • The two earlier review comments are addressed:
    • Repeated two-word flags (--diag_suppress A --diag_suppress B) now go through blis_flag_group as one SHELL: group, so CMake no longer de-duplicates them.
    • -B build now stops with a clear error (CMakeLists.txt:9), and the docs use out.

Should fix before merge

  1. .circleci/config.yml
    • cmake-port (the PR branch) was added to the branch filter. It should come out.
    • Comments say "ported 1:1 from .github/workflows/cmake.yml", and that file is no longer in the PR. Some steps also mention setup-cpp, which is a GitHub Actions tool.
    • Earlier in this thread, moving the CircleCI tests to CMake was suggested. The PR instead adds 17 CMake jobs (Windows, macOS, s390x under qemu) next to the existing ones, which roughly doubles CI time. The maintainers should decide whether that's wanted.
  2. docs/PluginHowTo.md is out of date. The new note says a CMake build does not install configure-plugin or the share/blis/*.mk fragments. cmake/make/BlisMakeShare.cmake:198-239 does install both.
  3. The PR description is out of date. It still mentions GitHub Actions CI.

Parity gaps against configure/common.mk

  1. The AOCC version checks use the wrong version number (cmake/BlisDetectCompiler.cmake:89-95). configure reads AOCC's own release number (2.x, 3.x). CMake's CMAKE_C_COMPILER_VERSION is AOCC's underlying LLVM version (10, 11, 12), so AOCC_OT_3_0_0 comes out no for AOCC 2.2 and 2.3. The zen2/zen3 make_defs.mk then pick flags meant for a newer AOCC, and zen3 would likely fail to build with those compilers.
  2. armclang is rejected (cmake/BlisDetectCompiler.cmake:63). CMake's ARMClang compiler ID hits the "unsupported compiler" fatal error. configure treats armclang as clang.
  3. The threading and warning flags are not limited by compiler vendor.
    • -fopenmp and -pthread (cmake/BlisFlags.cmake:188-194, cmake/BlisLinkItems.cmake:37-46) are added for every vendor. common.mk only adds them for gcc, icc and clang, so NVHPC, for example, gets -fopenmp from CMake.
    • -Wno-tautological-compare -Wno-pass-failed (cmake/BlisFlags.cmake:163) and the -fvisibility flags (:215) also apply to aocc. common.mk does not do that.
    • These may be improvements, but since the PR aims for exact parity, they should be documented or matched.
  4. -lrt is added more widely than make adds it (cmake/BlisLinkItems.cmake:80). CMake adds it on every non-Apple UNIX. common.mk adds it only when OS_NAME is Linux. This could matter for Emscripten.
  5. The icc 15 check is slightly off (cmake/BlisDetectCompiler.cmake:230). CMake blacklists skx on any icc 15.0.x. configure only does so when the third version number is below 1. Very minor.
  6. RISC-V cross-detection misses toolchain flags (cmake/BlisAutoDetect.cmake:12). The -E preprocessing call doesn't pass _blis_toolchain_flags. As a result, clang cross-builds that use CMAKE_C_COMPILER_TARGET fall through to the run-based detection and stop with a fatal error. Cross-compilers like riscv64-*-gcc work. The comment says detection "works even when cross-compiling", which isn't fully true.

Nits

  1. blastest can be skipped by mistake (CMakeLists.txt:155). _omit_list MATCHES "xerbla" is a substring match, so BLIS_OMIT_SYMBOLS=xerbla_array alone disables blastest. IN_LIST would be exact.
  2. auto-detect overwrites BLIS_CONFIG (cmake/BlisConfigSelect.cmake:14). The detected config is written back into BLIS_CONFIG with FORCE, so the build directory never re-detects. That's reasonable, but the docs should say so.
  3. The shared library is installed twice (cmake/BlisInstall.cmake:11-25): once through install(TARGETS …) and again through install(FILES $<TARGET_FILE:blis_shared>). If the second copy exists for the make-style partial install targets, a short comment would help.
  4. macOS naming and link differences. These are harmless and noted only for the parity claim:
    • The library file is named libblis.4.0.0.dylib; make names it libblis.4.dylib.
    • In the build tree, the library records an @rpath path. The installed copy gets an absolute path, as with make.
    • CMake drops -lpthread, which macOS's libSystem already provides.

Overall

The make_defs parser, the fail-at-build-time handling for configs whose make_defs stop with an error, and the SHELL: flag groups are all careful work. My suggestion: fix 1–3, fix or document 4–7, and merge as experimental, as the PR description proposes.

— Claude

DiamonDinoia added a commit to DiamonDinoia/blis that referenced this pull request Nov 7, 2026
Give the RISC-V preprocess probe the toolchain flags. A cross clang build can auto-detect its config.
Compare blastest omit names in full. Omitting only xerbla_array no longer disables the blastest drivers.
Correct the docs and the CircleCI comments. No comment names a file or tool that this tree does not have.
Make compiler detection agree with configure: the AOCC release parse, ARMClang to clang, the icc 15.0 skx rule, and correct yes and no flags.
Gate the vendor flags like common.mk: -fopenmp, -pthread, clang warnings, symbol flags and SIMD flags go to the gcc, icc and clang blocks only.
Add -lrt on Linux only, with the Bionic probe.
Give Darwin the raw LIBPTHREAD text that make writes in LDFLAGS and blis.pc.
Move the Windows export flags to the link command. The compile-only copy is gone, and the flags control DLL exports.
Add aocc to the OpenMP, visibility, SIMD and pthread flag branches. This differs from make on purpose, because common.mk has no aocc in those blocks.
Split LIBPTHREAD into one link item for each word and keep one copy on the libblis link rule, as make links it.
Use Threads::Threads for the default pthread item on Android. Bionic keeps the pthread functions in libc.
Rewrite the dylib install name at install time with CMAKE_INSTALL_NAME_TOOL. A --prefix relocation sets the id to the new libdir.
Keep CMake 3.20 compatibility: no IS_EXECUTABLE condition, no NO_CACHE option, and no newer minimum.

Known limits:
- Mach-O compatibility and current version fields stay at make's values (ld64 0.0.0). A dylib built by an earlier CMake revision of this branch with other fields cannot overwrite the installed file.
- A CMake-built executable can record a nonzero Mach-O compatibility version. dyld can then reject a 0.0.0 dylib that replaces the previous file.
- A LIBPTHREAD value with a space-separated option pair, like -L /opt/custom/lib -lpthread, splits into one item for each word. CMake can drop the bare directory item.
- No real AOCC, ARMClang, NVHPC, mingw, macOS or Android NDK toolchain compiled this code. CI is the first real test.

Checked and not changed:
- pthreads link flags with system support off. Probe F1 proved this FALSE. configure forces single threading when system support is off, so the branch does not run.
- A trailing comma in -Wl,--export-all-symbols, that goes to the linker. Probe F3 proved this FALSE. The flag is compile only and does not go to the link editor.

Assisted-by: Claude Opus 5.5 (claude-opus-5-5)
Assisted-by: Qwen/Qwen3.8-27B-FP8
Assisted-by: XiaomiMiMo/MiMo-V2.6-Pro-MOPD
Assisted-by: gpt-6.1-sol (review)
@bimalgaudel

Copy link
Copy Markdown
Collaborator

🤖 Follow-up review by Claude (Anthropic's AI model), posted by me at my request.

Re-checked at 399351a9, rebuilt on macOS arm64 (firestorm, clang 23, CMake 4.4). Thanks for the quick turnaround; nearly everything is addressed:

  • Fixed: the CircleCI branch filter and comments, PluginHowTo, AOCC release parsing, ARMClang, vendor gating of the threading/warning/visibility/SIMD flags (aocc kept on purpose and commented), -lrt on Linux only, the icc 15 rule, the RISC-V toolchain flags, the blastest IN_LIST match, the auto-detect docs, and Mach-O version fields matching make.
  • Good catch on _blis_set_yesno: the original macro always returned no, which my first review missed. The new version gives the right answers for 4.8.0, 10.2.1, 10.3.0 and 14.2.0.
  • Compile flags still match make for all 338 objects, and the libblis link line now matches too (-lm -lpthread).

One remaining bug: a default macOS install leaves the dylib id as @rpath.
cmake --install writes libblis.4.0.0.dylib twice: install(TARGETS …) first, then install(FILES $<TARGET_FILE:blis_shared>) (cmake/BlisInstall.cmake), which overwrites it with the build-tree file whose id is @rpath/libblis.4.dylib. The new install-name rewrite only runs when --prefix differs from the configured prefix, so:

  • cmake --install out → the id is @rpath/libblis.4.dylib, even though BLIS_DARWIN_RPATH is OFF by default. make sets <libdir>/libblis.4.dylib.
  • cmake --install out --prefix X → the id is X/lib/libblis.4.dylib (correct).

A consumer linked with the blis.pc flags records @rpath/libblis.4.dylib and has no LC_RPATH, so it fails to load at runtime. find_package consumers in CI hide this because CMake adds an rpath for them. A simple fix is to run the -id rewrite on every install (drop the NOT _blis_iddir STREQUAL _blis_idcfgdir skip). My first review said the installed copy gets an absolute id; that was not verified, and it's wrong.

Minor:

  • The PR description still says "GitHub Actions CI".
  • For ARMClang, CMAKE_C_COMPILER_VERSION is armclang's own release number, not the "based on LLVM" version that configure reads. The clang version checks therefore use the wrong number. This is harmless for the current arm configs, but worth a comment.

With the install-name fix, this looks ready to merge as experimental from my side.

— Claude

Give the RISC-V preprocess probe the toolchain flags. A cross clang build can auto-detect its config.
Compare blastest omit names in full. Omitting only xerbla_array no longer disables the blastest drivers.
Correct the docs and the CircleCI comments. No comment names a file or tool that this tree does not have.
Make compiler detection agree with configure: the AOCC release parse, ARMClang to clang, the icc 15.0 skx rule, and correct yes and no flags.
Gate the vendor flags like common.mk: -fopenmp, -pthread, clang warnings, symbol flags and SIMD flags go to the gcc, icc and clang blocks only.
Add -lrt on Linux only, with the Bionic probe.
Give Darwin the raw LIBPTHREAD text that make writes in LDFLAGS and blis.pc.
Move the Windows export flags to the link command. The compile-only copy is gone, and the flags control DLL exports.
Add aocc to the OpenMP, visibility, SIMD and pthread flag branches. This differs from make on purpose, because common.mk has no aocc in those blocks.
Split LIBPTHREAD into one link item for each word and keep one copy on the libblis link rule, as make links it.
Use Threads::Threads for the default pthread item on Android. Bionic keeps the pthread functions in libc.
Rewrite the dylib install name at install time with CMAKE_INSTALL_NAME_TOOL. A --prefix relocation sets the id to the new libdir.
Keep CMake 3.20 compatibility: no IS_EXECUTABLE condition, no NO_CACHE option, and no newer minimum.

Known limits:
- Mach-O compatibility and current version fields stay at make's values (ld64 0.0.0). A dylib built by an earlier CMake revision of this branch with other fields cannot overwrite the installed file.
- A CMake-built executable can record a nonzero Mach-O compatibility version. dyld can then reject a 0.0.0 dylib that replaces the previous file.
- A LIBPTHREAD value with a space-separated option pair, like -L /opt/custom/lib -lpthread, splits into one item for each word. CMake can drop the bare directory item.
- No real AOCC, ARMClang, NVHPC, mingw, macOS or Android NDK toolchain compiled this code. CI is the first real test.

Checked and not changed:
- pthreads link flags with system support off. Probe F1 proved this FALSE. configure forces single threading when system support is off, so the branch does not run.
- A trailing comma in -Wl,--export-all-symbols, that goes to the linker. Probe F3 proved this FALSE. The flag is compile only and does not go to the link editor.

Assisted-by: Claude Opus 5.5 (claude-opus-5-5)
Assisted-by: Qwen/Qwen3.8-27B-FP8
Assisted-by: XiaomiMiMo/MiMo-V2.6-Pro-MOPD
Assisted-by: gpt-6.1-sol (review)
@bimalgaudel

Copy link
Copy Markdown
Collaborator

Closing remark written by Claude (Anthropic's AI model). I'm posting it as-is, at my request.

All the findings from the earlier reviews are fixed at 8bc52908. This last note is about scope rather than correctness. It's mostly for the maintainers, since the PR itself only asks to be an experimental build that gets editors linting.

Where the lines go

The PR adds about 4,480 lines: roughly 3,860 of CMake, 430 of CI config and 210 of docs. Most of the CMake isn't building BLIS. It's reproducing make's interface: its target names, config.mk, share/blis, and the partial install and uninstall targets. On top of that, the CI adds a second matrix next to the existing one.

I'd question that goal. If configure eventually becomes a thin wrapper around CMake, as was suggested earlier in this thread, the code that imitates make turns into dead weight. A narrower definition of parity would cover most of what matters: the same objects, the same flags, and the same installed library, headers, blis.pc and find_package package. By my count that drops about a quarter of the PR, and no compile or link flag changes.

What I'd cut

  1. Make target-name aliases in cmake/make/BlisMakeTargets.cmake (about 180 lines). There are around 35 of them: check-env-mk, check-env-fragments, cleanmk, cleanh, distclean, testsuite-run-salt, run-<drv> and so on. Several only check make's own fragment files, which mean nothing in a CMake build. ctest -L fast, ctest -L blastest and cmake --build already cover the real uses.

  2. The make plugin system in CMake builds: most of cmake/make/BlisMakeShare.cmake, plus the raw-tool recording in BlisToolchainEnv.cmake (250 to 300 lines). Right now a CMake build installs two plugin mechanisms. One is configure-plugin with a generated config.mk and share/blis/*.mk, and it only works if that config.mk matches configure's output exactly. The other is BlisPluginHelpers, about 70 lines. I'd keep the second one only.

  3. Uninstall in cmake/make/BlisUninstall.cmake, BlisUninstallOld.cmake and cmake_uninstall.cmake (150 to 210 lines). There are four partial uninstall-* targets, uninstall-old* (which deletes other versions' files by glob), and a full uninstall. CMake projects usually point people at install_manifest.txt instead. At most I'd keep the one script that reads the manifest.

  4. The double dylib install and the install-name rewrite in cmake/BlisInstall.cmake (about 55 lines). install(FILES $<TARGET_FILE:blis_shared>) overwrites what install(TARGETS) just put down. It's only there to mirror make's install-libs/install-lib-symlinks split. That overwrite is what caused the @rpath bug, and the 42-line install_name_tool block exists to undo it. Setting INSTALL_NAME_DIR "$<INSTALL_PREFIX>/${BLIS_INSTALL_LIBDIR}" and installing once would remove both. CMake's docs say INSTALL_NAME_DIR accepts $<INSTALL_PREFIX>, but I haven't checked that it works at the 3.20 minimum.

  5. The CMake CI matrix in .circleci/config.yml (17 jobs, about 300 lines to cut). It runs alongside the make jobs instead of replacing them. For an experimental build, three jobs would do: Linux gcc with openmp plus an install and consumer check, one macOS, and one Windows. The matrix can grow if CMake becomes the main build.

  6. AR/RANLIB/PYTHON environment handling, the rest of cmake/BlisToolchainEnv.cmake (about 60 lines). It copies configure's word-splitting of AR="ar --plugin x" by rewriting CMake's archive rules before project(). CMake users pass -DCMAKE_AR=…, and once item 2 is gone nothing reads the raw strings.

That comes to about 1,000 to 1,100 lines. None of it touches flag assembly, so the per-object parity check (338 objects, identical to make) should still pass.

Smaller things

  • cmake/BlisDetectCompiler.cmake still handles gcc < 4.7, clang 3.x, Apple clang 5, icc 15 and icc 18. All of those are older than CMake 3.20. I'd remove them from configure in the same change, so the two builds keep agreeing.
  • cmake/BlisOptions.cmake accepts none, off, no, omp, pthread, posix and a + separator for threading. configure only documents single, openmp, pthreads, hpx and auto. A cache STRINGS property with exactly those values would replace the parser.
  • BlisFlags.cmake and BlisLinkItems.cmake repeat the gcc/icc/clang/aocc OR chain four times. One list and IN_LIST would replace them.
  • There are about 330 comment lines. Some record review history ("Probe F1 proved…") rather than explaining the code, and I think those belong in commit messages.

What I'd keep

BlisMakeDefs.cmake is the piece I went back and forth on. Having CMake parse make syntax is awkward. But while both build systems exist, it keeps one source of truth for the per-config flags. The alternative is copying about 40 configs' flag tables into CMake and watching them drift. If configure does become a CMake wrapper, convert make_defs.mk into CMake data once and drop the parser then.

The rest should stay as it is: registry expansion, compiler blacklisting and auto-detect, the find_package export and blis.pc, and a small CI matrix with an install and consumer check. These are the parts that are hard to add back later.

None of this blocks merging the build as experimental. It's more about keeping it small if it ever becomes the main build.

Claude

@bimalgaudel

Copy link
Copy Markdown
Collaborator

@devinamatthews Should we merge this?

@DiamonDinoia

DiamonDinoia commented Nov 11, 2026 via email •

Copy link
Copy Markdown
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants