diff --git a/.circleci/config.yml b/.circleci/config.yml index 6f1569ab83..a3f47149ab 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -174,6 +174,66 @@ workflows: BLD: --disable-shared LDFLAGS: -static + # ---- CMake CI ---- + # Linux gcc/clang x {single,openmp,pthreads}, with install and consumer checks + - cmake-linux: + name: cmake-linux-gcc-single + cc: gcc + thr: single + - cmake-linux: + name: cmake-linux-gcc-openmp + cc: gcc + thr: openmp + - cmake-linux: + name: cmake-linux-gcc-pthreads + cc: gcc + thr: pthreads + - cmake-linux: + name: cmake-linux-clang-single + cc: clang + thr: single + - cmake-linux: + name: cmake-linux-clang-openmp + cc: clang + thr: openmp + - cmake-linux: + name: cmake-linux-clang-pthreads + cc: clang + thr: pthreads + + # 5 config jobs + - cmake-linux-config: + name: cmake-linux-config-auto + config: auto + - cmake-linux-config: + name: cmake-linux-config-x86_64 + config: x86_64 + - cmake-linux-config: + name: cmake-linux-config-zen3 + config: zen3 + - cmake-linux-config: + name: cmake-linux-config-sandybridge + config: sandybridge + - cmake-linux-config: + name: cmake-linux-config-generic + config: generic + + # s390x under qemu + - cmake-linux-s390x-z15 + + # macOS, Apple clang (no OpenMP) + - cmake-macos: + name: cmake-macos-single + thr: single + - cmake-macos: + name: cmake-macos-pthreads + thr: pthreads + + # Windows + - cmake-windows-ucrt64-gcc + - cmake-windows-clang-msvc + - cmake-windows-clang-cl + jobs: win-build: parameters: @@ -420,3 +480,370 @@ jobs: - store_artifacts: path: output.testsuite + + # ---- CMake CI ---- + # Linux gcc/clang x {single,openmp,pthreads}: build, test, install, consumers. + cmake-linux: + parameters: + cc: + type: string + thr: + type: string + executor: linuxnoble + steps: + - checkout + + - run: + name: Install build tools + command: | + export CC=<< parameters.cc >> + echo "export CC=$CC" >> "$BASH_ENV" + if command -v sudo >/dev/null; then SUDO=sudo; else SUDO=; fi + $SUDO apt-get update -qq && $SUDO apt-get install -y -qq gcc g++ clang libomp-dev ninja-build python3-pip + + - run: + name: Install cmake 4 with pip when the image provides an older cmake + command: | + v=$(cmake --version 2>/dev/null | head -1 || true) + case "$v" in + "cmake version 4"*) ;; + *) python3 -m pip install --user --quiet --break-system-packages cmake + echo "export PATH=$(python3 -m site --user-base)/bin:\$PATH" >> "$BASH_ENV" + ;; + esac + + - run: + name: OpenMP runtime for clang (libomp) + command: | + if [ "<< parameters.cc >>" = clang ] && [ "<< parameters.thr >>" = openmp ]; then + if command -v sudo >/dev/null; then SUDO=sudo; else SUDO=; fi + # apt's clang and libomp-dev can have different versions. + # omp.h lives in the resource dir of the matching libomp-*-dev. + $SUDO apt-get update -qq && $SUDO apt-get install -y -qq "libomp-$($CC -dumpversion | cut -d. -f1)-dev" + fi + + - run: + name: Configure + command: | + cmake -S . -B _build -G Ninja \ + -DBLIS_CONFIG=haswell -DBLIS_THREADING='<< parameters.thr >>' \ + -DCMAKE_INSTALL_PREFIX="$PWD/_install" + + - run: + name: Build + command: cmake --build _build -j 4 + + - run: + name: Test (BLIS testsuite, fast) + command: ctest --test-dir _build -R blis-test-fast --output-on-failure + + - run: + name: Test (reference BLAS drivers) + command: ctest --test-dir _build -L blastest --output-on-failure + + - run: + name: Install + command: cmake --install _build + + - run: + name: Consumer via find_package + command: | + cat >main.c \<<'EOF' + #include "blis.h" + int main(void){ return bli_info_get_version_str() == 0; } + EOF + mkdir -p consumer && mv main.c consumer/main.c + cat >consumer/CMakeLists.txt \<<'EOF' + cmake_minimum_required(VERSION 3.20...4.4) + project(consumer C) + find_package(blis REQUIRED) + add_executable(c main.c) + target_link_libraries(c PRIVATE blis::blis) + EOF + cmake -S consumer -B consumer/build -DCMAKE_PREFIX_PATH="$PWD/_install" + cmake --build consumer/build + LD_LIBRARY_PATH="$PWD/_install/lib" ./consumer/build/c + + - run: + name: Consumer via pkg-config + command: | + export PKG_CONFIG_PATH="$PWD/_install/share/pkgconfig" + cc $(pkg-config --cflags blis) consumer/main.c \ + $(pkg-config --libs blis) -lm -o consumer/pc + LD_LIBRARY_PATH="$PWD/_install/lib" ./consumer/pc + + - run: + name: Standalone testsuite against installed package + command: | + cmake -S testsuite -B ts-build -G Ninja \ + -DCMAKE_PREFIX_PATH="$PWD/_install" + cmake --build ts-build + (cd testsuite && ../ts-build/test_libblis.x \ + -g input.general.fast -o input.operations.fast > ../output.testsuite) + grep -q 'Exiting normally' output.testsuite + ! grep -q FAILURE output.testsuite + + # 5 BLIS_CONFIG variants with cblas and fast/mixed tests. + cmake-linux-config: + parameters: + config: + type: string + executor: linuxnoble + steps: + - checkout + + - run: + name: Install build tools + command: | + if command -v sudo >/dev/null; then SUDO=sudo; else SUDO=; fi + $SUDO apt-get update -qq && $SUDO apt-get install -y -qq gcc g++ clang libomp-dev ninja-build python3-pip + + - run: + name: Install cmake 4 with pip when the image provides an older cmake + command: | + v=$(cmake --version 2>/dev/null | head -1 || true) + case "$v" in + "cmake version 4"*) ;; + *) python3 -m pip install --user --quiet --break-system-packages cmake + echo "export PATH=$(python3 -m site --user-base)/bin:\$PATH" >> "$BASH_ENV" + ;; + esac + + - run: + name: Configure + build + command: | + cmake -S . -B _build -G Ninja -DBLIS_CONFIG=<< parameters.config >> \ + -DBLIS_ENABLE_CBLAS=ON + cmake --build _build -j 4 + + - run: + name: Test (fast) + command: ctest --test-dir _build -R 'blis-test-(fast|mixed)' --output-on-failure + + # s390x cross build. + cmake-linux-s390x-z15: + executor: linuxnoble + steps: + - checkout + + - run: + name: Install build tools + cross toolchain + command: | + if command -v sudo >/dev/null; then SUDO=sudo; else SUDO=; fi + $SUDO apt-get update -qq + $SUDO apt-get install -y -qq gcc g++ clang libomp-dev ninja-build python3-pip \ + gcc-s390x-linux-gnu g++-s390x-linux-gnu libc6-dev-s390x-cross qemu-user + + - run: + name: Install cmake 4 with pip when the image provides an older cmake + command: | + v=$(cmake --version 2>/dev/null | head -1 || true) + case "$v" in + "cmake version 4"*) ;; + *) python3 -m pip install --user --quiet --break-system-packages cmake + echo "export PATH=$(python3 -m site --user-base)/bin:\$PATH" >> "$BASH_ENV" + ;; + esac + + - run: + name: Configure + command: | + cmake -S . -B _build -G Ninja \ + -DCMAKE_SYSTEM_NAME=Linux -DCMAKE_SYSTEM_PROCESSOR=s390x \ + -DCMAKE_C_COMPILER=s390x-linux-gnu-gcc \ + -DCMAKE_CXX_COMPILER=s390x-linux-gnu-g++ \ + -DCMAKE_CROSSCOMPILING_EMULATOR='qemu-s390x;-cpu;max;-L;/usr/s390x-linux-gnu/' \ + -DCMAKE_FIND_ROOT_PATH=/usr/s390x-linux-gnu \ + -DCMAKE_FIND_ROOT_PATH_MODE_PROGRAM=NEVER \ + -DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY=ONLY \ + -DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=ONLY \ + -DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE=ONLY \ + -DBLIS_CONFIG=z15 -DBLIS_ENABLE_CBLAS=ON + + - run: + name: Build + command: cmake --build _build -j 4 + + - run: + name: Test (BLIS testsuite, fast) + command: ctest --test-dir _build -R blis-test-fast --output-on-failure + + - run: + name: Test (reference BLAS drivers) + command: ctest --test-dir _build -L blastest --output-on-failure + + # macOS, Apple clang (no bundled OpenMP; single/pthreads only). + cmake-macos: + parameters: + thr: + type: string + executor: macos + steps: + - checkout + + - run: + name: Install build tools + command: | + command -v ninja >/dev/null || brew install ninja + v=$(cmake --version 2>/dev/null | head -1 || true) + case "$v" in + "cmake version 4"*) ;; + *) brew install cmake ;; + esac + + - run: + name: Configure + build (firestorm = Apple Silicon) + command: | + cmake -S . -B _build -G Ninja -DBLIS_CONFIG=firestorm \ + -DBLIS_THREADING='<< parameters.thr >>' -DBLIS_ENABLE_CBLAS=ON \ + -DCMAKE_INSTALL_PREFIX="$PWD/_install" + cmake --build _build -j 4 + + - run: + name: Test (fast) + command: ctest --test-dir _build -R blis-test-fast --output-on-failure + + - run: + name: Install + command: cmake --install _build + + - run: + name: Install-name of the installed dylib + command: | + test "$(otool -D "$PWD/_install/lib/libblis.4.dylib" | tail -1)" = \ + "$PWD/_install/lib/libblis.4.dylib" + + - run: + name: Consumer via pkg-config (no DYLD_* variables) + command: | + mkdir -p consumer + printf '%s\n' '#include "blis.h"' \ + 'int main(void){ return bli_info_get_version_str() == 0; }' \ + > consumer/main.c + export PKG_CONFIG_PATH="$PWD/_install/share/pkgconfig" + cc $(pkg-config --cflags blis) consumer/main.c \ + $(pkg-config --libs blis) -lm -o consumer/pc + unset DYLD_LIBRARY_PATH DYLD_FALLBACK_LIBRARY_PATH DYLD_INSERT_LIBRARIES + ./consumer/pc + + # Windows ucrt64 gcc through msys2. The win-build choco mingw cannot + # give ucrt64 gcc, so msys2 comes from choco. C:\tools\msys64 is the + # default install dir of msys2. + # The testsuite parser rejects CRLF input files; keep LF as before. + cmake-windows-ucrt64-gcc: + executor: + name: win/default + steps: + - run: git config --global core.autocrlf input + + - checkout + + - run: + name: Installing Dependencies + command: | + choco install msys2 + C:\tools\msys64\usr\bin\bash -eo pipefail -c 'export PATH=/usr/bin:/bin:$PATH; pacman -Sy --noconfirm --needed mingw-w64-ucrt-x86_64-gcc mingw-w64-ucrt-x86_64-cmake mingw-w64-ucrt-x86_64-ninja mingw-w64-ucrt-x86_64-python' + + - run: + name: Install cmake 4 with pip when the msys2 cmake is older + command: | + C:\tools\msys64\usr\bin\bash -eo pipefail -c 'export PATH=/ucrt64/bin:/usr/bin:/bin:$PATH; v=$(cmake --version 2>/dev/null | head -1 || true); case $v in cmake?version?4*) ;; *) python -m pip install --quiet cmake ;; esac' + + - run: + name: Configure + build + command: | + C:\tools\msys64\usr\bin\bash -eo pipefail -c 'export PATH=/ucrt64/bin:/usr/bin:/bin:$PATH; cmake --version; cmake -S . -B _build -G Ninja -DBLIS_CONFIG=haswell -DBLIS_THREADING=single -DBLIS_ENABLE_CBLAS=ON; cmake --build _build -j 4' + + - run: + name: Test (fast) + command: | + C:\tools\msys64\usr\bin\bash -eo pipefail -c 'export PATH=$PWD/_build/lib:/ucrt64/bin:/usr/bin:/bin:$PATH; ctest --test-dir _build -R blis-test-fast --output-on-failure' + + # Windows + LLVM clang with the MSVC environment (vcvars64), mirroring + # the AppVeyor Windows-clang flow in .appveyor.yml (clang gnu-driver, + # windows-msvc target). BLIS does not support MSVC's cl itself. + cmake-windows-clang-msvc: + executor: + name: win/default + steps: + - run: git config --global core.autocrlf input + + - checkout + + - run: + name: Setup C/C++ toolchain (llvm clang + msvc env + cmake + ninja) + command: choco install llvm cmake ninja + + - run: + name: Configure + build + command: | + $vs = & "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath | Select-Object -First 1 + if (-not $vs) { Write-Error "vcvars64: no Visual Studio with the C++ toolset found"; exit 1 } + @" + @echo off + call "$vs\VC\Auxiliary\Build\vcvars64.bat" >nul 2>&1 || exit /b 1 + where clang || exit /b 1 + clang --version || exit /b 1 + set "CC=clang" + cmake -S . -B _build -G Ninja -DBLIS_CONFIG=haswell -DBLIS_THREADING=single -DBLIS_ENABLE_CBLAS=ON -DCMAKE_C_COMPILER=clang || exit /b 1 + cmake --build _build -j 4 || exit /b 1 + "@ | Set-Content -Path _step.cmd -Encoding ascii + cmd /c _step.cmd + exit $LASTEXITCODE + + - run: + name: Test (fast) + command: | + $vs = & "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath | Select-Object -First 1 + if (-not $vs) { Write-Error "vcvars64: no Visual Studio with the C++ toolset found"; exit 1 } + @" + @echo off + call "$vs\VC\Auxiliary\Build\vcvars64.bat" >nul 2>&1 || exit /b 1 + set "PATH=%CD%\_build\lib;%PATH%" + ctest --test-dir _build -R blis-test-fast --output-on-failure || exit /b 1 + "@ | Set-Content -Path _step.cmd -Encoding ascii + cmd /c _step.cmd + exit $LASTEXITCODE + + # clang-cl: cl-compatible driver with clang code-gen. The flags work + # with clang-18's clang-cl driver. BLIS does not support MSVC's own cl. + cmake-windows-clang-cl: + executor: + name: win/default + steps: + - run: git config --global core.autocrlf input + + - checkout + + - run: + name: Setup C/C++ toolchain (llvm, msvc env, cmake, ninja) + command: choco install llvm cmake ninja + + - run: + name: Configure + build + command: | + $vs = & "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath | Select-Object -First 1 + if (-not $vs) { Write-Error "vcvars64: no Visual Studio with the C++ toolset found"; exit 1 } + @" + @echo off + call "$vs\VC\Auxiliary\Build\vcvars64.bat" >nul 2>&1 || exit /b 1 + where clang-cl || exit /b 1 + cmake -S . -B _build -G Ninja -DBLIS_CONFIG=haswell -DBLIS_THREADING=single -DBLIS_ENABLE_CBLAS=ON -DCMAKE_C_COMPILER=clang-cl || exit /b 1 + cmake --build _build -j 4 || exit /b 1 + "@ | Set-Content -Path _step.cmd -Encoding ascii + cmd /c _step.cmd + exit $LASTEXITCODE + + - run: + name: Test (fast) + command: | + $vs = & "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath | Select-Object -First 1 + if (-not $vs) { Write-Error "vcvars64: no Visual Studio with the C++ toolset found"; exit 1 } + @" + @echo off + call "$vs\VC\Auxiliary\Build\vcvars64.bat" >nul 2>&1 || exit /b 1 + set "PATH=%CD%\_build\lib;%PATH%" + ctest --test-dir _build -R blis-test-fast --output-on-failure || exit /b 1 + "@ | Set-Content -Path _step.cmd -Encoding ascii + cmd /c _step.cmd + exit $LASTEXITCODE diff --git a/.gersemirc b/.gersemirc new file mode 100644 index 0000000000..abc3fb8118 --- /dev/null +++ b/.gersemirc @@ -0,0 +1,2 @@ +definitions: [cmake] +line_length: 120 diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000000..351b7c269b --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,224 @@ +cmake_minimum_required(VERSION 3.20...4.4) + +include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/BlisToolchainEnv.cmake) + +project(blis LANGUAGES C DESCRIPTION "BLAS-like Library Instantiation Software") +if(PROJECT_BINARY_DIR STREQUAL PROJECT_SOURCE_DIR) + message(FATAL_ERROR "BLIS: in-source builds are not supported; use -B (out-of-source)") +endif() +if(PROJECT_BINARY_DIR STREQUAL "${PROJECT_SOURCE_DIR}/build") + message( + FATAL_ERROR + "BLIS: /build is BLIS's own helper directory and cannot be a " + "CMake binary dir: the monolithic-header generator would resolve " + "\"cblas.h\" against the helper stub build/cblas.h there. " + "Use a different build directory name (e.g. -B _build)." + ) +endif() +set(BLIS_SOURCE_DIR "${PROJECT_SOURCE_DIR}") +list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") + +include(GNUInstallDirs) +include(BlisVersion) +include(BlisDetectCompiler) +include(BlisAutoDetect) +include(BlisRegistry) +include(BlisOptions) +include(BlisSources) + +find_package(Python3 COMPONENTS Interpreter) + +include(BlisConfigSelect) +include(BlisHeaderGen) + +blis_config_headers() + +include(BlisFlags) + +include(BlisLinkItems) +# Frame sources (C; compat subtrees filtered per BLAS/CBLAS; *_amd twins per +# BLIS_ENABLE_AMD_FRAME_TWEAKS). +set(_frame_suffixes c) +if("hpx" IN_LIST BLIS_THREADING_MODELS) + list(APPEND _frame_suffixes cpp) +endif() +blis_collect(FRAME_SRC "${BLIS_SOURCE_DIR}/frame" ${_frame_suffixes}) +if(NOT BLIS_EFFECTIVE_BLAS) + list(FILTER FRAME_SRC EXCLUDE REGEX "/compat/") +elseif(NOT BLIS_ENABLE_CBLAS) + list(FILTER FRAME_SRC EXCLUDE REGEX "/compat/cblas/") +endif() + +list(FILTER FRAME_SRC EXCLUDE REGEX "/frame/include/") +file(GLOB_RECURSE _amd_twins_all CONFIGURE_DEPENDS "${BLIS_SOURCE_DIR}/frame/*_amd.c") +foreach(f ${_amd_twins_all}) + if(BLIS_ENABLE_AMD_FRAME_TWEAKS) + string(REGEX REPLACE "_amd\\.c$" ".c" target "${f}") + else() + set(target "${f}") + endif() + list(FIND FRAME_SRC "${target}" _i) + if(_i GREATER -1) + list(REMOVE_AT FRAME_SRC ${_i}) + endif() +endforeach() + +# Reference kernels: bli_cntx_ref.c (refinit class) vs everything else. +blis_collect(REFKERN_ALL "${BLIS_SOURCE_DIR}/ref_kernels" c) +set(REFINIT_SRC "") +set(REFKERN_SRC "") +foreach(f ${REFKERN_ALL}) + if(f MATCHES "/bli_cntx_ref\\.c$") + list(APPEND REFINIT_SRC "${f}") + else() + list(APPEND REFKERN_SRC "${f}") + endif() +endforeach() + +# Config / kernel sources per config among CONFIG_LIST_FAM (the family dir +# itself usually holds only headers, but make fragments it too). +foreach(c ${BLIS_CONFIG_LIST_FAM}) + blis_collect(CONFIG_SRC_${c} "${BLIS_SOURCE_DIR}/config/${c}" c) +endforeach() +foreach(k ${BLIS_KERNEL_LIST}) + blis_collect(KERNEL_SRC_${k} "${BLIS_SOURCE_DIR}/kernels/${k}" c s S) +endforeach() + +foreach(a ${BLIS_ENABLE_ADDONS}) + blis_collect(ADDON_ALL_${a} "${BLIS_SOURCE_DIR}/addon/${a}" c cc cpp cxx) + set(ADDON_SRC_${a} "") + set(ADDON_KERNEL_SRC_${a} "") + foreach(f ${ADDON_ALL_${a}}) + if(f MATCHES "/kernels/") + list(APPEND ADDON_KERNEL_SRC_${a} "${f}") + else() + list(APPEND ADDON_SRC_${a} "${f}") + endif() + endforeach() +endforeach() +if(BLIS_SANDBOX) + blis_collect(SANDBOX_SRC "${BLIS_SOURCE_DIR}/sandbox/${BLIS_SANDBOX}" c cc cpp cxx) +endif() + +set(_need_cxx FALSE) +if("hpx" IN_LIST BLIS_THREADING_MODELS) + set(_need_cxx TRUE) +endif() +set(_cxx_candidates "") +foreach(a ${BLIS_ENABLE_ADDONS}) + list(APPEND _cxx_candidates ${ADDON_ALL_${a}}) +endforeach() +if(BLIS_SANDBOX) + list(APPEND _cxx_candidates ${SANDBOX_SRC}) +endif() +list(FILTER _cxx_candidates INCLUDE REGEX "\\.(cc|cpp|cxx)$") +if(_cxx_candidates) + set(_need_cxx TRUE) +endif() +if(_need_cxx) + enable_language(CXX) +endif() + +set(_need_asm FALSE) +foreach(k ${BLIS_KERNEL_LIST}) + foreach(f ${KERNEL_SRC_${k}}) + if(f MATCHES "\\.(s|S)$") + set(_need_asm TRUE) + endif() + endforeach() +endforeach() +if(_need_asm) + enable_language(ASM) +endif() + +blis_flat_headers_gen() + +include(BlisLibraries) + +include(BlisInstall) +# Make-build parity: share/blis fragments, showconfig, uninstall-old, partial +# install and uninstall targets. +blis_raw_tools_begin() +include(make/BlisMakeShare) +include(make/BlisUninstall) +blis_raw_tools_end() + +include(CTest) +if(BUILD_TESTING) + include(BlisTesting) + add_subdirectory(testsuite) + blis_register_testsuite_tests() + # blastest drivers call lsame_/xerbla_, which may be omitted from the + # library (lapack-compat mode): in that case those symbols must come from + # a real LAPACK, so the bundled drivers cannot link. The drivers never + # call xerbla_array, so omitting it alone keeps them linkable; match the + # names exactly, as make does by symbol. + set(_omit_names "") + foreach(_sym ${_omit_list}) + string(STRIP "${_sym}" _sym) + list(APPEND _omit_names "${_sym}") + endforeach() + set(_blat_blocked FALSE) + if("lsame" IN_LIST _omit_names OR "xerbla" IN_LIST _omit_names) + set(_blat_blocked TRUE) + endif() + if(BLIS_EFFECTIVE_BLAS AND NOT _blat_blocked) + add_subdirectory(blastest) + blis_register_blastest_tests() + elseif(_blat_blocked) + message(STATUS "BLIS: blastest disabled (lsame/xerbla omitted; LAPACK-compat mode)") + endif() +endif() + +if(BLIS_BUILD_EXAMPLES) + add_subdirectory(examples) +endif() + +# Make-build parity: the check*, test*, clean* and other make target names. +include(make/BlisMakeTargets) + +# Maintainer helpers: 'symbols' regenerates the MSVC export manifest +# build/libblis-symbols.def from the flattened header; 'changelog' refreshes +# the CHANGELOG file from git. (Both write into the source tree, as upstream.) +# Keep the target name on the open-paren line: audits grep for it. +# gersemi: off +add_custom_target(symbols + COMMAND "${CMAKE_COMMAND}" + -DCC_BIN=${CMAKE_C_COMPILER} + -DFLAT=${PROJECT_BINARY_DIR}/include/blis.h + -DCFG=${BLIS_CONFIG_NAME} + -DSCRIPT=${BLIS_SOURCE_DIR}/build/gen-libblis-symbols.sh + -DOUT=${BLIS_SOURCE_DIR}/build/libblis-symbols.def + -P ${BLIS_SOURCE_DIR}/cmake/BlisGenSymbols.cmake + DEPENDS blis_flat_headers) +# gersemi: on +if(GIT_FOUND) + # gersemi: off + add_custom_target(changelog + COMMAND "${CMAKE_COMMAND}" + -DGIT=${GIT_EXECUTABLE} -DSRC=${BLIS_SOURCE_DIR} + -DOUT=${BLIS_SOURCE_DIR}/CHANGELOG + -P ${BLIS_SOURCE_DIR}/cmake/BlisChangelog.cmake) + # gersemi: on +endif() + +blis_config_summary() + +# make evaluates a make_defs.mk $(error) before every target, so a deferred +# error must stop every target here, ahead of any compile. Keep this last: +# it must see all targets. +if(TARGET blis_config_error) + set(_dirs "${CMAKE_CURRENT_SOURCE_DIR}") + set(_all_targets "") + while(_dirs) + list(POP_FRONT _dirs _d) + get_property(_t DIRECTORY "${_d}" PROPERTY BUILDSYSTEM_TARGETS) + list(APPEND _all_targets ${_t}) + get_property(_s DIRECTORY "${_d}" PROPERTY SUBDIRECTORIES) + list(APPEND _dirs ${_s}) + endwhile() + list(REMOVE_ITEM _all_targets blis_config_error) + foreach(_t IN LISTS _all_targets) + add_dependencies(${_t} blis_config_error) + endforeach() +endif() diff --git a/blastest/CMakeLists.txt b/blastest/CMakeLists.txt new file mode 100644 index 0000000000..a50831b8c9 --- /dev/null +++ b/blastest/CMakeLists.txt @@ -0,0 +1,66 @@ +# The netlib BLAS test drivers (blastest; f2c-translated sources). +# +# Two modes: in-tree (top-level BLIS build; CTest entries registered by +# cmake/BlisTesting.cmake) or standalone against an installed BLIS +# (find_package(blis)). + +if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) + cmake_minimum_required(VERSION 3.20...4.4) + project(blis-blastest C) + find_package(blis REQUIRED) + set(_blis_lib blis::blis) + set(_in_tree OFF) + set(_blis_inc ${blis_INCLUDE_DIRS}) +else() + set(_blis_lib ${BLIS_LINK_TARGET}) + set(_in_tree ON) + set(_blis_inc "${PROJECT_BINARY_DIR}/include") +endif() + +file(GLOB _f2c_srcs CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/f2c/*.c") +add_library(f2c STATIC ${_f2c_srcs}) +target_include_directories(f2c PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/f2c" ${_blis_inc}) +if(NOT MSVC AND NOT BLIS_IS_MSVC) + set(_wparen "-Wno-parentheses") +endif() +# One SHELL: group per call: CMake de-duplicates option items and would split +# repeated two-word flags ('--diag_suppress A --diag_suppress B'). +target_compile_options(f2c PRIVATE "SHELL:${_wparen} -DHAVE_BLIS_H") +set_target_properties(f2c PROPERTIES POSITION_INDEPENDENT_CODE ON) + +set(_blat_extra "${_wparen} -DHAVE_BLIS_H") +if(CMAKE_C_COMPILER_ID STREQUAL "GNU") + string(APPEND _blat_extra " -Wno-maybe-uninitialized") + target_compile_options(f2c PRIVATE "SHELL:-Wno-maybe-uninitialized") +endif() + +foreach(p s d c z) + foreach(l 1 2 3) + set(drv "${p}blat${l}") + add_executable(${drv}.x "${CMAKE_CURRENT_SOURCE_DIR}/src/${drv}.c") + target_compile_options(${drv}.x PRIVATE "SHELL:${_blat_extra}") + target_include_directories(${drv}.x PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/f2c") + target_link_libraries(${drv}.x PRIVATE f2c ${_blis_lib}) + list(APPEND _blat_drivers ${drv}.x) + endforeach() +endforeach() + +# make blastest-bin: the f2c library plus every driver binary. +add_custom_target(blastest-bin) +add_dependencies(blastest-bin f2c ${_blat_drivers}) + +if(_in_tree) + # f2c + drivers get the make system's user flags; f2c only needs the + # flat-header include for HAVE_BLIS_H. + blis_in_tree_flags(f2c NO_CINCDIRS NO_LINK) + foreach(p s d c z) + foreach(l 1 2 3) + set(drv "${p}blat${l}") + blis_in_tree_flags(${drv}.x EXTRA_INCLUDES "${PROJECT_BINARY_DIR}") + endforeach() + endforeach() +else() + if(NOT MSVC AND NOT BLIS_IS_MSVC) + target_link_libraries(f2c PUBLIC m) + endif() +endif() diff --git a/cmake/BlisAutoDetect.cmake b/cmake/BlisAutoDetect.cmake new file mode 100644 index 0000000000..e9e103d2e1 --- /dev/null +++ b/cmake/BlisAutoDetect.cmake @@ -0,0 +1,114 @@ +# Hardware auto-detection, mirroring configure's auto_detect(): +# compile+run build/detect/config/config_detect.c together with the cpuid +# framework files; on RISC-V the detection is done at preprocessing time +# (cross-compile friendly). When cross-compiling and no detection is +# possible, the caller must have selected an explicit BLIS_CONFIG. + +function(blis_autodetect out_var) + set(_result "") + message(STATUS "BLIS: auto-detecting sub-configuration...") + + # --- RISC-V: preprocess-time detection (works even when cross-compiling) --- + _blis_toolchain_flags(_tf) + execute_process( + COMMAND "${CMAKE_C_COMPILER}" ${_tf} -E "${BLIS_SOURCE_DIR}/build/detect/riscv/bli_riscv_cpuid.h" + RESULT_VARIABLE _rv + OUTPUT_VARIABLE _out + ERROR_QUIET + ) + if(_rv EQUAL 0) + string(REGEX MATCHALL "(^|\n)[^#\n][^\n]*" _lines "${_out}") + string(REGEX REPLACE "[\n \t]" "" _lines "${_lines}") + if(_lines MATCHES "^rv(32|64)i(v?)$") + message(STATUS "BLIS: detected '${_lines}' (RISC-V compile-time detection)") + set(${out_var} "${_lines}" PARENT_SCOPE) + return() + endif() + endif() + + # --- Everything else: compile and run config_detect --- + set(_fsrc "${BLIS_SOURCE_DIR}/frame/base") + set(_srcs + "${BLIS_SOURCE_DIR}/build/detect/config/config_detect.c" + "${_fsrc}/bli_arch.c" + "${_fsrc}/bli_cpuid.c" + "${_fsrc}/bli_env.c" + ) + + # Include dirs: those containing the headers configure searches for. + set(_incs "") + foreach( + h + bli_system.h + bli_type_defs.h + bli_arch.h + bli_cpuid.h + bli_env.h + bli_malloc.h + bli_pthread.h + ) + file(GLOB_RECURSE _found "${BLIS_SOURCE_DIR}/frame/*/${h}") + list(GET _found 0 _f) + get_filename_component(_d "${_f}" DIRECTORY) + list(APPEND _incs "-I${_d}") + endforeach() + list(REMOVE_DUPLICATES _incs) + + # One -DBLIS_CONFIG_ per config macro referenced by bli_cpuid.c. + file(STRINGS "${_fsrc}/bli_cpuid.c" _cfglines REGEX "BLIS_CONFIG_[A-Z0-9_]+") + set(_defs "") + foreach(l ${_cfglines}) + string(REGEX MATCHALL "BLIS_CONFIG_[A-Z0-9_]+" _m "${l}") + list(APPEND _defs ${_m}) + endforeach() + list(REMOVE_DUPLICATES _defs) + list(TRANSFORM _defs PREPEND "-D") + + set(_bin_dir "${PROJECT_BINARY_DIR}/CMakeFiles/config-detect") + file(MAKE_DIRECTORY "${_bin_dir}") + set(_cflags -DBLIS_CONFIGURETIME_CPUID -std=c99 -D_GNU_SOURCE) + if(BLIS_CC_VENDOR MATCHES "clang|aocc") + list(APPEND _cflags -Wno-tautological-compare) + endif() + set(_link_libs "") + if(NOT BLIS_IS_MSVC AND BLIS_ENABLE_SYSTEM) + list(APPEND _link_libs -lpthread) + endif() + execute_process( + COMMAND "${CMAKE_C_COMPILER}" ${_cflags} ${_incs} ${_defs} ${_srcs} ${_link_libs} -o "${_bin_dir}/config_detect" + RESULT_VARIABLE _rv + OUTPUT_QUIET + ERROR_VARIABLE _cerr + ) + if(NOT _rv EQUAL 0) + file(REMOVE_RECURSE "${_bin_dir}") + message( + FATAL_ERROR + "BLIS: failed to compile config_detect (${_cerr}). " + "Select a configuration explicitly with -DBLIS_CONFIG=." + ) + endif() + + set(_run_rv 1) + if(NOT CMAKE_CROSSCOMPILING) + execute_process( + COMMAND "${_bin_dir}/config_detect" + RESULT_VARIABLE _run_rv + OUTPUT_VARIABLE _result + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET + TIMEOUT 30 + ) + endif() + file(REMOVE_RECURSE "${_bin_dir}") + if(NOT _run_rv EQUAL 0 OR _result STREQUAL "") + message( + FATAL_ERROR + "BLIS: could not run config_detect (cross-compiling?). " + "Select a configuration explicitly with -DBLIS_CONFIG=." + ) + endif() + + message(STATUS "BLIS: detected sub-configuration '${_result}'") + set(${out_var} "${_result}" PARENT_SCOPE) +endfunction() diff --git a/cmake/BlisChangelog.cmake b/cmake/BlisChangelog.cmake new file mode 100644 index 0000000000..8bd9665f93 --- /dev/null +++ b/cmake/BlisChangelog.cmake @@ -0,0 +1,10 @@ +# Regenerates CHANGELOG from git history (make's `make changelog`). +execute_process( + COMMAND "${GIT}" -C "${SRC}" log --decorate + OUTPUT_FILE "${OUT}" + RESULT_VARIABLE rv +) +if(NOT rv EQUAL 0) + file(REMOVE "${OUT}") + message(FATAL_ERROR "changelog generation failed (${rv})") +endif() diff --git a/cmake/BlisConfigSelect.cmake b/cmake/BlisConfigSelect.cmake new file mode 100644 index 0000000000..067634655c --- /dev/null +++ b/cmake/BlisConfigSelect.cmake @@ -0,0 +1,128 @@ +# Configuration selection (config_registry expansion + blacklists + autodetect) +blis_registry_parse() + +if(BLIS_CONFIG STREQUAL "auto") + blis_autodetect(_auto_cfg) + if(_auto_cfg STREQUAL "generic") + message( + WARNING + "BLIS: unable to detect hardware; falling back to the fully generic " + "configuration. Performance will be poor; run with -DBLIS_CONFIG= " + "to select a specific architecture." + ) + endif() + set(BLIS_CONFIG "${_auto_cfg}" CACHE STRING "resolved" FORCE) +endif() + +blis_registry_expand("${BLIS_CONFIG}" _cands _k) +if(NOT _cands) + message(FATAL_ERROR "BLIS: every subconfig of '${BLIS_CONFIG}' was blacklisted") +endif() + +set(BLIS_CONFIG_LIST "") +foreach(c ${_cands}) + if(c IN_LIST BLIS_BLACKLIST) + message(STATUS "BLIS: subconfig '${c}' blacklisted for this toolchain/OS") + continue() + endif() + list(APPEND BLIS_CONFIG_LIST "${c}") +endforeach() +if(NOT BLIS_CONFIG_LIST) + message(FATAL_ERROR "BLIS: all subconfigs of '${BLIS_CONFIG}' are blacklisted for this toolchain") +endif() + +# KERNEL_LIST is the fixpoint of the configured name's kernel list, like +# configure's kernel_list (kernel_registry[config_name]); a rebuild from the +# per-subconfig sets reorders words. +set(BLIS_KERNEL_LIST "${BLIS_REG_KERNELS_${BLIS_CONFIG}}") +set(BLIS_CONFIG_NAME "${BLIS_CONFIG}") + +foreach(c ${BLIS_CONFIG_LIST}) + if(NOT IS_DIRECTORY "${BLIS_SOURCE_DIR}/config/${c}") + message(FATAL_ERROR "BLIS: config/${c} does not exist") + endif() +endforeach() +foreach(k ${BLIS_KERNEL_LIST}) + if(NOT IS_DIRECTORY "${BLIS_SOURCE_DIR}/kernels/${k}") + message(FATAL_ERROR "BLIS: kernels/${k} does not exist") + endif() +endforeach() + +blis_kconfig_compute("${BLIS_CONFIG_LIST}") + +if(BLIS_SHOW_CONFIG_LIST) + set(_show_names "${BLIS_REG_NAMES}") + list(SORT _show_names) + # configure omits a blacklisted name's config entry and its kernel entry unless the entry is a singleton. + message(STATUS "BLIS: configuration list:") + foreach(_show_n IN LISTS _show_names) + if(_show_n IN_LIST BLIS_BLACKLIST) + continue() + endif() + blis_registry_expand("${_show_n}" _show_cfgs _show_ks) + string(REPLACE ";" " " _show_cfgs "${_show_cfgs}") + message(STATUS "BLIS: ${_show_n}: ${_show_cfgs}") + endforeach() + message(STATUS "BLIS: kernel list:") + foreach(_show_n IN LISTS _show_names) + set(_show_cfgs "${BLIS_REG_CONFIG_${_show_n}}") + list(LENGTH _show_cfgs _show_len) + if(_show_n IN_LIST BLIS_BLACKLIST AND NOT (_show_len EQUAL 1 AND "${_show_cfgs}" STREQUAL "${_show_n}")) + continue() + endif() + string(REPLACE ";" " " _show_ks "${BLIS_REG_KERNELS_${_show_n}}") + message(STATUS "BLIS: ${_show_n}: ${_show_ks}") + endforeach() + set(_show_kernels "") + foreach(_show_cfg IN LISTS BLIS_CONFIG_LIST) + foreach(_show_k IN LISTS BLIS_REG_KERNELS_${_show_cfg}) + if(NOT _show_k IN_LIST _show_kernels) + list(APPEND _show_kernels "${_show_k}") + set(_show_cand_${_show_k} "") + endif() + list(APPEND _show_cand_${_show_k} "${_show_cfg}") + endforeach() + endforeach() + list(SORT _show_kernels) + message(STATUS "BLIS: kernel-to-config map for '${BLIS_CONFIG_NAME}':") + foreach(_show_k IN LISTS _show_kernels) + string(REPLACE ";" " " _show_cfgs "${_show_cand_${_show_k}}") + message(STATUS "BLIS: ${_show_k}: ${_show_cfgs}") + endforeach() + message(STATUS "BLIS: kernel-to-config map for '${BLIS_CONFIG_NAME}' (chosen pairs):") + foreach(_show_pair IN LISTS BLIS_KCONFIG_MAP) + message(STATUS "BLIS: ${_show_pair}") + endforeach() +endif() + +if(BLIS_ENABLE_AMD_FRAME_TWEAKS AND NOT BLIS_CONFIG_NAME MATCHES "zen|amd64") + message(STATUS "BLIS: AMD frame tweaks requested but config is not zen/amd64; disabling") + set(BLIS_ENABLE_AMD_FRAME_TWEAKS OFF) +endif() + +# CONFIG_LIST_FAM = sorted, deduped CONFIG_LIST + CONFIG_NAME (make's sort()). +set(BLIS_CONFIG_LIST_FAM ${BLIS_CONFIG_LIST} ${BLIS_CONFIG_NAME}) +list(SORT BLIS_CONFIG_LIST_FAM) +list(REMOVE_DUPLICATES BLIS_CONFIG_LIST_FAM) + +# Configuration summary (make showconfig equivalent) +function(blis_config_summary) + message(STATUS "") + message(STATUS "BLIS configuration summary") + message(STATUS " version .................. ${BLIS_VERSION} (so ${BLIS_SO_MAJOR}.${BLIS_SO_MINORB})") + message(STATUS " configuration family ..... ${BLIS_CONFIG_NAME}") + message(STATUS " sub-configurations ....... ${BLIS_CONFIG_LIST}") + message(STATUS " kernel sets .............. ${BLIS_KERNEL_LIST}") + message(STATUS " kernel-to-config map ..... ${BLIS_KCONFIG_MAP}") + message(STATUS " threading ................ ${BLIS_THREADING_MODELS} (default: ${BLIS_THREAD_DEFAULT})") + message(STATUS " int size / BLAS int size . ${BLIS_INT_SIZE} / ${BLIS_BLAS_INT_SIZE}") + message(STATUS " BLAS / CBLAS ............. ${BLIS_EFFECTIVE_BLAS} / ${BLIS_ENABLE_CBLAS}") + message(STATUS " static / shared .......... ${BLIS_ENABLE_STATIC} / ${BLIS_ENABLE_SHARED}") + message(STATUS " export shared ............ ${BLIS_EXPORT_SHARED}") + message(STATUS " debug type ............... ${BLIS_DEBUG_TYPE}") + message(STATUS " addons ................... ${BLIS_ENABLE_ADDONS}") + message(STATUS " sandbox .................. ${BLIS_SANDBOX}") + message(STATUS " memkind .................. ${enable_memkind}") + message(STATUS " install prefix ........... ${CMAKE_INSTALL_PREFIX}") + message(STATUS "") +endfunction() diff --git a/cmake/BlisDetectCompiler.cmake b/cmake/BlisDetectCompiler.cmake new file mode 100644 index 0000000000..47803cab4e --- /dev/null +++ b/cmake/BlisDetectCompiler.cmake @@ -0,0 +1,336 @@ +# Compiler/vendor detection + capability probes, mirroring configure's +# check_compiler()/check_assembler()/check_os() and the memkind/omp_simd +# probes. Sets (parent scope) BLIS_CC_VENDOR (gcc|clang|icc|aocc|NVIDIA|IBM), +# BLIS_APPLE_CLANG, the version-range yes/no strings BLIS_GCC_OT_*, +# BLIS_CLANG_OT_* and BLIS_AOCC_OT_* listed below, BLIS_IS_WIN, BLIS_IS_MSVC, +# BLIS_PRAGMA_OMP_SIMD, BLIS_HOST_OS and BLIS_HOST_MACHINE. +# +# function blis_compute_blacklist() -> list of blacklisted +# subconfig names given the detected toolchain and host OS. + +# cond is a whole if() condition in one string. ${} expansion does not split +# on spaces, so if() would see one argument and always test false; split first. +macro(_blis_set_yesno var cond) + separate_arguments(_blis_cond NATIVE_COMMAND "${cond}") + if(${_blis_cond}) + set(${var} yes) + else() + set(${var} no) + endif() +endmacro() + +set(BLIS_HOST_OS "${CMAKE_SYSTEM_NAME}") +set(BLIS_HOST_MACHINE "${CMAKE_SYSTEM_PROCESSOR}") + +if(WIN32 OR (CMAKE_C_COMPILER MATCHES "mingw|msys")) + set(BLIS_IS_WIN yes) +else() + set(BLIS_IS_WIN no) +endif() +# "MSVC environment" = cl/clang-cl (CMake's MSVC) OR a gcc-driver clang +# targeting windows-msvc (upstream detects this from _MSC_VER). Both take +# the MSVC code paths (no -fPIC, no -lm, implib naming, ...). +if(MSVC OR CMAKE_C_SIMULATE_ID STREQUAL "MSVC") + set(BLIS_IS_MSVC yes) +else() + set(BLIS_IS_MSVC no) +endif() + +# --- vendor detection (parallel to configure's $CC --version parsing) --- +execute_process( + COMMAND "${CMAKE_C_COMPILER}" --version + OUTPUT_VARIABLE _cc_version + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET +) + +set(BLIS_CC_VENDOR "") +set(BLIS_APPLE_CLANG no) +if(_cc_version MATCHES "AOCC") + set(BLIS_CC_VENDOR aocc) +elseif(_cc_version MATCHES "Apple") + set(BLIS_CC_VENDOR clang) + set(BLIS_APPLE_CLANG yes) +elseif(CMAKE_C_COMPILER_ID STREQUAL "GNU") + set(BLIS_CC_VENDOR gcc) +elseif(CMAKE_C_COMPILER_ID MATCHES "^(Apple)?Clang$|^ARMClang$") # armclang: configure treats armclang as clang + set(BLIS_CC_VENDOR clang) +elseif(CMAKE_C_COMPILER_ID STREQUAL "Intel") + set(BLIS_CC_VENDOR icc) +elseif(CMAKE_C_COMPILER_ID STREQUAL "IntelLLVM") + set(BLIS_CC_VENDOR clang) # icx: configure treats oneAPI as clang +elseif(CMAKE_C_COMPILER_ID STREQUAL "NVHPC") + set(BLIS_CC_VENDOR NVIDIA) +elseif(CMAKE_C_COMPILER_ID STREQUAL "XL") + set(BLIS_CC_VENDOR IBM) +else() + message(FATAL_ERROR "BLIS: unsupported C compiler ${CMAKE_C_COMPILER_ID} (${CMAKE_C_COMPILER})") +endif() +message(STATUS "BLIS: C compiler vendor: ${BLIS_CC_VENDOR} (${CMAKE_C_COMPILER_VERSION})") + +# --- version-range booleans (yes/no strings, like config.mk) --- +string(REPLACE "." ";" _cv "${CMAKE_C_COMPILER_VERSION}") +list(GET _cv 0 _vmaj) +# configure gets the revision as the third dot component (missing = 0). +list(LENGTH _cv _cn) +if(_cn GREATER 2) + list(GET _cv 2 _vrev) +else() + set(_vrev 0) +endif() + +if(BLIS_CC_VENDOR STREQUAL "gcc") + _blis_set_yesno(BLIS_GCC_OT_4_9_0 "CMAKE_C_COMPILER_VERSION VERSION_LESS 4.9.0") + _blis_set_yesno(BLIS_GCC_OT_6_1_0 "CMAKE_C_COMPILER_VERSION VERSION_LESS 6.1.0") + _blis_set_yesno(BLIS_GCC_OT_9_1_0 "CMAKE_C_COMPILER_VERSION VERSION_LESS 9.1.0") + _blis_set_yesno(BLIS_GCC_OT_10_3_0 "CMAKE_C_COMPILER_VERSION VERSION_LESS 10.3.0") +else() + foreach(v 4_9_0 6_1_0 9_1_0 10_3_0) + set(BLIS_GCC_OT_${v} no) + endforeach() +endif() +# configure sets the clang booleans only when the vendor is clang; aocc keeps +# the no defaults (its CMAKE_C_COMPILER_VERSION is the LLVM version). +# ARMClang: CMAKE_C_COMPILER_VERSION is armclang's own release number, not the "based on LLVM" version. +if(BLIS_CC_VENDOR STREQUAL "clang") + _blis_set_yesno(BLIS_CLANG_OT_9_0_0 "CMAKE_C_COMPILER_VERSION VERSION_LESS 9.0.0") + _blis_set_yesno(BLIS_CLANG_OT_12_0_0 "CMAKE_C_COMPILER_VERSION VERSION_LESS 12.0.0") +else() + set(BLIS_CLANG_OT_9_0_0 no) + set(BLIS_CLANG_OT_12_0_0 no) +endif() +if(BLIS_CC_VENDOR STREQUAL "aocc") + # The AOCC release is not CMAKE_C_COMPILER_VERSION (the LLVM version). + # Parse the release from the --version text the way configure does. + if(_cc_version MATCHES "AOCC.LLVM.2") + string(REGEX MATCH "AOCC.LLVM.[0-9]+\\.[0-9]+\\.?[0-9]*" _aocc_tag "${_cc_version}") + else() + string(REGEX MATCH "AOCC_[0-9]+\\.[0-9]+\\.?[0-9]*" _aocc_tag "${_cc_version}") + endif() + string(REGEX MATCH "[0-9]+\\.[0-9]+\\.?[0-9]*" _aocc_rel "${_aocc_tag}") + _blis_set_yesno(BLIS_AOCC_OT_2_0_0 "_aocc_rel VERSION_LESS 2.0.0") + _blis_set_yesno(BLIS_AOCC_OT_3_0_0 "_aocc_rel VERSION_LESS 3.0.0") +else() + set(BLIS_AOCC_OT_2_0_0 no) + set(BLIS_AOCC_OT_3_0_0 no) +endif() + +# Toolchain-flag list for probe compiles, so a cross probe tests the target. +function(_blis_toolchain_flags out_var) + set(_tc "") + if(CMAKE_C_COMPILER_TARGET AND DEFINED CMAKE_C_COMPILE_OPTIONS_TARGET) + set(_tc "${CMAKE_C_COMPILE_OPTIONS_TARGET}${CMAKE_C_COMPILER_TARGET}") + endif() + if(CMAKE_SYSROOT AND DEFINED CMAKE_C_COMPILE_OPTIONS_SYSROOT) + string(APPEND _tc " ${CMAKE_C_COMPILE_OPTIONS_SYSROOT}${CMAKE_SYSROOT}") + endif() + string(APPEND _tc " ${CMAKE_C_FLAGS}") + separate_arguments(_tf NATIVE_COMMAND "${_tc}") + set(${out_var} "${_tf}" PARENT_SCOPE) +endfunction() + +# --- assembler ISA probes (configure's check_assembler) --- +# Try: cc -c build/detect/iset/.s ; clang gets a -march retry for avx512. +function(_blis_try_assemble isa out_var) + set(_src "${BLIS_SOURCE_DIR}/build/detect/iset/${isa}.s") + if(NOT EXISTS "${_src}") + set(${out_var} FALSE PARENT_SCOPE) + return() + endif() + set(_bin "${PROJECT_BINARY_DIR}/CMakeFiles/blis-detect-${isa}.o") + _blis_toolchain_flags(_tf) + execute_process( + COMMAND "${CMAKE_C_COMPILER}" ${_tf} -c "${_src}" -o "${_bin}" + RESULT_VARIABLE _rv + OUTPUT_QUIET + ERROR_QUIET + ) + if(NOT _rv EQUAL 0 AND BLIS_CC_VENDOR STREQUAL "clang") + if(isa STREQUAL "avx512f") + execute_process( + COMMAND "${CMAKE_C_COMPILER}" ${_tf} -march=knl -c "${_src}" -o "${_bin}" + RESULT_VARIABLE _rv + OUTPUT_QUIET + ERROR_QUIET + ) + elseif(isa STREQUAL "avx512dq") + execute_process( + COMMAND "${CMAKE_C_COMPILER}" ${_tf} -march=skylake-avx512 -c "${_src}" -o "${_bin}" + RESULT_VARIABLE _rv + OUTPUT_QUIET + ERROR_QUIET + ) + endif() + endif() + file(REMOVE "${_bin}") + if(_rv EQUAL 0) + set(${out_var} TRUE PARENT_SCOPE) + else() + set(${out_var} FALSE PARENT_SCOPE) + endif() +endfunction() + +# --- #pragma omp simd support probe (configure's has_pragma_omp_simd) --- +function(_blis_probe_omp_simd out_var) + set(_ok FALSE) + if(BLIS_CC_VENDOR MATCHES "gcc|clang|icc") + file(READ "${BLIS_SOURCE_DIR}/build/detect/omp_simd/omp_simd_detect.c" _src) + if(BLIS_CC_VENDOR STREQUAL "icc") + set(_flag "-qopenmp-simd") + else() + set(_flag "-fopenmp-simd") + endif() + set(_bin_dir "${PROJECT_BINARY_DIR}/CMakeFiles/omp-simd-probe") + file(MAKE_DIRECTORY "${_bin_dir}") + file(WRITE "${_bin_dir}/probe.c" "${_src}") + _blis_toolchain_flags(_tf) + execute_process( + COMMAND + "${CMAKE_C_COMPILER}" ${_tf} -std=c99 -O3 -march=native ${_flag} -c "${_bin_dir}/probe.c" -o + "${_bin_dir}/probe.o" + RESULT_VARIABLE _rv + OUTPUT_QUIET + ERROR_QUIET + ) + file(REMOVE_RECURSE "${_bin_dir}") + if(_rv EQUAL 0) + set(_ok TRUE) + endif() + endif() + set(${out_var} ${_ok} PARENT_SCOPE) +endfunction() + +# --- memkind probe (configure's has_libmemkind) --- +function(_blis_probe_memkind out_var) + set(_bin_dir "${PROJECT_BINARY_DIR}/CMakeFiles/memkind-probe") + file(MAKE_DIRECTORY "${_bin_dir}") + file(COPY "${BLIS_SOURCE_DIR}/build/detect/memkind/libmemkind_detect.c" DESTINATION "${_bin_dir}") + _blis_toolchain_flags(_tf) + execute_process( + COMMAND "${CMAKE_C_COMPILER}" ${_tf} "${_bin_dir}/libmemkind_detect.c" -lmemkind -o "${_bin_dir}/probe" + RESULT_VARIABLE _rv + OUTPUT_QUIET + ERROR_QUIET + ) + file(REMOVE_RECURSE "${_bin_dir}") + if(_rv EQUAL 0) + set(${out_var} TRUE PARENT_SCOPE) + else() + set(${out_var} FALSE PARENT_SCOPE) + endif() +endfunction() + +# --- blacklist computation (configure's check_compiler + check_assembler + check_os) --- +function(blis_compute_blacklist out_var) + set(blacklist "") + if(BLIS_CC_VENDOR STREQUAL "NVIDIA") + list(APPEND blacklist knl) + elseif(BLIS_CC_VENDOR STREQUAL "gcc") + if(CMAKE_C_COMPILER_VERSION VERSION_LESS 4.7) + message(FATAL_ERROR "BLIS requires gcc 4.7 or newer") + endif() + if(_vmaj EQUAL 4) + list(APPEND blacklist knl) + endif() + if(CMAKE_C_COMPILER_VERSION VERSION_LESS 4.9) + list(APPEND blacklist excavator zen) + endif() + if(CMAKE_C_COMPILER_VERSION VERSION_LESS 5 OR CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 15) + list(APPEND blacklist knl) + endif() + if(CMAKE_C_COMPILER_VERSION VERSION_LESS 6) + list(APPEND blacklist skx power9) + endif() + if(CMAKE_C_COMPILER_VERSION VERSION_LESS 10) + list(APPEND blacklist armsve) + endif() + elseif(BLIS_CC_VENDOR STREQUAL "icc") + if(CMAKE_C_COMPILER_VERSION VERSION_LESS 15) + message(FATAL_ERROR "BLIS requires icc 15 or newer") + endif() + # configure: icc 15.0.1+ is needed for skx (revision below 1 blacklists). + if(_vmaj EQUAL 15 AND _vrev LESS 1) + list(APPEND blacklist skx) + endif() + if(_vmaj EQUAL 18) + message(WARNING "icc 18.x mis-compiles knl/skx kernels (upstream issue #371); disabling them") + list(APPEND blacklist knl skx) + endif() + if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 19) + message(FATAL_ERROR "BLIS does not support icc 19+; use icx (clang-based)") + endif() + elseif(BLIS_CC_VENDOR STREQUAL "aocc") + # configure applies no config blacklist for aocc. + elseif(BLIS_CC_VENDOR STREQUAL "clang") + if(BLIS_APPLE_CLANG STREQUAL "yes") + if(CMAKE_C_COMPILER_VERSION VERSION_LESS 5) + message(FATAL_ERROR "BLIS requires Apple clang 5 or newer") + endif() + if(_vmaj EQUAL 5) + list(APPEND blacklist excavator zen) + endif() + if(CMAKE_C_COMPILER_VERSION VERSION_LESS 7) + list(APPEND blacklist knl skx) + endif() + else() + if(CMAKE_C_COMPILER_VERSION VERSION_LESS 3) + message(FATAL_ERROR "BLIS requires clang 3.3 or newer") + endif() + if(_vmaj EQUAL 3 AND CMAKE_C_COMPILER_VERSION VERSION_LESS 3.3) + message(FATAL_ERROR "BLIS requires clang 3.3 or newer") + endif() + if(_vmaj EQUAL 3 AND CMAKE_C_COMPILER_VERSION VERSION_LESS 3.5) + list(APPEND blacklist excavator zen) + endif() + if(_vmaj EQUAL 3 AND CMAKE_C_COMPILER_VERSION VERSION_LESS 3.9) + list(APPEND blacklist knl skx) + endif() + if(CMAKE_C_COMPILER_VERSION VERSION_LESS 11) + list(APPEND blacklist armsve) + endif() + if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 19) + list(APPEND blacklist knl) + endif() + endif() + endif() + + # Probe each ISA and blacklist the configs that need it (same order as configure). + foreach( + spec + "fma4;bulldozer" + "avx;sandybridge" + "fma3;haswell;piledriver;steamroller;excavator;skx" + "avx512f;knl;skx" + "avx512dq;skx" + ) + list(GET spec 0 _isa) + list(SUBLIST spec 1 -1 _cfgs) + _blis_try_assemble(${_isa} _ok) + if(NOT _ok) + list(APPEND blacklist ${_cfgs}) + endif() + endforeach() + + if((BLIS_HOST_OS STREQUAL "Darwin" OR BLIS_IS_WIN STREQUAL "yes") AND BLIS_HOST_MACHINE MATCHES "arm64|aarch64") + list(APPEND blacklist armsve) + endif() + + list(REMOVE_DUPLICATES blacklist) + set(${out_var} "${blacklist}" PARENT_SCOPE) +endfunction() + +# configure accepts a config whose make_defs.mk calls $(error); make fails only +# when the build starts. Mirror that: the first deferred error fails the build. +# The end of the top-level CMakeLists.txt makes every other target depend on +# blis_config_error. +function(blis_defer_error msg) + if(NOT TARGET blis_config_error) + add_custom_target( + blis_config_error + ALL + VERBATIM + COMMAND ${CMAKE_COMMAND} -E echo "*** ${msg}. Stop." + COMMAND ${CMAKE_COMMAND} -E false + ) + endif() +endfunction() diff --git a/cmake/BlisFlags.cmake b/cmake/BlisFlags.cmake new file mode 100644 index 0000000000..71a17af108 --- /dev/null +++ b/cmake/BlisFlags.cmake @@ -0,0 +1,244 @@ +# Evaluates config//make_defs.mk (via BlisMakeDefs) and assembles +# the per-class compiler flags exactly as common.mk's get-*-cflags-for do. +# +# Inputs expected in scope: BLIS_CC_VENDOR, BLIS_DEBUG_TYPE, the CC +# compiler id/version booleans (BLIS_GCC_OT_* etc.), host OS thread +# flags BLIS_CTHREADFLAGS. +# +# Provides: +# blis_load_config_defs() -> BLIS_DEFS__ parent-scope vars +# blis_finalize_config_defs() -> global flag appends (warn/lang/proc/pic) +# blis_get_cflags( ) -> flag list; class in +# frame config kernel refinit refkern addon addon-kernel sandbox user + +# Bridge variables for the def files (make naming, "yes"/"no" strings). +set(CC "${CMAKE_C_COMPILER}") +set(CC_VENDOR "${BLIS_CC_VENDOR}") +set(OS_NAME "${BLIS_HOST_OS}") +set(IS_WIN "${BLIS_IS_WIN}") +set(DEBUG_TYPE "${BLIS_DEBUG_TYPE}") +foreach(v 4_9_0 6_1_0 9_1_0 10_3_0) + set(GCC_OT_${v} "${BLIS_GCC_OT_${v}}") +endforeach() +set(CLANG_OT_9_0_0 "${BLIS_CLANG_OT_9_0_0}") +set(CLANG_OT_12_0_0 "${BLIS_CLANG_OT_12_0_0}") +set(AOCC_OT_2_0_0 "${BLIS_AOCC_OT_2_0_0}") +set(AOCC_OT_3_0_0 "${BLIS_AOCC_OT_3_0_0}") + +include(BlisMakeDefs) + +# LDFLAGS is captured because some configs append to the link line +# (rv* => -latomic; bgq => SPI libs). The make-defs' per-config +# MK_ENABLE_MEMKIND override is intentionally NOT honored at link time: +# the make system never reads it there either (common.mk computes LDFLAGS +# before including the defs); knl's en-masse '-DBLIS_DISABLE_MEMKIND' +# CPPROCFLAGS define still applies (config/knl def file). +set(_BLIS_DEF_VARS + CPPROCFLAGS + CMISCFLAGS + CPICFLAGS + CWARNFLAGS + CDBGFLAGS + COPTFLAGS + CKOPTFLAGS + CKVECFLAGS + CROPTFLAGS + CRVECFLAGS + LDFLAGS +) + +function(blis_load_config_defs cfg) + if(NOT EXISTS "${BLIS_SOURCE_DIR}/config/${cfg}/make_defs.mk") + message(FATAL_ERROR "BLIS: no flag definitions for config '${cfg}'") + endif() + # Init LDFLAGS capture to empty so stale parent values never leak across configs. + set(LDFLAGS "") + blis_parse_make_defs(${cfg}) + foreach(v ${_BLIS_DEF_VARS}) + separate_arguments(_l UNIX_COMMAND "${${v}}") + set(BLIS_DEFS_${v}_${cfg} "${_l}" PARENT_SCOPE) + endforeach() +endfunction() + +# Global appends (common.mk "Configuration-agnostic flags" section). make +# appends the global value onto each config's stored value; MSVC empties PIC. +function(blis_finalize_config_defs cfg) + set(cwarn ${BLIS_DEFS_CWARNFLAGS_${cfg}} ${BLIS_GLOBAL_CWARNFLAGS}) + set(clang -std=c99) + set(cproc ${BLIS_DEFS_CPPROCFLAGS_${cfg}} ${BLIS_GLOBAL_CPPFLAGS}) + set(cpic ${BLIS_DEFS_CPICFLAGS_${cfg}}) + if(MSVC OR CMAKE_C_SIMULATE_ID STREQUAL "MSVC") + set(cpic "") + endif() + set(BLIS_DEFS_CWARNFLAGS_${cfg} "${cwarn}" PARENT_SCOPE) + set(BLIS_DEFS_CLANGFLAGS_${cfg} "${clang}" PARENT_SCOPE) + set(BLIS_DEFS_CPPROCFLAGS_${cfg} "${cproc}" PARENT_SCOPE) + set(BLIS_DEFS_CPICFLAGS_${cfg} "${cpic}" PARENT_SCOPE) +endfunction() + +# Unified flag assembly for C and C++. The class dispatch table is shared; +# only the base flags differ: C uses BLIS_DEFS_CLANGFLAGS_ (-std=c99), +# C++ uses BLIS_CXXLANGFLAGS (-std=c++11 or c++17). +function(blis_get_flags__impl cfg class lang out) + set(f + ${BLIS_DEFS_CDBGFLAGS_${cfg}} + ${BLIS_DEFS_CWARNFLAGS_${cfg}} + ${BLIS_DEFS_CPICFLAGS_${cfg}} + ${BLIS_DEFS_CMISCFLAGS_${cfg}} + ${BLIS_DEFS_CPPROCFLAGS_${cfg}} + ${BLIS_CTHREADFLAGS} + ) + + if(lang STREQUAL "C") + list(APPEND f ${BLIS_DEFS_CLANGFLAGS_${cfg}}) + elseif(lang STREQUAL "CXX") + list(APPEND f ${BLIS_CXXLANGFLAGS}) + else() + message(FATAL_ERROR "blis_get_flags__impl: unknown language '${lang}'") + endif() + + string(TOUPPER "${cfg}" CFGU) + if(class STREQUAL "kernel" OR class STREQUAL "addon-kernel") + list(APPEND f ${BLIS_DEFS_CKOPTFLAGS_${cfg}} ${BLIS_DEFS_CKVECFLAGS_${cfg}}) + elseif(class STREQUAL "refkern") + list(APPEND f ${BLIS_DEFS_CROPTFLAGS_${cfg}} ${BLIS_DEFS_CRVECFLAGS_${cfg}} ${BLIS_COMPSIMDFLAGS}) + elseif(NOT class MATCHES "^(frame|config|refinit|refkern|addon|sandbox|user)$") + message(FATAL_ERROR "blis_get_flags__impl: unknown class '${class}'") + else() + list(APPEND f ${BLIS_DEFS_COPTFLAGS_${cfg}}) + endif() + if(class MATCHES "^(config|kernel|addon-kernel|refinit|refkern)$") + list(APPEND f -DBLIS_CNAME=${cfg} -DBLIS_CNAME_UPPER=${CFGU}) + endif() + if(class MATCHES "^(frame|config|refinit|refkern)$") + list(APPEND f ${BLIS_ASANFLAGS}) + endif() + if(NOT class STREQUAL "user") + list(APPEND f -DBLIS_IS_BUILDING_LIBRARY ${BLIS_SYMFLAGS}) + endif() + if(class STREQUAL "refinit" OR class STREQUAL "refkern") + list(APPEND f -DBLIS_IN_REF_KERNEL=1) + # clang-cl ignores -include with a warning and reads the path as a source. + if(CMAKE_C_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC") + list(APPEND f "/FI${BLIS_SOURCE_DIR}/config/${cfg}/bli_kernel_defs_${cfg}.h") + else() + list(APPEND f -include "${BLIS_SOURCE_DIR}/config/${cfg}/bli_kernel_defs_${cfg}.h") + endif() + endif() + set(${out} "${f}" PARENT_SCOPE) +endfunction() + +function(blis_get_cxxflags cfg class out) + blis_get_flags__impl("${cfg}" "${class}" "CXX" _blis_flags_result) + set(${out} "${_blis_flags_result}" PARENT_SCOPE) +endfunction() + +# Mirrors get-noopt-cflags-for + per-class appends. Include dirs and defines +# are the caller's, apart from the class-specific -D flags included here. +function(blis_get_cflags cfg class out) + blis_get_flags__impl("${cfg}" "${class}" "C" _blis_flags_result) + set(${out} "${_blis_flags_result}" PARENT_SCOPE) +endfunction() + +# Flag scaffolding (mirrors common.mk) +# CMake's CMAKE_COLOR_DIAGNOSTICS default adds a color flag make withholds; +# the configs carry color in CMISCFLAGS already. +set(CMAKE_C_COMPILE_OPTIONS_COLOR_DIAGNOSTICS "") +set(CMAKE_C_COMPILE_OPTIONS_COLOR_DIAGNOSTICS_OFF "") +set(CMAKE_CXX_COMPILE_OPTIONS_COLOR_DIAGNOSTICS "") +set(CMAKE_CXX_COMPILE_OPTIONS_COLOR_DIAGNOSTICS_OFF "") +# NOTE: make's CFLAGS/CXXFLAGS "preset" (env at configure time) maps to +# CMAKE_C_FLAGS/CMAKE_CXX_FLAGS: CMake seeds them from the same env vars and +# puts them ahead of target options, the preset's place in the flag chain. +separate_arguments(BLIS_LDFLAGS_PRESET UNIX_COMMAND "$ENV{LDFLAGS}") + +set(BLIS_GLOBAL_CPPFLAGS "-D_POSIX_C_SOURCE=200112L") +if(APPLE) + list(APPEND BLIS_GLOBAL_CPPFLAGS "-D_DARWIN_C_SOURCE") +endif() + +set(BLIS_GLOBAL_CWARNFLAGS "") +if(NOT BLIS_CC_VENDOR STREQUAL "ibm") + list(APPEND BLIS_GLOBAL_CWARNFLAGS -Werror=implicit-function-declaration) + if(NOT BLIS_CC_VENDOR STREQUAL "NVIDIA") + list(APPEND BLIS_GLOBAL_CWARNFLAGS -Wall -Wno-unused-function -Wfatal-errors -Werror=pointer-arith) + else() + list(APPEND BLIS_GLOBAL_CWARNFLAGS -Wall -Wno-unused-function) + endif() +endif() +if(BLIS_CC_VENDOR STREQUAL "clang") + list(APPEND BLIS_GLOBAL_CWARNFLAGS -Wno-tautological-compare -Wno-pass-failed) +endif() +if(BLIS_CC_VENDOR STREQUAL "NVIDIA") + list( + APPEND BLIS_GLOBAL_CWARNFLAGS + --diag_suppress + unsigned_compare_with_zero + --diag_suppress + cast_to_qualified_type + --diag_suppress + bad_initializer_type + --diag_suppress + used_before_set + --diag_suppress + mixed_enum_type + --diag_suppress + incompatible_assignment_operands + ) +endif() +if(BLIS_CC_VENDOR STREQUAL "gcc") + list(APPEND BLIS_GLOBAL_CWARNFLAGS -Wno-comment) +endif() + +set(BLIS_CTHREADFLAGS "") +if("openmp" IN_LIST BLIS_THREADING_MODELS) + find_package(OpenMP REQUIRED COMPONENTS C) +endif() +# shortcut: aocc gets -fopenmp although common.mk omits it; drop when upstream common.mk adds aocc. +if((BLIS_CC_VENDOR STREQUAL "gcc" OR BLIS_CC_VENDOR STREQUAL "icc" OR BLIS_CC_VENDOR STREQUAL "clang" + OR BLIS_CC_VENDOR STREQUAL "aocc") AND "openmp" IN_LIST BLIS_THREADING_MODELS) + list(APPEND BLIS_CTHREADFLAGS -fopenmp) +endif() +# common.mk adds the threading flags only in its gcc, icc and clang blocks. +# shortcut: aocc gets -pthread although common.mk omits it; drop when upstream common.mk adds aocc. +if((BLIS_CC_VENDOR STREQUAL "gcc" OR BLIS_CC_VENDOR STREQUAL "icc" OR BLIS_CC_VENDOR STREQUAL "clang" + OR BLIS_CC_VENDOR STREQUAL "aocc")) + if("pthreads" IN_LIST BLIS_THREADING_MODELS) + list(APPEND BLIS_CTHREADFLAGS -pthread) + endif() +endif() + +set(BLIS_ASANFLAGS "") +if(BLIS_ENABLE_ASAN) + set(BLIS_ASANFLAGS -fsanitize=address) +endif() + +# shortcut: aocc gets -fopenmp-simd although common.mk omits it; drop when upstream common.mk adds aocc. +set(BLIS_COMPSIMDFLAGS "") +if(enable_pragma_omp_simd) + if(BLIS_CC_VENDOR STREQUAL "icc") + set(BLIS_COMPSIMDFLAGS -qopenmp-simd) + elseif(BLIS_CC_VENDOR STREQUAL "gcc" OR BLIS_CC_VENDOR STREQUAL "clang" OR BLIS_CC_VENDOR STREQUAL "aocc") + set(BLIS_COMPSIMDFLAGS -fopenmp-simd) + endif() +endif() + +# Symbol-visibility flags (common.mk BUILD_SYMFLAGS table). +# shortcut: aocc gets clang's visibility flags although common.mk omits them; drop when upstream common.mk adds aocc. +set(BLIS_SYMFLAGS "") +if(BLIS_ENABLE_SHARED) + if(BLIS_CC_VENDOR STREQUAL "icc" OR (NOT WIN32 AND BLIS_CC_VENDOR MATCHES "^(gcc|clang|aocc)$")) + if(BLIS_EXPORT_SHARED STREQUAL "all") + set(BLIS_SYMFLAGS -fvisibility=default) + else() + set(BLIS_SYMFLAGS -fvisibility=hidden) + endif() + endif() +endif() + +# Load per-config defs (family members in sorted order, like CONFIG_LIST_FAM), +# then apply the global appends. +foreach(c ${BLIS_CONFIG_LIST_FAM}) + blis_load_config_defs(${c}) + blis_finalize_config_defs(${c}) +endforeach() diff --git a/cmake/BlisGenSymbols.cmake b/cmake/BlisGenSymbols.cmake new file mode 100644 index 0000000000..0422f1d4c6 --- /dev/null +++ b/cmake/BlisGenSymbols.cmake @@ -0,0 +1,15 @@ +# The script expects a make-configured tree (config.mk + include//blis.h), +# so we create a minimal shim layout. +set(shim "${CMAKE_CURRENT_BINARY_DIR}/symbols-shim") +file(REMOVE_RECURSE "${shim}") +file(MAKE_DIRECTORY "${shim}/include/${CFG}") +file(WRITE "${shim}/config.mk" "CC := ${CC_BIN}\nCONFIG_NAME := ${CFG}\n") +execute_process( + COMMAND "${CMAKE_COMMAND}" -E create_symlink "${FLAT}" "${shim}/include/${CFG}/blis.h" +) +execute_process(COMMAND sh "${SCRIPT}" WORKING_DIRECTORY "${shim}" OUTPUT_FILE "${OUT}" RESULT_VARIABLE rv) +file(REMOVE_RECURSE "${shim}") +if(NOT rv EQUAL 0) + file(REMOVE "${OUT}") + message(FATAL_ERROR "symbols generation failed (${rv})") +endif() diff --git a/cmake/BlisHeaderGen.cmake b/cmake/BlisHeaderGen.cmake new file mode 100644 index 0000000000..5be5d3bb79 --- /dev/null +++ b/cmake/BlisHeaderGen.cmake @@ -0,0 +1,141 @@ +macro(blis_config_headers) + string(TOUPPER "${BLIS_CONFIG_NAME}" _cn_upper) + set(config_name_define "#define BLIS_FAMILY_${_cn_upper}\n") + set(config_list_defines "") + foreach(c ${BLIS_CONFIG_LIST}) + string(TOUPPER "${c}" CU) + string(APPEND config_list_defines "#define BLIS_CONFIG_${CU}\n") + endforeach() + set(kernel_list_defines "") + foreach(k ${BLIS_KERNEL_LIST}) + string(TOUPPER "${k}" KU) + string(APPEND kernel_list_defines "#define BLIS_KERNELS_${KU}\n") + endforeach() + + # Template anchors bound by BlisVersion/BlisOptions: + set(version "${BLIS_VERSION}") + set(version_major "${BLIS_VERSION_MAJOR}") + set(version_minor "${BLIS_VERSION_MINOR}") + set(version_revision "${BLIS_VERSION_REVISION}") + + _blis_probe_omp_simd(_omp_simd_ok) + if(_omp_simd_ok) + set(enable_pragma_omp_simd 1) + set(BLIS_PRAGMA_OMP_SIMD yes) + else() + set(enable_pragma_omp_simd 0) + set(BLIS_PRAGMA_OMP_SIMD no) + endif() + if(BLIS_PRAGMA_OMP_SIMD AND NOT BLIS_CC_VENDOR MATCHES "gcc|clang|icc") + set(enable_pragma_omp_simd 0) + endif() + + configure_file("${BLIS_SOURCE_DIR}/build/bli_config.h.in" "${PROJECT_BINARY_DIR}/bli_config.h" @ONLY) + configure_file("${BLIS_SOURCE_DIR}/build/bli_addon.h.in" "${PROJECT_BINARY_DIR}/bli_addon.h" @ONLY) +endmacro() + +macro(blis_flat_headers_gen) + blis_header_dirs(FLAT_HEADER_DIRS "${BLIS_SOURCE_DIR}/frame" "${BLIS_SOURCE_DIR}/ref_kernels") + blis_collect(ALL_HEADERS "${BLIS_SOURCE_DIR}/frame" h) + blis_collect(_h1 "${BLIS_SOURCE_DIR}/ref_kernels" h) + list(APPEND ALL_HEADERS ${_h1}) + foreach(c ${BLIS_CONFIG_LIST_FAM}) + blis_header_dirs(_hdirs "${BLIS_SOURCE_DIR}/config/${c}") + list(APPEND FLAT_HEADER_DIRS ${_hdirs}) + blis_collect(_h1 "${BLIS_SOURCE_DIR}/config/${c}" h) + list(APPEND ALL_HEADERS ${_h1}) + endforeach() + foreach(k ${BLIS_KERNEL_LIST}) + blis_header_dirs(_hdirs "${BLIS_SOURCE_DIR}/kernels/${k}") + list(APPEND FLAT_HEADER_DIRS ${_hdirs}) + blis_collect(_h1 "${BLIS_SOURCE_DIR}/kernels/${k}" h) + list(APPEND ALL_HEADERS ${_h1}) + endforeach() + foreach(a ${BLIS_ENABLE_ADDONS}) + blis_header_dirs(_hdirs "${BLIS_SOURCE_DIR}/addon/${a}") + list(APPEND FLAT_HEADER_DIRS ${_hdirs}) + blis_collect(_h1 "${BLIS_SOURCE_DIR}/addon/${a}" h hh hpp hxx) + list(APPEND ALL_HEADERS ${_h1}) + endforeach() + if(BLIS_SANDBOX) + blis_header_dirs(_hdirs "${BLIS_SOURCE_DIR}/sandbox/${BLIS_SANDBOX}") + list(APPEND FLAT_HEADER_DIRS ${_hdirs}) + blis_collect(_h1 "${BLIS_SOURCE_DIR}/sandbox/${BLIS_SANDBOX}" h hh hpp hxx) + list(APPEND ALL_HEADERS ${_h1}) + endif() + # The build directory comes first (it holds the generated bli_config.h and + # bli_addon.h, like '.' in the make-time in-tree build). + list(PREPEND FLAT_HEADER_DIRS "${PROJECT_BINARY_DIR}") + list(REMOVE_DUPLICATES FLAT_HEADER_DIRS) + + if(NOT Python3_Interpreter_FOUND) + message(FATAL_ERROR "BLIS: python3 is required (build/flatten-headers.py)") + endif() + + # Keep the paths recorded in #line directives relative to the build dir + # (like the make-based build), instead of leaking absolute build paths. + set(_flat_in "${BLIS_SOURCE_DIR}/frame/include/blis.h") + file(RELATIVE_PATH _flat_in_rel "${PROJECT_BINARY_DIR}" "${_flat_in}") + set(_flat_dirs_rel "") + foreach(d ${FLAT_HEADER_DIRS}) + file(RELATIVE_PATH _r "${PROJECT_BINARY_DIR}" "${d}") + if(_r STREQUAL "") + set(_r ".") + endif() + list(APPEND _flat_dirs_rel "${_r}") + endforeach() + string(REPLACE ";" " " FLAT_HEADER_DIRS_STR "${_flat_dirs_rel}") + + file(MAKE_DIRECTORY "${PROJECT_BINARY_DIR}/include") + add_custom_command( + OUTPUT "${PROJECT_BINARY_DIR}/include/blis.h" + COMMAND + ${Python3_EXECUTABLE} ${BLIS_PYTHON_ARGS} "${BLIS_SOURCE_DIR}/build/flatten-headers.py" -l -v1 + "${_flat_in_rel}" "include/blis.h" "include" "${FLAT_HEADER_DIRS_STR}" + WORKING_DIRECTORY "${PROJECT_BINARY_DIR}" + DEPENDS + ${ALL_HEADERS} + "${BLIS_SOURCE_DIR}/build/flatten-headers.py" + "${PROJECT_BINARY_DIR}/bli_config.h" + "${PROJECT_BINARY_DIR}/bli_addon.h" + COMMENT "Flattening monolithic header blis.h" + ) + + set(_flat_outputs "${PROJECT_BINARY_DIR}/include/blis.h") + if(BLIS_ENABLE_CBLAS) + file(RELATIVE_PATH _cblas_in_rel "${PROJECT_BINARY_DIR}" "${BLIS_SOURCE_DIR}/frame/compat/cblas/src/cblas.h") + add_custom_command( + OUTPUT "${PROJECT_BINARY_DIR}/include/cblas.h" + COMMAND + ${Python3_EXECUTABLE} ${BLIS_PYTHON_ARGS} "${BLIS_SOURCE_DIR}/build/flatten-headers.py" -l -v1 + "${_cblas_in_rel}" "include/cblas.h" "include" "${FLAT_HEADER_DIRS_STR}" + WORKING_DIRECTORY "${PROJECT_BINARY_DIR}" + DEPENDS ${ALL_HEADERS} "${PROJECT_BINARY_DIR}/include/blis.h" "${BLIS_SOURCE_DIR}/build/flatten-headers.py" + COMMENT "Flattening monolithic header cblas.h" + ) + list(APPEND _flat_outputs "${PROJECT_BINARY_DIR}/include/cblas.h") + endif() + add_custom_target(blis_flat_headers DEPENDS ${_flat_outputs}) + + file(STRINGS "${BLIS_SOURCE_DIR}/ref_kernels/bli_cntx_ref.c" _ref_inc REGEX "^[ \t]*#[ \t]*include") + set(BLIS_CINCDIRS "") + foreach(l ${_ref_inc}) + string(REGEX MATCH "[\"<]([a-zA-Z0-9_./-]+)[\">]" _m "${l}") + if(CMAKE_MATCH_1 AND NOT CMAKE_MATCH_1 STREQUAL "blis.h") + foreach(hf ${ALL_HEADERS}) + if(hf MATCHES "/${CMAKE_MATCH_1}$") + get_filename_component(_d "${hf}" DIRECTORY) + list(APPEND BLIS_CINCDIRS "${_d}") + endif() + endforeach() + endif() + endforeach() + list(APPEND BLIS_CINCDIRS "${BLIS_SOURCE_DIR}/frame/include") + if(BLIS_ENABLE_CBLAS) + list(APPEND BLIS_CINCDIRS "${BLIS_SOURCE_DIR}/frame/compat/cblas/src") + endif() + if("hpx" IN_LIST BLIS_THREADING_MODELS) + list(APPEND BLIS_CINCDIRS ${HPX_INCLUDE_DIRS}) + endif() + list(REMOVE_DUPLICATES BLIS_CINCDIRS) +endmacro() diff --git a/cmake/BlisInstall.cmake b/cmake/BlisInstall.cmake new file mode 100644 index 0000000000..2d82fcf74b --- /dev/null +++ b/cmake/BlisInstall.cmake @@ -0,0 +1,225 @@ +if(BLIS_ENABLE_STATIC) + set(_export_name_static "blis-static") + if(NOT BLIS_ENABLE_SHARED) + set(_export_name_static "blis") + endif() + set_target_properties(blis_static PROPERTIES EXPORT_NAME ${_export_name_static}) + install(TARGETS blis_static EXPORT blisTargets ARCHIVE DESTINATION ${BLIS_INSTALL_LIBDIR} COMPONENT libs) +endif() +if(BLIS_ENABLE_SHARED) + set_target_properties(blis_shared PROPERTIES EXPORT_NAME blis) + # The double install is deliberate: the components feed make's partial install targets (install-libs gets the real file, install-lib-symlinks the symlinks). + install( + TARGETS blis_shared + EXPORT blisTargets + LIBRARY DESTINATION ${BLIS_INSTALL_LIBDIR} COMPONENT lib-symlinks + RUNTIME DESTINATION ${BLIS_INSTALL_LIBDIR} COMPONENT lib-symlinks + ARCHIVE DESTINATION ${BLIS_INSTALL_LIBDIR} COMPONENT lib-symlinks + ) + if(NOT WIN32) + install( + FILES $ + DESTINATION ${BLIS_INSTALL_LIBDIR} + COMPONENT libs + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE + ) + endif() + if(APPLE AND NOT BLIS_DARWIN_RPATH) + # The link stamps the configure-time libdir, as make does. This rewrite + # follows the effective prefix with the blis.pc mechanism; DESTDIR stays out. + # A plain install matches the stamped id, so it skips the rewrite. + if(IS_ABSOLUTE "${BLIS_INSTALL_LIBDIR}") + set(_blis_iddir "${BLIS_INSTALL_LIBDIR}") + set(_blis_idcfgdir "${BLIS_INSTALL_LIBDIR}") + else() + set(_blis_iddir "@BLIS_INSTALL_PREFIX@/${BLIS_INSTALL_LIBDIR}") + set(_blis_idcfgdir "${CMAKE_INSTALL_PREFIX}/${BLIS_INSTALL_LIBDIR}") + endif() + set(_blis_idtool "${CMAKE_INSTALL_NAME_TOOL}") + if(_blis_idtool MATCHES "NOTFOUND") + set(_blis_idtool "") + elseif(NOT IS_ABSOLUTE "${_blis_idtool}") + # find_program keeps its result when the result variable has a value. + # The minimum is 3.20 and NO_CACHE is a 3.21 option. Clear the normal and cache entry. + unset(_blis_idtool_search) + unset(_blis_idtool_search CACHE) + find_program(_blis_idtool_search "${_blis_idtool}") + if(_blis_idtool_search MATCHES "NOTFOUND") + set(_blis_idtool "") + else() + set(_blis_idtool "${_blis_idtool_search}") + endif() + endif() + foreach(_blis_idc libs lib-symlinks) + install( + CODE + "set(_blis_iddir \"${_blis_iddir}\")\nset(_blis_idcfgdir \"${_blis_idcfgdir}\")\nset(_blis_idtool \"${_blis_idtool}\")\nset(_blis_dylib \"$\")\nset(_blis_idname \"$\")" + COMPONENT ${_blis_idc} + ) + install( + CODE + [[ + string(REPLACE "@BLIS_INSTALL_PREFIX@" "${CMAKE_INSTALL_PREFIX}" _blis_iddir "${_blis_iddir}") + set(_blis_idfile "$ENV{DESTDIR}${_blis_iddir}/${_blis_dylib}") + # IS_EXECUTABLE is a 3.29 condition; the minimum is 3.20. EXISTS catches an + # empty or missing tool. A tool that exists but cannot start fails below. + if(NOT EXISTS "${_blis_idtool}") + message(FATAL_ERROR "BLIS: install-name tool '${_blis_idtool}' is empty or missing; the id of ${_blis_dylib} keeps the stale value ${_blis_idcfgdir}/${_blis_idname}") + endif() + if(EXISTS "${_blis_idfile}" AND NOT IS_SYMLINK "${_blis_idfile}") + execute_process( + COMMAND "${_blis_idtool}" -id "${_blis_iddir}/${_blis_idname}" "${_blis_idfile}" + RESULT_VARIABLE _blis_idres + ) + if(NOT _blis_idres EQUAL 0) + message(FATAL_ERROR "BLIS: install-name tool '${_blis_idtool}' -id failed for ${_blis_idfile}; the id keeps the stale value ${_blis_idcfgdir}/${_blis_idname}") + endif() + endif() + ]] + COMPONENT ${_blis_idc} + ) + endforeach() + endif() +endif() + +# Generated monolithic headers + one-level-up helper headers. +install(FILES "${PROJECT_BINARY_DIR}/include/blis.h" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/blis COMPONENT headers) +install(FILES "${BLIS_SOURCE_DIR}/build/blis.h" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} COMPONENT helper-headers) +if(BLIS_ENABLE_CBLAS) + install( + FILES "${PROJECT_BINARY_DIR}/include/cblas.h" + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/blis + COMPONENT headers + ) + install(FILES "${BLIS_SOURCE_DIR}/build/cblas.h" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} COMPONENT helper-headers) +endif() + +install(EXPORT blisTargets NAMESPACE blis:: DESTINATION ${BLIS_INSTALL_LIBDIR}/cmake/blis COMPONENT cmake-package) +if(IS_ABSOLUTE "${BLIS_INSTALL_LIBDIR}") + install(CODE "set(_blis_tgtdir \"${BLIS_INSTALL_LIBDIR}/cmake/blis\")" COMPONENT cmake-package) + install( + CODE + [[ + set(_f "$ENV{DESTDIR}${_blis_tgtdir}/blisTargets.cmake") + file(READ "${_f}" _c) + string(REGEX REPLACE "set\\(_IMPORT_PREFIX \"[^\"]*\"\\)" + "set(_IMPORT_PREFIX \"${CMAKE_INSTALL_PREFIX}\")" _c "${_c}") + file(WRITE "${_f}" "${_c}") + ]] + COMPONENT cmake-package + ) +endif() + +# Per-subconfiguration flag metadata exported for plugins (consumed by BlisPluginHelpers.cmake). +set(BLISPLUGIN_EXPORTS "") +blis_get_cflags(${BLIS_CONFIG_NAME} addon _blisplugin_frame) +string(REPLACE ";" " " _blisplugin_frame_s "${_blisplugin_frame}") +string(APPEND BLISPLUGIN_EXPORTS "set(blis_FRAME_CFLAGS \"${_blisplugin_frame_s}\")\n") +foreach(c ${BLIS_CONFIG_LIST}) + blis_get_cflags(${c} kernel _kf) + string(REPLACE ";" " " _kfs "${_kf}") + string(APPEND BLISPLUGIN_EXPORTS "set(blis_KERNEL_CFLAGS_${c} \"${_kfs}\")\n") + blis_get_cflags(${c} refkern _rf) + list(FIND _rf "-include" _inc_idx) + if(_inc_idx GREATER -1) + math(EXPR _next_idx "${_inc_idx} + 1") + list(REMOVE_AT _rf ${_inc_idx} ${_next_idx}) + endif() + list(FILTER _rf EXCLUDE REGEX "^/FI") + list(REMOVE_ITEM _rf "-DBLIS_IN_REF_KERNEL=1") + string(REPLACE ";" " " _rfs "${_rf}") + string(APPEND BLISPLUGIN_EXPORTS "set(blis_REFKERN_CFLAGS_${c} \"${_rfs}\")\n") +endforeach() + +include(CMakePackageConfigHelpers) +set(BLIS_NEEDS_THREADS OFF) +if(BLIS_ENABLE_SYSTEM) + set(BLIS_NEEDS_THREADS ON) +endif() +set(BLIS_NEEDS_OPENMP OFF) +if("openmp" IN_LIST BLIS_THREADING_MODELS) + set(BLIS_NEEDS_OPENMP ON) +endif() +set(BLIS_NEEDS_MEMKIND OFF) +if(enable_memkind) + set(BLIS_NEEDS_MEMKIND ON) +endif() +configure_package_config_file( + "${BLIS_SOURCE_DIR}/cmake/blisConfig.cmake.in" + "${PROJECT_BINARY_DIR}/blisConfig.cmake" + INSTALL_DESTINATION ${BLIS_INSTALL_LIBDIR}/cmake/blis +) +write_basic_package_version_file( + "${PROJECT_BINARY_DIR}/blisConfigVersion.cmake" + VERSION "${BLIS_VERSION}" + COMPATIBILITY SameMajorVersion +) +configure_file( + "${BLIS_SOURCE_DIR}/cmake/BlisPluginHelpers.cmake.in" + "${PROJECT_BINARY_DIR}/BlisPluginHelpers.cmake" + @ONLY +) +install( + FILES + "${PROJECT_BINARY_DIR}/blisConfig.cmake" + "${PROJECT_BINARY_DIR}/blisConfigVersion.cmake" + "${PROJECT_BINARY_DIR}/BlisPluginHelpers.cmake" + DESTINATION ${BLIS_INSTALL_LIBDIR}/cmake/blis + COMPONENT cmake-package +) + +# @BLIS_INSTALL_PREFIX@ is replaced at install time, so `cmake --install --prefix +# ` yields a .pc that matches the actual install. +set(prefix "@BLIS_INSTALL_PREFIX@") +if(NOT "${BLIS_INSTALL_EXEC_PREFIX}" STREQUAL "") + set(exec_prefix "${BLIS_INSTALL_EXEC_PREFIX}") +else() + set(exec_prefix "@BLIS_INSTALL_PREFIX@") +endif() +if(IS_ABSOLUTE "${BLIS_INSTALL_LIBDIR}") + set(libdir "${BLIS_INSTALL_LIBDIR}") +else() + set(libdir "@BLIS_INSTALL_PREFIX@/${BLIS_INSTALL_LIBDIR}") +endif() +if(IS_ABSOLUTE "${CMAKE_INSTALL_INCLUDEDIR}") + set(includedir "${CMAKE_INSTALL_INCLUDEDIR}") +else() + set(includedir "@BLIS_INSTALL_PREFIX@/${CMAKE_INSTALL_INCLUDEDIR}") +endif() +set(PACKAGE_VERSION "${BLIS_VERSION}") +foreach(l ${BLIS_PC_LIBS}) + if(TARGET "${l}" OR l MATCHES "^[^-/][^/]*::") + message(FATAL_ERROR "BLIS: target name '${l}' cannot go into blis.pc") + endif() +endforeach() +string(REPLACE ";" " " LDFLAGS "${BLIS_PC_LIBS}") +configure_file("${BLIS_SOURCE_DIR}/blis.pc.in" "${PROJECT_BINARY_DIR}/blis.pc" @ONLY) + +# Install a staged file after substituting the real install prefix, so the +# result lands in the install manifest and honors DESTDIR. +function(_blis_install_staged _staged_file _filename _target_dir) + install( + CODE "set(_blis_sf \"${_staged_file}\")\nset(_blis_fn \"${_filename}\")\nset(_blis_td \"${_target_dir}\")" + COMPONENT share + ) + install( + CODE + [[ + file(READ "${_blis_sf}" _c) + string(REPLACE "@BLIS_INSTALL_PREFIX@" "${CMAKE_INSTALL_PREFIX}" _c "${_c}") + file(WRITE "${_blis_sf}.d/${_blis_fn}" "${_c}") + cmake_path(ABSOLUTE_PATH _blis_td BASE_DIRECTORY "${CMAKE_INSTALL_PREFIX}") + file(INSTALL "${_blis_sf}.d/${_blis_fn}" DESTINATION "${_blis_td}" + PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ) + ]] + COMPONENT share + ) +endfunction() +_blis_install_staged("${PROJECT_BINARY_DIR}/blis.pc" "blis.pc" "${BLIS_SHAREDIR}/pkgconfig") + +# Vendor C++ headers (equivalent of `make install INSTALL_HH=yes`). +option(BLIS_INSTALL_VENDOR_CPP_HEADERS "Additionally install AMD's C++ template headers (vendor/cpp/*.hh)" OFF) +if(BLIS_INSTALL_VENDOR_CPP_HEADERS) + file(GLOB _vendor_hh "${BLIS_SOURCE_DIR}/vendor/cpp/*.hh") + install(FILES ${_vendor_hh} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/blis COMPONENT headers) +endif() diff --git a/cmake/BlisLibraries.cmake b/cmake/BlisLibraries.cmake new file mode 100644 index 0000000000..37f567fc14 --- /dev/null +++ b/cmake/BlisLibraries.cmake @@ -0,0 +1,218 @@ +# One SHELL: option group per target call: CMake de-duplicates option items +# and would split repeated two-word flags ('--diag_suppress A --diag_suppress +# B' becomes '--diag_suppress A B'). The group is re-parsed UNIX_COMMAND-style, +# so each flag is quoted here to survive embedded spaces. +function(blis_flag_group out) + set(_parts "") + foreach(_f IN LISTS ARGN) + string(REPLACE "\\" "\\\\" _f "${_f}") + string(REPLACE "\"" "\\\"" _f "${_f}") + list(APPEND _parts "\"${_f}\"") + endforeach() + if(_parts) + list(JOIN _parts " " _g) + set(${out} "SHELL:${_g}" PARENT_SCOPE) + else() + set(${out} "" PARENT_SCOPE) + endif() +endfunction() + +function(blis_add_objects name) + cmake_parse_arguments(ARG "" "CONFIG;CLASS" "SOURCES;EXTRA_INCLUDES" ${ARGN}) + if(NOT ARG_SOURCES) + return() + endif() + blis_get_cflags("${ARG_CONFIG}" "${ARG_CLASS}" _cflags) + add_library(${name} OBJECT ${ARG_SOURCES}) + blis_flag_group(_cflags_group ${_cflags}) + target_compile_options(${name} PRIVATE "$<$,$>:${_cflags_group}>") + set(_has_cxx FALSE) + foreach(src ${ARG_SOURCES}) + if(src MATCHES "\\.(cc|cpp|cxx)$") + set(_has_cxx TRUE) + endif() + endforeach() + if(_has_cxx) + blis_get_cxxflags("${ARG_CONFIG}" "${ARG_CLASS}" _cxxflags) + blis_flag_group(_cxxflags_group ${_cxxflags}) + target_compile_options(${name} PRIVATE "$<$:${_cxxflags_group}>") + endif() + target_include_directories(${name} PRIVATE "${PROJECT_BINARY_DIR}/include" ${BLIS_CINCDIRS} ${ARG_EXTRA_INCLUDES}) + add_dependencies(${name} blis_flat_headers) + # .s/.S compile in the ASM dialect, like make's kernels rule: gcc reads the + # assembler mode from the extension (.S preprocessed, .s not), no -x needed. + foreach(src ${ARG_SOURCES}) + if(src MATCHES "\\.(s|S)$") + set_source_files_properties(${src} PROPERTIES LANGUAGE ASM) + endif() + endforeach() + set_property(GLOBAL APPEND PROPERTY BLIS_OBJLIBS ${name}) +endfunction() + +# NO_CINCDIRS and NO_LINK cover support libraries that use only the flat header. +function(blis_in_tree_flags target) + cmake_parse_arguments(ARG "NO_CINCDIRS;NO_LINK" "" "EXTRA_INCLUDES" ${ARGN}) + blis_get_cflags("${BLIS_CONFIG_NAME}" user _uflags) + blis_flag_group(_uflags_group ${_uflags}) + target_compile_options(${target} PRIVATE ${_uflags_group}) + set(_incdirs "${PROJECT_BINARY_DIR}/include") + if(NOT ARG_NO_CINCDIRS) + list(APPEND _incdirs ${BLIS_CINCDIRS}) + endif() + target_include_directories(${target} PRIVATE ${_incdirs} ${ARG_EXTRA_INCLUDES}) + if(NOT ARG_NO_LINK AND NOT BLIS_LINK_TARGET STREQUAL "blis_static") + # blis_static's PUBLIC BLIS_LINK_LIBS reach the consumer transitively + # already; make's link lines carry the presets once. + set(_consumer_libs ${BLIS_LINK_LIBS}) + if(BLIS_LDFLAGS_PRESET) + # CMAKE_EXE_LINKER_FLAGS already carries the preset on this consumer. + list(REMOVE_ITEM _consumer_libs ${BLIS_LDFLAGS_PRESET}) + endif() + target_link_libraries(${target} PRIVATE ${_consumer_libs}) + endif() + add_dependencies(${target} blis_flat_headers) +endfunction() + +blis_add_objects(blis_obj_frame CONFIG ${BLIS_CONFIG_NAME} CLASS frame SOURCES ${FRAME_SRC}) + +foreach(c ${BLIS_CONFIG_LIST_FAM}) + blis_add_objects(blis_obj_config_${c} CONFIG ${c} CLASS config SOURCES ${CONFIG_SRC_${c}}) +endforeach() +foreach(c ${BLIS_CONFIG_LIST}) + blis_add_objects(blis_obj_refinit_${c} CONFIG ${c} CLASS refinit SOURCES ${REFINIT_SRC}) + blis_add_objects(blis_obj_refkern_${c} CONFIG ${c} CLASS refkern SOURCES ${REFKERN_SRC}) +endforeach() + +foreach(k ${BLIS_KERNEL_LIST}) + blis_kconfig_for(${k} _kc) + blis_add_objects(blis_obj_kern_${k} CONFIG ${_kc} CLASS kernel SOURCES ${KERNEL_SRC_${k}}) +endforeach() + +set(ADDON_INCDIRS "") +foreach(a ${BLIS_ENABLE_ADDONS}) + blis_header_dirs(_ai "${BLIS_SOURCE_DIR}/addon/${a}") + list(APPEND ADDON_INCDIRS ${_ai}) + blis_add_objects( + blis_obj_addon_${a} + CONFIG ${BLIS_CONFIG_NAME} + CLASS addon + SOURCES ${ADDON_SRC_${a}} + EXTRA_INCLUDES ${ADDON_INCDIRS} + ) + blis_add_objects( + blis_obj_addonkern_${a} + CONFIG ${BLIS_CONFIG_NAME} + CLASS addon-kernel + SOURCES ${ADDON_KERNEL_SRC_${a}} + EXTRA_INCLUDES ${ADDON_INCDIRS} + ) +endforeach() + +if(BLIS_SANDBOX) + blis_header_dirs(SANDBOX_INCDIRS "${BLIS_SOURCE_DIR}/sandbox/${BLIS_SANDBOX}") + blis_add_objects( + blis_obj_sandbox + CONFIG ${BLIS_CONFIG_NAME} + CLASS sandbox + SOURCES ${SANDBOX_SRC} + EXTRA_INCLUDES ${SANDBOX_INCDIRS} + ) +endif() + +# Libraries +get_property(_objlibs GLOBAL PROPERTY BLIS_OBJLIBS) +set(_obj_exprs "") +foreach(o ${_objlibs}) + list(APPEND _obj_exprs $) +endforeach() + +set(_blis_iface_incdirs + $ + $ + $ +) + +if(BLIS_ENABLE_STATIC) + add_library(blis_static STATIC ${_obj_exprs}) + set(_blis_static_name blis) + if(BLIS_IS_MSVC STREQUAL "yes" AND BLIS_ENABLE_SHARED) + # The MSVC import library takes libblis.lib; move the static name aside. + set(_blis_static_name blis_static) + endif() + set_target_properties( + blis_static + PROPERTIES OUTPUT_NAME ${_blis_static_name} PREFIX "lib" ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib" + ) + # CMAKE_EXE_LINKER_FLAGS already carries the LDFLAGS preset on every in-tree + # executable consumer; blis.pc keeps its own copy (BLIS_PC_LIBS). + set(_blis_static_libs ${BLIS_LINK_LIBS}) + set(_blis_static_opts ${BLIS_LINK_OPTS}) + if(BLIS_LDFLAGS_PRESET) + list(REMOVE_ITEM _blis_static_libs ${BLIS_LDFLAGS_PRESET}) + list(REMOVE_ITEM _blis_static_opts ${BLIS_LDFLAGS_PRESET}) + endif() + target_link_libraries(blis_static PUBLIC ${_blis_static_libs}) + blis_flag_group(_blis_static_optgroup ${_blis_static_opts}) + target_link_options(blis_static PUBLIC ${_blis_static_optgroup}) + if("openmp" IN_LIST BLIS_THREADING_MODELS) + target_link_libraries(blis_static INTERFACE "$") + endif() + target_include_directories(blis_static INTERFACE ${_blis_iface_incdirs}) +endif() + +if(BLIS_ENABLE_SHARED) + add_library(blis_shared SHARED ${_obj_exprs}) + set_target_properties( + blis_shared + PROPERTIES + OUTPUT_NAME blis + PREFIX "lib" + LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib" + RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib" + VERSION "${BLIS_SO_MMB}" + SOVERSION "${BLIS_SO_MAJOR}" + ) + if(APPLE) + # make links with no -compatibility_version or -current_version; keep ld's 0.0.0 fields, not CMake's VERSION/SOVERSION defaults. No released dylib has other fields. make does not give Mach-O version flags, and no one released this CMake build. Equal fields let a make-built dylib replace a CMake-built one. + set_target_properties(blis_shared PROPERTIES MACHO_COMPATIBILITY_VERSION "" MACHO_CURRENT_VERSION "") + endif() + if(APPLE AND NOT BLIS_DARWIN_RPATH) + set_target_properties(blis_shared PROPERTIES INSTALL_NAME_DIR "${BLIS_INSTALL_FULL_LIBDIR}") + endif() + if(WIN32) + set_target_properties(blis_shared PROPERTIES RUNTIME_OUTPUT_NAME "libblis.${BLIS_SO_MAJOR}" PREFIX "") + if(BLIS_IS_MSVC STREQUAL "yes") + set_target_properties(blis_shared PROPERTIES ARCHIVE_OUTPUT_NAME libblis) + endif() + endif() + set(_blis_shared_libs ${BLIS_LINK_LIBS}) + set(_blis_shared_opts ${BLIS_LINK_OPTS}) + if(BLIS_LDFLAGS_PRESET) + list(REMOVE_ITEM _blis_shared_libs ${BLIS_LDFLAGS_PRESET}) + list(REMOVE_ITEM _blis_shared_opts ${BLIS_LDFLAGS_PRESET}) + endif() + target_link_libraries(blis_shared PRIVATE ${_blis_shared_libs}) + blis_flag_group(_blis_shared_optgroup ${_blis_shared_opts}) + target_link_options(blis_shared PRIVATE ${_blis_shared_optgroup}) + target_link_options(blis_shared INTERFACE "$") + # shortcut: make puts these flags on compile lines only, where ld ignores them; drop if upstream common.mk moves them to the link line. + if(WIN32 AND BLIS_IS_MSVC STREQUAL "no" AND BLIS_CC_VENDOR MATCHES "^(gcc|clang|aocc)$") + if(BLIS_EXPORT_SHARED STREQUAL "all") + target_link_options(blis_shared PRIVATE "SHELL:-Wl,--export-all-symbols -Wl,--enable-auto-import") + else() + target_link_options(blis_shared PRIVATE "SHELL:-Wl,--exclude-all-symbols") + endif() + endif() + if(NOT BLIS_IS_MSVC STREQUAL "yes") + target_link_libraries(blis_shared INTERFACE m) + endif() + target_include_directories(blis_shared INTERFACE ${_blis_iface_incdirs}) +endif() + +# In-tree consumers (testsuite, blastest, examples): link static when +# available; on Windows the make build uses the dll regardless. +if(BLIS_ENABLE_STATIC AND NOT (WIN32 AND BLIS_ENABLE_SHARED)) + set(BLIS_LINK_TARGET blis_static) +else() + set(BLIS_LINK_TARGET blis_shared) +endif() diff --git a/cmake/BlisLinkItems.cmake b/cmake/BlisLinkItems.cmake new file mode 100644 index 0000000000..cf1453496b --- /dev/null +++ b/cmake/BlisLinkItems.cmake @@ -0,0 +1,141 @@ +# Two lists grow in step. BLIS_PC_LIBS holds the flags exactly as make writes +# them (blis.pc, exported package). BLIS_LINK_LIBS holds the CMake link items. +set(BLIS_LINK_LIBS "") +set(BLIS_PC_LIBS "") +macro(blis_add_link _cmake _pc) + list(APPEND BLIS_LINK_LIBS ${_cmake}) + list(APPEND BLIS_PC_LIBS ${_pc}) +endmacro() +blis_add_link("${BLIS_LDFLAGS_PRESET}" "${BLIS_LDFLAGS_PRESET}") +if(NOT BLIS_IS_MSVC STREQUAL "yes") + blis_add_link(m -lm) +endif() +# pthread library: -lpthread by default (LIBPTHREAD overrides it), needed for +# pthread_once unless --disable-system. common.mk links the raw $(LIBPTHREAD) +# on every OS, so Darwin keeps -lpthread where Threads::Threads drops it. +# The Bionic probe runs here because the pthread items below need its result. +if(BLIS_HOST_OS STREQUAL "Linux") + include(CheckCSourceCompiles) + check_c_source_compiles("#ifndef __BIONIC__\n#error not bionic\n#endif\nint main(void){return 0;}" BLIS_ON_BIONIC) +endif() +set(_blis_libpthread FALSE) +if(BLIS_ENABLE_SYSTEM) + if(BLIS_CC_VENDOR STREQUAL "icc") + list(REMOVE_ITEM BLIS_LINK_LIBS m -lm) + list(REMOVE_ITEM BLIS_PC_LIBS -lm) + endif() + if(NOT (WIN32 AND BLIS_CC_VENDOR MATCHES "clang|aocc") AND NOT BLIS_IS_MSVC STREQUAL "yes") + set(_blis_libpthread TRUE) + if(NOT DEFINED LIBPTHREAD) + set(_blis_libpthread_lib "-lpthread") + else() + set(_blis_libpthread_lib "${LIBPTHREAD}") + endif() + # Split the override into one link item for each word, as make's + # unquoted $(LIBPTHREAD) is shell-split. blis.pc keeps the raw string. + separate_arguments(_blis_libpthread_items UNIX_COMMAND "${_blis_libpthread_lib}") + # Bionic keeps pthread in libc and the NDK ships no libpthread, so the + # default -lpthread becomes Threads::Threads there. An explicit + # LIBPTHREAD override still goes in raw, split as above. + # shortcut: Bionic link items use Threads::Threads while make keeps raw + # -lpthread in LDFLAGS, drop when common.mk drops -lpthread on Bionic. + if(NOT DEFINED LIBPTHREAD AND (BLIS_ON_BIONIC OR BLIS_HOST_OS STREQUAL "Android")) + find_package(Threads) + set(_blis_libpthread_items Threads::Threads) + endif() + blis_add_link("${_blis_libpthread_items}" "${_blis_libpthread_lib}") + endif() +endif() +if(enable_memkind AND NOT BLIS_DEBUG_TYPE STREQUAL "sde") + blis_add_link(-lmemkind -lmemkind) +endif() +if(BLIS_ENABLE_ASAN) + blis_add_link(-fsanitize=address -fsanitize=address) +endif() +# shortcut: aocc gets -fopenmp although common.mk omits it; drop when upstream common.mk adds aocc. +if((BLIS_CC_VENDOR STREQUAL "gcc" OR BLIS_CC_VENDOR STREQUAL "icc" OR BLIS_CC_VENDOR STREQUAL "clang" + OR BLIS_CC_VENDOR STREQUAL "aocc") AND "openmp" IN_LIST BLIS_THREADING_MODELS) + blis_add_link(-fopenmp -fopenmp) +endif() +# common.mk adds the threading link flags only in its gcc, icc and clang blocks. +# shortcut: aocc gets -pthread although common.mk omits it; drop when upstream common.mk adds aocc. +if(BLIS_CC_VENDOR STREQUAL "gcc" OR BLIS_CC_VENDOR STREQUAL "icc" OR BLIS_CC_VENDOR STREQUAL "clang" OR BLIS_CC_VENDOR STREQUAL "aocc") + # configure:3821 and BlisOptions.cmake:192 force single threading when system support is off, so this branch never sees pthreads with BLIS_ENABLE_SYSTEM OFF. + if("pthreads" IN_LIST BLIS_THREADING_MODELS) + # The pthreads blocks add LIBPTHREAD to LDFLAGS again, so blis.pc + # carries it twice (the unconditional copy sits above). + if(_blis_libpthread) + # shortcut: make links LIBPTHREAD twice for pthreads; CMake links it once so a whole-archive override does not duplicate symbols. + list(APPEND BLIS_PC_LIBS "${_blis_libpthread_lib}") + endif() + endif() +endif() +foreach(c ${BLIS_CONFIG_LIST_FAM}) + if(BLIS_DEFS_LDFLAGS_${c}) + separate_arguments(_cfg_ld UNIX_COMMAND "${BLIS_DEFS_LDFLAGS_${c}}") + blis_add_link("${_cfg_ld}" "${_cfg_ld}") + endif() +endforeach() + +# HPX threading: pkg-config based, requires C++. +if("hpx" IN_LIST BLIS_THREADING_MODELS) + enable_language(CXX) + find_package(PkgConfig REQUIRED) + pkg_check_modules(HPX REQUIRED hpx_component) + set(BLIS_CXXLANGFLAGS -std=c++17) + # common.mk: LDFLAGS += pkg-config --libs minus -shared (raw flags, no target + # name, so blis.pc and the exported target carry real linker options). + set(_hpx_ld ${HPX_LDFLAGS}) + list(FILTER _hpx_ld EXCLUDE REGEX "^-shared$") + blis_add_link("${_hpx_ld}" "${_hpx_ld}") + if(APPLE) + foreach(_f ${_hpx_ld}) + if(_f MATCHES "^-L(.+)") + blis_add_link("-Wl,-rpath,${CMAKE_MATCH_1}" "-Wl,-rpath,${CMAKE_MATCH_1}") + endif() + endforeach() + endif() + # common.mk: CTHREADFLAGS += HPX cflags minus -std=*, used for C and C++. + set(_hpx_cf ${HPX_CFLAGS_OTHER}) + list(FILTER _hpx_cf EXCLUDE REGEX "^-std=") + list(APPEND BLIS_CTHREADFLAGS ${_hpx_cf}) +else() + set(BLIS_CXXLANGFLAGS -std=c++11) +endif() + +if(BLIS_HOST_OS STREQUAL "Linux") + # common.mk adds -lrt for OS_NAME Linux only, last (after the threading and + # HPX flags), except on Android/Bionic. + if(NOT BLIS_ON_BIONIC) + blis_add_link(rt -lrt) + endif() +endif() + +# CMake links an item as given: "-framework X" is one item, not two, or the +# second word becomes -lX. blis.pc keeps the two words (BLIS_PC_LIBS). +set(_blis_joined "") +set(_blis_pend "") +foreach(_l ${BLIS_LINK_LIBS}) + if(_blis_pend) + list(APPEND _blis_joined "${_blis_pend} ${_l}") + set(_blis_pend "") + elseif(_l MATCHES "^-(weak_|needed_|reexport_)?framework$") + set(_blis_pend "${_l}") + else() + list(APPEND _blis_joined "${_l}") + endif() +endforeach() +if(_blis_pend) + list(APPEND _blis_joined "${_blis_pend}") +endif() +set(BLIS_LINK_LIBS "") +set(BLIS_LINK_OPTS "") +foreach(_l ${_blis_joined}) + # target_link_libraries rejects any item with "::" that names no target + # (CMP0028), so a flag such as -Wl,-rpath,a::b goes through target_link_options. + if(_l MATCHES "^[-/].*::") + list(APPEND BLIS_LINK_OPTS "${_l}") + else() + list(APPEND BLIS_LINK_LIBS "${_l}") + endif() +endforeach() diff --git a/cmake/BlisMakeDefs.cmake b/cmake/BlisMakeDefs.cmake new file mode 100644 index 0000000000..3659d0eec1 --- /dev/null +++ b/cmake/BlisMakeDefs.cmake @@ -0,0 +1,296 @@ +# Evaluates config//make_defs.mk directly, so the per-config flag +# variables track the stock upstream def files. The make dialect is closed: +# ':=' and '+=' on plain variables, ifeq/ifneq/else/else-if/endif, $(var) +# references, $(findstring), $(shell), $(error), $(info), and the trailing +# $(eval $(call store-make-defs,...)) line, a no-op here since the loader +# captures the variables itself. A line outside the dialect aborts the parse. + +# Splits at its first comma outside any parentheses. +function(blis_make_split_args text outa outb) + string(LENGTH "${text}" tlen) + set(depth 0) + set(i 0) + while(i LESS tlen) + string(SUBSTRING "${text}" ${i} 1 ch) + if(ch STREQUAL "(") + math(EXPR depth "${depth} + 1") + elseif(ch STREQUAL ")") + math(EXPR depth "${depth} - 1") + elseif(ch STREQUAL "," AND depth EQUAL 0) + break() + endif() + math(EXPR i "${i} + 1") + endwhile() + if(i EQUAL tlen) + message(FATAL_ERROR "BlisMakeDefs: no top-level comma in: ${text}") + endif() + string(SUBSTRING "${text}" 0 ${i} a) + math(EXPR bstart "${i} + 1") + string(SUBSTRING "${text}" ${bstart} -1 b) + string(STRIP "${a}" a) + string(STRIP "${b}" b) + set(${outa} "${a}" PARENT_SCOPE) + set(${outb} "${b}" PARENT_SCOPE) +endfunction() + +# Expands make references in : plain $(var) lookups (undefined is +# empty, as in make), $(findstring needle,hay), and $(shell cmd) run +# through sh so pipes and quoting behave as they do under make. $(info) +# expands to the empty string. Anything else is rejected. +function(blis_make_expand in out) + set(res "") + set(rest "${in}") + while(TRUE) + string(FIND "${rest}" "$(" pos) + if(pos EQUAL -1) + string(APPEND res "${rest}") + break() + endif() + string(SUBSTRING "${rest}" 0 ${pos} pre) + string(APPEND res "${pre}") + math(EXPR bstart "${pos} + 2") + string(LENGTH "${rest}" rlen) + set(depth 1) + set(close -1) + set(i ${bstart}) + while(i LESS rlen) + string(SUBSTRING "${rest}" ${i} 1 ch) + if(ch STREQUAL "(") + math(EXPR depth "${depth} + 1") + elseif(ch STREQUAL ")") + math(EXPR depth "${depth} - 1") + if(depth EQUAL 0) + set(close ${i}) + break() + endif() + endif() + math(EXPR i "${i} + 1") + endwhile() + if(close EQUAL -1) + message(FATAL_ERROR "BlisMakeDefs: unbalanced '$(...)' in: ${in}") + endif() + math(EXPR blen "${close} - ${bstart}") + string(SUBSTRING "${rest}" ${bstart} ${blen} body) + blis_make_expand("${body}" body) + if(body MATCHES "^[A-Za-z0-9_]+$") + string(APPEND res "${${body}}") + elseif(body MATCHES "^findstring[ \t]+(.*)$") + blis_make_split_args("${CMAKE_MATCH_1}" needle hay) + string(FIND "${hay}" "${needle}" fpos) + if(NOT fpos EQUAL -1) + string(APPEND res "${needle}") + endif() + elseif(body MATCHES "^shell[ \t]+(.*)$") + execute_process( + COMMAND sh -c "${CMAKE_MATCH_1}" + WORKING_DIRECTORY "${BLIS_SOURCE_DIR}" + OUTPUT_VARIABLE shout + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + # make $(shell) folds inner newlines into single spaces. + string(REGEX REPLACE "[\r\n]+" " " shout "${shout}") + string(APPEND res "${shout}") + elseif(body MATCHES "^info([ \t]+.*)?$") + # make prints the argument at parse time; nothing carries it. + else() + message(FATAL_ERROR "BlisMakeDefs: unsupported make construct '$(${body})' in: ${in}") + endif() + math(EXPR rstart "${close} + 1") + string(SUBSTRING "${rest}" ${rstart} -1 rest) + endwhile() + set(${out} "${res}" PARENT_SCOPE) +endfunction() + +# Removes a make comment: the first '#' not preceded by a backslash and +# everything after it. +function(blis_make_strip_comment line out) + string(LENGTH "${line}" n) + set(off 0) + set(result "${line}") + while(off LESS n) + string(SUBSTRING "${line}" ${off} -1 tail) + string(FIND "${tail}" "#" hp) + if(hp EQUAL -1) + break() + endif() + math(EXPR abs "${off} + ${hp}") + if(abs GREATER 0) + math(EXPR ph "${abs} - 1") + string(SUBSTRING "${line}" ${ph} 1 pc) + else() + set(pc "") + endif() + if(pc STREQUAL "\\") + math(EXPR off "${abs} + 1") + else() + string(SUBSTRING "${line}" 0 ${abs} result) + break() + endif() + endwhile() + set(${out} "${result}" PARENT_SCOPE) +endfunction() + +# eff <- TRUE only while every open branch on the conditional stack is active. +macro(_blis_stk_eff) + set(eff TRUE) + foreach(a IN LISTS stk_active) + if(NOT a) + set(eff FALSE) + endif() + endforeach() +endmacro() + +# Evaluates config//make_defs.mk and leaves every variable the def +# file assigns in the caller's scope, exactly as a make include would. +function(blis_parse_make_defs cfg) + set(mkfile "${BLIS_SOURCE_DIR}/config/${cfg}/make_defs.mk") + if(NOT EXISTS "${mkfile}") + message(FATAL_ERROR "BlisMakeDefs: no make_defs.mk for config '${cfg}'") + endif() + if(NOT CMAKE_SCRIPT_MODE_FILE) + set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${mkfile}") + endif() + file(READ "${mkfile}" raw) + string(REGEX REPLACE "\r\n" "\n" raw "${raw}") + # Conditional stack, one triplet per level: parent-effective, a branch + # of this level was already taken, current branch active. + set(stk_pe "") + set(stk_taken "") + set(stk_active "") + set(eff TRUE) + set(assigned "") + set(pending "") + # Lines are split by hand: a CMake list cannot hold an element that ends + # in a backslash, which is exactly what a make continuation line is. + string(APPEND raw "\n") + while(NOT raw STREQUAL "") + string(FIND "${raw}" "\n" nl) + string(SUBSTRING "${raw}" 0 ${nl} l) + math(EXPR lstart "${nl} + 1") + string(LENGTH "${raw}" rlen) + math(EXPR rlen "${rlen} - ${lstart}") + string(SUBSTRING "${raw}" ${lstart} ${rlen} raw) + + set(process TRUE) + # Fold backslash continuations the way make does: one single space. + if(l MATCHES "\\\\$") + string(REGEX REPLACE "\\\\$" "" l "${l}") + string(APPEND pending "${l} ") + set(process FALSE) + endif() + if(process) + set(l "${pending}${l}") + set(pending "") + blis_make_strip_comment("${l}" l) + string(STRIP "${l}" l) + if(l STREQUAL "") + set(process FALSE) + endif() + endif() + if(NOT process) + continue() + endif() + + set(condmatch FALSE) + set(is_else FALSE) + set(cond TRUE) + set(condtext "${l}") + if(l STREQUAL "else") + set(is_else TRUE) + elseif(l MATCHES "^else[ \t]+if") + set(is_else TRUE) + string(REGEX REPLACE "^else[ \t]+" "" condtext "${l}") + endif() + if(condtext MATCHES "^if(n?)eq[ \t]*\\((.*)\\)$") + set(condmatch TRUE) + set(negate "${CMAKE_MATCH_1}") + set(args "${CMAKE_MATCH_2}") + endif() + if(condmatch) + blis_make_split_args("${args}" ra rb) + blis_make_expand("${ra}" ra) + blis_make_expand("${rb}" rb) + string(STRIP "${ra}" ra) + string(STRIP "${rb}" rb) + set(cond FALSE) + if(ra STREQUAL rb) + set(cond TRUE) + endif() + if(negate STREQUAL "n") + if(cond) + set(cond FALSE) + else() + set(cond TRUE) + endif() + endif() + elseif(is_else AND NOT l STREQUAL "else") + message(FATAL_ERROR "BlisMakeDefs: ${mkfile}: cannot parse line: ${l}") + endif() + + if(condmatch OR is_else) + if(is_else) + list(POP_BACK stk_pe pe) + list(POP_BACK stk_taken taken) + list(POP_BACK stk_active prev) + set(active FALSE) + if(pe AND NOT taken AND cond) + set(active TRUE) + set(taken TRUE) + endif() + list(APPEND stk_pe "${pe}") + list(APPEND stk_taken "${taken}") + list(APPEND stk_active "${active}") + else() + set(active FALSE) + if(eff AND cond) + set(active TRUE) + endif() + list(APPEND stk_pe "${eff}") + list(APPEND stk_taken "${active}") + list(APPEND stk_active "${active}") + endif() + _blis_stk_eff() + continue() + elseif(l STREQUAL "endif") + list(POP_BACK stk_pe) + list(POP_BACK stk_taken) + list(POP_BACK stk_active) + _blis_stk_eff() + continue() + endif() + + if(NOT eff) + continue() + endif() + + if(l MATCHES "^([A-Za-z0-9_]+)[ \t]*(:=|\\+=)[ \t]*(.*)$") + set(var "${CMAKE_MATCH_1}") + set(op "${CMAKE_MATCH_2}") + blis_make_expand("${CMAKE_MATCH_3}" val) + string(STRIP "${val}" val) + list(APPEND assigned "${var}") + if(op STREQUAL ":=") + set(${var} "${val}") + elseif(DEFINED ${var}) + set(${var} "${${var}} ${val}") + else() + set(${var} "${val}") + endif() + elseif(l MATCHES "^\\$\\(error[ \t]+(.*)\\)$") + blis_make_expand("${CMAKE_MATCH_1}" msg) + blis_defer_error("${msg}") + elseif(l MATCHES "^\\$\\(info[ \t]" OR l MATCHES "^\\$\\(eval[ \t]") + # No-op: the print is intentional noise only in make, and the + # store-make-defs call is covered by the loader's own capture. + else() + message(FATAL_ERROR "BlisMakeDefs: ${mkfile}: cannot parse line: ${l}") + endif() + endwhile() + if(stk_pe) + message(FATAL_ERROR "BlisMakeDefs: ${mkfile}: unbalanced conditionals") + endif() + foreach(v IN LISTS assigned) + set(${v} "${${v}}" PARENT_SCOPE) + endforeach() +endfunction() diff --git a/cmake/BlisOptions.cmake b/cmake/BlisOptions.cmake new file mode 100644 index 0000000000..b71fc0c29a --- /dev/null +++ b/cmake/BlisOptions.cmake @@ -0,0 +1,330 @@ +# All user-facing cache options + validation, mirroring ./configure options. +# After inclusion, plain (non-cache) variables with the template anchor names +# of build/bli_config.h.in are set for configure_file(... @ONLY). + +set(BLIS_CONFIG "auto" CACHE STRING "BLIS configuration family (see config_registry) or 'auto' for hardware detection") + +set(BLIS_THREADING + "single" + CACHE STRING + "Threading models to build (first = runtime default): ';'-separated list of single,openmp,pthreads,hpx,auto" +) +set(BLIS_THREAD_JRIR_PARTITION "slab" CACHE STRING "jr/ir loop partitioning method: slab, rr, or tlb") +set_property(CACHE BLIS_THREAD_JRIR_PARTITION PROPERTY STRINGS slab rr tlb) + +set(BLIS_INT_SIZE "auto" CACHE STRING "BLIS integer type size: 32, 64, or auto") +set_property(CACHE BLIS_INT_SIZE PROPERTY STRINGS auto 32 64) +set(BLIS_BLAS_INT_SIZE "32" CACHE STRING "BLAS compatibility integer size: 32 or 64") +set_property(CACHE BLIS_BLAS_INT_SIZE PROPERTY STRINGS 32 64) + +option(BLIS_ENABLE_BLAS "Enable the BLAS compatibility layer" ON) +option(BLIS_ENABLE_CBLAS "Enable the CBLAS compatibility layer" OFF) +option(BLIS_ENABLE_SUP_HANDLING "Enable small/unpacked (sup) handling" ON) +option(BLIS_ENABLE_TRSM_PREINVERSION "Enable trsm pre-inversion" ON) +option(BLIS_ENABLE_MIXED_PRECISION_NORM "Accumulate snrm2/scnrm2 in double precision" ON) +option(BLIS_ENABLE_LAPACK_COMPAT "Omit symbols that clash with reference LAPACK" OFF) +option(BLIS_ENABLE_SCALAPACK_COMPAT "Omit symbols that clash with ScaLAPACK" OFF) +set(BLIS_OMIT_SYMBOLS "" CACHE STRING "Comma-separated list of additional symbols to omit") + +option(BLIS_ENABLE_STATIC "Build the static library (libblis.a)" ON) +option(BLIS_ENABLE_SHARED "Build the shared library (libblis.so)" ON) +set(BLIS_EXPORT_SHARED "public" CACHE STRING "Shared library exports: public (annotated) or all") +set_property(CACHE BLIS_EXPORT_SHARED PROPERTY STRINGS public all) +# configure defaults to --disable-rpath (absolute install_name on Darwin); +# keep the CMake default off to match. +option(BLIS_DARWIN_RPATH "Use @rpath-based install_name on macOS" OFF) + +set(BLIS_DEBUG_TYPE "off" CACHE STRING "Debug info: off, noopt (unoptimized), opt, or sde (Intel SDE)") +set_property( + CACHE BLIS_DEBUG_TYPE + PROPERTY STRINGS off noopt opt sde +) +option(BLIS_ENABLE_ASAN "Compile and link with AddressSanitizer" OFF) +option(BLIS_ENABLE_MEM_TRACING "Enable memory-tracing output" OFF) + +option(BLIS_ENABLE_PBA_POOLS "Enable packing-block allocator pools" ON) +option(BLIS_ENABLE_SBA_POOLS "Enable small-block allocator pools" ON) +set(BLIS_ENABLE_MEMKIND "auto" CACHE STRING "Use libmemkind for certain allocations: auto, on, or off") +set_property(CACHE BLIS_ENABLE_MEMKIND PROPERTY STRINGS auto on off) +set(BLIS_COMPLEX_RETURN "auto" CACHE STRING "Hidden complex-return-value convention: auto, gnu, or intel") +set_property(CACHE BLIS_COMPLEX_RETURN PROPERTY STRINGS auto gnu intel) + +option(BLIS_ENABLE_SYSTEM "Enable system interfaces (os threads, timers)" ON) +option(BLIS_ENABLE_TLS "Use thread-local storage qualifiers" ON) + +set(BLIS_ENABLE_ADDONS "" CACHE STRING "';'-separated list of addons under addon/ to build") +set(BLIS_SANDBOX "" CACHE STRING "Name of a sandbox under sandbox/ to enable") + +# --- install layout (mirrors configure's --exec-prefix / --sharedir) --- +# Library dir layout. 'make' installs under ${exec_prefix}/lib like the make +# build; 'gnu' keeps the GNUInstallDirs dir (lib/ on a /usr prefix). +set(BLIS_INSTALL_LAYOUT "make" CACHE STRING "Library install dir: make (lib) or gnu (GNUInstallDirs)") +set_property(CACHE BLIS_INSTALL_LAYOUT PROPERTY STRINGS make gnu) +if(NOT BLIS_INSTALL_LAYOUT MATCHES "^(make|gnu)$") + message(FATAL_ERROR "BLIS: BLIS_INSTALL_LAYOUT must be make or gnu") +endif() +set(BLIS_INSTALL_EXEC_PREFIX + "" + CACHE STRING + "Separate prefix for libraries (configure --exec-prefix). Empty = CMAKE_INSTALL_PREFIX" +) +set(BLIS_INSTALL_SHAREDIR + "" + CACHE STRING + "Directory for shared data such as pkg-config files (configure --sharedir). Empty = /share" +) +# BLIS_INSTALL_LIBDIR is the one library install dir. Every consumer (install +# DESTINATION, blis.pc, config.mk, uninstall-old, Darwin install_name) derives +# from it. It stays relative without an exec prefix, so --prefix and DESTDIR move it. +# GNUInstallDirs already set CMAKE_INSTALL_LIBDIR, so a caller-provided value is +# told apart from that default by the helpstring the cache entry carries; an +# absolute dir is caller-provided by construction. +get_property(_blis_libdir_hs CACHE CMAKE_INSTALL_LIBDIR PROPERTY HELPSTRING) +if( + BLIS_INSTALL_LAYOUT STREQUAL "make" + AND NOT _blis_libdir_hs MATCHES "command line" + AND NOT IS_ABSOLUTE "${CMAKE_INSTALL_LIBDIR}" +) + set(_blis_libdir "lib") +else() + set(_blis_libdir "${CMAKE_INSTALL_LIBDIR}") +endif() +if(NOT "${BLIS_INSTALL_EXEC_PREFIX}" STREQUAL "" AND NOT IS_ABSOLUTE "${_blis_libdir}") + set(BLIS_INSTALL_LIBDIR "${BLIS_INSTALL_EXEC_PREFIX}/${_blis_libdir}") +else() + set(BLIS_INSTALL_LIBDIR "${_blis_libdir}") +endif() +set(BLIS_INSTALL_FULL_LIBDIR "${BLIS_INSTALL_LIBDIR}") +cmake_path(ABSOLUTE_PATH BLIS_INSTALL_FULL_LIBDIR BASE_DIRECTORY "${CMAKE_INSTALL_PREFIX}") +if(NOT "${BLIS_INSTALL_SHAREDIR}" STREQUAL "") + set(BLIS_SHAREDIR "${BLIS_INSTALL_SHAREDIR}") +else() + set(BLIS_SHAREDIR "${CMAKE_INSTALL_DATAROOTDIR}") +endif() + +option(BLIS_ENABLE_AMD_FRAME_TWEAKS "Use AMD-customized framework files (zen configs only)" OFF) +set(BLIS_FORCE_VERSION "" CACHE STRING "Version string override (skips git/version-file detection)") +option(BLIS_BUILD_EXAMPLES "Build the example programs" OFF) + +option(BLIS_ENABLE_VERBOSE_MAKE "Enable verbose compilation output during make" OFF) +if(BLIS_ENABLE_VERBOSE_MAKE) + set(CMAKE_VERBOSE_MAKEFILE ON) + message(STATUS "BLIS: enabling verbose make output. (disable with 'make V=0'.)") +else() + message(STATUS "BLIS: disabling verbose make output. (enable with 'make V=1'.)") +endif() + +option( + BLIS_ENABLE_ARG_MAX_HACK + "Allow archiving and linking when the command line exceeds the system limit (ARG_MAX). This knob only writes ARG_MAX_HACK into config.mk for make-side consumers; the CMake build is unaffected" + OFF +) +if(BLIS_ENABLE_ARG_MAX_HACK) + message(STATUS "BLIS: enabling ARG_MAX hack.") +else() + message(STATUS "BLIS: disabling ARG_MAX hack.") +endif() + +option(BLIS_SHOW_CONFIG_LIST "Print the config and kernel lists and the kernel-to-config map at configure time" OFF) + +if(NOT BLIS_ENABLE_STATIC AND NOT BLIS_ENABLE_SHARED) + message(FATAL_ERROR "BLIS: cannot disable both static and shared libraries") +endif() +if(NOT BLIS_BLAS_INT_SIZE MATCHES "^(32|64)$") + message(FATAL_ERROR "BLIS: BLIS_BLAS_INT_SIZE must be 32 or 64") +endif() +if(BLIS_INT_SIZE STREQUAL "auto") + set(BLIS_INT_SIZE "0") +endif() +if(NOT BLIS_INT_SIZE MATCHES "^(0|32|64)$") + message(FATAL_ERROR "BLIS: BLIS_INT_SIZE must be 32, 64, or auto") +endif() +if(BLIS_BLAS_INT_SIZE STREQUAL "64" AND BLIS_INT_SIZE STREQUAL "32") + message(FATAL_ERROR "BLIS: BLIS_BLAS_INT_SIZE=64 requires BLIS_INT_SIZE=64") +endif() + +# AMD frame tweaks validation happens in the top-level CMakeLists, after +# BLIS_CONFIG ('auto') has been resolved. + +foreach(a IN LISTS BLIS_ENABLE_ADDONS) + if(NOT IS_DIRECTORY "${BLIS_SOURCE_DIR}/addon/${a}") + message(FATAL_ERROR "BLIS: addon '${a}' does not exist under addon/") + endif() +endforeach() +if(BLIS_SANDBOX AND NOT IS_DIRECTORY "${BLIS_SOURCE_DIR}/sandbox/${BLIS_SANDBOX}") + message(FATAL_ERROR "BLIS: sandbox '${BLIS_SANDBOX}' does not exist under sandbox/") +endif() + +# --- threading model resolution (configure's --enable-threading) --- +# Accept ';' or ',' separators; map synonyms; 'single' is always built and +# the first listed model becomes the runtime default. +string(REPLACE "," ";" _tm "${BLIS_THREADING}") +string(REPLACE "+" ";" _tm "${_tm}") +set(_models "") +foreach(m ${_tm}) + string(STRIP "${m}" m) + string(TOLOWER "${m}" m) + if(m MATCHES "^(single|none|off|no)$") + list(APPEND _models single) + elseif(m MATCHES "^(openmp|omp)$") + list(APPEND _models openmp) + elseif(m MATCHES "^(pthreads|pthread|posix)$") + list(APPEND _models pthreads) + elseif(m STREQUAL "hpx") + list(APPEND _models hpx) + elseif(m STREQUAL "auto") + # configure: auto -> openmp for gcc/icc, pthreads for clang; + # 'auto' is dropped for other vendors (leaving 'single'). + if(BLIS_CC_VENDOR MATCHES "gcc|icc") + list(APPEND _models openmp) + elseif(BLIS_CC_VENDOR MATCHES "clang") + list(APPEND _models pthreads) + endif() + else() + message(FATAL_ERROR "BLIS: unknown threading model '${m}'") + endif() +endforeach() +list(REMOVE_DUPLICATES _models) # keeps FIRST occurrences; first = default +list(FIND _models single _has_single) +if(_has_single EQUAL -1) + list(APPEND _models single) +endif() +if(NOT BLIS_ENABLE_SYSTEM) + set(_models single) +endif() +set(BLIS_THREADING_MODELS "${_models}") +list(GET _models 0 _tm_default) + +foreach(t openmp pthreads hpx) + set(enable_${t} 0) + set(enable_${t}_as_def 0) +endforeach() +foreach(t ${BLIS_THREADING_MODELS}) + if(NOT t STREQUAL "single") + set(enable_${t} 1) + if(t STREQUAL _tm_default) + set(enable_${t}_as_def 1) + endif() + endif() +endforeach() +set(BLIS_THREAD_DEFAULT "${_tm_default}") + +# HPX requires C++17 (mirrors common.mk); enable the language lazily from the +# top-level CMakeLists. + +set(enable_jrir_slab 0) +set(enable_jrir_rr 0) +set(enable_jrir_tlb 0) +set(enable_jrir_${BLIS_THREAD_JRIR_PARTITION} 1) +if(NOT BLIS_THREAD_JRIR_PARTITION MATCHES "^(slab|rr|tlb)$") + message(FATAL_ERROR "BLIS: BLIS_THREAD_JRIR_PARTITION must be slab, rr, or tlb") +endif() + +macro(_blis_01 anchor opt) + if(${opt}) + set(${anchor} 1) + else() + set(${anchor} 0) + endif() +endmacro() + +_blis_01(enable_system BLIS_ENABLE_SYSTEM) +_blis_01(enable_tls BLIS_ENABLE_TLS) +_blis_01(enable_pba_pools BLIS_ENABLE_PBA_POOLS) +_blis_01(enable_sba_pools BLIS_ENABLE_SBA_POOLS) +_blis_01(enable_mem_tracing BLIS_ENABLE_MEM_TRACING) +_blis_01(enable_mixed_precision_norm BLIS_ENABLE_MIXED_PRECISION_NORM) +_blis_01(enable_sup_handling BLIS_ENABLE_SUP_HANDLING) +_blis_01(enable_trsm_preinversion BLIS_ENABLE_TRSM_PREINVERSION) +_blis_01(enable_scalapack_compat BLIS_ENABLE_SCALAPACK_COMPAT) +_blis_01(enable_sandbox BLIS_SANDBOX) +_blis_01(enable_shared BLIS_ENABLE_SHARED) +# NOTE: like configure, the bli_config.h BLAS macro reflects the *requested* +# value, while the build-level BLAS objects are force-enabled by CBLAS. +_blis_01(enable_blas BLIS_ENABLE_BLAS) +_blis_01(enable_cblas BLIS_ENABLE_CBLAS) +if(BLIS_ENABLE_CBLAS) + set(BLIS_EFFECTIVE_BLAS ON) # make-level: CBLAS requires BLAS +else() + set(BLIS_EFFECTIVE_BLAS "${BLIS_ENABLE_BLAS}") +endif() + +set(int_type_size "${BLIS_INT_SIZE}") +set(blas_int_type_size "${BLIS_BLAS_INT_SIZE}") + +set(_omit "${BLIS_OMIT_SYMBOLS}") +if(BLIS_ENABLE_LAPACK_COMPAT) + string(APPEND _omit ",crot,zrot,csymv,zsymv,csyr,zsyr,csyr2,zsyr2,lsame,xerbla,xerbla_array") +endif() +if(BLIS_ENABLE_SCALAPACK_COMPAT) + string(APPEND _omit ",csymv,zsymv,csyr,zsyr,csyr2,zsyr2") +endif() +string(REPLACE "," ";" _omit_list "${_omit}") +set(omit_symbol_list_defines "") +foreach(s ${_omit_list}) + string(STRIP "${s}" s) + if(NOT s MATCHES "^[a-z0-9_]+$") + message(FATAL_ERROR "BLIS: invalid symbol name in omit list: '${s}'") + endif() + string(TOUPPER "${s}" S) + string(APPEND omit_symbol_list_defines "#define BLIS_DISABLE_${S}\n") +endforeach() + +if(BLIS_COMPLEX_RETURN STREQUAL "auto") + set(complex_return_intel 0) + find_program( + _blis_fc + NAMES $ENV{FC} gfortran ifort ifx nvfortran + ) + if(_blis_fc) + execute_process( + COMMAND "${_blis_fc}" --version + OUTPUT_VARIABLE _fc_v + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET + TIMEOUT 15 + ) + if(_fc_v MATCHES "IFORT|IFX") + set(complex_return_intel 1) + elseif(_fc_v MATCHES "NVIDIA|PGI") + set(complex_return_intel 1) + if(BLIS_HOST_MACHINE MATCHES "ppc64le") + set(complex_return_intel 0) + endif() + if(BLIS_HOST_MACHINE MATCHES "aarch64" AND _fc_v MATCHES "([0-9]+)\\.([0-9]+)") + if(CMAKE_MATCH_1 GREATER 23 OR (CMAKE_MATCH_1 EQUAL 23 AND CMAKE_MATCH_2 GREATER_EQUAL 9)) + # nvfortran >= 23.9 on aarch64 uses the gnu convention. + set(complex_return_intel 0) + endif() + endif() + endif() + endif() + unset(_blis_fc CACHE) +elseif(BLIS_COMPLEX_RETURN STREQUAL "intel") + set(complex_return_intel 1) +else() + set(complex_return_intel 0) +endif() + +if(BLIS_ENABLE_MEMKIND STREQUAL "auto") + _blis_probe_memkind(_mk_found) + if(_mk_found) + set(enable_memkind 1) + else() + set(enable_memkind 0) + endif() +elseif(BLIS_ENABLE_MEMKIND STREQUAL "on") + _blis_probe_memkind(_mk_found) + if(NOT _mk_found) + message(FATAL_ERROR "BLIS: BLIS_ENABLE_MEMKIND=on but libmemkind was not found") + endif() + set(enable_memkind 1) +else() + set(enable_memkind 0) +endif() + +set(addon_list_includes "") +_blis_01(enable_addons BLIS_ENABLE_ADDONS) +foreach(a IN LISTS BLIS_ENABLE_ADDONS) + string(APPEND addon_list_includes "#include \"${a}.h\"\n") +endforeach() diff --git a/cmake/BlisPluginHelpers.cmake.in b/cmake/BlisPluginHelpers.cmake.in new file mode 100644 index 0000000000..3363838da5 --- /dev/null +++ b/cmake/BlisPluginHelpers.cmake.in @@ -0,0 +1,53 @@ +# Helper functions for out-of-tree BLIS plugins (the CMake replacement for +# make's configure-plugin machinery). Included by blisConfig.cmake. + +function(_blis_plugin_apply target flags) + # One SHELL: group: CMake de-duplicates option items and would split + # repeated two-word flags ('--diag_suppress A --diag_suppress B'). + target_compile_options(${target} PRIVATE "SHELL:${flags}") + target_include_directories(${target} PRIVATE ${blis_INCLUDE_DIRS}) +endfunction() + +function(blis_plugin_frame_flags target) + _blis_plugin_apply(${target} "${blis_FRAME_CFLAGS}") +endfunction() + +# Kernel flags of carry ISA flags like -mavx2/-march=haswell, so the same +# target can be built once per subconfiguration as an OBJECT library. +function(blis_plugin_kernel_flags target cfg) + if(NOT DEFINED blis_KERNEL_CFLAGS_${cfg}) + message( + FATAL_ERROR + "blis_plugin_kernel_flags: '${cfg}' is not among the enabled " + "subconfigurations (${blis_CONFIG_LIST})" + ) + endif() + _blis_plugin_apply(${target} "${blis_KERNEL_CFLAGS_${cfg}}") +endfunction() + +# DEFS_HEADER is force-included (like BLIS's own bli_kernel_defs_.h) for +# blocksize/ukernel macros. +function(blis_plugin_refkern_flags target cfg) + cmake_parse_arguments(P "" "DEFS_HEADER" "" ${ARGN}) + if(NOT DEFINED blis_REFKERN_CFLAGS_${cfg}) + message( + FATAL_ERROR + "blis_plugin_refkern_flags: '${cfg}' is not among the enabled " + "subconfigurations (${blis_CONFIG_LIST})" + ) + endif() + _blis_plugin_apply(${target} "${blis_REFKERN_CFLAGS_${cfg}}") + if(P_DEFS_HEADER) + # Escape like blis_flag_group (BlisLibraries.cmake is not visible to an + # installed plugin): the SHELL: group re-parses with + # separate_arguments(UNIX_COMMAND), so the item needs real quote + # characters and doubled backslashes to survive as one argv item. + string(REPLACE "\\" "\\\\" _hdr "${P_DEFS_HEADER}") + string(REPLACE "\"" "\\\"" _hdr "${_hdr}") + if(CMAKE_C_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC") + target_compile_options(${target} PRIVATE "SHELL:-DBLIS_IN_REF_KERNEL=1 \"/FI${_hdr}\"") + else() + target_compile_options(${target} PRIVATE "SHELL:-DBLIS_IN_REF_KERNEL=1 -include \"${_hdr}\"") + endif() + endif() +endfunction() diff --git a/cmake/BlisRegistry.cmake b/cmake/BlisRegistry.cmake new file mode 100644 index 0000000000..6850ccdd6a --- /dev/null +++ b/cmake/BlisRegistry.cmake @@ -0,0 +1,199 @@ +# Parses config_registry and reproduces configure's registry semantics: +# family expansion to a fixpoint, duplicate removal keeping the LAST +# occurrence, kernel-set tracking ("subconfig/kset1/kset2" chains), and the +# kernel->subconfig map (singleton -> itself; name match -> kernel; else +# LAST subconfig in list order). + +# blis_registry_parse(): fills BLIS_REG_CONFIG_ / BLIS_REG_KERNELS_ +# / BLIS_REG_RAW_KERNELS_ (lists, parent scope) and BLIS_REG_NAMES (all +# registered names). +function(blis_registry_parse) + file(STRINGS "${BLIS_SOURCE_DIR}/config_registry" _lines) + set(all_names "") + foreach(line ${_lines}) + string(REGEX REPLACE "#.*$" "" line "${line}") + string(STRIP "${line}" line) + if(line STREQUAL "") + continue() + endif() + if(NOT line MATCHES "^([a-zA-Z0-9_]+):[ \t]*(.*)$") + message(FATAL_ERROR "config_registry: malformed line: ${line}") + endif() + set(name "${CMAKE_MATCH_1}") + separate_arguments(entries UNIX_COMMAND "${CMAKE_MATCH_2}") + # Each entry may be a chain "subconfig/k1/k2": the first element is the + # subconfig name only; the kernel sets are the REMAINING elements. + # Without a slash the subconfig and the (only) kernel set share the name. + set(configs "") + set(kernels "") + foreach(e ${entries}) + string(REPLACE "/" ";" chain "${e}") + list(GET chain 0 subconfig) + list(APPEND configs "${subconfig}") + list(LENGTH chain _clen) + if(_clen GREATER 1) + list(SUBLIST chain 1 -1 _krest) + list(APPEND kernels ${_krest}) + else() + list(APPEND kernels "${subconfig}") + endif() + endforeach() + blis_dedup_last(kernels) + set(_c_${name} "${configs}") + set(_k_${name} "${kernels}") + # The raw kernel list is exported because the fixpoint runs after the export. + set(BLIS_REG_RAW_KERNELS_${name} "${kernels}" PARENT_SCOPE) + list(APPEND all_names "${name}") + endforeach() + # configure's pass 1 drops blacklisted subconfigs from + # umbrella entries before the fixpoint; singleton lists stay as written. + blis_compute_blacklist(_bl) + set(BLIS_BLACKLIST "${_bl}" PARENT_SCOPE) + foreach(_n ${all_names}) + list(LENGTH _c_${_n} _len) + if(NOT (_len EQUAL 1 AND "${_c_${_n}}" STREQUAL "${_n}")) + foreach(_b ${_bl}) + list(REMOVE_ITEM _c_${_n} "${_b}") + list(REMOVE_ITEM _k_${_n} "${_b}") + endforeach() + endif() + set(BLIS_REG_CONFIG_${_n} "${_c_${_n}}" PARENT_SCOPE) + endforeach() + # Kernel-registry fixpoint: replace each family + # name in a kernel list with that family's kernel set until a full pass + # makes no non-singleton substitution. Entries iterate in sorted order, + # like bash's ${!kernel_registry_*}; the inner loop walks the pre- + # substitution list while each lookup uses the live value. + set(_knames "${all_names}") + list(SORT _knames) + set(_iterate TRUE) + while(_iterate) + set(_iterate FALSE) + foreach(_n ${_knames}) + if("${_n}" IN_LIST _k_${_n}) + continue() # singleton entries never substitute + endif() + foreach(_ker ${_k_${_n}}) + set(_kers "${_k_${_ker}}") + if("${_kers}" STREQUAL "") + continue() # kernel set without a config entry (e.g. armv8a) + endif() + if(NOT "${_ker}" STREQUAL "${_kers}") + set(_new "") + foreach(_w ${_k_${_n}}) + if(_w STREQUAL _ker) + list(APPEND _new ${_kers}) + else() + list(APPEND _new "${_w}") + endif() + endforeach() + blis_dedup_last(_new) + set(_k_${_n} "${_new}") + if(NOT "${_ker}" IN_LIST _kers) + set(_iterate TRUE) + endif() + endif() + endforeach() + endforeach() + endwhile() + foreach(_n ${all_names}) + set(BLIS_REG_KERNELS_${_n} "${_k_${_n}}" PARENT_SCOPE) + endforeach() + set(BLIS_REG_NAMES "${all_names}" PARENT_SCOPE) +endfunction() + +# blis_registry_expand( ) +# Expands families within families until fixpoint; dedup keeps LAST. +function(blis_registry_expand name out_configs out_kernels) + if(NOT DEFINED BLIS_REG_CONFIG_${name}) + message(FATAL_ERROR "'${name}' is not a registered configuration. " "Valid names: ${BLIS_REG_NAMES}") + endif() + set(configs "${BLIS_REG_CONFIG_${name}}") + set(kernels "${BLIS_REG_KERNELS_${name}}") + set(changed TRUE) + while(changed) + set(changed FALSE) + set(new_configs "") + set(new_kernels "") + foreach(c ${configs}) + # A member is itself a family when it maps to something other than (or + # more than) itself. + set(_is_fam FALSE) + if(DEFINED BLIS_REG_CONFIG_${c}) + set(_members "${BLIS_REG_CONFIG_${c}}") + list(LENGTH _members _n) + if((_n GREATER 1) OR (NOT "${_members}" STREQUAL "${c}")) + set(_is_fam TRUE) + endif() + endif() + if(_is_fam) + set(changed TRUE) + list(APPEND new_configs ${BLIS_REG_CONFIG_${c}}) + list(APPEND new_kernels ${BLIS_REG_KERNELS_${c}}) + else() + list(APPEND new_configs "${c}") + list(APPEND new_kernels "${BLIS_REG_KERNELS_${c}}") + endif() + endforeach() + set(configs "${new_configs}") + set(kernels "${new_kernels}") + endwhile() + # Dedup keeping LAST occurrence (mirrors configure's rm_duplicate_words). + blis_dedup_last(configs) + blis_dedup_last(kernels) + set(${out_configs} "${configs}" PARENT_SCOPE) + set(${out_kernels} "${kernels}" PARENT_SCOPE) +endfunction() + +function(blis_dedup_last listvar) + set(_in "${${listvar}}") + set(_out "") + foreach(x ${_in}) + list(FIND _out "${x}" _idx) + if(_idx GREATER -1) + list(REMOVE_AT _out ${_idx}) + endif() + list(APPEND _out "${x}") + endforeach() + set(${listvar} "${_out}" PARENT_SCOPE) +endfunction() + +# blis_kconfig_compute(): sets BLIS_KCONFIG_MAP (list of +# "kernel:subconfig" pairs, kernels sorted) in the caller scope. +function(blis_kconfig_compute config_list) + set(all_kernels "") + foreach(c ${config_list}) + foreach(k ${BLIS_REG_KERNELS_${c}}) + list(APPEND KCAND_${k} "${c}") + list(APPEND all_kernels "${k}") + endforeach() + endforeach() + list(REMOVE_DUPLICATES all_kernels) + list(SORT all_kernels) + set(map "") + foreach(k ${all_kernels}) + set(cands "${KCAND_${k}}") + list(LENGTH cands n) + list(FIND cands "${k}" idx) + if(n EQUAL 1) + list(GET cands 0 chosen) + elseif(idx GREATER -1) + set(chosen "${k}") + else() + list(GET cands -1 chosen) + endif() + list(APPEND map "${k}:${chosen}") + endforeach() + set(BLIS_KCONFIG_MAP "${map}" PARENT_SCOPE) +endfunction() + +# blis_kconfig_for( ): subconfig whose flags compile . +function(blis_kconfig_for kernel out) + foreach(pair ${BLIS_KCONFIG_MAP}) + if(pair MATCHES "^${kernel}:(.*)$") + set(${out} "${CMAKE_MATCH_1}" PARENT_SCOPE) + return() + endif() + endforeach() + message(FATAL_ERROR "kernel '${kernel}' not in KCONFIG_MAP") +endfunction() diff --git a/cmake/BlisRunBlastest.cmake b/cmake/BlisRunBlastest.cmake new file mode 100644 index 0000000000..4a6c4a8ca0 --- /dev/null +++ b/cmake/BlisRunBlastest.cmake @@ -0,0 +1,38 @@ +# Runs one blastest driver in SCRATCH and mirrors blastest/check-blastest.sh. + +# Always start from a clean scratch: a stale out.* from a previously crashed +# run must not false-fail this one. +file(REMOVE_RECURSE "${SCRATCH}") +file(MAKE_DIRECTORY "${SCRATCH}") +set(out "${SCRATCH}/out.driver") + +if(LEVEL STREQUAL "1") + execute_process(COMMAND "${EXE}" RESULT_VARIABLE rv OUTPUT_FILE "${out}" ERROR_QUIET) +else() + execute_process( + COMMAND "${EXE}" + RESULT_VARIABLE rv + INPUT_FILE "${INPUT}" + OUTPUT_FILE "${out}" + ERROR_QUIET + WORKING_DIRECTORY "${SCRATCH}" + ) +endif() +if(NOT rv EQUAL 0) + message(FATAL_ERROR "${EXE} exited with status ${rv}") +endif() + +# Drivers write their summaries into files named by the first record of the +# .in file (out. in SCRATCH); level-1 goes to stdout (out.driver). +file(GLOB summaries "${SCRATCH}/out.*") +set(failed FALSE) +foreach(s ${summaries}) + file(STRINGS "${s}" bad REGEX "\\*\\*\\*\\*") + if(bad) + set(failed TRUE) + message(STATUS "failures found in ${s}:\n${bad}") + endif() +endforeach() +if(failed) + message(FATAL_ERROR "BLAS test driver reported failures (see ${SCRATCH}/out.*)") +endif() diff --git a/cmake/BlisRunTest.cmake b/cmake/BlisRunTest.cmake new file mode 100644 index 0000000000..b1fb703518 --- /dev/null +++ b/cmake/BlisRunTest.cmake @@ -0,0 +1,48 @@ +# Test runner for the BLIS testsuite. + +set(_args) +foreach(_n RANGE 1 20) + if(DEFINED arg${_n}) + list(APPEND _args "${arg${_n}}") + else() + break() + endif() +endforeach() + +if(NOT EXE) + message(FATAL_ERROR "EXE must be specified") +endif() + +if(NOT WORK_DIR) + set(WORK_DIR "${CMAKE_CURRENT_BINARY_DIR}") +endif() + +set(_cmd "${EXE}" ${_args}) +if(LAUNCHER) + separate_arguments(_launch_list NATIVE_COMMAND "${LAUNCHER}") + list(LENGTH _launch_list _launch_len) + if(_launch_len GREATER 0) + list(INSERT _cmd 0 ${_launch_list}) + endif() +endif() + +execute_process( + COMMAND ${_cmd} + WORKING_DIRECTORY "${WORK_DIR}" + RESULT_VARIABLE _exit_code + OUTPUT_VARIABLE _output + ERROR_VARIABLE _error + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_STRIP_TRAILING_WHITESPACE +) + +if(NOT (_exit_code EQUAL 0 AND "${_output}" MATCHES "Exiting normally" AND NOT "${_output}" MATCHES "FAILURE")) + if(NOT _exit_code EQUAL 0) + message(STATUS "Test failed: exit code ${_exit_code}") + elseif(NOT "${_output}" MATCHES "Exiting normally") + message(STATUS "Test failed: missing 'Exiting normally' in output") + else() + message(STATUS "Test failed: 'FAILURE' found in output") + endif() + message(FATAL_ERROR "Test failed") +endif() diff --git a/cmake/BlisSources.cmake b/cmake/BlisSources.cmake new file mode 100644 index 0000000000..a565a36f3b --- /dev/null +++ b/cmake/BlisSources.cmake @@ -0,0 +1,61 @@ +set(BLIS_FRAGMENT_IGNORE_DIRS + attic + broken + old + other + temp + tmp + test + p10_testsuite +) + +function(blis_collect out root) + set(all "") + foreach(suf ${ARGN}) + file(GLOB_RECURSE found CONFIGURE_DEPENDS "${root}/*.${suf}") + list(APPEND all ${found}) + endforeach() + set(keep "") + foreach(f ${all}) + file(RELATIVE_PATH rel "${root}" "${f}") + set(skip FALSE) + foreach(ig ${BLIS_FRAGMENT_IGNORE_DIRS}) + if(rel MATCHES "(^|/)${ig}(/|$)") + set(skip TRUE) + break() + endif() + endforeach() + if(NOT skip) + list(APPEND keep "${f}") + endif() + endforeach() + set(${out} ${keep} PARENT_SCOPE) +endfunction() + +# Directories containing header files under (deduped) — used both for +# flatten-headers.py's search list and for -I flags. +function(blis_header_dirs out) + set(dirs "") + foreach(root ${ARGN}) + foreach(suf h hh hpp hxx) + file(GLOB_RECURSE hdrs CONFIGURE_DEPENDS "${root}/*.${suf}") + foreach(h ${hdrs}) + file(RELATIVE_PATH rel "${root}" "${h}") + set(skip FALSE) + foreach(ig ${BLIS_FRAGMENT_IGNORE_DIRS}) + if(rel MATCHES "(^|/)${ig}(/|$)") + set(skip TRUE) + break() + endif() + endforeach() + if(skip) + continue() + endif() + get_filename_component(d "${h}" DIRECTORY) + list(APPEND dirs "${d}") + endforeach() + endforeach() + endforeach() + list(REMOVE_DUPLICATES dirs) + set(${out} ${dirs} PARENT_SCOPE) +endfunction() diff --git a/cmake/BlisTesting.cmake b/cmake/BlisTesting.cmake new file mode 100644 index 0000000000..309a4321f1 --- /dev/null +++ b/cmake/BlisTesting.cmake @@ -0,0 +1,70 @@ +# Semantics mirror testsuite/check-blistest.sh and blastest/check-blastest.sh. + +# Same meaning as make's TESTSUITE_WRAPPER: a command that prefixes every +# testsuite and blastest run (for example `qemu-aarch64 -L /usr/aarch64-linux-gnu`). +set(TESTSUITE_WRAPPER "$ENV{TESTSUITE_WRAPPER}" CACHE STRING "Command that prefixes every testsuite and blastest run") + +# One launcher per test command: TESTSUITE_WRAPPER replaces the cross-compiling +# emulator, as make's TESTSUITE_WRAPPER does, so a command never gets two prefixes. +if(TESTSUITE_WRAPPER) + set(_blis_launcher "${TESTSUITE_WRAPPER}") +elseif(CMAKE_CROSSCOMPILING AND CMAKE_CROSSCOMPILING_EMULATOR) + list(JOIN CMAKE_CROSSCOMPILING_EMULATOR " " _blis_launcher) +else() + set(_blis_launcher "") +endif() + +function(blis_register_testsuite_tests) + set(_variants + "full:testsuite/input.general:testsuite/input.operations" + "fast:testsuite/input.general.fast:testsuite/input.operations.fast" + "mixed:testsuite/input.general.mixed:testsuite/input.operations.mixed" + "salt:testsuite/input.general.salt:testsuite/input.operations.salt" + ) + foreach(v ${_variants}) + string(REPLACE ":" ";" parts "${v}") + list(GET parts 0 name) + list(GET parts 1 gen) + list(GET parts 2 ops) + set(_exe "$") + add_test( + NAME blis-test-${name} + COMMAND + ${CMAKE_COMMAND} "-DEXE=${_exe}" "-DLAUNCHER=${_blis_launcher}" -Darg1=-g + -Darg2=${BLIS_SOURCE_DIR}/${gen} -Darg3=-o -Darg4=${BLIS_SOURCE_DIR}/${ops} + -DWORK_DIR=${PROJECT_BINARY_DIR} -P ${BLIS_SOURCE_DIR}/cmake/BlisRunTest.cmake + ) + set_tests_properties(blis-test-${name} PROPERTIES LABELS "${name}") + if(WIN32) + # The testsuite links the shared library on Windows; add its dir to PATH. + set_tests_properties(blis-test-${name} PROPERTIES ENVIRONMENT "PATH=${PROJECT_BINARY_DIR}/lib\;$ENV{PATH}") + endif() + endforeach() +endfunction() + +function(blis_register_blastest_tests) + foreach(p s d c z) + foreach(l 1 2 3) + set(drv "${p}blat${l}") + set(_scratch "${PROJECT_BINARY_DIR}/blastest_run/${drv}") + file(MAKE_DIRECTORY "${_scratch}") + set(_exe "$") + if(_blis_launcher) + # The runner executes a single file, so hand it a launcher script. + set(_exe "${PROJECT_BINARY_DIR}/blastest_run/${drv}.sh") + file( + GENERATE OUTPUT "${_exe}" + CONTENT "#!/bin/sh\nexec ${_blis_launcher} $ \"$@\"\n" + FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE + ) + endif() + add_test( + NAME blastest-${drv} + COMMAND + ${CMAKE_COMMAND} -DEXE=${_exe} -DINPUT=${BLIS_SOURCE_DIR}/blastest/input/${drv}.in -DLEVEL=${l} + -DSCRATCH=${_scratch} -P ${BLIS_SOURCE_DIR}/cmake/BlisRunBlastest.cmake + ) + set_tests_properties(blastest-${drv} PROPERTIES LABELS "blastest") + endforeach() + endforeach() +endfunction() diff --git a/cmake/BlisToolchainEnv.cmake b/cmake/BlisToolchainEnv.cmake new file mode 100644 index 0000000000..5d511b99fb --- /dev/null +++ b/cmake/BlisToolchainEnv.cmake @@ -0,0 +1,88 @@ +# configure honors AR, RANLIB and PYTHON from the environment, splits the value +# on whitespace (program first, then arguments), records the raw string in +# config.mk and exits 1 when the program is missing. Do the same: seed the cache +# before project() so CMake's own find_program keeps the program, keep the raw +# string in the cache entry BLIS__CMD (CMake's automatic reconfigure runs +# without the environment), and put the arguments into the archive rules and the +# python commands below. An explicit -D value wins and takes no arguments. +# shortcut: arguments are split on whitespace like configure's unquoted ${tool}; no shell quoting. +set(_blis_envs AR RANLIB PYTHON) +set(_blis_vars CMAKE_AR CMAKE_RANLIB Python3_EXECUTABLE) +set(_blis_descs "library archiver" "archive indexer" "python interpreter") +foreach(_t _v _d IN ZIP_LISTS _blis_envs _blis_vars _blis_descs) + get_property(_blis_help CACHE ${_v} PROPERTY HELPSTRING) + # Seed when unset, or again when an earlier run seeded it (reconfigure keeps the arguments). + if(NOT "$ENV{${_t}}" STREQUAL "" AND (NOT DEFINED ${_v} OR _blis_help STREQUAL "${_t} from the environment")) + separate_arguments(_blis_words UNIX_COMMAND "$ENV{${_t}}") + list(POP_FRONT _blis_words _blis_prog) + # Unset after the copy: each configure must re-search, and the cache floor + # (3.20) has no find_program NO_CACHE (3.21). + find_program(_blis_env_${_t} NAMES "${_blis_prog}") + set(_blis_prog_path "${_blis_env_${_t}}") + unset(_blis_env_${_t} CACHE) + if(NOT _blis_prog_path) + message( + FATAL_ERROR + "BLIS: *** Could not find the ${_d} specified via ${_t} ('$ENV{${_t}}').\n" + "BLIS: *** A working ${_d} is required. Please set ${_t}\n" + "BLIS: *** to a ${_d} that exists (or unset ${_t})." + ) + endif() + set(${_v} "${_blis_prog_path}" CACHE FILEPATH "${_t} from the environment" FORCE) + set(BLIS_${_t}_CMD "$ENV{${_t}}" CACHE INTERNAL "raw ${_t} command from the environment") + elseif(DEFINED BLIS_${_t}_CMD AND NOT _blis_help STREQUAL "${_t} from the environment") + # An explicit -D value now selects the program; its seeded arguments do not apply. + unset(BLIS_${_t}_CMD CACHE) + endif() +endforeach() +# Capture the raw -D CMAKE_C_COMPILER so config.mk records the value the user +# gave; project() below resolves CMAKE_C_COMPILER to an absolute path. +if(DEFINED CMAKE_C_COMPILER AND NOT DEFINED BLIS_CC_CMD) + set(BLIS_CC_CMD "${CMAKE_C_COMPILER}" CACHE INTERNAL "raw CC command from -D") +endif() +# configure honors LIBPTHREAD from the environment (default -lpthread); keep it +# in the cache so an automatic reconfigure without the environment keeps the +# value, and let an explicit -D win. +get_property(_blis_pth_help CACHE LIBPTHREAD PROPERTY HELPSTRING) +if(DEFINED ENV{LIBPTHREAD} AND (NOT DEFINED LIBPTHREAD OR _blis_pth_help STREQUAL "LIBPTHREAD from the environment")) + set(LIBPTHREAD "$ENV{LIBPTHREAD}" CACHE STRING "LIBPTHREAD from the environment") +endif() +# Arguments of AR and RANLIB go into every archive rule (CMake's default rules +# take the program only); the rules must exist before project() loads the languages. +foreach(_t _k IN ZIP_LISTS _blis_envs _blis_vars) + if(_t STREQUAL "PYTHON") + continue() + endif() + separate_arguments(_blis_words UNIX_COMMAND "${BLIS_${_t}_CMD}") + list(POP_FRONT _blis_words) + string(JOIN " " _blis_args ${_blis_words}) + if(NOT "${_blis_args}" STREQUAL "") + foreach(_l C CXX) + if(_t STREQUAL "AR") + set(CMAKE_${_l}_ARCHIVE_CREATE " ${_blis_args} qc ") + set(CMAKE_${_l}_ARCHIVE_APPEND " ${_blis_args} q ") + else() + set(CMAKE_${_l}_ARCHIVE_FINISH " ${_blis_args} ") + endif() + endforeach() + endif() +endforeach() +separate_arguments(BLIS_PYTHON_ARGS UNIX_COMMAND "${BLIS_PYTHON_CMD}") +list(POP_FRONT BLIS_PYTHON_ARGS) + +# config.mk records the raw AR, RANLIB and PYTHON strings, as configure does; +# BlisMakeShare reads the CMake variables, so shadow them for its include. +macro(blis_raw_tools_begin) + foreach(_t _v IN ZIP_LISTS _blis_envs _blis_vars) + if(DEFINED BLIS_${_t}_CMD) + set(${_v} "${BLIS_${_t}_CMD}") + endif() + endforeach() +endmacro() +macro(blis_raw_tools_end) + foreach(_t _v IN ZIP_LISTS _blis_envs _blis_vars) + if(DEFINED BLIS_${_t}_CMD) + unset(${_v}) + endif() + endforeach() +endmacro() diff --git a/cmake/BlisVersion.cmake b/cmake/BlisVersion.cmake new file mode 100644 index 0000000000..033a70b2c4 --- /dev/null +++ b/cmake/BlisVersion.cmake @@ -0,0 +1,39 @@ +# Computes the BLIS version string the same way configure does: +# an exact-match git tag wins; otherwise build/version is used; everything +# from the first '-' onwards is dropped. BLIS_FORCE_VERSION overrides both. +# Sets: BLIS_VERSION, BLIS_VERSION_MAJOR/MINOR/REVISION, BLIS_SO_MAJOR, +# BLIS_SO_MINORB (so_version minor.build). + +find_package(Git QUIET) +if(BLIS_FORCE_VERSION) + set(_version_raw "${BLIS_FORCE_VERSION}") +else() + set(_version_raw "") + if(GIT_FOUND AND EXISTS "${BLIS_SOURCE_DIR}/.git") + execute_process( + COMMAND "${GIT_EXECUTABLE}" -C "${BLIS_SOURCE_DIR}" describe --tags --exact-match + OUTPUT_VARIABLE _version_raw + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + endif() + if(NOT _version_raw) + file(STRINGS "${BLIS_SOURCE_DIR}/build/version" _version_raw LIMIT_COUNT 1) + endif() +endif() + +string(REGEX REPLACE "-.*$" "" BLIS_VERSION "${_version_raw}") +string(REPLACE "." ";" _vparts "${BLIS_VERSION}") +list(GET _vparts 0 BLIS_VERSION_MAJOR) +list(GET _vparts 1 BLIS_VERSION_MINOR) +list(LENGTH _vparts _vlen) +if(_vlen GREATER 2) + list(GET _vparts 2 BLIS_VERSION_REVISION) +else() + set(BLIS_VERSION_REVISION 0) +endif() + +file(STRINGS "${BLIS_SOURCE_DIR}/build/so_version" _so_lines) +list(GET _so_lines 0 BLIS_SO_MAJOR) +list(GET _so_lines 1 BLIS_SO_MINORB) +set(BLIS_SO_MMB "${BLIS_SO_MAJOR}.${BLIS_SO_MINORB}") diff --git a/cmake/blisConfig.cmake.in b/cmake/blisConfig.cmake.in new file mode 100644 index 0000000000..789bc6a8eb --- /dev/null +++ b/cmake/blisConfig.cmake.in @@ -0,0 +1,38 @@ +@PACKAGE_INIT@ + +include(CMakeFindDependencyMacro) + +if(@BLIS_NEEDS_THREADS@) + set(THREADS_PREFER_PTHREAD_FLAG ON) + find_dependency(Threads) +endif() +if(@BLIS_NEEDS_OPENMP@) + find_dependency(OpenMP) +endif() +if(@BLIS_NEEDS_MEMKIND@) + find_library(MEMKIND_LIBRARY memkind) + if(NOT MEMKIND_LIBRARY) + set(blis_FOUND FALSE) + set(blis_NOT_FOUND_MESSAGE "blis was built with memkind but libmemkind was not found") + return() + endif() +endif() + +include("${CMAKE_CURRENT_LIST_DIR}/blisTargets.cmake") + +# The target carries the include dirs resolved from where blisTargets.cmake sits. +get_target_property(blis_INCLUDE_DIRS blis::blis INTERFACE_INCLUDE_DIRECTORIES) +set(blis_LIBRARIES blis::blis) + +# Configuration metadata (used by plugins and other advanced consumers). +set(blis_VERSION "@BLIS_VERSION@") +set(blis_CONFIG_NAME "@BLIS_CONFIG_NAME@") +set(blis_CONFIG_LIST "@BLIS_CONFIG_LIST@") + +@BLISPLUGIN_EXPORTS@ + +if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/BlisPluginHelpers.cmake") + include("${CMAKE_CURRENT_LIST_DIR}/BlisPluginHelpers.cmake") +endif() + +check_required_components(blis) diff --git a/cmake/cmake_uninstall.cmake b/cmake/cmake_uninstall.cmake new file mode 100644 index 0000000000..0adb908a31 --- /dev/null +++ b/cmake/cmake_uninstall.cmake @@ -0,0 +1,41 @@ +if(EXISTS "${RECORD}") + include("${RECORD}") +else() + set(blis_rec_prefix "${PREFIX}") + set(blis_rec_destdir "$ENV{DESTDIR}") +endif() +# A relative path would resolve against this process's directory and could +# delete from the wrong tree. The install writes absolute paths; the fallback +# has to give absolute ones too. +foreach(v blis_rec_prefix blis_rec_destdir) + if(NOT "${${v}}" STREQUAL "" AND NOT IS_ABSOLUTE "${${v}}") + message( + FATAL_ERROR + "uninstall: ${v} '${${v}}' is not absolute. Install again, or set an absolute CMAKE_INSTALL_PREFIX and DESTDIR." + ) + endif() +endforeach() +set(ENV{DESTDIR} "${blis_rec_destdir}") +foreach(f IN ITEMS FILES DIRS) + string(REPLACE "|" ";" ${f} "${${f}}") + set(_abs_${f} "") + foreach(i IN LISTS ${f}) + cmake_path(ABSOLUTE_PATH i BASE_DIRECTORY "${blis_rec_prefix}" OUTPUT_VARIABLE _a) + list(APPEND _abs_${f} "${_a}") + endforeach() +endforeach() +if(WHAT) + foreach(v LIBDIR INCDIR) + cmake_path(ABSOLUTE_PATH ${v} BASE_DIRECTORY "${blis_rec_prefix}") + endforeach() + include("${CMAKE_CURRENT_LIST_DIR}/make/BlisUninstallOld.cmake") + return() +endif() +foreach(f IN LISTS _abs_FILES) + message(STATUS "Uninstalling $ENV{DESTDIR}${f}") + file(REMOVE "$ENV{DESTDIR}${f}") +endforeach() +foreach(d IN LISTS _abs_DIRS) + message(STATUS "Uninstalling $ENV{DESTDIR}${d}") + file(REMOVE_RECURSE "$ENV{DESTDIR}${d}") +endforeach() diff --git a/cmake/make/BlisMakeShare.cmake b/cmake/make/BlisMakeShare.cmake new file mode 100644 index 0000000000..2cfa209ad1 --- /dev/null +++ b/cmake/make/BlisMakeShare.cmake @@ -0,0 +1,272 @@ +function(_blis_yn out) + if(${ARGN}) + set(${out} yes PARENT_SCOPE) + else() + set(${out} no PARENT_SCOPE) + endif() +endfunction() + +function(_blis_row label value) + string(LENGTH "${label}" _n) + math(EXPR _pad "28 - ${_n}") + string(REPEAT " " ${_pad} _sp) + string(APPEND _showconfig "${label}${_sp}${value}\n") + set(_showconfig "${_showconfig}" PARENT_SCOPE) +endfunction() + +set(version "${BLIS_VERSION}") +set(so_version_major "${BLIS_SO_MAJOR}") +set(so_version_minorbuild "${BLIS_SO_MINORB}") +set(config_name "${BLIS_CONFIG_NAME}") +string(JOIN " " config_list ${BLIS_CONFIG_LIST}) +string(JOIN " " kernel_list ${BLIS_KERNEL_LIST}) +string(JOIN " " kconfig_map ${BLIS_KCONFIG_MAP}) + +set(_fc "") +set(_fs "") +set(_fk "") +foreach(n ${BLIS_REG_NAMES}) + list(APPEND _fc ${n}) + if("${BLIS_REG_CONFIG_${n}}" STREQUAL "${n}") + list(APPEND _fs ${n}) + # the raw kernel list is exported because the fixpoint runs after the export + list(APPEND _fk ${BLIS_REG_RAW_KERNELS_${n}}) + endif() +endforeach() +list(REMOVE_DUPLICATES _fk) +string(JOIN " " full_config_list ${_fc}) +string(JOIN " " full_subconfig_list ${_fs}) +string(JOIN " " full_kernel_list ${_fk}) + +set(os_name "${CMAKE_SYSTEM_NAME}") +_blis_yn(is_win WIN32) +_blis_yn(is_msvc MSVC) +set(dist_path "${BLIS_SOURCE_DIR}") +set(CC_VENDOR "${BLIS_CC_VENDOR}") +foreach(v 4_9_0 6_1_0 9_1_0 10_3_0) + set(gcc_older_than_${v} "${BLIS_GCC_OT_${v}}") +endforeach() +foreach(v 9_0_0 12_0_0) + set(clang_older_than_${v} "${BLIS_CLANG_OT_${v}}") +endforeach() +foreach(v 2_0_0 3_0_0) + set(aocc_older_than_${v} "${BLIS_AOCC_OT_${v}}") +endforeach() +# Record each tool the way configure records it: the raw value the user gave +# (via env or -D) when there is one, else the bare name of the first tool from +# configure's search list. CMake's resolved absolute paths would not match. +# configure treats an empty CC/CXX/FC as unset ([ -n ... ]); DEFINED does not. +if(NOT "$ENV{CC}" STREQUAL "") + set(CC "$ENV{CC}") +elseif(DEFINED BLIS_CC_CMD) + set(CC "${BLIS_CC_CMD}") +else() + if(CMAKE_SYSTEM_NAME MATCHES "OpenBSD|FreeBSD") + find_program(_blis_cc_pick NAMES cc clang gcc) + else() + find_program(_blis_cc_pick NAMES gcc clang cc) + endif() + if(_blis_cc_pick) + get_filename_component(CC "${_blis_cc_pick}" NAME) + else() + set(CC "${CMAKE_C_COMPILER}") + endif() +endif() +if(NOT "$ENV{CXX}" STREQUAL "") + set(CXX "$ENV{CXX}") +elseif(CMAKE_CXX_COMPILER) + set(CXX "${CMAKE_CXX_COMPILER}") +else() + if(CMAKE_SYSTEM_NAME MATCHES "OpenBSD|FreeBSD") + find_program(_blis_cxx_pick NAMES c++ clang++ g++) + else() + find_program(_blis_cxx_pick NAMES g++ clang++ c++) + endif() + if(_blis_cxx_pick) + get_filename_component(CXX "${_blis_cxx_pick}" NAME) + else() + set(CXX "") + endif() +endif() +if(NOT "$ENV{FC}" STREQUAL "") + set(FC "$ENV{FC}") +else() + find_program(_blis_fc_pick NAMES gfortran ifort ifx nvfortran) + if(_blis_fc_pick) + get_filename_component(FC "${_blis_fc_pick}" NAME) + else() + set(FC "") + endif() +endif() +if(DEFINED BLIS_AR_CMD) + set(AR "${BLIS_AR_CMD}") +else() + find_program(_blis_ar_pick NAMES ar) + if(_blis_ar_pick) + get_filename_component(AR "${_blis_ar_pick}" NAME) + else() + set(AR "${CMAKE_AR}") + endif() +endif() +if(DEFINED BLIS_RANLIB_CMD) + set(RANLIB "${BLIS_RANLIB_CMD}") +else() + find_program(_blis_ranlib_pick NAMES ranlib) + if(_blis_ranlib_pick) + get_filename_component(RANLIB "${_blis_ranlib_pick}" NAME) + else() + set(RANLIB "${CMAKE_RANLIB}") + endif() +endif() +if(DEFINED BLIS_PYTHON_CMD) + set(PYTHON "${BLIS_PYTHON_CMD}") +else() + find_program(_blis_py_pick NAMES python python3 python2) + if(_blis_py_pick) + get_filename_component(PYTHON "${_blis_py_pick}" NAME) + else() + set(PYTHON "${Python3_EXECUTABLE}") + endif() +endif() +set(cflags_preset "${CMAKE_C_FLAGS}") +if(DEFINED CMAKE_CXX_FLAGS) + set(cxxflags_preset "${CMAKE_CXX_FLAGS}") +else() + set(cxxflags_preset "$ENV{CXXFLAGS}") # no C++ language enabled in a C-only build +endif() +string(JOIN " " ldflags_preset ${BLIS_LDFLAGS_PRESET}) +set(debug_type "${BLIS_DEBUG_TYPE}") +_blis_yn(enable_debug NOT "${BLIS_DEBUG_TYPE}" STREQUAL "off") +_blis_yn(enable_asan BLIS_ENABLE_ASAN) +_blis_yn(mk_enable_system BLIS_ENABLE_SYSTEM) +string(JOIN " " threading_model ${BLIS_THREADING_MODELS}) +set(pragma_omp_simd "${BLIS_PRAGMA_OMP_SIMD}") +if(NOT DEFINED LIBPTHREAD) + set(libpthread "-lpthread") +else() + set(libpthread "${LIBPTHREAD}") +endif() +if((WIN32 AND BLIS_CC_VENDOR STREQUAL "clang") OR NOT BLIS_ENABLE_SYSTEM) + set(libpthread "") +endif() +_blis_yn(enable_verbose BLIS_ENABLE_VERBOSE_MAKE) +set(configured_oot yes) +_blis_yn(enable_arg_max_hack BLIS_ENABLE_ARG_MAX_HACK) +_blis_yn(mk_enable_static BLIS_ENABLE_STATIC) +_blis_yn(mk_enable_shared BLIS_ENABLE_SHARED) +_blis_yn(enable_rpath APPLE AND BLIS_DARWIN_RPATH) +set(export_shared "${BLIS_EXPORT_SHARED}") +_blis_yn(mk_enable_blas BLIS_EFFECTIVE_BLAS) +_blis_yn(mk_enable_cblas BLIS_ENABLE_CBLAS) +_blis_yn(mk_enable_memkind enable_memkind) +string(JOIN " " addon_list ${BLIS_ENABLE_ADDONS}) +set(sandbox "${BLIS_SANDBOX}") +_blis_yn(enable_amd_frame_tweaks BLIS_ENABLE_AMD_FRAME_TWEAKS) + +set(prefix "@BLIS_INSTALL_PREFIX@") +if(NOT "${BLIS_INSTALL_EXEC_PREFIX}" STREQUAL "") + set(exec_prefix "${BLIS_INSTALL_EXEC_PREFIX}") +else() + set(exec_prefix "\${prefix}") +endif() +if(IS_ABSOLUTE "${BLIS_INSTALL_LIBDIR}") + set(libdir "${BLIS_INSTALL_LIBDIR}") +else() + set(libdir "\${exec_prefix}/${BLIS_INSTALL_LIBDIR}") +endif() +if(IS_ABSOLUTE "${CMAKE_INSTALL_INCLUDEDIR}") + set(includedir "${CMAKE_INSTALL_INCLUDEDIR}") +else() + set(includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}") +endif() +if(NOT "${BLIS_INSTALL_SHAREDIR}" STREQUAL "") + set(sharedir "${BLIS_INSTALL_SHAREDIR}") +elseif(IS_ABSOLUTE "${CMAKE_INSTALL_DATAROOTDIR}") + set(sharedir "${CMAKE_INSTALL_DATAROOTDIR}") +else() + set(sharedir "\${prefix}/${CMAKE_INSTALL_DATAROOTDIR}") +endif() + +set(_mkdir "${PROJECT_BINARY_DIR}/make-share") +set(anchor "@anchor@") +configure_file("${BLIS_SOURCE_DIR}/build/config.mk.in" "${_mkdir}/config.mk" @ONLY) +unset(anchor) + +# --- install share/blis (Makefile: install-share) --- +set(_share "${BLIS_SHAREDIR}/blis") +set(_src "${BLIS_SOURCE_DIR}") +install( + FILES + "${_src}/common.mk" + "${_src}/config_registry" + "${_src}/build/flatten-headers.py" + "${_src}/build/gen-make-frags/fragment.mk" + "${_src}/build/gen-make-frags/ignore_list" + "${_src}/build/gen-make-frags/special_list" + "${_src}/build/gen-make-frags/suffix_list" + "${_src}/build/detect/iset/avx.s" + "${_src}/build/detect/iset/avx512dq.s" + "${_src}/build/detect/iset/avx512f.s" + "${_src}/build/detect/iset/fma3.s" + "${_src}/build/detect/iset/fma4.s" + DESTINATION ${_share} + COMPONENT share +) +install( + PROGRAMS "${_src}/build/gen-make-frags/gen-make-frag.sh" "${_src}/build/mirror-tree.sh" + DESTINATION ${_share} + COMPONENT share +) +install(PROGRAMS "${_src}/configure" DESTINATION ${_share} RENAME configure-plugin COMPONENT share) +install( + FILES + "${_src}/build/plugin/bli_plugin_init_ref.c" + "${_src}/build/plugin/bli_plugin_init_zen3.c" + "${_src}/build/plugin/bli_plugin_register.c" + "${_src}/build/plugin/my_kernel_1_ref.c" + "${_src}/build/plugin/my_kernel_2_ref.c" + "${_src}/build/plugin/my_kernel_1_zen3.c" + "${_src}/build/plugin/bli_plugin.h.in" + "${_src}/build/plugin/config.mk.in" + "${_src}/build/plugin/Makefile" + DESTINATION ${_share}/plugin + COMPONENT share +) +# Like the Makefile loop, skip a config that lacks one of the two files. +foreach(c ${_fc}) + foreach(f make_defs.mk bli_kernel_defs_${c}.h) + if(EXISTS "${_src}/config/${c}/${f}") + install(FILES "${_src}/config/${c}/${f}" DESTINATION ${_share}/config/${c} COMPONENT share) + endif() + endforeach() +endforeach() + +_blis_install_staged("${_mkdir}/config.mk" "config.mk" "${_share}") + +# --- showconfig (Makefile: showconfig) --- +set(_showconfig "") +_blis_row("configuration family:" "${config_name}") +_blis_row("sub-configurations:" "${config_list}") +_blis_row("requisite kernels sets:" "${kernel_list}") +_blis_row("kernel-to-config map:" "${kconfig_map}") +string(APPEND _showconfig "-------------------------\n") +_blis_row("BLIS version string:" "${version}") +_blis_row(".so major version:" "${so_version_major}") +_blis_row(".so minor.build vers:" "${so_version_minorbuild}") +set(_eff_incdir "${CMAKE_INSTALL_INCLUDEDIR}") +cmake_path(ABSOLUTE_PATH _eff_incdir BASE_DIRECTORY "${CMAKE_INSTALL_PREFIX}") +set(_eff_sharedir "${BLIS_SHAREDIR}") +cmake_path(ABSOLUTE_PATH _eff_sharedir BASE_DIRECTORY "${CMAKE_INSTALL_PREFIX}") +_blis_row("install libdir:" "${BLIS_INSTALL_FULL_LIBDIR}") +_blis_row("install includedir:" "${_eff_incdir}") +_blis_row("install sharedir:" "${_eff_sharedir}") +_blis_row("debugging status:" "${debug_type}") +_blis_row("enable AddressSanitizer?" "${enable_asan}") +_blis_row("enabled threading model(s):" "${threading_model}") +_blis_row("enable BLAS API?" "${mk_enable_blas}") +_blis_row("enable CBLAS API?" "${mk_enable_cblas}") +_blis_row("build static library?" "${mk_enable_static}") +_blis_row("build shared library?" "${mk_enable_shared}") +_blis_row("ARG_MAX hack enabled?" "${enable_arg_max_hack}") +file(WRITE "${PROJECT_BINARY_DIR}/showconfig.txt" "${_showconfig}") +add_custom_target(showconfig COMMAND "${CMAKE_COMMAND}" -E cat "${PROJECT_BINARY_DIR}/showconfig.txt" VERBATIM) diff --git a/cmake/make/BlisMakeTargets.cmake b/cmake/make/BlisMakeTargets.cmake new file mode 100644 index 0000000000..1fa4a96e17 --- /dev/null +++ b/cmake/make/BlisMakeTargets.cmake @@ -0,0 +1,181 @@ +if(CMAKE_SCRIPT_MODE_FILE) + file(GLOB_RECURSE _objs "${DIR}/*.o" "${DIR}/*.obj") + # make builds the examples outside obj/, so cleanlib keeps them. + list(FILTER _objs EXCLUDE REGEX "^${DIR}/examples/") + if(_objs) + file(REMOVE ${_objs}) + endif() + return() +endif() + +function(_blis_target name deps) + foreach(_d IN LISTS deps) + if(NOT TARGET ${_d}) + add_custom_target( + ${name} + VERBATIM + COMMAND "${CMAKE_COMMAND}" -E echo "${name}: no target ${_d} (BUILD_TESTING off or blastest disabled)" + COMMAND "${CMAKE_COMMAND}" -E false + ) + return() + endif() + endforeach() + if(ARGN) + add_custom_target(${name} COMMAND ${ARGN} USES_TERMINAL VERBATIM COMMAND_EXPAND_LISTS) + else() + add_custom_target(${name}) + endif() + if(deps) + add_dependencies(${name} ${deps}) + endif() +endfunction() + +set(_libs "") +foreach(_t blis_static blis_shared) + if(TARGET ${_t}) + list(APPEND _libs ${_t}) + endif() +endforeach() +_blis_target(libblis "${_libs}") +_blis_target(libs libblis) + +_blis_target(check-env-mk "") +_blis_target(check-env-fragments check-env-mk) +_blis_target(check-env-make-defs check-env-fragments) +_blis_target(check-env "check-env-make-defs;check-env-fragments;check-env-mk") + +add_custom_target(flat-header DEPENDS "${PROJECT_BINARY_DIR}/include/blis.h") +_blis_target(flat-cblas-header blis_flat_headers) + +set(_ctest + "${CMAKE_CTEST_COMMAND}" + --test-dir + "${PROJECT_BINARY_DIR}" + "$<$>:-C$$>" + --output-on-failure +) + +_blis_target(testsuite-bin test_libblis.x) +_blis_target(testsuite-run test_libblis.x ${_ctest} -L "^full$") +_blis_target(testsuite-run-fast test_libblis.x ${_ctest} -L "^fast$") +_blis_target(testsuite-run-md test_libblis.x ${_ctest} -L "^mixed$") +_blis_target(testsuite-run-salt test_libblis.x ${_ctest} -L "^salt$") +_blis_target(testsuite testsuite-run) +_blis_target(testblis testsuite) +_blis_target(checkblis testsuite-run) +foreach(_v fast md salt) + _blis_target(testblis-${_v} testsuite-run-${_v}) + _blis_target(checkblis-${_v} testsuite-run-${_v}) +endforeach() + +# blastest-run: the 12 drivers; run-: one driver (Makefile run-$(1)). +_blis_target(blastest-f2c f2c) +_blis_target(blastest-run blastest-bin ${_ctest} -L "^blastest$") +_blis_target(testblas blastest-run) +_blis_target(checkblas blastest-run) + +set(_check_deps test_libblis.x) +if(TARGET blastest-bin) + list(APPEND _check_deps blastest-bin) +endif() +_blis_target(check "${_check_deps}" ${_ctest} -L "^(fast|blastest)$") + +_blis_target(checkblis-checkblas "checkblis;checkblas") + +foreach(_p s d c z) + foreach(_l 1 2 3) + set(_drv ${_p}blat${_l}) + _blis_target(run-${_drv} ${_drv}.x ${_ctest} -R "^blastest-${_drv}$") + endforeach() +endforeach() + +set(_rm "${CMAKE_COMMAND}" -E rm -f) +function(_blis_rm_cmd out) + if(ARGN) + set(${out} COMMAND ${_rm} ${ARGN} PARENT_SCOPE) + endif() +endfunction() + +_blis_target(cleanh "" ${_rm} "${PROJECT_BINARY_DIR}/include/blis.h" "${PROJECT_BINARY_DIR}/include/cblas.h") + +set(_lib_files "") +if(TARGET blis_static) + list(APPEND _lib_files "$/$") +endif() +if(TARGET blis_shared) + list( + APPEND _lib_files + "$/$" + "$/$" + ) + if(NOT WIN32) + list(APPEND _lib_files "$/$") + endif() +endif() +_blis_rm_cmd(_rm_lib ${_lib_files}) +# Keep the target name on the open-paren line: audits grep for it. +# gersemi: off +_blis_target(cleanlib "" "${CMAKE_COMMAND}" -DDIR=${PROJECT_BINARY_DIR} + -P "${CMAKE_CURRENT_LIST_FILE}" ${_rm_lib}) +# gersemi: on + +_blis_target(cleanh-cleanlib "cleanh;cleanlib") + +_blis_target(cleanmk "") +_blis_target(cleanblastestdir "") +_blis_target(cleanblistestdir "") + +set(_blat_files "") +if(TARGET f2c) + list(APPEND _blat_files "$/$") +endif() +foreach(_p s d c z) + foreach(_l 1 2 3) + set(_drv ${_p}blat${_l}) + if(TARGET ${_drv}.x) + list( + APPEND _blat_files + "$/$" + "${PROJECT_BINARY_DIR}/blastest_run/${_drv}/out.driver" + "${PROJECT_BINARY_DIR}/blastest_run/${_drv}/out.${_drv}" + ) + endif() + endforeach() +endforeach() +_blis_rm_cmd(_rm_blastesttop ${_blat_files}) +_blis_target( + cleanblastesttop + "" + "${CMAKE_COMMAND}" + -DDIR=${PROJECT_BINARY_DIR}/blastest + -P + "${CMAKE_CURRENT_LIST_FILE}" + ${_rm_blastesttop} +) + +set(_ts_files "") +if(TARGET test_libblis.x) + set(_ts_files "$/$") +endif() +_blis_rm_cmd(_rm_blistesttop ${_ts_files}) +_blis_target( + cleanblistesttop + "" + "${CMAKE_COMMAND}" + -DDIR=${PROJECT_BINARY_DIR}/testsuite + -P + "${CMAKE_CURRENT_LIST_FILE}" + ${_rm_blistesttop} +) + +_blis_target(cleanblastest "cleanblastesttop;cleanblastestdir") +_blis_target(cleanblistest "cleanblistesttop;cleanblistestdir") +_blis_target(cleantest "cleanblastest;cleanblistest") + +# gersemi: off +_blis_target(distclean "cleanmk;cleanh;cleanlib;cleantest" + ${_rm} "${PROJECT_BINARY_DIR}/bli_config.h" "${PROJECT_BINARY_DIR}/bli_addon.h" + "${PROJECT_BINARY_DIR}/make-share/config.mk" "${PROJECT_BINARY_DIR}/blis.pc" + COMMAND "${CMAKE_COMMAND}" -E rm -rf "${PROJECT_BINARY_DIR}/lib" "${PROJECT_BINARY_DIR}/include" + COMMAND "${CMAKE_COMMAND}" -E touch "${PROJECT_BINARY_DIR}/CMakeCache.txt") +# gersemi: on diff --git a/cmake/make/BlisUninstall.cmake b/cmake/make/BlisUninstall.cmake new file mode 100644 index 0000000000..dda05715b8 --- /dev/null +++ b/cmake/make/BlisUninstall.cmake @@ -0,0 +1,131 @@ +set(_blis_record "${PROJECT_BINARY_DIR}/blis_install_record.cmake") +foreach( + _c + headers + helper-headers + libs + lib-symlinks + share + cmake-package +) + install(CODE "set(_blis_record \"${_blis_record}\")" COMPONENT ${_c}) + install( + CODE + [[ + set(_p "${CMAKE_INSTALL_PREFIX}") + set(_d "$ENV{DESTDIR}") + cmake_path(ABSOLUTE_PATH _p NORMALIZE) + if(NOT "${_d}" STREQUAL "") + cmake_path(ABSOLUTE_PATH _d NORMALIZE) + endif() + file(WRITE "${_blis_record}" + "set(blis_rec_prefix [==[${_p}]==])\nset(blis_rec_destdir [==[${_d}]==])\n") + ]] + COMPONENT ${_c} + ) +endforeach() +set(_blis_unargs "-DRECORD=${_blis_record}" "-DPREFIX=${CMAKE_INSTALL_PREFIX}") + +add_custom_target(uninstall-old) +foreach(w libs symlinks headers) + # Keep the target name on the open-paren line: audits grep for it. + # gersemi: off + add_custom_target(uninstall-old-${w} + COMMAND "${CMAKE_COMMAND}" -DWHAT=${w} + -DLIBDIR=${BLIS_INSTALL_LIBDIR} -DINCDIR=${CMAKE_INSTALL_INCLUDEDIR} + -DSO_MAJOR=${BLIS_SO_MAJOR} -DSO_MMB=${BLIS_SO_MMB} -DDARWIN=${APPLE} + ${_blis_unargs} + -P "${BLIS_SOURCE_DIR}/cmake/cmake_uninstall.cmake" + VERBATIM) + # gersemi: on + add_dependencies(uninstall-old uninstall-old-${w}) +endforeach() + +set(_ic_headers headers helper-headers) +foreach(_n helper-headers libs lib-symlinks share) + set(_ic_${_n} ${_n}) +endforeach() +set(_blis_libtargets "") +foreach(_t blis_static blis_shared) + if(TARGET ${_t}) + list(APPEND _blis_libtargets ${_t}) + endif() +endforeach() +foreach( + _n + headers + helper-headers + libs + lib-symlinks + share +) + set(_cmds "") + foreach(_c ${_ic_${_n}}) + list( + APPEND _cmds + COMMAND + "${CMAKE_COMMAND}" + -DCMAKE_INSTALL_COMPONENT=${_c} + -P + "${PROJECT_BINARY_DIR}/cmake_install.cmake" + ) + endforeach() + add_custom_target(install-${_n} ${_cmds} VERBATIM) + if(_n MATCHES "headers$") + add_dependencies(install-${_n} blis_flat_headers) + elseif(_n MATCHES "^lib") + add_dependencies(install-${_n} ${_blis_libtargets}) + endif() +endforeach() + +set(_ul_libs "") +set(_ul_syml "") +if(TARGET blis_static) + list(APPEND _ul_libs "${BLIS_INSTALL_LIBDIR}/$") +endif() +if(TARGET blis_shared) + if(WIN32) + list( + APPEND _ul_syml + "${BLIS_INSTALL_LIBDIR}/$" + "${BLIS_INSTALL_LIBDIR}/$" + ) + else() + list(APPEND _ul_libs "${BLIS_INSTALL_LIBDIR}/$") + list( + APPEND _ul_syml + "${BLIS_INSTALL_LIBDIR}/$" + "${BLIS_INSTALL_LIBDIR}/$" + ) + endif() +endif() +set(_ul_hdrs "${CMAKE_INSTALL_INCLUDEDIR}/blis" "${CMAKE_INSTALL_INCLUDEDIR}/blis.h") +if(BLIS_ENABLE_CBLAS) + list(APPEND _ul_hdrs "${CMAKE_INSTALL_INCLUDEDIR}/cblas.h") +endif() +foreach(_n libs lib-symlinks headers share) + set(_f "") + set(_d "") + if(_n STREQUAL "libs") + list(JOIN _ul_libs "|" _f) + elseif(_n STREQUAL "lib-symlinks") + list(JOIN _ul_syml "|" _f) + elseif(_n STREQUAL "headers") + list(JOIN _ul_hdrs "|" _d) + else() + set(_d "${BLIS_SHAREDIR}/blis") + endif() + # gersemi: off + add_custom_target(uninstall-${_n} + COMMAND "${CMAKE_COMMAND}" "-DFILES=${_f}" "-DDIRS=${_d}" ${_blis_unargs} + -P "${BLIS_SOURCE_DIR}/cmake/cmake_uninstall.cmake" + VERBATIM) + # gersemi: on +endforeach() +# gersemi: off +add_custom_target(uninstall + COMMAND "${CMAKE_COMMAND}" "-DDIRS=${BLIS_INSTALL_LIBDIR}/cmake/blis" ${_blis_unargs} + -P "${BLIS_SOURCE_DIR}/cmake/cmake_uninstall.cmake" + VERBATIM) +# gersemi: on +add_dependencies(uninstall uninstall-libs uninstall-lib-symlinks uninstall-headers uninstall-share) diff --git a/cmake/make/BlisUninstallOld.cmake b/cmake/make/BlisUninstallOld.cmake new file mode 100644 index 0000000000..990b171e27 --- /dev/null +++ b/cmake/make/BlisUninstallOld.cmake @@ -0,0 +1,40 @@ +set(_d "$ENV{DESTDIR}${LIBDIR}") +if(DARWIN) + set(_so "dylib") + set(_full "${_d}/libblis.?.?.?.dylib") + set(_cur "${_d}/libblis.${SO_MMB}.dylib") + set(_maj "${_d}/libblis.?.dylib") + set(_curmaj "${_d}/libblis.${SO_MAJOR}.dylib") +else() + set(_so "so") + set(_full "${_d}/libblis.so.?.?.?") + set(_cur "${_d}/libblis.so.${SO_MMB}") + set(_maj "${_d}/libblis.so.?") + set(_curmaj "${_d}/libblis.so.${SO_MAJOR}") +endif() + +set(_old "") +if(WHAT STREQUAL "libs") + # Static libraries are overwritten by newer ones, so only versioned shared + # libraries other than the current one are old. + file(GLOB _old "${_full}") + list(REMOVE_ITEM _old "${_cur}") +elseif(WHAT STREQUAL "symlinks") + file(GLOB _old "${_maj}") + list(REMOVE_ITEM _old "${_curmaj}") + # Older-style names that carry a BLIS version and configuration family. + file(GLOB _named "${_d}/libblis-*.a" "${_d}/libblis-*.${_so}") + list(APPEND _old ${_named}) +elseif(WHAT STREQUAL "headers") + file(GLOB _old "$ENV{DESTDIR}${INCDIR}/blis/*.h") + list(FILTER _old EXCLUDE REGEX "/(blis|cblas)\\.h$") +else() + message(FATAL_ERROR "BlisUninstallOld: WHAT must be libs, symlinks or headers") +endif() + +foreach(f ${_old}) + cmake_path(GET f FILENAME _n) + cmake_path(GET f PARENT_PATH _p) + message(STATUS "Uninstalling ${_n} from ${_p}/") + file(REMOVE "${f}") +endforeach() diff --git a/docs/BuildSystem.md b/docs/BuildSystem.md index b9afab3fdf..65d374db54 100644 --- a/docs/BuildSystem.md +++ b/docs/BuildSystem.md @@ -21,6 +21,8 @@ This document describes how to configure, compile, and install a BLIS library on your local system. +**Note:** BLIS also ships with a CMake-based build; see [BuildSystemCMake.md](BuildSystemCMake.md) for instructions and a full option-mapping table. + The BLIS build system was designed for use with GNU/Linux (or some other sane UNIX). Other requirements are: * Python (2.7 or later for python2; 3.4 or later for python3) diff --git a/docs/BuildSystemCMake.md b/docs/BuildSystemCMake.md new file mode 100644 index 0000000000..d5675b222f --- /dev/null +++ b/docs/BuildSystemCMake.md @@ -0,0 +1,186 @@ +# Building BLIS with CMake + +BLIS can be built with CMake (≥ 3.20) instead of `configure` + `make`. The +CMake build ports the full feature surface: configuration families with +hardware auto-detection, per-kernel-set ISA compilation, threading models +(single/pthreads/OpenMP/HPX), BLAS and CBLAS compatibility layers, addons, +sandboxes, static and shared libraries, the testsuite, the reference-BLAS +(blastest) tests, and installation with pkg-config + `find_package` support. + +## Quick start + +```sh +cmake -S . -B out -DBLIS_CONFIG=auto -DBLIS_THREADING=openmp +cmake --build out -j +ctest --test-dir out -L fast # fast correctness check +ctest --test-dir out # full testsuite + BLAS tests +ctest --test-dir out -L blastest # netlib BLAS tests only +cmake --install out --prefix /opt/blis +``` + +`cmake --build out --target uninstall` removes a previous installation. +It runs the four `uninstall-*` targets and also removes `lib/cmake/blis` +and `blis.pc`. + +The auto-detected config caches, so a second configure in the same build +dir does not detect again. Change it with `-DBLIS_CONFIG=` or a +new build dir. + +## Option mapping (`configure` → CMake) + +| configure | CMake cache variable | default | +|------------------------------|-----------------------------------|---------| +| `[config name]` / `auto` | `BLIS_CONFIG=` (`z15`, `zarch` for s390x) | `auto` | +| `-t {openmp,pthreads,single,hpx,auto[,...]}` | `BLIS_THREADING="openmp;pthreads"` | `single` | +| `-p PREFIX` / `--prefix` | `CMAKE_INSTALL_PREFIX` | system default | +| `--libdir/--includedir/...` | `CMAKE_INSTALL_LIBDIR/...` (GNUInstallDirs) | | +| `--exec-prefix` | `BLIS_INSTALL_EXEC_PREFIX` | empty | +| | Libraries install to `${BLIS_INSTALL_EXEC_PREFIX}/${CMAKE_INSTALL_LIBDIR}`. | +| | `blis.pc`, `config.mk`, `uninstall-old`, and Darwin install name all use this dir. +| `--sharedir` | `BLIS_INSTALL_SHAREDIR` | empty | +| `make install` layout | `BLIS_INSTALL_LAYOUT={make,gnu}` | `make` | +| `--enable/disable-static` | `BLIS_ENABLE_STATIC` | `ON` | +| `--enable/disable-shared` | `BLIS_ENABLE_SHARED` | `ON` | +| `-e {public,all}` | `BLIS_EXPORT_SHARED` | `public`| +| `--enable/disable-rpath` | `BLIS_DARWIN_RPATH` (macOS) | `OFF` | +| `--int-size={32,64}` | `BLIS_INT_SIZE` | `auto` | +| `-b {32,64}` | `BLIS_BLAS_INT_SIZE` | `32` | +| `--enable/disable-blas` | `BLIS_ENABLE_BLAS` | `ON` | +| `--enable/disable-cblas` | `BLIS_ENABLE_CBLAS` | `OFF` | +| `--disable-sup-handling` | `BLIS_ENABLE_SUP_HANDLING=OFF` | `ON` | +| `--enable/disable-trsm-preinversion` | `BLIS_ENABLE_TRSM_PREINVERSION` | `ON` | +| `--enable/disable-mixed-precision-norm` | `BLIS_ENABLE_MIXED_PRECISION_NORM` | `ON` | +| `--enable-lapack-compat` | `BLIS_ENABLE_LAPACK_COMPAT` | `OFF` | +| `--enable-scalapack-compat` | `BLIS_ENABLE_SCALAPACK_COMPAT` | `OFF` | +| `--omit-symbols=a,b` | `BLIS_OMIT_SYMBOLS="a,b"` | empty | +| `-d {noopt,opt,sde}` | `BLIS_DEBUG_TYPE=...` | `off` | +| `--enable-asan` | `BLIS_ENABLE_ASAN` | `OFF` | +| `--enable-mem-tracing` | `BLIS_ENABLE_MEM_TRACING` | `OFF` | +| `--disable-pba-pools` | `BLIS_ENABLE_PBA_POOLS=OFF` | `ON` | +| `--disable-sba-pools` | `BLIS_ENABLE_SBA_POOLS=OFF` | `ON` | +| `--with/without-memkind` | `BLIS_ENABLE_MEMKIND=on/off/auto` | `auto` | +| `--complex-return={gnu,intel}` | `BLIS_COMPLEX_RETURN` | `auto` | +| `-a NAME` | `BLIS_ENABLE_ADDONS="a;b"` | empty | +| `-s NAME` | `BLIS_SANDBOX=name` | empty | +| `--enable-amd-frame-tweaks` | `BLIS_ENABLE_AMD_FRAME_TWEAKS` | `OFF` | +| `--enable/disable-arg-max-hack` | `BLIS_ENABLE_ARG_MAX_HACK` | `OFF` | +| `--enable/disable-verbose-make` | `BLIS_ENABLE_VERBOSE_MAKE` | `OFF` | +| `--show-config-list` | `BLIS_SHOW_CONFIG_LIST` | `OFF` | +| `--disable-system/-tls` | `BLIS_ENABLE_SYSTEM/BLIS_ENABLE_TLS` | `ON` | +| `-r {slab,rr,tlb}` | `BLIS_THREAD_JRIR_PARTITION` | `slab` | +| `--force-version=x.y.z` | `BLIS_FORCE_VERSION` | unset | +| `CC/CFLAGS/LDFLAGS` env | `CMAKE_C_COMPILER`, `CMAKE_C_FLAGS` (env-seeded), env `LDFLAGS` | | +| `AR`, `RANLIB`, `PYTHON` env | `CMAKE_AR`, `CMAKE_RANLIB`, `Python3_EXECUTABLE` (seeded from the environment at the first configure with arguments. The raw value stays cached on reconfigure. A new environment value replaces it. An explicit `-D` value wins and drops the cached arguments) | | +| `TESTSUITE_WRAPPER` env | `TESTSUITE_WRAPPER` (cache entry, seeded from env, replaces `CMAKE_CROSSCOMPILING_EMULATOR` for the test commands) | empty | +| `make checkblis[-fast,-md,-salt]` | `ctest -R blis-test-{full,fast,mixed,salt}` | | +| `make checkblas` | `ctest -L blastest` | | +| `make showconfig` | `showconfig` target | | +| `make check` | `check` (ctest `fast` and `blastest` labels) | | +| `make blastest-bin` | `blastest-bin` (f2c, all BLAT drivers) | | +| `make uninstall-old` | `uninstall-old` (-libs, -symlinks, -headers) | | +| `make install-{headers,helper-headers,libs,lib-symlinks,share}` | same names as targets; see the install components below | | +| `make uninstall-{headers,libs,lib-symlinks,share}` | same names as targets | | +| other make target names | one CMake target each; see the table below | | + +Notes on deliberate differences from the make build: + +- Per-flag compilation semantics are identical (compile lines were verified + file-for-file against a `make V=1` reference build). Environment `CFLAGS` + is applied via `CMAKE_C_FLAGS` (CMake's own mechanism, seeded from the + same environment), not re-injected as the make system's "preset". +- The old Makefile's helper targets map as follows: `make checkblis*` → + `ctest -R blis-test-*`, `make checkblas` → `ctest -L blastest`, + `make symbols` → `ninja symbols` (or the `symbols` build target), + `make changelog` → `changelog` target, `make showconfig` → configure-time + summary, `make distclean` → `distclean` target, + `make uninstall` → `uninstall` target, `make install INSTALL_HH=yes` → + `-DBLIS_INSTALL_VENDOR_CPP_HEADERS=ON`. +- testsuite/, blastest/ and examples/ additionally work as *standalone* + CMake projects against an installed BLIS (`cmake -S -B out + -DCMAKE_PREFIX_PATH=`), replacing their share/blis-based + standalone Makefiles. +- Out-of-tree *plugins* are ordinary CMake projects that + `find_package(blis)` and use the installed helper functions + (`blis_plugin_frame_flags`, `blis_plugin_kernel_flags`, + `blis_plugin_refkern_flags`); see docs/PluginHowTo.md. The install puts + the make-based `configure-plugin` fragments under `/share/blis`, + with `config.mk`, the gen-make-frags scripts, the plugin templates, and + the per-config `make_defs.mk` and kernel files. +- `-lrt` is linked on Linux/glibc (skipped on Android/Bionic); memkind is + auto-detected, forceable with `BLIS_ENABLE_MEMKIND=on`. +- HPX threading enables C++ and requires `pkg-config hpx_component`. + `blis.pc` has the HPX `LDFLAGS` from `pkg-config`. +- The `LDFLAGS` from `pkg-config` (HPX) do not change: `-framework X` + stays together, and `-pthread` stays `-pthread` where `make` writes + it raw. +- A config that calls `$(error ...)` in `make_defs.mk` stops at build start. + The `blis_defer_error()` helper does this. +- `cmake --build DIR --target install` builds the code, then installs it. +- `cmake --install DIR` installs the built code. +- `cmake --install DIR --component X` installs one component. The components + are `headers`, `helper-headers`, `libs`, `lib-symlinks`, `share` and + `cmake-package`. The target `install-X` does the same for the first five + names, after it builds the needed files. `install-headers` also installs + `helper-headers`. +- The installed CMake package is relocatable. It works after `--prefix`, + `DESTDIR` and `BLIS_INSTALL_EXEC_PREFIX` change the install location. +- The uninstall targets are `uninstall-headers`, `uninstall-libs`, + `uninstall-lib-symlinks` and `uninstall-share`. They get the file names + from the install layout. Each `uninstall` and `uninstall-*` target + removes files from the prefix and the `DESTDIR` of the last install + from the build dir. The install record holds the prefix and `DESTDIR` + as absolute paths. No uninstall path resolves against the current directory. + Before the first install, uninstall uses the configured prefix and + the `DESTDIR` environment, as `make uninstall` does. It stops with + an error when `DESTDIR` is relative. + One build dir records only its last install. +- With HPX threading, the C++ sources of `frame/` build with the HPX + compile flags. The HPX include directories apply to all sources. +- The first configure uses `AR`, `RANLIB` and `PYTHON` from the environment, + as `configure` does. Each value can have arguments. The first word is + the program. The other words are its arguments. `config.mk` records + the raw string. A missing program named by `AR`, `RANLIB` + or `PYTHON` stops the configure step with the message of `configure`. + An explicit `-DCMAKE_AR`, `-DCMAKE_RANLIB` or `-DPython3_EXECUTABLE` + wins and skips the check. Do not add arguments to it. +- `TESTSUITE_WRAPPER` replaces `CMAKE_CROSSCOMPILING_EMULATOR`. Each test has + one launcher. +- With `BLIS_IS_MSVC` (cl, clang-cl or clang targeting windows-msvc) the + DLL is `libblis..dll` and its import library is `libblis.lib`. + The static library is `libblis_static.lib` when the shared library also + builds, and `libblis.lib` in a static-only build. + +## Make targets as CMake targets + +Each make target name is also a CMake target. Build it with +`cmake --build DIR --target NAME`. + +| make targets | CMake behavior | +|--------------|----------------| +| `libblis`, `libs` | build the static and shared libraries | +| `flat-header`, `flat-cblas-header` | make `include/blis.h` (and `cblas.h` when CBLAS is `ON`) | +| `check-env`, `check-env-mk`, `check-env-fragments`, `check-env-make-defs` | do nothing, because the configure step does the checks | +| `testsuite-bin`, `blastest-f2c` | build `test_libblis.x`, `libf2c.a` | +| `testsuite-run[-fast,-md,-salt]` | run ctest on the label `full`, `fast`, `mixed` or `salt` | +| `testsuite`, `testblis`, `checkblis` | run `testsuite-run` | +| `testblis-{fast,md,salt}`, `checkblis-{fast,md,salt}` | run `testsuite-run-{fast,md,salt}` | +| `blastest-run`, `testblas`, `checkblas` | run ctest on the label `blastest` | +| `run-DRIVER` (`run-sblat1` to `run-zblat3`) | run one blastest driver | +| `cleanh`, `cleanlib`, `cleanmk` | remove the flat headers, the objects and libraries, and nothing (CMake writes no fragments) | +| `cleanblastest`, `cleanblistest`, `cleantest` | remove the test objects and binaries | +| `distclean` | run the clean targets, then remove the configure outputs, `lib/` and `include/` | + +Known differences from make: + +- `cleanblistest` keeps the ctest `Testing/` directory, where the test output is. +- `cleanlib` also removes the soname links of the shared library. +- After `distclean`, the next build runs the configure step again. Make needs + a manual `./configure`. +- If `BUILD_TESTING` is `OFF` or blastest is off, the test targets stop + with an error that names the missing target. +- With Ninja, `cmake --build DIR --target help` lists only the top-level + targets. `ninja -C DIR -t targets all` lists all names. All names build + with `--target`. +- For an `hpx` build with static libraries off, `make` writes build-tree + rpaths with an empty base path (`-Wl,-rpath,'/lib/haswell'`) into + `Libs.private`. The CMake `blis.pc` does not. diff --git a/docs/PluginHowTo.md b/docs/PluginHowTo.md index 0dd340dccf..857f1cc4e1 100644 --- a/docs/PluginHowTo.md +++ b/docs/PluginHowTo.md @@ -35,6 +35,26 @@ A BLIS plugin is a piece of user-defined code that provides additional linear al Plugins are defined completely externally to BLIS (that is, the BLIS source code is not required). However, an installed copy of BLIS 2.0 or later is required (assumed installed to `$PREFIX`) in order to configure or build a plugin. Building a plugin then results in a shared and/or static library which can be distributed or linked into your code. The template and example files generated by BLIS are all in C99, but C++ is also supported. +**Note (CMake builds):** when BLIS is built with CMake (see [BuildSystemCMake.md](BuildSystemCMake.md)), the CMake build installs the same `configure-plugin` script and `$PREFIX/share/blis/*.mk` fragments as the make build. Plugins are ordinary CMake projects that use `find_package(blis)` and the helper functions exported alongside it (`blis_plugin_frame_flags`, `blis_plugin_kernel_flags `, `blis_plugin_refkern_flags DEFS_HEADER `), which apply the exact per-subconfiguration ISA/optimization flags that the installed BLIS was built with. A minimal `CMakeLists.txt`: + +```cmake +cmake_minimum_required(VERSION 3.20...4.4) +project(blis_plugin_hello C) +find_package(blis REQUIRED) + +add_library(hello_core OBJECT bli_plugin_register.c) +blis_plugin_frame_flags(hello_core) + +add_library(hello_kern_haswell OBJECT kernels/haswell/my_kernel_1_haswell.c) +blis_plugin_kernel_flags(hello_kern_haswell haswell) # -mavx2 -march=haswell ... + +add_library(blis_hello SHARED $ + $) +target_link_libraries(blis_hello PRIVATE blis::blis) +``` + +The template sources (`bli_plugin_.h`, `bli_plugin_register.c`, the `bli_plugin_init_*` skeletons and example kernels) are unchanged and remain available under `build/plugin/` in the BLIS source tree. + ## Example Plugin A new plugin is created by running `$PREFIX/share/blis/configure-plugin `, where `` is the name you wish to give to the plugin which must be a valid C99 identifier. By default, this generates a fully-functioning example plugin containing the following files: diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 0000000000..f794a93de2 --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,29 @@ +# Usable in-tree or standalone: +# cmake -S /examples -B -DCMAKE_PREFIX_PATH= + +if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) + cmake_minimum_required(VERSION 3.20...4.4) + project(blis-examples C) + find_package(blis REQUIRED) + set(_blis_lib blis::blis) + set(_in_tree OFF) +else() + set(_blis_lib ${BLIS_LINK_TARGET}) + set(_in_tree ON) +endif() + +foreach(dir oapi tapi) + file(GLOB _examples CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${dir}/*.c") + foreach(src ${_examples}) + get_filename_component(name "${src}" NAME_WE) + set(tgt "example-${dir}-${name}") + add_executable(${tgt} ${src}) + target_link_libraries(${tgt} PRIVATE ${_blis_lib}) + if(_in_tree) + blis_in_tree_flags(${tgt}) + set_target_properties(${tgt} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/examples/${dir}") + elseif(NOT MSVC AND NOT BLIS_IS_MSVC) + target_link_libraries(${tgt} PRIVATE m) + endif() + endforeach() +endforeach() diff --git a/testsuite/CMakeLists.txt b/testsuite/CMakeLists.txt new file mode 100644 index 0000000000..d80d066167 --- /dev/null +++ b/testsuite/CMakeLists.txt @@ -0,0 +1,27 @@ +# Standalone: cmake -S -B out +# -DCMAKE_PREFIX_PATH=, then `cmake --build` and run the +# binary from the (source) testsuite directory where input.general and +# input.operations live. + +if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) + cmake_minimum_required(VERSION 3.20...4.4) + project(blis-testsuite C) + find_package(blis REQUIRED) + set(_blis_lib blis::blis) + set(BLIS_TESTSUITE_STANDALONE ON) +else() + set(_blis_lib ${BLIS_LINK_TARGET}) +endif() + +file(GLOB _ts_srcs CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/*.c") +add_executable(test_libblis.x ${_ts_srcs}) +target_include_directories(test_libblis.x PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src") +target_link_libraries(test_libblis.x PRIVATE ${_blis_lib}) + +if(BLIS_TESTSUITE_STANDALONE) + if(NOT MSVC AND NOT BLIS_IS_MSVC) + target_link_libraries(test_libblis.x PRIVATE m) + endif() +else() + blis_in_tree_flags(test_libblis.x) +endif()