Skip to content
Snippets Groups Projects
Unverified Commit 3e825925 authored by Tim Kaune's avatar Tim Kaune
Browse files

Add initial CMake build script for linking LAPACKE against Accelerate

parent d621e806
No related branches found
No related tags found
No related merge requests found
#[[
MIT License
CMake build script for the Accelerate LAPACKE project
Copyright (c) 2024 Tim Kaune
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]]
cmake_minimum_required(VERSION 3.12)
set(CMAKE_MAXIMUM_SUPPORTED_VERSION 3.29)
include("./cmake/HandlePolicies.cmake" NO_POLICY_SCOPE)
project(AccelerateLAPACKE VERSION 1.0.0 LANGUAGES C)
include(FetchContent)
string(COMPARE EQUAL "${CMAKE_PROJECT_NAME}" "${PROJECT_NAME}" IS_TOP_LEVEL)
if (NOT IS_TOP_LEVEL)
message(FATAL_ERROR "This project is meant to be built as a stand-alone project, only.")
endif ()
if (CMAKE_HOST_SYSTEM_NAME MATCHES "Darwin")
# Mac OS X 13.3 Ventura
if (CMAKE_HOST_SYSTEM_VERSION VERSION_GREATER_EQUAL 22.4)
set(ACCELERATE_NEW_LAPACK_VERSION 3.9.1)
else ()
message(FATAL_ERROR "You need at least Mac OS X 13.3 Ventura for Accelerate with BLAS/LAPACK v3.9.1!")
endif ()
endif ()
set(BLA_VENDOR "Apple")
find_package(LAPACK MODULE)
if (NOT LAPACK_FOUND OR NOT LAPACK_Accelerate_LIBRARY)
message(FATAL_ERROR "Couldn't find Accelerate framework!")
endif ()
add_subdirectory(src)
{
"version": 2,
"cmakeMinimumRequired": {
"major": 3,
"minor": 20,
"patch": 0
},
"configurePresets": [
{
"name": "base",
"hidden": true,
"binaryDir": "${sourceDir}/build",
"cacheVariables": {
"CMAKE_INSTALL_PREFIX": "$env{HOME}/.local"
}
},
{
"name": "ninja",
"hidden": true,
"generator": "Ninja"
},
{
"name": "accelerate-lapacke32",
"inherits": [
"base",
"ninja"
],
"binaryDir": "${sourceDir}/build/32",
"cacheVariables": {
"BUILD_INDEX64": false
},
"displayName": "LAPACKE 32bit configuration linking against Accelerate"
},
{
"name": "accelerate-lapacke64",
"inherits": [
"base",
"ninja"
],
"binaryDir": "${sourceDir}/build/64",
"cacheVariables": {
"BUILD_INDEX64": true
},
"displayName": "LAPACKE 64bit configuration linking against Accelerate ILP64 interface"
}
],
"buildPresets": [
{
"name": "accelerate-lapacke32",
"displayName": "LAPACKE 32bit build linking against Accelerate",
"configurePreset": "accelerate-lapacke32"
},
{
"name": "accelerate-lapacke64",
"displayName": "LAPACKE 64bit build linking against Accelerate ILP64 interface",
"configurePreset": "accelerate-lapacke64"
}
]
}
README.md 0 → 100644
<!---
MIT License
CMake build script for the Accelerate LAPACKE project
Copyright (c) 2024 Tim Kaune
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
--->
# Accelerate LAPACKE #
Since Mac OS X 13.3 Ventura, Apple's Accelerate framework comes with a new
[BLAS/LAPACK
interface](https://developer.apple.com/documentation/accelerate/blas) compatible
with [Reference LAPACK
v3.9.1](https://github.com/Reference-LAPACK/lapack/tree/v3.9.1). It also
provides an ILP64 interface. On Apple Silicon M-processors, it utilises the
[proprietary AMX co-processor](https://github.com/corsix/amx), which makes it
especially interesting. Unfortunately, it comes without the LAPACKE C-interface
library.
These new interfaces are hidden behind the preprocessor defines
`ACCELERATE_NEW_LAPACK` and `ACCELERATE_LAPACK_ILP64` and they only work, if you
include the Accelerate C/C++ headers.
## The Problem ##
But what if you have to or just want to link against the Accelerate framework
without including the C/C++ headers, e.&nbsp;g. when compiling Fortran code or a
third-party project, that uses the standard BLAS/LAPACK API? Well, you're out of
luck. The binary symbols for the new LAPACK version exported by the Accelerate
framework do not adhere to the BLAS/LAPACK API. Thus, they cannot be resolved by
the linker, when linking any program or library that uses the standard
BLAS/LAPACK API.
Take, for example, the `dgeqrt` LAPACK routine, that is used by the [Reference
LAPACK CMake
script](https://github.com/Reference-LAPACK/lapack/blob/v3.9.1/CMakeLists.txt#L315-L316)
to determine, if the user provided LAPACK version is recent enough. When the
Fortran test executable is compiled, the `gfortran` compiler creates a function
call with the binary symbol `_dgeqrt_`, which results in the following error
when linking to Accelerate (`ld` is the Apple system linker, here):
```plaintext
ld: Undefined symbols:
_dgeqrt_, referenced from:
_MAIN__ in testFortranCompiler.f.o
```
The reason for this is, that the binary symbol provided by the Accelerate
framework is called `_dgeqrt$NEWLAPACK`, literally. This is a symbol, that no
Fortran compiler will probably ever emit voluntarily. So, what to do?
## The Solution ##
According to its `man` page, the Apple system linker `ld` provides the options
`-alias` and `-alias_list`, which let you create alias names for existing binary
symbols. Calling the linker with `-alias '_dgeqrt$NEWLAPACK' _dgeqrt_` makes the
linking of the above Fortran test executable finish successfully.
Because BLAS and LAPACK contain quite a number of subroutines and functions,
this CMake scipt uses the `-alias_list` option, which loads a plaintext file
listing all the aliases.
To generate the full alias list for the Accelerate NEWLAPACK interface, it
parses the symbols listed in the BLAS and LAPACK text-based `.dylib` stubs. For
every symbol that ends in `$NEWLAPACK` (or `$NEWLAPACK$ILP64` for the ILP64
interface), an alias is added to the alias file.
The linker option is injected into the Reference LAPACK configure process using
the `CMAKE_EXE_LINKER_FLAGS` variable for the test compiles and the
`CMAKE_SHARED_LINKER_FLAGS` variable for the shared LAPACKE library.
This enables the compilation of the LAPACKE C-interface library for the
Accelerate framework (e.&nbsp;g. to be used in the `Eigen3` library). Analyzing
the resulting `.dylib` with `otool`, you can see:
```shell
$ otool -L ./build/32/_deps/reference-lapack-build/lib/liblapacke.dylib
./build/32/_deps/reference-lapack-build/lib/liblapacke.dylib:
@rpath/liblapacke.3.dylib (compatibility version 3.0.0, current version 3.9.1)
/System/Library/Frameworks/Accelerate.framework/Versions/A/Accelerate (compatibility version 1.0.0, current version 4.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1336.61.1)
```
Only the Accelerate framework and the System library are linked into the
`.dylib`. No `libgfortran` or other libraries are needed.
### The alias files (to use in other projects) ###
After the CMake configuration, the alias files can be found in `./build/<32|64>/src`:
```plaintext
./build/<32|64>/src
├── new-lapack-ilp64.alias
└── new-lapack.alias
```
These files can be used to link other projects against Accelerate, too, of course!
## How to compile ##
It is recommended to use the Apple System C Compiler `/usr/bin/cc`. You can also
use a more recent Clang compiler provided by Homebrew or MacPorts. If you have
other compilers installed on your system, make sure CMake finds the correct one.
Otherwise, help CMake by setting the environment variable `$ export
CC=/usr/bin/cc` in your terminal window.
It is also recommended to use at least CMake v3.20 with presets, but the CMake
script also works down to CMake v3.12, if you set the required variables on the
command line.
### Prerequisites ###
Obviously, your operating system must be Mac OS X >=v13.3 Ventura with XCode
installed. Additionally, you'll need the following software (easily obtainable
via Homebrew or MacPorts):
- CMake
- Fortran compiler (e.&nbsp;g. `gfortran` contained in the `gcc` package) to
make it through the Reference LAPACK configure script
### Configuration with CMake ###
Use the `accelerate-lapacke32` preset (or the `accelerate-lapacke64` preset for
the ILP64 interface) with CMake:
```shell
$ cmake --preset accelerate-lapacke32
```
This will configure LAPACKE to be installed in your `~/.local` directory by
default. If you prefer a different install location (e.&nbsp;g. `/opt/custom`),
you can change it like this:
```shell
$ cmake --preset accelerate-lapacke32 -D "CMAKE_INSTALL_PREFIX=/opt/custom"
```
I wouldn't recommend installing to `/usr/local` (used by Homebrew on Intel Macs)
or `/opt/local` (used by MacPorts).
### Build and install ###
When the configuration finished successfully (fingers crossed), you can build with the
same preset name (`accelerate-lapacke32` or `accelerate-lapacke64`):
```shell
$ cmake --build --preset accelerate-lapacke32 --verbose
```
If everything worked as intended, linking the library should be successful. Now,
you can install to the previously configured install prefix by building the
install target:
```shell
$ cmake --build --preset accelerate-lapacke32 --verbose --target install
```
### Using LAPACKE in another project ###
You can use your self-compiled LAPACKE library in other projects by importing
the CMake package in the other project's `CMakeLists.txt` file:
```cmake
find_package(LAPACKE CONFIG)
```
and providing the above install location via the `CMAKE_PREFIX_PATH` variable
from the command line:
```shell
$ cmake -S . -B ./build -D "CMAKE_PREFIX_PATH=~/.local"
```
This makes the imported `lapacke` shared library target available in the other
project's `CMakeLists.txt`.
#[[
MIT License
CMake build script for the Accelerate LAPACKE project
Copyright (c) 2024 Tim Kaune
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]]
if (NOT DEFINED CMAKE_MAXIMUM_SUPPORTED_VERSION)
set(CMAKE_MAXIMUM_SUPPORTED_VERSION ${CMAKE_MINIMUM_REQUIRED_VERSION})
endif ()
# If CMAKE_VERSION <= CMAKE_MAXIMUM_SUPPORTED_VERSION is used, set policies up
# to CMAKE_VERSION to NEW
if (${CMAKE_VERSION} VERSION_LESS_EQUAL ${CMAKE_MAXIMUM_SUPPORTED_VERSION})
cmake_policy(VERSION ${CMAKE_VERSION})
# If CMAKE_VERSION > CMAKE_MAXIMUM_SUPPORTED_VERSION is used, set policies up to
# CMAKE_MAXIMUM_SUPPORTED_VERSION to NEW
else ()
cmake_policy(VERSION ${CMAKE_MAXIMUM_SUPPORTED_VERSION})
endif()
#[[
MIT License
CMake build script for the Accelerate LAPACKE project
Copyright (c) 2024 Tim Kaune
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]]
# Macro to parse the symbol names from the .tbd files
# Provides the `${_LIB_NAME}_SYMBOLS` list variable.
macro (parse_tbd_symbols _LIB_NAME _TBD_PATH)
file(READ "${_TBD_PATH}" _TBD_CONTENT)
# First, match all symbol arrays in the .tbd file
string(REGEX MATCHALL "symbols:[\r\n\t ]+\\[[\r\n\t ][^]]*[\r\n\t ]\\]" _TBD_SYMBOL_ARRAY_MATCHES "${_TBD_CONTENT}")
set("${_LIB_NAME}_SYMBOLS" "")
foreach (_SYMBOL_ARRAY_MATCH IN LISTS _TBD_SYMBOL_ARRAY_MATCHES)
# Extract the array contents from the symbol array match
string(REGEX MATCH "\\[[\r\n\t ]([^]]*)[\r\n\t ]\\]" _DUMMY_MATCH "${_SYMBOL_ARRAY_MATCH}")
# Replace commas and white space with semi colons
string(REGEX REPLACE ",[\r\n\t ]*" ";" _SYMBOLS_IN_ARRAY "${CMAKE_MATCH_1}")
# Remove single quotes around the symbols containing $ signs
string(REPLACE "'" "" _SYMBOLS_IN_ARRAY "${_SYMBOLS_IN_ARRAY}")
list(APPEND "${_LIB_NAME}_SYMBOLS" ${_SYMBOLS_IN_ARRAY})
endforeach ()
endmacro ()
# Macro to filter the full list for $NEWLAPACK symbols
# Provides the `${_LIB_NAME}_NEWLAPACK_SYMBOLS` list variable.
macro (filter_new_lapack_symbols _LIB_NAME)
set("${_LIB_NAME}_NEWLAPACK_SYMBOLS" ${${_LIB_NAME}_SYMBOLS})
list(FILTER "${_LIB_NAME}_NEWLAPACK_SYMBOLS" INCLUDE REGEX "\\$NEWLAPACK$")
endmacro ()
# Macro to filter the full list for $NEWLAPACK$ILP64 symbols
# Provides the `${_LIB_NAME}_NEWLAPACK_ILP64_SYMBOLS` list variable.
macro (filter_new_lapack_ilp64_symbols _LIB_NAME)
set("${_LIB_NAME}_NEWLAPACK_ILP64_SYMBOLS" ${${_LIB_NAME}_SYMBOLS})
list(FILTER "${_LIB_NAME}_NEWLAPACK_ILP64_SYMBOLS" INCLUDE REGEX "\\$NEWLAPACK\\$ILP64$")
endmacro ()
# Macro to build the aliases from the filtered symbol lists
# For a symbols list variable ending in `_SYMBOLS`, provides the corresponding
# `_ALIASES` string variable.
macro (build_aliases _SYMBOL_LIST_VAR_NAME)
string(REPLACE "_SYMBOLS" "_ALIASES" _ALIASES_VAR_NAME "${_SYMBOL_LIST_VAR_NAME}")
# Transform the symbols `_<symbol>$NEWLAPACK($ILP64)` to aliases
# `_<symbol>$NEWLAPACK($ILP64) _<symbol>_`
list(TRANSFORM ${_SYMBOL_LIST_VAR_NAME} REPLACE "^([^\\$]+)\\$.+$" "\\0\t\\1_" OUTPUT_VARIABLE ${_ALIASES_VAR_NAME})
# Join the list with line breaks
list(JOIN ${_ALIASES_VAR_NAME} "\n" ${_ALIASES_VAR_NAME})
endmacro ()
#[[
MIT License
CMake build script for the Accelerate LAPACKE project
Copyright (c) 2024 Tim Kaune
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]]
include("../cmake/ParseTBDMacros.cmake")
# Paths to text-based .dylib stubs
set(BLAS_TBD_PATH "${LAPACK_Accelerate_LIBRARY}/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.tbd")
set(LAPACK_TBD_PATH "${LAPACK_Accelerate_LIBRARY}/Versions/A/Frameworks/vecLib.framework/Versions/A/libLAPACK.tbd")
parse_tbd_symbols(BLAS "${BLAS_TBD_PATH}")
filter_new_lapack_symbols(BLAS)
filter_new_lapack_ilp64_symbols(BLAS)
parse_tbd_symbols(LAPACK "${LAPACK_TBD_PATH}")
filter_new_lapack_symbols(LAPACK)
filter_new_lapack_ilp64_symbols(LAPACK)
build_aliases(BLAS_NEWLAPACK_SYMBOLS)
build_aliases(BLAS_NEWLAPACK_ILP64_SYMBOLS)
build_aliases(LAPACK_NEWLAPACK_SYMBOLS)
build_aliases(LAPACK_NEWLAPACK_ILP64_SYMBOLS)
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/new-lapack.alias"
"\
# BLAS
${BLAS_NEWLAPACK_ALIASES}
# LAPACK
${LAPACK_NEWLAPACK_ALIASES}
"
)
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/new-lapack-ilp64.alias"
"\
# BLAS
${BLAS_NEWLAPACK_ILP64_ALIASES}
# LAPACK
${LAPACK_NEWLAPACK_ILP64_ALIASES}
"
)
# Set configuration options for Reference LAPACK
set(BUILD_SHARED_LIBS ON CACHE BOOL "")
set(CBLAS OFF CACHE BOOL "")
set(LAPACKE ON CACHE BOOL "")
# Add the $NEWLAPACK symbols to the linker flags used in try_compile()
set(CMAKE_EXE_LINKER_FLAGS "-Wl,-alias_list,${CMAKE_CURRENT_BINARY_DIR}/new-lapack.alias")
if (BUILD_INDEX64)
# Add the $NEWLAPACK$ILP64 symbols to the linker flags used for shared libraries
set(CMAKE_SHARED_LINKER_FLAGS "-Wl,-alias_list,${CMAKE_CURRENT_BINARY_DIR}/new-lapack-ilp64.alias")
else ()
# Add the $NEWLAPACK symbols to the linker flags used for shared libraries
set(CMAKE_SHARED_LINKER_FLAGS "-Wl,-alias_list,${CMAKE_CURRENT_BINARY_DIR}/new-lapack.alias")
endif ()
FetchContent_Declare(
reference-lapack
GIT_REPOSITORY "https://github.com/Reference-LAPACK/lapack.git"
GIT_TAG "v${ACCELERATE_NEW_LAPACK_VERSION}"
)
FetchContent_MakeAvailable(reference-lapack)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment