Skip to content

macOS: find libomp in the environment being built into - #58

Open
Jim Garrison (garrison) wants to merge 1 commit into
mainfrom
darwin-openmp-build-prefix
Open

Jim Garrison (garrison) wants to merge 1 commit into
mainfrom
darwin-openmp-build-prefix

Conversation

@garrison

Copy link
Copy Markdown
Member

On macOS the build locates libomp by probing $CONDA_PREFIX/include/omp.h, falling back to Homebrew. That misses the case where the activated environment is not the one being built into. conda-build and rattler-build activate their own base installation and install into $PREFIX, so a recipe that provides llvm-openmp still fails:

Configuring CPU backend (_core_cpu)
Error: no OpenMP runtime found on this macOS host.
       Looked in $CONDA_PREFIX/include and /opt/homebrew/opt/libomp/include.

on a builder with no Homebrew libomp — while the header sits unexamined in $PREFIX. This is what the conda-forge osx_64 build hits (staged-recipes#35125), where CONDA_PREFIX is /Users/runner/miniforge3 and llvm-openmp 21.1.8 is installed in the host environment.

The change

Consult sys.prefix, then sys.base_prefix, after CONDA_PREFIX and before Homebrew. sys.prefix is the prefix of the interpreter running setup.py, which under both build backends is the environment the extension is built for. It is also the convention _mpi_prefix_from_env_prefix() already uses — which is why MPI detection succeeded in the same build where OpenMP failed.

CONDA_PREFIX stays first, so an ordinary activated environment behaves exactly as before. The new candidates are reachable only where the code previously fell through to Homebrew, so no build that passes today changes behavior.

This serves the intent of the original logic rather than reversing it: build against the libomp that will be loaded at import time, resolved through the interpreter's rpath. Linking the base installation's copy while installing into $PREFIX is one way to end up with two LLVM OpenMP runtimes in one process — the abort described in the README and tracked as #27.

The error message named $CONDA_PREFIX literally, which no longer describes the set of prefixes searched, so it now reports the paths actually tried, deduplicated.

Verification

The changed branch is platform.system() == 'Darwin'-only, so it is unreachable on Linux. I exercised the prefix selection directly across six cases: activated environment with the header present (unchanged); activated prefix without it but the build prefix with it (the failure above, now resolved); neither, so the Homebrew fallback still engages; both, confirming CONDA_PREFIX keeps precedence; CONDA_PREFIX unset; and only sys.base_prefix carrying it.

I do not have a macOS host, so the end-to-end check is the conda-forge osx_64 build. I will report back once it has run against this change.


This pull request was drafted by Claude Opus 5 under my guidance.

The Darwin block probes $CONDA_PREFIX/include/omp.h and falls back to Homebrew.
That misses the case where the activated environment is not the one being built
into: conda-build and rattler-build activate their own base installation and
install into $PREFIX, so a recipe that puts llvm-openmp in the host environment
still fails with

    Error: no OpenMP runtime found on this macOS host.
           Looked in $CONDA_PREFIX/include and /opt/homebrew/opt/libomp/include.

on a builder with no Homebrew libomp -- while the header sits unexamined in
$PREFIX. This is what the conda-forge osx_64 build hits.

Consult sys.prefix, then sys.base_prefix, after CONDA_PREFIX and before
Homebrew. sys.prefix is the prefix of the interpreter running setup.py, which
under both build backends is the environment the extension is built for; it is
also the convention _mpi_prefix_from_env_prefix() already uses, which is why MPI
detection succeeded in the same build where OpenMP failed.

CONDA_PREFIX stays first, so an ordinary activated environment behaves exactly
as before. The new candidates can only be reached where the code previously
fell through to Homebrew, so no build that passes today changes behavior.

This serves the intent of the original change rather than reversing it: build
against the libomp that will be LOADED at import time, resolved through the
interpreter's rpath. Linking the base installation's copy while installing into
$PREFIX is how a process ends up with two LLVM OpenMP runtimes, the abort
described in the README and tracked as #27.

The error message listed $CONDA_PREFIX literally, which no longer describes the
set of prefixes searched, so report the paths actually tried, deduplicated.

Assisted-by: Claude Opus 5
@garrison Jim Garrison (garrison) added the stable backport potential Eligible for backporting label Oct 9, 2026
@garrison
Jim Garrison (garrison) marked this pull request as ready for review October 9, 2026 21:47
Jim Garrison (garrison) added a commit to garrison/staged-recipes that referenced this pull request Oct 9, 2026
The osx_64 build failed with

    Error: no OpenMP runtime found on this macOS host.
           Looked in $CONDA_PREFIX/include and /opt/homebrew/opt/libomp/include.

although llvm-openmp was installed in the host environment as the recipe asks.
setup.py locates macOS libomp by probing $CONDA_PREFIX/include/omp.h, and under
rattler-build CONDA_PREFIX is the base installation (/Users/runner/miniforge3 on
the builder) rather than the build environment, so the header in $PREFIX went
unexamined and the probe fell through to Homebrew, which the builder does not
have.

Export CONDA_PREFIX=$PREFIX for osx so the probe looks in the environment the
extension is being built into. Scoped to osx because no other platform consults
it: the Darwin branch is the only caller.

This is a workaround for an upstream path assumption, fixed properly in
Qiskit/sbd-eigensolver-python#58, which also consults sys.prefix. Remove it once
a release carrying that change is available.

Assisted-by: Claude Opus 5
@garrison

Copy link
Copy Markdown
Member Author

The conda-forge osx_64 build I mentioned has now run green: staged-recipes#35125 built and tested 8 osx-64 artifacts (mpich and openmpi × Python 3.11–3.14), with pip check, the import tests, and a 2-rank mpiexec run all passing.

That run confirms the diagnosis behind this PR, but it did not execute this PR's code, so I want to be exact about what it shows. The recipe builds from the released 1.7.0 sdist, which carries the current setup.py. To get it green I set CONDA_PREFIX=$PREFIX in the recipe's build script as a temporary workaround. The build log then prints

Darwin: libomp and BLAS from conda env $PREFIX

— the existing wording, from the released code, but resolving to $PREFIX instead of the /Users/runner/miniforge3 it picked before. The probe was looking in the wrong environment and nothing else was wrong: once pointed at the build prefix, the extension compiled against Apple clang and passed its tests on the first attempt.

The macOS jobs in this repository do not cover the new code path either. They brew install libomp, so CONDA_PREFIX is unset and the Homebrew branch runs — which this PR leaves unchanged. Those passing jobs show the fallback still works, not that the new candidate does.

Validating this change on macOS

The new path needs a build where the activated environment differs from the one being installed into, which is what conda-build and rattler-build do. On a macOS host:

# 1. This branch, with the submodule the build needs
git clone --recurse-submodules -b darwin-openmp-build-prefix \
  https://github.com/Qiskit/sbd-eigensolver-python

# 2. The conda-forge recipe
curl -O https://raw.githubusercontent.com/garrison/staged-recipes/sbd-eigensolver/recipes/sbd-eigensolver/recipe.yaml
curl -O https://raw.githubusercontent.com/garrison/staged-recipes/sbd-eigensolver/recipes/sbd-eigensolver/conda_build_config.yaml

Then edit recipe.yaml: replace the source: block's url/sha256 with path: ./sbd-eigensolver-python, and delete the if: osx / export CONDA_PREFIX=$PREFIX entry from build.script — that workaround is what this PR makes unnecessary.

rattler-build build --recipe recipe.yaml --variant-config conda_build_config.yaml

Expected: the build succeeds and prints Darwin: libomp and BLAS from env prefix /…. Without this change the same command fails with no OpenMP runtime found on this macOS host, because CONDA_PREFIX names the base installation rather than the build environment.

The authentic test arrives on its own once a release carries this change and the recipe drops the workaround: conda-forge's macOS CI then exercises the sys.prefix path directly.


This comment was drafted by Claude Opus 5 under my guidance.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

stable backport potential Eligible for backporting

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant