Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions docs/How-to/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,16 @@ PETSC_ARCH=arch-xxx-yyy
export PETSC_ARCH=arch-xxx-yyy
```

## Troubleshooting

### PETSc configure: "Downloaded sundials2 could not be used"

PETSc configure on macOS stops with:

```
Downloaded sundials2 could not be used. Please check install in .../arch-darwin-c-opt
```

and `configure.log` reports `_CVDense` and `_CVSpgmr` as undefined symbols.

A SUNDIALS 7 from Homebrew is in the linker search path ahead of the SUNDIALS 2.5 that PETSc downloads, and SUNDIALS 7 has no `CVDense`. `get_petsc.sh` passes no Homebrew library path, so on Apple Silicon, where Homebrew lives in `/opt/homebrew`, PETSc builds with a Homebrew SUNDIALS installed. On an Intel Mac, Homebrew installs into `/usr/local/lib`, which the linker searches by default, so a Homebrew SUNDIALS can still hide the downloaded one there. `brew list --versions sundials` shows whether Homebrew's SUNDIALS is installed.
Comment thread
timlichtenberg marked this conversation as resolved.
Resolved
81 changes: 81 additions & 0 deletions tests/test_get_petsc_ldflags.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
"""Tests for the macOS configure arguments of ``tools/get_petsc.sh``.

The platform and configure steps are read from the shipped script and run with stub tools
and a stub ``configure``: no build and no network.
"""

from __future__ import annotations

import subprocess
from pathlib import Path

import pytest

pytestmark = [pytest.mark.unit, pytest.mark.timeout(30)]

SCRIPT = Path(__file__).resolve().parents[1] / 'tools' / 'get_petsc.sh'


# A stub configure: records its arguments, its environment and four named variables.
CONFIGURE_STUB = """#!/bin/bash
printf "%s\\n" "$@" > args
export -p > env
for v in LDFLAGS LIBRARY_PATH LIBS CPATH; do printf "%s=%s\\n" "$v" "${!v-unset}"; done > named
"""


def _run_macos(tmp_path, tools, path_extra=':/usr/bin:/bin'):
"""Run the shipped platform and configure steps as macOS with stub ``tools``."""
text = SCRIPT.read_text()
start = text.index('current_step="Determining platform-specific flags"')
block = text[start : text.index('# 9. Build PETSc')]
for name in tools:
(tmp_path / name).write_text('#!/bin/bash\necho /sdk\n')
(tmp_path / 'configure').write_text(CONFIGURE_STUB)
for stub in tmp_path.iterdir():
stub.chmod(0o755)
snippet = 'set -euo pipefail\nannounce() { :; }\nOSTYPE=darwin24\nworkpath=.\n' + block
return subprocess.run(
['/bin/bash', '-c', snippet],
capture_output=True,
text=True,
cwd=tmp_path,
env={'PATH': f'{tmp_path}{path_extra}'},
)


def _configure_args(tmp_path) -> list[str]:
"""Return the configure arguments, after checking no build variable names a library path."""
named = (tmp_path / 'named').read_text().split()
assert named == [f'{v}=unset' for v in ('LDFLAGS', 'LIBRARY_PATH', 'LIBS', 'CPATH')]
assert 'homebrew/lib' not in (tmp_path / 'env').read_text()
args = (tmp_path / 'args').read_text().splitlines()
assert [a for a in args if '-L' in a] == []
return args


def test_configure_on_macos_gets_no_library_path(tmp_path):
"""On macOS with a system MPI, PETSc configure gets LDFLAGS=-Wl,-w and no -L in any
argument or build variable, so no library directory comes ahead of the SUNDIALS 2.5 it
downloads."""
result = _run_macos(tmp_path, ('xcrun', 'mpicc', 'mpirun'))
assert result.returncode == 0, result.stderr
args = _configure_args(tmp_path)
assert {'LDFLAGS=-Wl,-w', '--download-sundials2'} <= set(args)
assert '--download-mpich' not in args


def test_configure_on_macos_without_mpicc_downloads_mpich(tmp_path):
"""Without mpicc on PATH, PETSc downloads MPICH and still gets only LDFLAGS=-Wl,-w."""
result = _run_macos(tmp_path, ('xcrun',), path_extra='')
assert result.returncode == 0, result.stderr
args = _configure_args(tmp_path)
assert {'LDFLAGS=-Wl,-w', '--download-sundials2', '--download-mpich'} <= set(args)


def test_macos_without_xcrun_stops_before_configure(tmp_path):
"""Without xcrun the script stops with its install hint and never runs configure."""
result = _run_macos(tmp_path, ('mpicc', 'mpirun'), path_extra='')
assert result.returncode == 1
assert 'xcrun not found' in result.stderr and 'xcode-select --install' in result.stderr
assert not (tmp_path / 'args').exists()
10 changes: 3 additions & 7 deletions tools/get_petsc.sh
Original file line number Diff line number Diff line change
Expand Up @@ -341,13 +341,9 @@ if [[ "$OSTYPE" == "darwin"* ]]; then
# macOS provides Accelerate framework with BLAS/LAPACK
blas_flag=""

if [[ "$(uname -m)" == "arm64" ]]; then
default_brew_prefix="/opt/homebrew"
else
default_brew_prefix="/usr/local"
fi
brew_prefix="$(brew --prefix 2>/dev/null || echo "$default_brew_prefix")"
ldflags="-L${brew_prefix}/lib -Wl,-w"
# -Wl,-w: macOS 26+ turns deprecated-flag warnings into configure errors. No Homebrew -L:
# mpicc brings its own, and a Homebrew SUNDIALS there would hide PETSc's SUNDIALS 2.5.
ldflags="-Wl,-w"
fi

if [[ -z "$mpi_flag" ]] && ! command -v mpirun >/dev/null 2>&1; then
Expand Down
Loading