Skip to content

Commit b69a885

Browse files
authored
[docs] Add more build tips about CMake and linking to BLAS (#1314)
* [docs] [wizard] Suggest CMake syntax for building in dedicated directory * [docs] Provide more information about linking to BLAS * [CI] Add also `GITHUB_TOKEN` to doc-building job
1 parent 546aa95 commit b69a885

File tree

3 files changed

+47
-4
lines changed

3 files changed

+47
-4
lines changed

.github/workflows/documentation.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ env:
1313
jobs:
1414
build:
1515
runs-on: ubuntu-latest
16+
permissions:
17+
contents: write
18+
statuses: write
1619
steps:
1720
- uses: actions/checkout@v4
1821
- uses: julia-actions/setup-julia@latest
@@ -30,4 +33,5 @@ jobs:
3033
Pkg.instantiate()
3134
- uses: julia-actions/julia-docdeploy@v1
3235
env:
36+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # If authenticating with GitHub Actions token
3337
DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }}

docs/src/build_tips.md

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,13 @@ Here are examples of autoconfigure build scripts:
5353
For CMake, the wizard will suggest a template for running CMake. Typically, this will look like:
5454

5555
```sh
56-
cmake -DCMAKE_INSTALL_PREFIX=${prefix} -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TARGET_TOOLCHAIN} -DCMAKE_BUILD_TYPE=Release
56+
cmake -B build -DCMAKE_INSTALL_PREFIX=${prefix} -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TARGET_TOOLCHAIN} -DCMAKE_BUILD_TYPE=Release
57+
cmake --build build --parallel ${nproc}
58+
cmake --install build
5759
```
5860

61+
CMake makes it hard to cleanup a partial failed build and start over, so we always recommend configuring and building a CMake project in a dedicated new directory, `build` in the example above.
62+
5963
The toolchain file sets up several CMake environment variables for better cross-platform support, such as `CMAKE_SYSROOT`, `CMAKE_C_COMPILER`, etc... Examples of builds that include CMake parts include:
6064

6165
* [JpegTurbo](https://github.com/JuliaPackaging/Yggdrasil/blob/8d5a27e24016c0ff2eae379f15dca17e79fd4be4/J/JpegTurbo/build_tarballs.jl#L19-L21)
@@ -181,6 +185,36 @@ if [[ "${target}" == *-freebsd* ]] || [[ "${target}" == *-apple-* ]]; then
181185
fi
182186
```
183187

188+
## [Linking to BLAS/LAPACK libraries](@id link-blas)
189+
190+
Many numerical libraries link to [BLAS](https://en.wikipedia.org/wiki/Basic_Linear_Algebra_Subprograms)/[LAPACK](https://en.wikipedia.org/wiki/LAPACK) libraries to execute optimised linear algebra routines.
191+
It is important to understand that the elements of the arrays manipulated by these libraries can be indexed by either 32-bit integer numbers ([LP64](https://en.wikipedia.org/wiki/64-bit_computing#64-bit_data_models)), or 64-bit integers (ILP64).
192+
For example, Julia itself employs BLAS libraries for linear algebra, and it expects ILP64 model on 64-bit platforms (e.g. the `x86_64` and `aarch64` architectures) and LP64 on LP64 on 32-bit platforms (e.g. the `i686` and `armv7l` architectures).
193+
Furthermore, Julia comes by default with [`libblastrampoline`](https://github.com/JuliaLinearAlgebra/libblastrampoline), a library which doesn't implement itself any BLAS/LAPACK routine but it forwards all BLAS/LAPACK function calls to another library (by default OpenBLAS) which can be designated at runtime, allowing you to easily switch between different backends if needed.
194+
`libblastrampoline` provides both ILP64 and LP64 interfaces on 64-bit platforms, in the former case BLAS function calls are expected to have the `_64` suffix to the standard BLAS names.
195+
196+
If in your build you need to use a package to a BLAS/LAPACK library you have the following options:
197+
198+
* use ILP64 interface on 64-bit systems and LP64 interface on 32-bit ones, just like Julia itself.
199+
In this case, when targeting 64-bit systems you will need to make sure all BLAS/LAPACK function calls in the package you want to build will follow the expected naming convention of using the `_64` suffix, something which most packages would not do automatically.
200+
The build systems of some packages (e.g. [`OpenBLAS`](https://github.com/JuliaPackaging/Yggdrasil/blob/b7f5e3c48f292078bbed4c9fdad071da7875c0bc/O/OpenBLAS/common.jl#L125) and [`SuiteSparse`](https://github.com/JuliaPackaging/Yggdrasil/blob/master/S/SuiteSparse/SuiteSparse%407/build_tarballs.jl#L30-L39)) provide this option out-of-the-box, but in most cases you will need to rename the symbols manually using the preprocessor, see for example the [`armadillo`](https://github.com/JuliaPackaging/Yggdrasil/blob/b7f5e3c48f292078bbed4c9fdad071da7875c0bc/A/armadillo/build_tarballs.jl#L29-L41) recipe.
201+
If you are ready to use ILP64 interface on 64-bit systems, you can choose different libraries to link to:
202+
- `libblastrampoline`, using the `libblastrampoline_jll` dependency.
203+
This is the recommended solution, as it is also what is used by Julia itself, it does not introduce new dependencies, a default backing BLAS/LAPACK is always provided, and also the package you are building can take advantage of `libblastrampoline`'s mechanism to switch between different BLAS/LAPACK backend for optimal performance.
204+
A couple of caveats to be aware of:
205+
* for compatibility reasons it's recommended to use
206+
```julia
207+
Dependency("libblastrampoline_jll"; compat="5.4.0")
208+
```
209+
as dependency and also pass `julia_compat="1.9"` as keyword argument to the [`build_tarballs`](@ref) function
210+
* to link to `libblastrampoline` you should use `-lblastrampoline` when targeting Unix systems, and `-lblastrampoline-5` (`5` being the major version of the library) when targeting Windows.
211+
- link directly to other libraries which provide the ILP64 interface on 64-bit systems and the LP64 interface on 32-bit systems, like `OpenBLAS_jll` (what is used by default by julia to back `libblastrampoline`), but once you made the effort to respect the ILP64 interface linking to `libbastrampoline` may be more convenient
212+
* always use LP64 interface, also on 64-bit systems.
213+
This may be a simpler option if renamining the BLAS/LAPACK symbols is too cumbersome in your case.
214+
In terms of libraries to link to:
215+
- also in this case you can link to `libblastrampoline`, however you _must_ make sure an LP64 BLAS/LAPACK library is backing `libblastrampoline`, otherwise all BLAS/LAPACK calls from the library will result in hard-to-debug segmentation faults, because in this case Julia does not provided a default backing LP64 BLAS/LAPACK library on 64-bit systems
216+
- alternatively, you can use builds of BLAS/LAPACK libraries which always use LP64 interface also on 64-bit platforms, like the package `OpenBLAS32_jll`.
217+
184218
## Dependencies for the target system vs host system
185219

186220
BinaryBuilder provides a cross-compilation environment, which means that in general there is a distinction between the target platform (where the build binaries will eventually run) and the host platform (where compilation is currently happening). In particular, inside the build environment in general you cannot run binary executables built for the target platform.

src/wizard/hints.jl

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ function print_autoconf_hint(state::WizardState)
99
printstyled(state.outs, " ./configure --prefix=\${prefix} --build=\${MACHTYPE} --host=\${target}", bold=true)
1010
println(state.outs)
1111
println(state.outs)
12-
println(state.outs, " followed by `make` and `make install`. Since the prefix environment")
12+
println(state.outs, " followed by `make -j\${nproc}` and `make install`. Since the prefix environment")
1313
println(state.outs, " variable is set already, this will automatically perform the installation")
1414
println(state.outs, " into the correct directory.")
1515
end
@@ -66,10 +66,15 @@ function provide_hints(state::WizardState, path::AbstractString)
6666
print(state.outs, " This file is likely input to CMake. ")
6767
println(state.outs, "The recommended options for CMake are")
6868
println(state.outs)
69-
printstyled(state.outs, " cmake -DCMAKE_INSTALL_PREFIX=\$prefix -DCMAKE_TOOLCHAIN_FILE=\${CMAKE_TARGET_TOOLCHAIN} -DCMAKE_BUILD_TYPE=Release", bold=true)
69+
printstyled(state.outs,
70+
"""
71+
cmake -B build -DCMAKE_INSTALL_PREFIX=\$prefix -DCMAKE_TOOLCHAIN_FILE=\${CMAKE_TARGET_TOOLCHAIN} -DCMAKE_BUILD_TYPE=Release
72+
cmake --build build --parallel \${nproc}
73+
cmake --install build
74+
""", bold=true)
7075
println(state.outs)
7176
println(state.outs)
72-
println(state.outs, " followed by `make` and `make install`. Since the prefix environment")
77+
println(state.outs, " Since the prefix environment")
7378
println(state.outs, " variable is set already, this will automatically perform the installation")
7479
println(state.outs, " into the correct directory.\n")
7580
elseif file == "meson.build"

0 commit comments

Comments
 (0)