Skip to content

Commit 3950da7

Browse files
ErjieWuclaude
andauthored
Native Windows (MSYS2/MinGW-w64) build for ABACUS (Useful Information for Native Windows system) (#7423)
* Native Windows port (Phase 1 scaffolding): serial PW build on MinGW-w64 Lay the groundwork for a native Windows serial plane-wave build (no MPI, no LCAO, no ELPA/PEXSI/hybrid). Targets MinGW-w64 GCC, which ships the POSIX headers ABACUS uses and accepts its GCC attributes, so the source needs only minimal, Linux-safe portability shims. - source_base/fs_compat.h (new): portable ModuleBase::make_directory() wrapping _mkdir (Windows) / mkdir(path,0755) (POSIX). The Windows CRT mkdir takes no permission-mode argument. - global_file.cpp, global_function.cpp: route the 7 mkdir(path,0755) call sites through the helper; drop unistd.h/sys/stat.h includes. - CMakeLists.txt: * gate find_package(ScaLAPACK REQUIRED) on ENABLE_MPI so the serial build does not require a distributed-memory library; * define _USE_MATH_DEFINES/NOMINMAX/_CRT_SECURE_NO_WARNINGS on WIN32; * skip -O3 -g default flags and the -lm link for MSVC; * skip the post-install abacus symlink on Windows. - tools/windows/build-native-serial.ps1 (new): MinGW configure/build helper. - docs/advanced/install_windows_native.md (new): native-build documentation. All changes are guarded or platform-neutral; the Linux build is unaffected. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Native Windows port (Phase 1): serial PW build compiles, links, runs With these fixes the native Windows serial plane-wave build (abacus_pw_ser.exe, MinGW-w64 GCC + OpenBLAS + FFTW) compiles, links, and runs examples/02_scf/01_pw_Si2 to SCF convergence with a deterministic total energy (-215.5057 eV, bit-identical across runs). Build-system fixes: - cmake/FindBlas.cmake, cmake/FindLapack.cmake: the wrappers delegate to CMake's builtin FindBLAS/FindLAPACK, but on the case-insensitive Windows filesystem the wrapper matched itself and recursed forever. Drop our module dir from CMAKE_MODULE_PATH around the builtin call (no-op on Linux). Source portability fixes (all guarded or platform-neutral; Linux unaffected): - module_fft/fft_base.h, fft_cpu.h: remove __attribute__((weak)) from the FFT virtuals. The weak-without-definition pattern relied on the ELF linker resolving unbound weak symbols to null; on Windows/PE (MinGW) it produced null vtable slots, so the first FFT dispatch (FFT_Bundle::setupFFT) called address 0 and segfaulted. Base virtuals get trivial default bodies; the float overrides become concrete via ENABLE_FLOAT_FFTW=ON. - module_parameter/input_conv.h: port the POSIX <regex.h> expression parser to C++ <regex> (MinGW has no <regex.h>). - module_container/base/core/cpu_allocator.cpp: replace posix_memalign with _aligned_malloc/_aligned_free on Windows, applied consistently to both allocate overloads and free. - module_restart/restart.cpp: map POSIX S_IRUSR/S_IWUSR to _S_IREAD/_S_IWRITE and include <io.h> for low-level open/read/write/close on Windows. Tooling/docs: - tools/windows/build-native-serial.ps1: use the verified flags (BLA_VENDOR=OpenBLAS, ENABLE_FLOAT_FFTW=ON, COMMIT_INFO=OFF, the GCC-16 force-include workaround). - docs/advanced/install_windows_native.md: document the gcc-fortran package, the verified build/run, and every source change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Fix all-zero seeded random wavefunctions in serial (non-MPI) PW build psi_initializer::random_t, in the pw_seed>0 branch, generates per-stick random amplitude/phase into stickrr/stickarg and then distributes them into the gathered tmprr/tmparg arrays via stick_to_pool() -- but that call is guarded by #ifdef __MPI. In a serial build tmprr/tmparg therefore stay zero-initialized, so every seeded random wavefunction is all-zero. This later trips Gram-Schmidt orthonormalization ("psi_norm <= 0.0") and aborts the run. The path is never hit in CI because the integration tests run under MPI. Add the serial counterpart: copy each stick directly into tmprr/tmparg using the same mapping as stick_to_pool()'s rank-0 branch (out[ixy2is_[ir]*nz + iz] = stick[iz]). ixy2is_ is populated for both serial and MPI builds via pw_wfc_->getfftixy2is(). Verified on a representative set of 15 tests/01_PW cases run with the native Windows serial PW build (abacus_pw_ser.exe): all converged total energies now match the official result.ref references to <= ~7e-7 eV. Before this fix the 6 cases using pw_seed with random wavefunctions aborted; the other 9 already matched to ~1e-9 eV. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Windows: use the existing toolchain + serial test harness, drop bespoke scripts Per review feedback, the native-Windows support should plug into ABACUS's existing build/test infrastructure (like any other backend/variant) rather than carry its own scripts. Build: add a Windows toolchain variant, mirroring toolchain_gnu.sh / build_abacus_gnu.sh: - toolchain/toolchain_windows.sh -- installs the MinGW-w64 prerequisites via pacman on MSYS2 (gcc, gfortran, openblas, fftw, cmake, ninja) plus bc for the test harness; records the prefix in install/setup like the Linux variants. - toolchain/build_abacus_windows.sh -- configures + builds the serial PW binary (ENABLE_MPI/LCAO=OFF, OpenBLAS+FFTW) and writes abacus_env.sh. Removed the one-off tools/windows/build-native-serial.ps1. Test: reuse tests/integrate/Autotest.sh instead of a separate script. Added a serial mode: with -n 0 the harness runs the binary directly (no mpirun), so a serial build (any OS) reuses the standard catch_properties.sh / result.ref comparison. Added tests/integrate/CASES_SERIAL_PW.txt listing serial-PW cases. Validation (build_abacus_windows.sh, then Autotest.sh -n 0 -f CASES_SERIAL_PW.txt): all 15 01_PW cases run; total energies/forces/stresses match the Linux result.ref to ~1e-7 relative. The few WARNINGs (016/017 etot ~1e-7 eV; 003/009/019 stress/force) are absolute-threshold exceedances from cross-platform / cross-BLAS floating point, classified WARNING (not ERROR) by the harness. docs/advanced/install_windows_native.md updated to describe the toolchain + serial-Autotest flow. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Windows test: run the whole 01_PW suite, drop the curated case list Per review: the serial PW build should be checked against the existing PW test suite (tests/01_PW) via the standard harness, not a hand-picked subset. - Remove tests/integrate/CASES_SERIAL_PW.txt. The canonical list already exists at tests/01_PW/CASES_CPU.txt and is used by the standard ctest registration (tests/01_PW/CMakeLists.txt runs Autotest.sh from that directory). Serial runs just add -n 0: cd tests/01_PW bash ../integrate/Autotest.sh -a <abacus_pw_ser.exe> -n 0 - .gitattributes: force LF for *.sh and CASES_*.txt so the toolchain scripts, Autotest.sh and the bash-parsed case lists work on a fresh Windows checkout (core.autocrlf would otherwise rewrite them to CRLF). - docs/advanced/install_windows_native.md: document the whole-01_PW serial run. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Windows toolchain: provide a generic `abacus` command after build Mirror the Linux toolchain UX: `source abacus_env.sh` then run `abacus`. build_abacus_windows.sh now copies the configured binary (abacus_pw_ser.exe) to abacus.exe in the build dir. Native Windows symlinks need elevation (so the CMake `abacus` symlink step is skipped on WIN32); the .exe copy lets a bare `abacus` resolve in the MSYS2 shell and in cmd/PowerShell. abacus_env.sh already puts that directory (and the MinGW runtime DLLs via the toolchain setup) on PATH. Verified: source abacus_env.sh; abacus --version -> runs from any directory. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Fix Binstream binary file I/O on Windows (force binary fopen mode) Binstream::Binstream/open pass the caller's fopen mode ("r"/"w"/"a") straight through. On Windows that opens in *text* mode, which translates CRLF and treats 0x1A as EOF, corrupting the binary wavefunction/charge files Binstream is built to read -> "Error in Binstream: Some data didn't be read". On POSIX "r" == "rb", so the bug is Windows-only. Binstream is always a binary stream, so append "b" to the mode when the caller omitted it. Harmless no-op on Linux. Fixes these serial 01_PW cases on the native Windows build (verified): - 056_PW_IW (init_wfc=file: read wfc from binary file) - 057_PW_SO_IW (SOC + init_wfc=file) - 075_PW_CHG_BINARY (binary charge I/O) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Fix uninitialized structure factor in serial bspline_sf (wrong energy) Structure_Factor::bspline_sf (nbspline>0, B-spline structure factor) scatters each real-space plane into tmpr via Parallel_Grid::zpiece_to_all, which is guarded by #ifdef __MPI. In a serial build tmpr is never filled (it is new double[nrxx], uninitialized), so real2recip(tmpr, strucFac) produces a garbage structure factor -> grossly wrong total energy, force and stress. CI never hits this path (integration tests run under MPI). Add the serial branch: fill tmpr directly using the SAME real-space layout as zpiece_to_all's serial path, rho[ir*nczp + znow] (xy outer, z innermost; nczp==nz, znow==iz when serial). Verified on tests/01_PW/032_PW_15_CF_CS_bspline (native Windows serial): energy and stress now match the reference to ~1e-8 (was ~1480 eV / 30000 kbar off); residual force ~5e-3 is B-spline interpolation + cross-platform float noise. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(windows): note pw_seed cross-platform non-reproducibility (078 is not a bug) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * toolchain(windows): clarify to run abacus_env.sh inside a mingw bash Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(lcao): guard null deref of DeePKS overlap_orb_alpha when DeePKS is off before_scf() unconditionally dereferenced *(two_center_bundle_.overlap_orb_alpha) to pass it to deepks.build_overlap(). overlap_orb_alpha is only built when DeePKS is enabled (descriptor orbitals); with DeePKS off it is a null unique_ptr, so forming the reference is undefined behaviour (caught as an abort in a debug libstdc++ build; benign in release as the DeePKS stub ignores it). Guard the call on the integrator being present. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Windows toolchain: add LCAO + MPI (MS-MPI + ScaLAPACK) build Extend the native-Windows toolchain to the full supported configuration, mirroring build_abacus_gnu.sh: - toolchain_windows.sh: also pacman-install cereal (LCAO), msmpi (MPI), and scalapack (distributed LCAO eigensolver). Documents that the MS-MPI runtime is a separate system-wide Microsoft redistributable. - build_abacus_windows.sh: build MPI + LCAO by default (abacus_basic_para.exe); ENABLE_MPI / ENABLE_LCAO env toggles select serial / PW-only. Point FindMPI at the MinGW MS-MPI import lib; ScaLAPACK is found automatically when ENABLE_MPI. abacus_env.sh now also exports OPENBLAS_NUM_THREADS=1 (required so OpenBLAS's multithread buffer allocator does not fail under multiple MPI ranks). - docs/advanced/install_windows_native.md: document the LCAO+MPI build, parallel testing (mpiexec / mpirun shim), and the known serial gamma-only LCAO bug (use the MPI build, which is correct to ~1e-11 even on a single rank). Validated against 01_PW / 02_NAO_Gamma / 03_NAO_multik via the standard harness: under MPI all three pass within the cross-platform error range; residual differences are float noise at strict absolute thresholds, gauge-dependent outputs, or excluded features (SCAN/meta-GGA needs LibXC, DFT+U needs MPI). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * toolchain(windows): make the unmodified test harness drive MS-MPI Running tests/integrate/Autotest.sh directly failed with "no mpirun found": MS-MPI ships only mpiexec, and the harness invokes `mpirun -np N`. Three Windows-specific gaps, all fixed in build_abacus_windows.sh so the standard harness works unchanged: * mpirun shim. The build now drops an `mpirun`->`mpiexec` shim next to the binary (on PATH via abacus_env.sh). MS-MPI's `-n`/`-np <N> <prog>` syntax matches what the harness passes, so forwarding args is enough. * OpenBLAS thread pinning. MSYS2's OpenBLAS is OpenMP-threaded (links libgomp), so OMP_NUM_THREADS -- not OPENBLAS_NUM_THREADS -- caps its threads. Autotest sets OMP_NUM_THREADS=nproc/np, so each rank spawned a multithreaded BLAS, the ranks oversubscribed the cores, and OpenBLAS's buffer allocator died ("Memory allocation still failed after 10 retries"). The shim and abacus_env.sh now pin OMP_NUM_THREADS=1 (ABACUS is built USE_OPENMP=OFF, so parallelism is MPI; the BLAS pin costs nothing). * DLL bundling. mpiexec does not propagate PATH to child ranks when stdout is redirected to a file (as the harness does), so the child abacus.exe failed to load libopenblas/libfftw3/libscalapack ("error while loading shared libraries"). The build now copies the dependent MinGW/OpenBLAS/FFTW/ScaLAPACK DLLs next to abacus.exe; Windows searches the application directory before PATH, making the binary self-contained. Verified end to end with the default invocation `bash Autotest.sh -a abacus` (np=4, via the shim): 01_PW/001, 02_NAO_Gamma/scf_afm (gamma-only LCAO), and 03_NAO_multik/scf_pp_upf201 all pass. Corrects the earlier docs/notes that cited OPENBLAS_NUM_THREADS and a hand-made shim. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * toolchain(windows): add MS-MPI Bin (MSMPI_BIN) to PATH in abacus_env.sh The mpirun shim died with `exec: mpiexec: not found`: MSYS2's MinGW shell does not inherit the Windows PATH, and MS-MPI's mpiexec.exe lives in its own Bin dir (only msmpi.dll is in System32). The MSMPI_BIN env var (set by the MS-MPI installer) *is* inherited, so abacus_env.sh now prepends `cygpath -u "$MSMPI_BIN"` to PATH, making both `mpiexec` and the shim resolve. Verified from a minimal PATH: which mpiexec/mpirun both resolve and 01_PW/001 passes via the default harness invocation. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: restore Linux link of FFT_CPU<float> and harden parse_expression Two issues from code review of the Windows-port commits: 1. FFT_CPU<float> undefined references on Linux (regression). The port removed __attribute__((weak)) from the FFT virtuals (it left null vtable slots on PE/MinGW and crashed). But the real FFT_CPU<float> methods live in fft_cpu_float.cpp, which is compiled only when ENABLE_FLOAT_FFTW=ON. With weak gone and float off (the Linux default), the FFT_CPU<float> vtable -- still emitted wherever the class is constructed (FFT_Bundle) -- referenced undefined symbols: undefined reference to `ModuleBase::FFT_CPU<float>::setupFFT()' ... Provide trivial FFT_CPU<float> method definitions in the always-compiled fft_cpu.cpp, guarded by `#if !defined(__ENABLE_FLOAT_FFTW)`, so every vtable slot is valid on any ABI without weak and without pulling in libfftw3f. The float CPU path stays unreachable at runtime (FFT_Bundle::setupFFT WARNING_QUITs for single/mixing CPU FFT unless the macro is set). When the macro is on, the stubs are excluded and fft_cpu_float.cpp supplies the real definitions -- no duplicate symbols. Verified by linking the float vtable TU against fft_cpu.o in both macro states (off: links via stubs; on: links via fft_cpu_float.o), and that dropping both reproduces the reported errors. 2. parse_expression (input_conv.h) could push indeterminate values into vec. If std::regex_search found no match, sub_str stayed empty and was parsed anyway; in the non-multiplication branch `T occ` was uninitialized and the `convert >> occ` extraction was unchecked. Now: a no-match token is an input error (WARNING_QUIT), occ is value-initialized, and a failed extraction fails fast. Consistent with the other expression parsers. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fft: make the weak-vtable trick Windows-safe without touching Linux code Rework the FFT_CPU<float> vtable handling so Linux builds byte-for-byte as upstream and only Windows gets a delta. My earlier port had (a) removed __attribute__((weak)) outright and (b) added trivial float stubs in fft_cpu.cpp -- both changed working Linux core code, and (b) didn't even reach targets that compile fft_bundle.cpp without linking fft_cpu.cpp (e.g. MODULE_HAMILT_XCTest_VXC), so Linux still failed to link: undefined reference to `ModuleBase::FFT_CPU<float>::setupFFT()' ... Root cause: the upstream virtuals are __attribute__((weak)) so the ELF linker nulls the unused FFT_CPU<float> vtable slots when ENABLE_FLOAT_FFTW is off. MinGW/PE has no equivalent -- weak template members there collide ("multiple definition") or leave null slots that crash on dispatch (verified both empirically with g++). Fix, keeping Linux untouched: * Introduce ABACUS_FFT_WEAK = __attribute__((weak)) on non-Windows, empty on _WIN32, and use it in place of the raw attribute in fft_base.h / fft_cpu.h. Preprocessing with -U_WIN32 reproduces the upstream headers exactly (14 weak attrs, no extra defs); fft_cpu.cpp is reverted to pristine. * On Windows the empty macro makes the slots ordinary symbols; the build already sets ENABLE_FLOAT_FFTW=ON, so fft_cpu_float.cpp supplies the real FFT_CPU<float> methods. The non-pure FFT_BASE<T> virtuals (which had no body, relying on weak) get trivial bodies in a `#if defined(_WIN32)` block -- never executed (abstract base; backends override what they use). This block is compiled only on Windows. Verified with MinGW g++: constructing FFT_CPU<float> and dispatching through its vtable links (no multiple-definition, no undefined base/derived refs) and runs (no null-vtable crash); and the Linux-simulated preprocess output matches upstream. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * toolchain(windows): cap default build parallelism by available RAM The Windows build defaulted to -j nproc. On a 20-core box, 20 concurrent -O3 compilations of heavy template TUs (source_cell/module_symmetry/symmetry.cpp, read_pp_upf201.cpp, ...) exhausted memory and ninja died with "cc1plus.exe: out of memory allocating N bytes" -- even with 31 GB RAM. Default -j is now min(nproc, MemTotalGB / 3) (~3 GB budget per job), read from /proc/meminfo; an explicit -j still overrides, and the chosen value is printed with a hint to lower it if cc1plus runs out of memory. Falls back to nproc if /proc/meminfo is unreadable. Not a code issue -- the sources compiled fine up to the OOM. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(windows): remove install_windows_native.md This was a working note for the native-Windows build trial, not reference documentation for the repository. Drop it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 1919027 commit 3950da7

19 files changed

Lines changed: 570 additions & 80 deletions

File tree

.gitattributes

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
# Shell scripts and the bash-parsed integration-test case lists must keep LF
2+
# endings so they run under bash, including MSYS2/Git-Bash on Windows where
3+
# core.autocrlf may rewrite them to CRLF (which breaks `#!/bin/bash` and adds
4+
# stray \r to parsed lines such as the case names in CASES_*.txt).
5+
*.sh text eol=lf
6+
CASES_*.txt text eol=lf
7+
18
.gitattributes export-ignore
29
.gitignore export-ignore
310
.gitmodules export-ignore

CMakeLists.txt

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ if(ENABLE_ASAN)
268268
set(CMAKE_BUILD_TYPE "RelWithDebInfo")
269269
endif()
270270

271-
if(NOT CMAKE_BUILD_TYPE)
271+
if(NOT CMAKE_BUILD_TYPE AND NOT MSVC)
272272
add_compile_options(-O3 -g)
273273
endif()
274274

@@ -289,6 +289,14 @@ if(ENABLE_NATIVE_OPTIMIZATION)
289289
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native -mtune=native")
290290
endif()
291291

292+
# Windows (native build, e.g. MinGW-w64 or MSVC) portability defines:
293+
# _USE_MATH_DEFINES - expose M_PI and friends from <cmath>
294+
# NOMINMAX - stop <windows.h> defining min()/max() macros
295+
# _CRT_SECURE_NO_WARNINGS - silence CRT "use _s function" deprecations
296+
if(WIN32)
297+
add_compile_definitions(_USE_MATH_DEFINES NOMINMAX _CRT_SECURE_NO_WARNINGS)
298+
endif()
299+
292300
if(ENABLE_LCAO)
293301
find_package(Cereal REQUIRED)
294302
include_directories(${CEREAL_INCLUDE_DIR})
@@ -610,8 +618,13 @@ elseif(NOT USE_SW)
610618
find_package(Lapack REQUIRED)
611619
include_directories(${FFTW3_INCLUDE_DIRS})
612620
list(APPEND math_libs FFTW3::FFTW3 LAPACK::LAPACK BLAS::BLAS)
613-
find_package(ScaLAPACK REQUIRED)
614-
list(APPEND math_libs ScaLAPACK::ScaLAPACK)
621+
# ScaLAPACK is a distributed-memory library and is only needed for the
622+
# MPI build. A serial build (e.g. the native Windows serial version)
623+
# must not require it.
624+
if(ENABLE_MPI)
625+
find_package(ScaLAPACK REQUIRED)
626+
list(APPEND math_libs ScaLAPACK::ScaLAPACK)
627+
endif()
615628
if(USE_OPENMP)
616629
list(APPEND math_libs FFTW3::FFTW3_OMP)
617630
endif()
@@ -869,16 +882,23 @@ if (USE_SW)
869882
list(APPEND math_libs gfortran)
870883
endif()
871884

872-
list(APPEND math_libs m)
885+
# libm exists on Linux and MinGW-w64 but not in the MSVC CRT.
886+
if(NOT MSVC)
887+
list(APPEND math_libs m)
888+
endif()
873889
target_link_libraries(${ABACUS_BIN_NAME} ${math_libs})
874890

875891
install(PROGRAMS ${ABACUS_BIN_PATH}
876892
TYPE BIN
877893
# DESTINATION ${CMAKE_INSTALL_BINDIR}
878894
)
879895

880-
# Create a symbolic link 'abacus' pointing to the actual executable
881-
install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink ${ABACUS_BIN_NAME} ${CMAKE_INSTALL_PREFIX}/bin/abacus WORKING_DIRECTORY ${CMAKE_INSTALL_PREFIX}/bin)")
896+
# Create a symbolic link 'abacus' pointing to the actual executable.
897+
# Skipped on Windows: symlink creation needs elevated/developer-mode
898+
# privileges there and the executable carries an .exe suffix anyway.
899+
if(NOT WIN32)
900+
install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink ${ABACUS_BIN_NAME} ${CMAKE_INSTALL_PREFIX}/bin/abacus WORKING_DIRECTORY ${CMAKE_INSTALL_PREFIX}/bin)")
901+
endif()
882902

883903
if(ENABLE_COVERAGE)
884904
coverage_evaluate()

cmake/FindBlas.cmake

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,16 @@ if(DEFINED BLAS_LIBRARY)
55
set(BLAS_LIBRARIES ${BLAS_LIBRARY})
66
endif()
77

8+
# Delegate to CMake's builtin FindBLAS module. On case-insensitive
9+
# filesystems (Windows, macOS) this file "FindBlas.cmake" and the builtin
10+
# "FindBLAS.cmake" resolve to the same name, so a plain find_package(BLAS)
11+
# recurses into this very file. Temporarily remove our module directory
12+
# from CMAKE_MODULE_PATH so the builtin module is used instead. Harmless
13+
# no-op on case-sensitive filesystems.
14+
set(_abacus_blas_saved_module_path "${CMAKE_MODULE_PATH}")
15+
list(REMOVE_ITEM CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}")
816
find_package(BLAS REQUIRED)
17+
set(CMAKE_MODULE_PATH "${_abacus_blas_saved_module_path}")
918

1019
if(NOT TARGET BLAS::BLAS)
1120
add_library(BLAS::BLAS UNKNOWN IMPORTED)

cmake/FindLapack.cmake

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,18 @@ if(DEFINED LAPACK_LIBRARY)
66
set(LAPACK_LIBRARIES ${LAPACK_LIBRARY})
77
endif()
88

9+
# find_package(Blas) must resolve to our cmake/FindBlas.cmake wrapper, so
10+
# leave CMAKE_MODULE_PATH intact for it.
911
find_package(Blas REQUIRED)
12+
13+
# Delegate to CMake's builtin FindLAPACK module. As with FindBlas, the names
14+
# "FindLapack.cmake" and builtin "FindLAPACK.cmake" collide on
15+
# case-insensitive filesystems, so drop our module directory from
16+
# CMAKE_MODULE_PATH around the call to avoid infinite recursion.
17+
set(_abacus_lapack_saved_module_path "${CMAKE_MODULE_PATH}")
18+
list(REMOVE_ITEM CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}")
1019
find_package(LAPACK REQUIRED)
20+
set(CMAKE_MODULE_PATH "${_abacus_lapack_saved_module_path}")
1121

1222
if(NOT TARGET LAPACK::LAPACK)
1323
add_library(LAPACK::LAPACK UNKNOWN IMPORTED)

source/source_base/fs_compat.h

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#ifndef MODULEBASE_FS_COMPAT_H
2+
#define MODULEBASE_FS_COMPAT_H
3+
4+
//==========================================================
5+
// Small filesystem-portability helpers.
6+
//
7+
// The POSIX `mkdir(path, mode)` takes a permission-mode argument that
8+
// does not exist in the Windows CRT (`_mkdir`/MinGW `mkdir` take only a
9+
// path). This header provides a single cross-platform directory-creation
10+
// helper so call sites stay identical on every platform.
11+
//==========================================================
12+
13+
#include <cerrno>
14+
#include <string>
15+
16+
#ifdef _WIN32
17+
#include <direct.h> // _mkdir
18+
#else
19+
#include <sys/stat.h> // mkdir
20+
#include <sys/types.h>
21+
#endif
22+
23+
namespace ModuleBase
24+
{
25+
26+
/**
27+
* @brief Create a single directory, portably.
28+
*
29+
* @param path directory path to create
30+
* @return 0 on success; -1 on failure with `errno` set (e.g. EEXIST when
31+
* the directory already exists), matching POSIX `mkdir` semantics.
32+
*
33+
* On Windows the permission mode is not applicable and is ignored; on
34+
* POSIX systems the directory is created with mode 0755 (subject to umask),
35+
* preserving the previous behaviour of the call sites.
36+
*/
37+
inline int make_directory(const std::string& path)
38+
{
39+
#ifdef _WIN32
40+
return _mkdir(path.c_str());
41+
#else
42+
return mkdir(path.c_str(), 0755);
43+
#endif
44+
}
45+
46+
} // namespace ModuleBase
47+
48+
#endif // MODULEBASE_FS_COMPAT_H

source/source_base/global_file.cpp

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,9 @@
88
#ifdef __MPI
99
#include <mpi.h>
1010
#endif
11-
#include <unistd.h>
12-
#include <sys/stat.h>
13-
#include <sys/types.h>
1411
#include <cerrno>
1512
#include <sstream>
13+
#include "source_base/fs_compat.h"
1614
#include "global_function.h"
1715
#include "global_variable.h"
1816
#include "source_base/parallel_common.h"
@@ -57,7 +55,7 @@ void ModuleBase::Global_File::make_dir_out(
5755
{
5856
if(rank==times)
5957
{
60-
int ret = mkdir(global_out_dir.c_str(), 0755);
58+
int ret = ModuleBase::make_directory(global_out_dir);
6159
if ( ret == 0 || errno == EEXIST )
6260
{
6361
std::cout << " MAKE THE DIR : " << global_out_dir << std::endl;
@@ -95,7 +93,7 @@ void ModuleBase::Global_File::make_dir_out(
9593
{
9694
if(rank==times)
9795
{
98-
int ret = mkdir(global_stru_dir.c_str(), 0755);
96+
int ret = ModuleBase::make_directory(global_stru_dir);
9997
if ( ret == 0 || errno == EEXIST )
10098
{
10199
std::cout << " MAKE THE STRU DIR : " << global_stru_dir << std::endl;
@@ -135,7 +133,7 @@ void ModuleBase::Global_File::make_dir_out(
135133
{
136134
if(rank==times)
137135
{
138-
int ret = mkdir(global_matrix_dir.c_str(), 0755);
136+
int ret = ModuleBase::make_directory(global_matrix_dir);
139137
if ( ret == 0 || errno == EEXIST )
140138
{
141139
std::cout << " MAKE THE MATRIX DIR : " << global_matrix_dir << std::endl;
@@ -174,7 +172,7 @@ void ModuleBase::Global_File::make_dir_out(
174172
{
175173
if(rank==times)
176174
{
177-
int ret = mkdir(global_wfc_dir.c_str(), 0755);
175+
int ret = ModuleBase::make_directory(global_wfc_dir);
178176
if ( ret == 0 || errno == EEXIST )
179177
{
180178
std::cout << " MAKE THE WFC DIR : " << global_wfc_dir << std::endl;
@@ -213,7 +211,7 @@ void ModuleBase::Global_File::make_dir_out(
213211
{
214212
if(rank==times)
215213
{
216-
int ret = mkdir(global_mlkedf_descriptor_dir.c_str(), 0755);
214+
int ret = ModuleBase::make_directory(global_mlkedf_descriptor_dir);
217215
if ( ret == 0 || errno == EEXIST )
218216
{
219217
std::cout << " MAKE THE MLKEDF DESCRIPTOR DIR : " << global_mlkedf_descriptor_dir << std::endl;
@@ -254,7 +252,7 @@ void ModuleBase::Global_File::make_dir_out(
254252
{
255253
if(rank==times)
256254
{
257-
int ret = mkdir(global_deepks_label_elec_dir.c_str(), 0755);
255+
int ret = ModuleBase::make_directory(global_deepks_label_elec_dir);
258256
if ( ret == 0 || errno == EEXIST )
259257
{
260258
std::cout << " MAKE THE DEEPKS LABELS (ELEC) DIR : " << global_deepks_label_elec_dir << std::endl;

source/source_base/global_function.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,8 @@
1616
#include <fstream>
1717
#include <iostream>
1818
#include <string>
19-
#include <sys/stat.h>
20-
#include <sys/types.h>
2119
#include <cerrno>
22-
#include <unistd.h>
20+
#include "source_base/fs_compat.h"
2321

2422
namespace ModuleBase
2523
{
@@ -68,7 +66,7 @@ void MAKE_DIR(const std::string &fn)
6866
// ModuleBase::TITLE("global_function","MAKE_DIR");
6967
if (GlobalV::MY_RANK == 0)
7068
{
71-
int ret = mkdir(fn.c_str(), 0755);
69+
int ret = ModuleBase::make_directory(fn);
7270
if (ret != 0 && errno != EEXIST)
7371
{
7472
ModuleBase::WARNING_QUIT("MAKE_DIR", fn);

source/source_base/module_container/base/core/cpu_allocator.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,48 @@
11
#include <base/core/cpu_allocator.h>
22

3+
#include <cstddef>
4+
#ifdef _WIN32
5+
#include <malloc.h> // _aligned_malloc / _aligned_free
6+
#endif
7+
38
namespace base {
49
namespace core {
510

611
// Allocate a block of CPU memory with the given size and default alignment.
12+
// On Windows the aligned allocator family is used consistently so that every
13+
// pointer handed out by this class can be released through free() below
14+
// (_aligned_malloc memory must not be released with ::operator delete).
715
void *CPUAllocator::allocate(size_t size) {
816
this->allocated_size_ = size;
17+
#ifdef _WIN32
18+
return _aligned_malloc(size, alignof(std::max_align_t));
19+
#else
920
return ::operator new(size);
21+
#endif
1022
}
1123

1224
// Allocate a block of CPU memory with the given size and alignment.
1325
void *CPUAllocator::allocate(size_t size, size_t alignment) {
1426
this->allocated_size_ = size;
1527
void *ptr = nullptr;
28+
#ifdef _WIN32
29+
ptr = _aligned_malloc(size, alignment); // posix_memalign has no Windows CRT equivalent
30+
#else
1631
if (posix_memalign(&ptr, alignment, size) != 0) {
1732
ptr = nullptr;
1833
}
34+
#endif
1935
return ptr;
2036
}
2137

2238
// Free a block of CPU memory that was previously allocated by this allocator.
2339
void CPUAllocator::free(void *ptr) {
2440
this->allocated_size_ = 0;
41+
#ifdef _WIN32
42+
_aligned_free(ptr);
43+
#else
2544
::operator delete(ptr);
45+
#endif
2646
}
2747

2848
// Get the type of device used by the TensorBuffer.

0 commit comments

Comments
 (0)