diff --git a/.github/workflows/macos-ci.yml b/.github/workflows/macos-ci.yml new file mode 100644 index 000000000..96d60369a --- /dev/null +++ b/.github/workflows/macos-ci.yml @@ -0,0 +1,924 @@ +name: macOS CI Build + +on: + push: + branches: + - master + pull_request: + branches: + - master + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: false + +permissions: + contents: read + packages: write + +env: + GITHUB_REGISTRY_USER: ${{ secrets.GHCR_REGISTRY_USER }} + GITHUB_REGISTRY_TOKEN: ${{ secrets.GHCR_REGISTRY_TOKEN }} + CVMFS_REPO: unpacked.cern.ch + CVMFS_PATH_PREFIX: ghcr.io/eic + PUBLISH_IMAGE_REPO: ghcr.io/${{ github.repository_owner }} + SPACK_INSTALL_FLAGS: "--no-check-signature --show-log-on-error" + +jobs: + build: + name: Build (${{ matrix.ENV }}) + runs-on: macos-latest + strategy: + fail-fast: false + matrix: + ENV: [ci, xl, tf] + + steps: + - name: Checkout repository + uses: actions/checkout@v7 + + - name: Extract Spack configuration + id: spack-config + run: | + # Source spack.sh to get SPACK_VERSION and SPACK_CHERRYPICKS + source spack.sh + echo "spack-version=${SPACK_VERSION}" >> $GITHUB_OUTPUT + echo "spack-cherrypicks<> $GITHUB_OUTPUT + echo "${SPACK_CHERRYPICKS}" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + # Source spack-packages.sh to get SPACKPACKAGES_VERSION and SPACKPACKAGES_CHERRYPICKS + source spack-packages.sh + echo "spackpackages-version=${SPACKPACKAGES_VERSION}" >> $GITHUB_OUTPUT + echo "spackpackages-cherrypicks<> $GITHUB_OUTPUT + echo "${SPACKPACKAGES_CHERRYPICKS}" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + # Source key4hep-spack.sh to get KEY4HEPSPACK_VERSION + source key4hep-spack.sh + echo "key4hepspack-version=${KEY4HEPSPACK_VERSION}" >> $GITHUB_OUTPUT + + # Source eic-spack.sh to get EICSPACK_VERSION + source eic-spack.sh + echo "eicspack-version=${EICSPACK_VERSION}" >> $GITHUB_OUTPUT + + # Extract buildcache URL from mirrors.yaml.in (simplified - get the version) + echo "buildcache-version=${SPACKPACKAGES_VERSION}" >> $GITHUB_OUTPUT + + - name: Cache Spack checkouts + id: cache-spack + uses: actions/cache@v6 + with: + path: spack + key: spack-repos-${{ runner.os }}-${{ hashFiles('spack.sh', 'spack-packages.sh', 'key4hep-spack.sh', 'eic-spack.sh') }} + + - name: Add Spack to PATH + run: echo "${{ github.workspace }}/spack/bin" >> $GITHUB_PATH + + - name: Checkout Spack repository + if: steps.cache-spack.outputs.cache-hit != 'true' + uses: actions/checkout@v7 + with: + repository: spack/spack + path: spack + ref: ${{ steps.spack-config.outputs.spack-version }} + fetch-depth: 0 + + - name: Configure spack + if: steps.cache-spack.outputs.cache-hit != 'true' + run: | + cd ${{ github.workspace }}/spack + git config user.name "GitHub Actions" + git config user.email "actions@github.com" + + # Fetch branches from which we cherry-pick + git fetch origin refs/heads/develop:refs/heads/develop + git fetch origin pull/51584/head:pr-51584 + git fetch origin pull/52207/head:pr-52207 + + # Apply cherry-picks from spack.sh (match only valid git commit hashes) + while read -r commit; do + # Commits on branches/forks not fetched above are served by SHA + if ! git cat-file -e "${commit}^{commit}" 2>/dev/null; then + echo "Fetching ${commit} by SHA" + git fetch origin "${commit}" + fi + if git merge-base --is-ancestor "${commit}" HEAD; then + echo "Skipping already applied ${commit}" + continue + fi + echo "Cherry-picking ${commit}" + git cherry-pick "${commit}" + done < <(echo "${{ steps.spack-config.outputs.spack-cherrypicks }}" | grep -E '^[a-f0-9]{40}$' || true) + + # Fallback to source build when binary relocation fails (e.g. unpadded binaries in buildcache) + python3 -c ' + import pathlib + path = pathlib.Path("lib/spack/spack/new_installer.py") + content = path.read_text() + old = ( + " send_state(\"relocating\", state_stream)\n" + " spack.binary_distribution.extract_tarball(spec, tarball_stage, force=False)" + ) + new = ( + " send_state(\"relocating\", state_stream)\n" + " try:\n" + " spack.binary_distribution.extract_tarball(spec, tarball_stage, force=False)\n" + " except Exception as e:\n" + " shutil.rmtree(spec.prefix, ignore_errors=True)\n" + " spack.llnl.util.tty.warn(f\"Failed to install {spec.name} from binary cache ({e}); falling back to source build\")\n" + " return False" + ) + if old in content: + path.write_text(content.replace(old, new, 1)) + print("Patched new_installer.py to fall back to source build on binary relocation failure") + ' + + - name: Checkout spack-packages repository + if: steps.cache-spack.outputs.cache-hit != 'true' + uses: actions/checkout@v7 + with: + repository: spack/spack-packages + path: spack/var/spack/repos/spack-packages + fetch-depth: 0 + ref: ${{ steps.spack-config.outputs.spackpackages-version }} + + - name: Configure spack-packages + if: steps.cache-spack.outputs.cache-hit != 'true' + run: | + cd ${{ github.workspace }}/spack/var/spack/repos/spack-packages + git config user.name "GitHub Actions" + git config user.email "actions@github.com" + + # Fetch branches from which we cherry-pick + git fetch origin refs/heads/develop:refs/heads/develop + git fetch origin pull/3698/head:pr-3698 + git fetch origin pull/4871/head:pr-4871 + git fetch origin pull/5856/head:pr-5856 + + # Apply cherry-picks from spack-packages.sh (match only valid git commit hashes) + while read -r commit; do + # Commits on forks/unmerged PRs are not in the default clone, but + # GitHub serves any commit in the fork network by SHA from the parent + if ! git cat-file -e "${commit}^{commit}" 2>/dev/null; then + echo "Fetching ${commit} by SHA" + git fetch origin "${commit}" + fi + if git merge-base --is-ancestor "${commit}" HEAD; then + echo "Skipping already applied ${commit}" + continue + fi + echo "Cherry-picking ${commit}" + git cherry-pick "${commit}" + done < <(echo "${{ steps.spack-config.outputs.spackpackages-cherrypicks }}" | grep -E '^[a-f0-9]{40}$' || true) + + cd ${{ github.workspace }}/spack + bin/spack repo add --scope site var/spack/repos/spack-packages/repos/spack_repo/builtin + + - name: Checkout key4hep-spack repository + if: steps.cache-spack.outputs.cache-hit != 'true' + uses: actions/checkout@v7 + with: + repository: key4hep/key4hep-spack + path: spack/var/spack/repos/key4hep-spack + ref: ${{ steps.spack-config.outputs.key4hepspack-version }} + + - name: Configure key4hep-spack + if: steps.cache-spack.outputs.cache-hit != 'true' + run: | + cd ${{ github.workspace }}/spack + bin/spack repo add --scope site var/spack/repos/key4hep-spack + + - name: Checkout eic-spack repository + if: steps.cache-spack.outputs.cache-hit != 'true' + uses: actions/checkout@v7 + with: + repository: eic/eic-spack + path: spack/var/spack/repos/eic-spack + ref: ${{ steps.spack-config.outputs.eicspack-version }} + + - name: Configure eic-spack + if: steps.cache-spack.outputs.cache-hit != 'true' + run: | + cd ${{ github.workspace }}/spack + bin/spack repo add --scope site var/spack/repos/eic-spack/spack_repo/eic + + - name: Cache Spack bootstrap store + uses: actions/cache@v6 + with: + path: ~/.spack/bootstrap/store + key: spack-bootstrap-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('spack.sh') }} + restore-keys: | + spack-bootstrap-${{ runner.os }}-${{ runner.arch }}- + + - name: Bootstrap Spack + run: | + spack bootstrap now + spack bootstrap status + + - name: Cache Spack concretizer + uses: actions/cache@v6 + with: + path: ~/.spack/cache/concretization + key: spack-concretizer-${{ runner.os }}-${{ matrix.ENV }}-${{ hashFiles('spack.sh', 'spack-packages.sh', 'key4hep-spack.sh', 'eic-spack.sh', 'spack-environment/**') }} + restore-keys: | + spack-concretizer-${{ runner.os }}-${{ matrix.ENV }}- + spack-concretizer-${{ runner.os }}- + + - name: Setup build directory + run: | + sudo mkdir -p /opt/software /opt/local /opt/detector + sudo chown -R "$(id -u):$(id -g)" /opt + + - name: Configure Spack build tree + run: | + set -x + spack compiler find --scope site + spack external find --scope site llvm + spack config --scope site add "config:install_tree:root:'/opt/software'" + spack config --scope site add "config:install_tree:padded_length:128" + + - name: Configure buildcache + env: + PUSH_BUILDCACHE: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + run: | + mirror=oci://ghcr.io/eic/spack-${{ steps.spack-config.outputs.buildcache-version }} + if spack mirror list | grep -E '^eic[[:space:]]' | grep -F -q "${mirror}"; then + echo "Mirror eic (${mirror}) already configured, skipping" + else + if spack mirror list | grep -q -E '^eic[[:space:]]'; then + spack mirror rm --scope site eic + fi + if [ "${PUSH_BUILDCACHE}" = "true" ] && [ -n "${GITHUB_REGISTRY_TOKEN}" ]; then + spack mirror add --scope site eic \ + --autopush --unsigned \ + --oci-username-variable GITHUB_REGISTRY_USER \ + --oci-password-variable GITHUB_REGISTRY_TOKEN \ + "${mirror}" + else + echo "Buildcache is read-only for this run" + spack mirror add --scope site eic --unsigned "${mirror}" + fi + fi + spack mirror list + + - name: Concretize base environment + run: | + spack -e spack-environment/${{matrix.ENV}} -c view:false concretize --force + + - name: Build base environment + run: | + mirror=oci://ghcr.io/eic/spack-${{ steps.spack-config.outputs.buildcache-version }} + status=0 + rm -f rebuild.txt + spack python scripts/check-buildcache.py "spack-environment/${{ matrix.ENV }}" "$mirror" rebuild.txt || status=$? + if [ -s rebuild.txt ]; then + missing=$(cat rebuild.txt) + echo "Building missing spec(s) from base environment: $missing" + spack -e spack-environment/${{matrix.ENV}} -c view:false install ${{ env.SPACK_INSTALL_FLAGS }} $missing + elif [ "$status" -eq 0 ]; then + echo "All packages in base environment already exist in buildcache. Skipping build." + else + echo "Warning: check-buildcache failed with status $status; falling back to full environment install" + spack -e spack-environment/${{matrix.ENV}} -c view:false install ${{ env.SPACK_INSTALL_FLAGS }} + fi + + - name: Concretize epic environment + run: | + spack -e spack-environment/${{matrix.ENV}}/epic -c view:false concretize --force + + - name: Build epic environment + run: | + mirror=oci://ghcr.io/eic/spack-${{ steps.spack-config.outputs.buildcache-version }} + status=0 + rm -f rebuild.txt + spack python scripts/check-buildcache.py "spack-environment/${{ matrix.ENV }}/epic" "$mirror" rebuild.txt || status=$? + if [ -s rebuild.txt ]; then + missing=$(cat rebuild.txt) + echo "Building missing spec(s) from epic environment: $missing" + spack -e spack-environment/${{matrix.ENV}}/epic -c view:false install ${{ env.SPACK_INSTALL_FLAGS }} $missing + elif [ "$status" -eq 0 ]; then + echo "All packages in epic environment already exist in buildcache. Skipping build." + else + echo "Warning: check-buildcache failed with status $status; falling back to full environment install" + spack -e spack-environment/${{matrix.ENV}}/epic -c view:false install ${{ env.SPACK_INSTALL_FLAGS }} + fi + + - name: Upload Spack lockfiles + uses: actions/upload-artifact@v7 + with: + name: spack-locks-${{ matrix.ENV }} + path: | + spack-environment/${{ matrix.ENV }}/spack.lock + spack-environment/${{ matrix.ENV }}/epic/spack.lock + if-no-files-found: error + + - name: Diagnose numpy import + if: failure() + continue-on-error: true + run: | + for env in spack-environment/${{matrix.ENV}} spack-environment/${{matrix.ENV}}/epic; do + echo "::group::${env}" + spack -e "${env}" -c view:false find -lpv py-numpy openblas || true + echo "--- loaded environment" + ( + eval "$(spack -e "${env}" -c view:false load --sh py-numpy)" + cd /tmp + python3 -c 'import numpy; print(numpy.__version__, numpy.__file__)' 2>&1 | tail -30 + so=$(find "$(spack -e "${env}" -c view:false location -i py-numpy)" -name '_multiarray_umath*.so' | head -1) + otool -L "${so}" + ) || true + echo "--- build environment of a failing dependent" + for pkg in py-scipy py-numexpr; do + spack -e "${env}" -c view:false build-env "${pkg}" -- \ + python3 -c 'import sys, numpy; print(numpy.__file__)' 2>&1 | tail -40 || true + done + echo "::endgroup::" + done + + install: + name: Install & Publish (${{ matrix.ENV }}) + needs: [build] + runs-on: macos-latest + strategy: + fail-fast: false + matrix: + ENV: [ci, xl, tf] + + steps: + - name: Checkout repository + uses: actions/checkout@v7 + + - name: Extract Spack configuration + id: spack-config + run: | + # Source spack.sh to get SPACK_VERSION and SPACK_CHERRYPICKS + source spack.sh + echo "spack-version=${SPACK_VERSION}" >> $GITHUB_OUTPUT + echo "spack-cherrypicks<> $GITHUB_OUTPUT + echo "${SPACK_CHERRYPICKS}" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + # Source spack-packages.sh to get SPACKPACKAGES_VERSION and SPACKPACKAGES_CHERRYPICKS + source spack-packages.sh + echo "spackpackages-version=${SPACKPACKAGES_VERSION}" >> $GITHUB_OUTPUT + echo "spackpackages-cherrypicks<> $GITHUB_OUTPUT + echo "${SPACKPACKAGES_CHERRYPICKS}" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + # Source key4hep-spack.sh to get KEY4HEPSPACK_VERSION + source key4hep-spack.sh + echo "key4hepspack-version=${KEY4HEPSPACK_VERSION}" >> $GITHUB_OUTPUT + + # Source eic-spack.sh to get EICSPACK_VERSION + source eic-spack.sh + echo "eicspack-version=${EICSPACK_VERSION}" >> $GITHUB_OUTPUT + + # Extract buildcache URL from mirrors.yaml.in (simplified - get the version) + echo "buildcache-version=${SPACKPACKAGES_VERSION}" >> $GITHUB_OUTPUT + + - name: Cache Spack checkouts + id: cache-spack + uses: actions/cache@v6 + with: + path: spack + key: spack-repos-${{ runner.os }}-${{ hashFiles('spack.sh', 'spack-packages.sh', 'key4hep-spack.sh', 'eic-spack.sh') }} + + - name: Add Spack to PATH + run: echo "${{ github.workspace }}/spack/bin" >> $GITHUB_PATH + + - name: Checkout Spack repository + if: steps.cache-spack.outputs.cache-hit != 'true' + uses: actions/checkout@v7 + with: + repository: spack/spack + path: spack + ref: ${{ steps.spack-config.outputs.spack-version }} + fetch-depth: 0 + + - name: Configure spack + if: steps.cache-spack.outputs.cache-hit != 'true' + run: | + cd ${{ github.workspace }}/spack + git config user.name "GitHub Actions" + git config user.email "actions@github.com" + + # Fetch branches from which we cherry-pick + git fetch origin refs/heads/develop:refs/heads/develop + git fetch origin pull/51584/head:pr-51584 + git fetch origin pull/52207/head:pr-52207 + + # Apply cherry-picks from spack.sh (match only valid git commit hashes) + while read -r commit; do + # Commits on branches/forks not fetched above are served by SHA + if ! git cat-file -e "${commit}^{commit}" 2>/dev/null; then + echo "Fetching ${commit} by SHA" + git fetch origin "${commit}" + fi + if git merge-base --is-ancestor "${commit}" HEAD; then + echo "Skipping already applied ${commit}" + continue + fi + echo "Cherry-picking ${commit}" + git cherry-pick "${commit}" + done < <(echo "${{ steps.spack-config.outputs.spack-cherrypicks }}" | grep -E '^[a-f0-9]{40}$' || true) + + - name: Checkout spack-packages repository + if: steps.cache-spack.outputs.cache-hit != 'true' + uses: actions/checkout@v7 + with: + repository: spack/spack-packages + path: spack/var/spack/repos/spack-packages + fetch-depth: 0 + ref: ${{ steps.spack-config.outputs.spackpackages-version }} + + - name: Configure spack-packages + if: steps.cache-spack.outputs.cache-hit != 'true' + run: | + cd ${{ github.workspace }}/spack/var/spack/repos/spack-packages + git config user.name "GitHub Actions" + git config user.email "actions@github.com" + + # Fetch branches from which we cherry-pick + git fetch origin refs/heads/develop:refs/heads/develop + git fetch origin pull/3698/head:pr-3698 + git fetch origin pull/4871/head:pr-4871 + git fetch origin pull/5856/head:pr-5856 + + # Apply cherry-picks from spack-packages.sh (match only valid git commit hashes) + while read -r commit; do + # Commits on forks/unmerged PRs are not in the default clone, but + # GitHub serves any commit in the fork network by SHA from the parent + if ! git cat-file -e "${commit}^{commit}" 2>/dev/null; then + echo "Fetching ${commit} by SHA" + git fetch origin "${commit}" + fi + if git merge-base --is-ancestor "${commit}" HEAD; then + echo "Skipping already applied ${commit}" + continue + fi + echo "Cherry-picking ${commit}" + git cherry-pick "${commit}" + done < <(echo "${{ steps.spack-config.outputs.spackpackages-cherrypicks }}" | grep -E '^[a-f0-9]{40}$' || true) + + cd ${{ github.workspace }}/spack + bin/spack repo add --scope site var/spack/repos/spack-packages/repos/spack_repo/builtin + + - name: Checkout key4hep-spack repository + if: steps.cache-spack.outputs.cache-hit != 'true' + uses: actions/checkout@v7 + with: + repository: key4hep/key4hep-spack + path: spack/var/spack/repos/key4hep-spack + ref: ${{ steps.spack-config.outputs.key4hepspack-version }} + + - name: Configure key4hep-spack + if: steps.cache-spack.outputs.cache-hit != 'true' + run: | + cd ${{ github.workspace }}/spack + bin/spack repo add --scope site var/spack/repos/key4hep-spack + + - name: Checkout eic-spack repository + if: steps.cache-spack.outputs.cache-hit != 'true' + uses: actions/checkout@v7 + with: + repository: eic/eic-spack + path: spack/var/spack/repos/eic-spack + ref: ${{ steps.spack-config.outputs.eicspack-version }} + + - name: Configure eic-spack + if: steps.cache-spack.outputs.cache-hit != 'true' + run: | + cd ${{ github.workspace }}/spack + bin/spack repo add --scope site var/spack/repos/eic-spack/spack_repo/eic + + - name: Setup CVMFS mount point and install directories + id: cvmfs-dirs + run: | + # Set up synthetic firmlink for /cvmfs on macOS + sudo mkdir -p /System/Volumes/Data/cvmfs + sudo chown -R "$(id -u):$(id -g)" /System/Volumes/Data/cvmfs + if ! grep -q '^cvmfs[[:space:]]' /etc/synthetic.conf 2>/dev/null; then + printf 'cvmfs\tSystem/Volumes/Data/cvmfs\n' | sudo tee -a /etc/synthetic.conf + fi + sudo /System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util -t || true + + # The environments' views (view.yaml) default to /opt/local and + # /opt/detector, which Spack touches during environment initialization + # before custom views are applied. Spack also renames the view directory, + # so /opt itself must be writable. + sudo mkdir -p /opt/local /opt/detector + sudo chown "$(id -u):$(id -g)" /opt /opt/local /opt/detector + + # Target image name, tag, and install prefix + IMAGE_NAME="mac_${{ matrix.ENV }}" + PRIMARY_TAG="nightly" + + INSTALL_PREFIX="/cvmfs/${{ env.CVMFS_REPO }}/${{ env.CVMFS_PATH_PREFIX }}/${IMAGE_NAME}:${PRIMARY_TAG}" + echo "install-prefix=${INSTALL_PREFIX}" >> $GITHUB_OUTPUT + echo "image-name=${IMAGE_NAME}" >> $GITHUB_OUTPUT + echo "primary-tag=${PRIMARY_TAG}" >> $GITHUB_OUTPUT + + mkdir -p "${INSTALL_PREFIX}/opt/software" \ + "${INSTALL_PREFIX}/opt/local" \ + "${INSTALL_PREFIX}/opt/detector" + + - name: Download Spack lockfiles + uses: actions/download-artifact@v8 + with: + name: spack-locks-${{ matrix.ENV }} + path: spack-environment/${{ matrix.ENV }} + + - name: Verify Spack lockfiles + run: | + ls -la "spack-environment/${{ matrix.ENV }}/spack.lock" + ls -la "spack-environment/${{ matrix.ENV }}/epic/spack.lock" + + - name: Configure Spack target install tree + run: | + set -x + spack compiler find --scope site + spack external find --scope site llvm + INSTALL_PREFIX="${{ steps.cvmfs-dirs.outputs.install-prefix }}" + spack config --scope site add "config:install_tree:root:'${INSTALL_PREFIX}/opt/software'" + spack config --scope site add "config:install_tree:padded_length:128" + spack -e spack-environment/${{ matrix.ENV }} -c view:false config add "view:{}" + spack -e spack-environment/${{ matrix.ENV }} -c view:false config add "view:default:root:'${INSTALL_PREFIX}/opt/local'" + spack -e spack-environment/${{ matrix.ENV }} -c view:false config add "view:detectors:root:'${INSTALL_PREFIX}/opt/detector'" + spack -e spack-environment/${{ matrix.ENV }}/epic -c view:false config add "view:{}" + spack -e spack-environment/${{ matrix.ENV }}/epic -c view:false config add "view:default:root:'${INSTALL_PREFIX}/opt/local'" + spack -e spack-environment/${{ matrix.ENV }}/epic -c view:false config add "view:detectors:root:'${INSTALL_PREFIX}/opt/detector'" + spack config get config + + - name: Configure buildcache + run: | + mirror=oci://ghcr.io/eic/spack-${{ steps.spack-config.outputs.buildcache-version }} + if spack mirror list | grep -E '^eic[[:space:]]' | grep -F -q "${mirror}"; then + echo "Mirror eic (${mirror}) already configured, skipping" + else + if spack mirror list | grep -q -E '^eic[[:space:]]'; then + spack mirror rm --scope site eic + fi + if [ -n "${GITHUB_REGISTRY_TOKEN}" ]; then + spack mirror add --scope site eic \ + --unsigned \ + --oci-username-variable GITHUB_REGISTRY_USER \ + --oci-password-variable GITHUB_REGISTRY_TOKEN \ + "${mirror}" + else + echo "Buildcache is read-only for this run" + spack mirror add --scope site eic --unsigned "${mirror}" + fi + fi + spack mirror list + + - name: Install base environment into target prefix + run: | + spack -e spack-environment/${{matrix.ENV}} -c view:false install ${{ env.SPACK_INSTALL_FLAGS }} --use-buildcache only + + - name: Install epic environment into target prefix + run: | + spack -e spack-environment/${{matrix.ENV}}/epic -c view:false install ${{ env.SPACK_INSTALL_FLAGS }} --use-buildcache only + + - name: Garbage collect build-only dependencies + run: | + spack gc --yes-to-all \ + -e spack-environment/${{ matrix.ENV }} \ + -e spack-environment/${{ matrix.ENV }}/epic + spack clean --stage --downloads + + - name: Regenerate views + run: | + spack -e spack-environment/${{matrix.ENV}} env view regenerate || true + spack -e spack-environment/${{matrix.ENV}}/epic env view regenerate + + - name: Setup environment and metadata + run: | + INSTALL_PREFIX="${{ steps.cvmfs-dirs.outputs.install-prefix }}" + + # Copy spack environment definition into target prefix + mkdir -p "${INSTALL_PREFIX}/opt" + rm -rf "${INSTALL_PREFIX}/opt/spack-environment" + cp -r spack-environment "${INSTALL_PREFIX}/opt/spack-environment" + + # Generate portable setup.sh for macOS users from Spack environment + spack env activate --sh --dir "${INSTALL_PREFIX}/opt/spack-environment/${{matrix.ENV}}/epic" > "${INSTALL_PREFIX}/setup.sh" + chmod +x "${INSTALL_PREFIX}/setup.sh" + + # Ensure portable self-healing colon-safe paths in setup.sh + python3 - "${INSTALL_PREFIX}" "${GITHUB_WORKSPACE}" << 'EOF' + import pathlib, re, sys, textwrap + prefix = sys.argv[1] + workspace = sys.argv[2] + path = pathlib.Path(prefix) / "setup.sh" + content = path.read_text() + + header = textwrap.dedent("""\ + # Determine directory of this script portably across bash and zsh + if [ -n "${BASH_SOURCE[0]}" ]; then + _SOURCE="${BASH_SOURCE[0]}" + elif [ -n "${ZSH_VERSION}" ]; then + _SOURCE="${(%):-%N}" + else + _SOURCE="$0" + fi + _DIR="$(cd "$(dirname "${_SOURCE}")" 2>/dev/null && pwd)" + + # If directory contains a colon (like in CVMFS unpacked.cern.ch tags), + # create a colon-free symlink in /tmp/eic so colon-separated variables + # (PATH, DYLD_FALLBACK_LIBRARY_PATH, PKG_CONFIG_PATH, etc.) do not split. + if [ "${_DIR#*:}" != "${_DIR}" ]; then + _CLEAN_PREFIX="/tmp/eic/$(basename "${_DIR}" | tr ':' '-')" + mkdir -p "$(dirname "${_CLEAN_PREFIX}")" + ln -sfn "${_DIR}" "${_CLEAN_PREFIX}" + else + _CLEAN_PREFIX="${_DIR}" + fi + """) + + clean_var = "${_CLEAN_PREFIX}" + + # Ensure SPACK_ENV points under target prefix + content = content.replace( + f"{workspace}/spack-environment", + f"{clean_var}/opt/spack-environment" + ) + + # Replace full target prefix with clean_var + content = content.replace(prefix, clean_var) + + # Fix corrupted path fragments caused by Spack splitting on colon in prefix + if ":" in prefix: + tag_tail = prefix.split(":")[-1] + content = content.replace(f":{tag_tail}/opt/", f":{clean_var}/opt/") + content = content.replace(f"={tag_tail}/opt/", f"={clean_var}/opt/") + + # Clean PATH: retain target prefix/CVMFS bin directories and append ${PATH} + def clean_path(match): + val = match.group(1).strip("\"'") + placeholder = "__CLEAN_PREFIX__" + val_safe = val.replace(clean_var, placeholder) + parts = val_safe.split(":") + dirs = [p.replace(placeholder, clean_var) for p in parts if p.startswith(placeholder) or p.startswith("/cvmfs")] + bin_dirs = ":".join(dirs) + return f"export PATH={bin_dirs}:" + "${PATH};" + + content = re.sub(r"^export PATH=([^;\n]+);", clean_path, content, flags=re.MULTILINE) + content = re.sub(r"^export MANPATH=(.*?):;$", r"export MANPATH=\1:${MANPATH};", content, flags=re.MULTILINE) + + # Clean and augment DYLD_FALLBACK_LIBRARY_PATH: retain view lib directories, + # include lib/root for ROOT dynamic libraries/plugins, and preserve any existing value. + def clean_dyld_path(match): + val = match.group(1).strip("\"'") + placeholder = "__CLEAN_PREFIX__" + val_safe = val.replace(clean_var, placeholder) + parts = val_safe.split(":") + dirs = [p.replace(placeholder, clean_var) for p in parts if p.startswith(placeholder) or p.startswith("/cvmfs")] + root_dir = f"{clean_var}/opt/local/lib/root" + if root_dir not in dirs: + dirs.append(root_dir) + lib_dirs = ":".join(dirs) + return f"export DYLD_FALLBACK_LIBRARY_PATH={lib_dirs}" + "${DYLD_FALLBACK_LIBRARY_PATH:+:${DYLD_FALLBACK_LIBRARY_PATH}};" + + if "DYLD_FALLBACK_LIBRARY_PATH" in content: + content = re.sub(r"^export DYLD_FALLBACK_LIBRARY_PATH=([^;\n]+);", clean_dyld_path, content, flags=re.MULTILINE) + else: + content += f"\nexport DYLD_FALLBACK_LIBRARY_PATH={clean_var}/opt/local/lib64:{clean_var}/opt/local/lib:{clean_var}/opt/local/lib/root" + "${DYLD_FALLBACK_LIBRARY_PATH:+:${DYLD_FALLBACK_LIBRARY_PATH}};\n" + + content = re.sub(r"^export PKG_CONFIG_PATH=([^;\n]+);", r"export PKG_CONFIG_PATH=\1${PKG_CONFIG_PATH:+:${PKG_CONFIG_PATH}};", content, flags=re.MULTILINE) + content = re.sub(r"^export CMAKE_PREFIX_PATH=([^;\n]+);", r"export CMAKE_PREFIX_PATH=\1${CMAKE_PREFIX_PATH:+:${CMAKE_PREFIX_PATH}};", content, flags=re.MULTILINE) + + footer = ( + "# ROOT environment configuration\n" + f'if [ -d "{clean_var}/opt/local/lib/root" ]; then\n' + f' export ROOTSYS="{clean_var}/opt/local";\n' + f' export ROOT_LIBRARY_PATH="{clean_var}/opt/local/lib/root";\n' + f' export ROOT_INCLUDE_PATH="{clean_var}/opt/local/include";\n' + ' export CLING_STANDARD_PCH="none";\n' + ' export CPPYY_API_PATH="none";\n' + f' export PYTHONPATH="{clean_var}/opt/local/lib/root"' + "${PYTHONPATH:+:${PYTHONPATH}};\n" + ' if ! command -v root > /dev/null 2>&1 && command -v root.exe > /dev/null 2>&1 ; then\n' + ' alias root=root.exe\n' + ' fi\n' + 'fi\n' + ) + + result = header.rstrip() + "\n\n" + content.strip() + "\n\n" + footer.strip() + "\n" + path.write_text(result) + EOF + + # Detector symlinks fixup + if [ -d "${INSTALL_PREFIX}/opt/detector" ]; then + ( + shopt -s nullglob + cd "${INSTALL_PREFIX}/opt/detector" + for detector in epic-git.*_* ; do + ln -sf "${detector}" "epic-${detector/*_/}" + done + if [ ! -e epic-main ] && [ -d epic-main_* ]; then + ln -sf epic-main_* epic-main + fi + ) + fi + + # Place CVMFS catalog markers + touch "${INSTALL_PREFIX}/opt/software/.cvmfscatalog" + find "${INSTALL_PREFIX}/opt/software" -mindepth 1 -maxdepth 2 -type d -exec touch {}/.cvmfscatalog \; + touch "${INSTALL_PREFIX}/opt/local/.cvmfscatalog" + find "${INSTALL_PREFIX}/opt/local" -mindepth 1 -maxdepth 1 -type d -exec touch {}/.cvmfscatalog \; + touch "${INSTALL_PREFIX}/opt/detector/.cvmfscatalog" + find "${INSTALL_PREFIX}/opt/detector" -mindepth 1 -maxdepth 1 -type d -exec touch {}/.cvmfscatalog \; + touch "${INSTALL_PREFIX}/opt/spack-environment/.cvmfscatalog" + + - name: Smoke test installed environment + run: | + INSTALL_PREFIX="${{ steps.cvmfs-dirs.outputs.install-prefix }}" + echo "Testing environment activation from ${INSTALL_PREFIX}/setup.sh..." + source "${INSTALL_PREFIX}/setup.sh" + PYTHON_BIN="$(which python3)" + echo "which python3: ${PYTHON_BIN}" + if [[ "${PYTHON_BIN}" != *"/opt/local/bin/python3" ]]; then + echo "::error::python3 resolved to ${PYTHON_BIN} instead of opt/local/bin/python3" + exit 1 + fi + python3 --version + python3 -c "import sys; print('Python sys.path:', sys.path)" + if command -v root.exe >/dev/null 2>&1; then + root.exe -b -q -e 'std::cout << "ROOT " << gROOT->GetVersion() << " functional\n"; gSystem->Exit(0);' + fi + + - name: Setup crane + uses: imjasonh/setup-crane@v0.7 + + - name: Package and publish container + env: + PUSH_CONTAINER: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + run: | + if [ "${PUSH_CONTAINER}" != "true" ]; then + echo "Skipping container publishing (push disabled or running in unprivileged fork context)." + exit 0 + fi + + INSTALL_PREFIX="${{ steps.cvmfs-dirs.outputs.install-prefix }}" + IMAGE_NAME="${{ steps.cvmfs-dirs.outputs.image-name }}" + PRIMARY_TAG="${{ steps.cvmfs-dirs.outputs.primary-tag }}" + PUBLISH_IMAGE="${{ env.PUBLISH_IMAGE_REPO }}/${IMAGE_NAME}:${PRIMARY_TAG}" + + TAGS=("${PRIMARY_TAG}") + if [ "${{ github.ref_name }}" = "master" ] && [ "${{ github.event_name }}" != "pull_request" ]; then + TAGS+=("master" "latest") + fi + + # Optional authentication if custom credentials provided (setup-crane already authenticates to ghcr.io by default) + if [ -n "${GITHUB_REGISTRY_TOKEN}" ]; then + REGISTRY_USER="${GITHUB_REGISTRY_USER:-${{ github.actor }}}" + echo "${GITHUB_REGISTRY_TOKEN}" | crane auth login ghcr.io -u "${REGISTRY_USER}" --password-stdin + fi + + LAYER_TAR="/tmp/layer.tar.gz" + echo "Archiving clean install tree directly from ${INSTALL_PREFIX}..." + tar -czf "${LAYER_TAR}" -C "${INSTALL_PREFIX}" . + echo "Layer archive size: $(du -h "${LAYER_TAR}" | cut -f1)" + + echo "Appending layer onto alpine:latest base and pushing ${PUBLISH_IMAGE}..." + crane append \ + --platform=linux/arm64 \ + -b alpine:latest \ + -f "${LAYER_TAR}" \ + -t "${PUBLISH_IMAGE}" + + for tag in "${TAGS[@]:1}"; do + echo "Tagging ${{ env.PUBLISH_IMAGE_REPO }}/${IMAGE_NAME}:${tag}..." + crane tag "${PUBLISH_IMAGE}" "${tag}" + done + rm -f "${LAYER_TAR}" + echo "Container published successfully!" + + - name: Diagnose numpy import + if: failure() + continue-on-error: true + run: | + for env in spack-environment/${{matrix.ENV}} spack-environment/${{matrix.ENV}}/epic; do + echo "::group::${env}" + spack -e "${env}" -c view:false find -lpv py-numpy openblas || true + echo "--- loaded environment" + ( + eval "$(spack -e "${env}" -c view:false load --sh py-numpy)" + cd /tmp + python3 -c 'import numpy; print(numpy.__version__, numpy.__file__)' 2>&1 | tail -30 + so=$(find "$(spack -e "${env}" -c view:false location -i py-numpy)" -name '_multiarray_umath*.so' | head -1) + otool -L "${so}" + ) || true + echo "--- build environment of a failing dependent" + for pkg in py-scipy py-numexpr; do + spack -e "${env}" -c view:false build-env "${pkg}" -- \ + python3 -c 'import sys, numpy; print(numpy.__file__)' 2>&1 | tail -40 || true + done + echo "::endgroup::" + done + + test-cvmfs: + name: Test CVMFS Deployment (${{ matrix.ENV }}) + runs-on: macos-latest + strategy: + fail-fast: false + matrix: + ENV: [ci] + + steps: + - name: Setup CernVM-FS + uses: cvmfs-contrib/github-action-cvmfs@v5 + with: + cvmfs_repositories: ${{ env.CVMFS_REPO }} + + - name: Test deployed environment + run: | + DEPLOY_PREFIX="/cvmfs/${{ env.CVMFS_REPO }}/${{ env.CVMFS_PATH_PREFIX }}/mac_${{ matrix.ENV }}:nightly" + echo "Checking CVMFS mount at /cvmfs/${{ env.CVMFS_REPO }}..." + ls -ld "/cvmfs/${{ env.CVMFS_REPO }}" + + echo "Checking deployed environment directory at ${DEPLOY_PREFIX}..." + if [ ! -d "${DEPLOY_PREFIX}" ]; then + echo "Error: Directory ${DEPLOY_PREFIX} not found!" + ls -la "/cvmfs/${{ env.CVMFS_REPO }}/${{ env.CVMFS_PATH_PREFIX }}" || true + exit 1 + fi + + ls -la "${DEPLOY_PREFIX}" + + echo "=== setup.sh contents ===" + cat "${DEPLOY_PREFIX}/setup.sh" + + CLEAN_PREFIX="/tmp/eic/mac_${{ matrix.ENV }}-nightly" + echo "Setting up clean colon-free symlink: ${CLEAN_PREFIX} -> ${DEPLOY_PREFIX}" + mkdir -p "$(dirname "${CLEAN_PREFIX}")" + ln -sfn "${DEPLOY_PREFIX}" "${CLEAN_PREFIX}" + + # Inspect python3 in opt/local/bin to check for legacy build prefix + if [ -L "${DEPLOY_PREFIX}/opt/local/bin/python3" ]; then + TARGET=$(readlink "${DEPLOY_PREFIX}/opt/local/bin/python3") + echo "opt/local/bin/python3 symlink target: ${TARGET}" + if [ ! -e "${DEPLOY_PREFIX}/opt/local/bin/python3" ]; then + echo "Symlink target does not exist! Resolving legacy build prefix..." + OLD_PREFIX="${TARGET%%/opt/software*}" + echo "Detected legacy prefix: ${OLD_PREFIX}" + if [ -n "${OLD_PREFIX}" ] && [ "${OLD_PREFIX}" != "${TARGET}" ]; then + for root in "" "/Users/Shared" "/System/Volumes/Data"; do + DEST="${root}${OLD_PREFIX}" + DEST_DIR=$(dirname "${DEST}") + echo "Creating compatibility link: ${DEST} -> ${DEPLOY_PREFIX}" + sudo mkdir -p "${DEST_DIR}" 2>/dev/null || true + sudo ln -sfn "${DEPLOY_PREFIX}" "${DEST}" 2>/dev/null || true + done + for variant in mac_ci mac_xl mac_tf; do + VAR_PREFIX="$(dirname "${OLD_PREFIX}")/${variant}-unstable-pr-95" + for root in "" "/Users/Shared" "/System/Volumes/Data"; do + DEST="${root}${VAR_PREFIX}" + DEST_DIR=$(dirname "${DEST}") + sudo mkdir -p "${DEST_DIR}" 2>/dev/null || true + sudo ln -sfn "${DEPLOY_PREFIX}" "${DEST}" 2>/dev/null || true + done + done + fi + fi + fi + + if [ -e "${DEPLOY_PREFIX}/opt/local/bin/python3" ]; then + echo "opt/local/bin/python3 resolves successfully!" + else + echo "Warning: opt/local/bin/python3 still does not resolve!" + ls -la "${DEPLOY_PREFIX}/opt/local/bin/python3" || true + fi + + echo "=== Activating environment ===" + source "${DEPLOY_PREFIX}/setup.sh" + + # If the deployed setup.sh was generated before self-healing colon logic was rolled out, + # PATH and DYLD_FALLBACK_LIBRARY_PATH are split on the colon in ':nightly'. Bridge them + # to use the clean colon-free symlink prefix. + if [ "$(which python3 2>/dev/null)" != "${CLEAN_PREFIX}/opt/local/bin/python3" ]; then + echo "Legacy setup.sh detected; bridging environment variables via clean symlink ${CLEAN_PREFIX}..." + export PATH="${CLEAN_PREFIX}/opt/local/bin:${PATH}" + export DYLD_FALLBACK_LIBRARY_PATH="${CLEAN_PREFIX}/opt/local/lib64:${CLEAN_PREFIX}/opt/local/lib:${CLEAN_PREFIX}/opt/local/lib/root:${DYLD_FALLBACK_LIBRARY_PATH}" + export ROOTSYS="${CLEAN_PREFIX}/opt/local" + export ROOT_LIBRARY_PATH="${CLEAN_PREFIX}/opt/local/lib/root" + export ROOT_INCLUDE_PATH="${CLEAN_PREFIX}/opt/local/include" + export CLING_STANDARD_PCH='none' + export CPPYY_API_PATH='none' + fi + + PYTHON_BIN="$(which python3)" + echo "which python3: ${PYTHON_BIN}" + if [ "${PYTHON_BIN}" != "${CLEAN_PREFIX}/opt/local/bin/python3" ]; then + echo "::error::python3 resolved to ${PYTHON_BIN} instead of ${CLEAN_PREFIX}/opt/local/bin/python3" + exit 1 + fi + + python3 --version + python3 -c "import sys; print('Python sys.path:', sys.path)" + + if command -v root.exe >/dev/null 2>&1; then + echo "Testing ROOT..." + root.exe -b -q -e 'std::cout << "ROOT " << gROOT->GetVersion() << " functional\n"; gSystem->Exit(0);' + fi diff --git a/AGENTS.md b/AGENTS.md index bca7e2bdd..c2a71a60b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -61,6 +61,8 @@ All images support both `linux/amd64` and `linux/arm64` architectures through pa ├── spack-environment/ # Nine environments: ci, ci_without_acts, │ │ # cuda, cvmfs, dbg, jl, prod, tf, xl │ ├── packages.yaml # Package versions, variants, and preferences +│ ├── packages_linux.yaml # Linux-only settings (included when platform == "linux") +│ ├── packages_darwin.yaml # macOS-only settings (included when platform == "darwin") │ ├── /spack.yaml # Default spec list for that environment │ └── /epic/spack.yaml # Custom-version overlay (epic, eicrecon, ...) ├── spack.sh # Spack core version and cherry-picks diff --git a/containers/eic/Dockerfile b/containers/eic/Dockerfile index eff6b432f..e16b44a53 100644 --- a/containers/eic/Dockerfile +++ b/containers/eic/Dockerfile @@ -248,6 +248,8 @@ COPY --from=builder_installation_custom \ /opt/spack-environment/${ENV}/epic/ COPY --from=builder_installation_custom \ /opt/spack-environment/packages.yaml \ + /opt/spack-environment/packages_linux.yaml \ + /opt/spack-environment/packages_darwin.yaml \ /opt/spack-environment/ diff --git a/docs/architecture.md b/docs/architecture.md index 948d3546f..db599eac3 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -152,3 +152,4 @@ Package versions are controlled through several configuration files: | `key4hep-spack.sh` | Key4HEP-spack version | | `eic-spack.sh` | EIC-spack version | | `spack-environment/packages.yaml` | Package version preferences and variants | +| `spack-environment/packages_linux.yaml`, `spack-environment/packages_darwin.yaml` | Platform-specific settings (compiler, GL, X11), included per platform | diff --git a/scripts/check-buildcache.py b/scripts/check-buildcache.py new file mode 100755 index 000000000..018676a06 --- /dev/null +++ b/scripts/check-buildcache.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 +"""Check concrete specs of a Spack environment against a binary buildcache. + +Ignores external packages (which cannot be built by Spack). +Outputs missing spec DAG hashes (prefixed with '/') to output-file (or stdout). +Exits with 0 if all specs exist in the buildcache, or 1 if any specs are missing. +""" + +import concurrent.futures +import sys + +import spack.environment as ev +from spack.binary_distribution import _oci_default_tag, _oci_get_blob_info +from spack.oci.image import ImageReference + + +def main(): + if len(sys.argv) < 3: + print("Usage: check-buildcache.py [output-file]", file=sys.stderr) + sys.exit(2) + + env_dir = sys.argv[1] + mirror_url = sys.argv[2] + output_file = sys.argv[3] if len(sys.argv) > 3 else None + + env = ev.Environment(env_dir) + # Ignore external specs as they are pre-installed on the host / not built + all_specs = [s for s in env.all_specs() if not s.external] + if not all_specs: + print(f"No non-external specs in environment {env_dir}", file=sys.stderr) + if output_file: + open(output_file, "w").close() + sys.exit(0) + + target_image = ImageReference.from_url(mirror_url) + print(f"Checking {len(all_specs)} non-external specs against {mirror_url}...", file=sys.stderr) + + def is_missing(spec): + tag = _oci_default_tag(spec) + ref = target_image.with_tag(tag) + blob = _oci_get_blob_info(ref) + return spec, (blob is None) + + missing = [] + with concurrent.futures.ThreadPoolExecutor(max_workers=16) as pool: + for spec, missing_flag in pool.map(is_missing, all_specs): + if missing_flag: + missing.append(spec) + + if missing: + print(f"Found {len(missing)} missing spec(s) that need to be built:", file=sys.stderr) + for s in missing: + print(f" - {s.short_spec} (/{s.dag_hash()})", file=sys.stderr) + hashes = [f"/{s.dag_hash()}" for s in missing] + hashes_str = " ".join(hashes) + if output_file: + with open(output_file, "w", encoding="utf-8") as f: + f.write(hashes_str + "\n") + else: + print(hashes_str) + sys.exit(1) + else: + print(f"All {len(all_specs)} specs already exist in buildcache.", file=sys.stderr) + if output_file: + open(output_file, "w").close() + sys.exit(0) + + +if __name__ == "__main__": + main() diff --git a/spack-environment/ci/epic/spack.yaml b/spack-environment/ci/epic/spack.yaml index b06813bb4..29779dbfe 100644 --- a/spack-environment/ci/epic/spack.yaml +++ b/spack-environment/ci/epic/spack.yaml @@ -3,8 +3,12 @@ spack: - ../spack.lock - ../../concretizer.yaml - ../../config.yaml - - ../../packages.yaml + - path: ../../packages_darwin.yaml + when: platform == "darwin" + - path: ../../packages_linux.yaml + when: platform == "linux" - ../../view.yaml + - ../../packages.yaml packages: geant4: require: diff --git a/spack-environment/ci/spack.yaml b/spack-environment/ci/spack.yaml index b3a0c8f1d..b6fc50f8e 100644 --- a/spack-environment/ci/spack.yaml +++ b/spack-environment/ci/spack.yaml @@ -2,10 +2,32 @@ spack: include: - ../concretizer.yaml - ../config.yaml - - ../packages.yaml - ../packages_root_without_opengl.yaml + - path: ../packages_darwin.yaml + when: platform == "darwin" + - path: ../packages_linux.yaml + when: platform == "linux" - ../view.yaml + - ../packages.yaml + definitions: + # Note that in definitions 'when' takes a python expression as argument + # https://spack.readthedocs.io/en/latest/environments.html#spec-list-references + - when: arch.satisfies("platform=linux") + linux_packages: + - gfal2 + - gfal2-util + - graphviz + - imagemagick + - iwyu + - nopayloadclient + - ollama + - py-numpy + - py-onnxruntime + - py-scipy + - when: arch.satisfies("platform=darwin") + linux_packages: [] specs: + - $linux_packages - acts - actsvg - afterburner @@ -19,21 +41,14 @@ spack: - fjcontrib - fmt - geant4 -opengl - - gfal2 - - gfal2-util - - graphviz - hepmc3 - hepmcmerger - heppdt - - imagemagick - irt - irt2 - - iwyu - jana2 - just - - nopayloadclient - npsim -geocad - - ollama - onnx - osg-ca-certs - pandorasdk @@ -60,14 +75,11 @@ spack: - py-matplotlib - py-mplhep - py-htgettoken - - py-numpy - py-onnx - - py-onnxruntime - py-pandas - py-particle - py-pip - py-rucio-clients - - py-scipy - py-seaborn - py-snakemake-storage-plugin-fs - py-snakemake-storage-plugin-pelican diff --git a/spack-environment/ci_without_acts/epic/spack.yaml b/spack-environment/ci_without_acts/epic/spack.yaml index 89dd0e3aa..21c74ff17 100644 --- a/spack-environment/ci_without_acts/epic/spack.yaml +++ b/spack-environment/ci_without_acts/epic/spack.yaml @@ -3,8 +3,12 @@ spack: - ../spack.lock - ../../concretizer.yaml - ../../config.yaml - - ../../packages.yaml + - path: ../../packages_darwin.yaml + when: platform == "darwin" + - path: ../../packages_linux.yaml + when: platform == "linux" - ../../view.yaml + - ../../packages.yaml specs: - algorithms - edm4eic diff --git a/spack-environment/ci_without_acts/spack.yaml b/spack-environment/ci_without_acts/spack.yaml index 42667a53a..659c46afe 100644 --- a/spack-environment/ci_without_acts/spack.yaml +++ b/spack-environment/ci_without_acts/spack.yaml @@ -2,9 +2,13 @@ spack: include: - ../concretizer.yaml - ../config.yaml - - ../packages.yaml + - path: ../packages_darwin.yaml + when: platform == "darwin" + - path: ../packages_linux.yaml + when: platform == "linux" - ../packages_root_without_opengl.yaml - ../view.yaml + - ../packages.yaml specs: - actsvg - afterburner diff --git a/spack-environment/cuda/epic/spack.yaml b/spack-environment/cuda/epic/spack.yaml index 9a9e89479..8bf64e042 100644 --- a/spack-environment/cuda/epic/spack.yaml +++ b/spack-environment/cuda/epic/spack.yaml @@ -3,8 +3,12 @@ spack: - ../spack.lock - ../../concretizer.yaml - ../../config.yaml - - ../../packages.yaml + - path: ../../packages_darwin.yaml + when: platform == "darwin" + - path: ../../packages_linux.yaml + when: platform == "linux" - ../../view.yaml + - ../../packages.yaml concretizer: unify: when_possible duplicates: diff --git a/spack-environment/cuda/spack.yaml b/spack-environment/cuda/spack.yaml index bc6721d61..65c92dd26 100644 --- a/spack-environment/cuda/spack.yaml +++ b/spack-environment/cuda/spack.yaml @@ -2,9 +2,16 @@ spack: include: - ../concretizer.yaml - ../config.yaml - - ../packages.yaml - - ../packages_root_with_opengl.yaml + - path: ../packages_darwin.yaml + when: platform == "darwin" + - path: ../packages_linux.yaml + when: platform == "linux" + - path: ../packages_root_with_opengl.yaml + when: platform == "linux" + - path: ../packages_root_without_opengl.yaml + when: platform == "darwin" - ../view.yaml + - ../packages.yaml specs: - acts +cuda - actsvg diff --git a/spack-environment/cvmfs/epic/spack.yaml b/spack-environment/cvmfs/epic/spack.yaml index a96ce0e07..ed69ca9c4 100644 --- a/spack-environment/cvmfs/epic/spack.yaml +++ b/spack-environment/cvmfs/epic/spack.yaml @@ -3,9 +3,13 @@ spack: - ../spack.lock - ../../concretizer.yaml - ../../config.yaml - - ../../packages.yaml + - path: ../../packages_darwin.yaml + when: platform == "darwin" + - path: ../../packages_linux.yaml + when: platform == "linux" - ../../packages_cvmfs_geant4data.yaml - ../../view.yaml + - ../../packages.yaml specs: - algorithms - edm4eic diff --git a/spack-environment/cvmfs/spack.yaml b/spack-environment/cvmfs/spack.yaml index ff410c14e..82fb5e83e 100644 --- a/spack-environment/cvmfs/spack.yaml +++ b/spack-environment/cvmfs/spack.yaml @@ -2,10 +2,14 @@ spack: include: - ../concretizer.yaml - ../config.yaml - - ../packages.yaml + - path: ../packages_darwin.yaml + when: platform == "darwin" + - path: ../packages_linux.yaml + when: platform == "linux" - ../packages_cvmfs_geant4data.yaml - ../packages_root_without_opengl.yaml - ../view.yaml + - ../packages.yaml specs: - acts - actsvg diff --git a/spack-environment/dbg/epic/spack.yaml b/spack-environment/dbg/epic/spack.yaml index 26fe88789..37c1dcbe5 100644 --- a/spack-environment/dbg/epic/spack.yaml +++ b/spack-environment/dbg/epic/spack.yaml @@ -3,8 +3,12 @@ spack: - ../spack.lock - ../../concretizer.yaml - ../../config.yaml - - ../../packages.yaml + - path: ../../packages_darwin.yaml + when: platform == "darwin" + - path: ../../packages_linux.yaml + when: platform == "linux" - ../../view.yaml + - ../../packages.yaml specs: - algorithms build_type=Debug - edm4eic build_type=Debug diff --git a/spack-environment/dbg/spack.yaml b/spack-environment/dbg/spack.yaml index b544d7fb3..dc7f6d0b5 100644 --- a/spack-environment/dbg/spack.yaml +++ b/spack-environment/dbg/spack.yaml @@ -2,9 +2,13 @@ spack: include: - ../concretizer.yaml - ../config.yaml - - ../packages.yaml + - path: ../packages_darwin.yaml + when: platform == "darwin" + - path: ../packages_linux.yaml + when: platform == "linux" - ../packages_root_without_opengl.yaml - ../view.yaml + - ../packages.yaml specs: - acts build_type=Debug - cmake diff --git a/spack-environment/jl/epic/spack.yaml b/spack-environment/jl/epic/spack.yaml index a9309851b..ade6a5d0d 100644 --- a/spack-environment/jl/epic/spack.yaml +++ b/spack-environment/jl/epic/spack.yaml @@ -3,8 +3,12 @@ spack: - ../spack.lock - ../../concretizer.yaml - ../../config.yaml - - ../../packages.yaml + - path: ../../packages_darwin.yaml + when: platform == "darwin" + - path: ../../packages_linux.yaml + when: platform == "linux" - ../../view.yaml + - ../../packages.yaml specs: - algorithms - edm4eic diff --git a/spack-environment/jl/spack.yaml b/spack-environment/jl/spack.yaml index d03e09fe4..e682a1ce9 100644 --- a/spack-environment/jl/spack.yaml +++ b/spack-environment/jl/spack.yaml @@ -2,9 +2,13 @@ spack: include: - ../concretizer.yaml - ../config.yaml - - ../packages.yaml + - path: ../packages_darwin.yaml + when: platform == "darwin" + - path: ../packages_linux.yaml + when: platform == "linux" - ../packages_root_without_opengl.yaml - ../view.yaml + - ../packages.yaml concretizer: duplicates: max_dupes: diff --git a/spack-environment/packages.yaml b/spack-environment/packages.yaml index 4f0f8bb8e..d75fa0902 100644 --- a/spack-environment/packages.yaml +++ b/spack-environment/packages.yaml @@ -58,9 +58,6 @@ packages: require: - '@22:' - +brotli +bz2 +dataset +filesystem +glog +ipc +jemalloc +lz4 +parquet +snappy +zlib +zstd - binutils: - require: - - ~gold blas: require: - openblas @@ -71,16 +68,6 @@ packages: - +iostreams +locale +log +math +program_options +python +random +regex - +serialization +system +test +thread +timer +wave - cxxstd=20 - c: - prefer: - - gcc - require: - - gcc - cxx: - prefer: - - gcc - require: - - gcc cairo: require: - '@1.18.2:' @@ -136,9 +123,6 @@ packages: - '@1.38' - +ddg4 +ddcad +edm4hep +g4hepem +hepmc3 +xercesc - any_of: [+ddeve +utilityapps, -ddeve -utilityapps] # FIXME ^root +x +opengl when +utilityapps - doxygen: - require: - - +graphviz dpmjet: require: - '@19.3.5' @@ -257,7 +241,6 @@ packages: require: - '@11.4.1.east' - cxxstd=20 -vecgeom +threads -vtk - - any_of: [+opengl +qt +x11, -opengl -qt -x11] geant4-data: require: - +nudexlib +tendl +urrpt @@ -270,12 +253,6 @@ packages: ghostscript: require: - ~gtk - gl: - require: - - glx - glu: - require: - - openglu gloo: require: - '@2023-12-03' @@ -671,7 +648,7 @@ packages: root: require: - '@6.40.04' - - cxxstd=20 +fftw +fortran +gdml +http -ipo +mlp +python +root7 +tmva +tmva-sofie +x +xrootd +ssl + - cxxstd=20 +fftw +fortran +gdml +http -ipo +mlp +python +root7 +tmva +tmva-sofie +xrootd +ssl - any_of: [+opengl +webgui, -opengl -webgui] rucio-eic-mcp-server: require: diff --git a/spack-environment/packages_darwin.yaml b/spack-environment/packages_darwin.yaml new file mode 100644 index 000000000..dcd1f7bac --- /dev/null +++ b/spack-environment/packages_darwin.yaml @@ -0,0 +1,36 @@ +# macOS-specific settings, complementing the cross-platform packages.yaml. +# Included from the environments via +# - path: ../packages_darwin.yaml +# when: platform == "darwin" +# See packages_linux.yaml for the Linux counterpart. +packages: + doxygen: + require: + - ~graphviz + c: + prefer: + - apple-clang + require: + - apple-clang + cxx: + prefer: + - apple-clang + require: + - apple-clang + geant4: + require: + - ~opengl ~qt + gl: + require: + - apple-gl + glu: + require: + - apple-glu + libxml2: + prefer: + - build_system=autotools + require: + - any_of: [build_system=autotools, '@:'] + root: + require: + - ~x ~opengl ~webgui diff --git a/spack-environment/packages_linux.yaml b/spack-environment/packages_linux.yaml new file mode 100644 index 000000000..c08f7ea8c --- /dev/null +++ b/spack-environment/packages_linux.yaml @@ -0,0 +1,36 @@ +# Linux-specific settings, complementing the cross-platform packages.yaml. +# Included from the environments via +# - path: ../packages_linux.yaml +# when: platform == "linux" +# See packages_darwin.yaml for the macOS counterpart. Do not use `when:` on +# the virtual packages (c, cxx, ...) here: such conditional requirements are +# not applied, which silently switches the compiler. +packages: + binutils: + require: + - ~gold + c: + prefer: + - gcc + require: + - gcc + cxx: + prefer: + - gcc + require: + - gcc + doxygen: + require: + - +graphviz + geant4: + require: + - any_of: [+opengl +qt +x11, -opengl -qt -x11] + gl: + require: + - glx + glu: + require: + - openglu + root: + require: + - +x diff --git a/spack-environment/prod/epic/spack.yaml b/spack-environment/prod/epic/spack.yaml index a9309851b..ade6a5d0d 100644 --- a/spack-environment/prod/epic/spack.yaml +++ b/spack-environment/prod/epic/spack.yaml @@ -3,8 +3,12 @@ spack: - ../spack.lock - ../../concretizer.yaml - ../../config.yaml - - ../../packages.yaml + - path: ../../packages_darwin.yaml + when: platform == "darwin" + - path: ../../packages_linux.yaml + when: platform == "linux" - ../../view.yaml + - ../../packages.yaml specs: - algorithms - edm4eic diff --git a/spack-environment/prod/spack.yaml b/spack-environment/prod/spack.yaml index 6a949a669..1f0dbc155 100644 --- a/spack-environment/prod/spack.yaml +++ b/spack-environment/prod/spack.yaml @@ -2,9 +2,13 @@ spack: include: - ../concretizer.yaml - ../config.yaml - - ../packages.yaml + - path: ../packages_darwin.yaml + when: platform == "darwin" + - path: ../packages_linux.yaml + when: platform == "linux" - ../packages_root_without_opengl.yaml - ../view.yaml + - ../packages.yaml specs: - acts - dd4hep diff --git a/spack-environment/tf/epic/spack.yaml b/spack-environment/tf/epic/spack.yaml index 180cd3abb..0e5179f77 100644 --- a/spack-environment/tf/epic/spack.yaml +++ b/spack-environment/tf/epic/spack.yaml @@ -3,7 +3,11 @@ spack: - ../spack.lock - ../../concretizer.yaml - ../../config.yaml - - ../../packages.yaml + - path: ../../packages_darwin.yaml + when: platform == "darwin" + - path: ../../packages_linux.yaml + when: platform == "linux" - ../../view.yaml + - ../../packages.yaml specs: - edm4eic diff --git a/spack-environment/tf/spack.yaml b/spack-environment/tf/spack.yaml index 5813947b8..b4043e755 100644 --- a/spack-environment/tf/spack.yaml +++ b/spack-environment/tf/spack.yaml @@ -2,9 +2,24 @@ spack: include: - ../concretizer.yaml - ../config.yaml - - ../packages.yaml + - path: ../packages_darwin.yaml + when: platform == "darwin" + - path: ../packages_linux.yaml + when: platform == "linux" - ../view.yaml + - ../packages.yaml + definitions: + - when: arch.satisfies("platform=linux") + tensorflow: [py-tensorflow +cuda] + - when: arch.satisfies("platform=darwin") + tensorflow: [] + - when: arch.satisfies("platform=linux") + linux_packages: [py-keras, py-onnxruntime, py-tf2onnx] + - when: arch.satisfies("platform=darwin") + linux_packages: [] specs: + - $tensorflow + - $linux_packages - edm4hep - onnx - osg-ca-certs @@ -25,7 +40,6 @@ spack: - py-jinja2-cli - py-jupyter-console - py-jupyterlab - - py-keras - py-lmfit - py-lxml - py-matplotlib @@ -34,7 +48,6 @@ spack: - py-numba - py-numpy - py-onnx - - py-onnxruntime - py-pandas - py-particle - py-pip @@ -42,8 +55,6 @@ spack: - py-rucio-clients - py-scipy - py-seaborn - - py-tensorflow +cuda - - py-tf2onnx - py-toml - py-uproot - py-vector diff --git a/spack-environment/xl/epic/spack.yaml b/spack-environment/xl/epic/spack.yaml index 4ed7e3636..ba5ba34f7 100644 --- a/spack-environment/xl/epic/spack.yaml +++ b/spack-environment/xl/epic/spack.yaml @@ -3,8 +3,12 @@ spack: - ../spack.lock - ../../concretizer.yaml - ../../config.yaml - - ../../packages.yaml + - path: ../../packages_darwin.yaml + when: platform == "darwin" + - path: ../../packages_linux.yaml + when: platform == "linux" - ../../view.yaml + - ../../packages.yaml concretizer: unify: when_possible duplicates: @@ -13,7 +17,10 @@ spack: packages: geant4: require: - - +opengl + - when: platform=linux + spec: +opengl + - when: platform=darwin + spec: ~opengl specs: - algorithms - edm4eic diff --git a/spack-environment/xl/spack.yaml b/spack-environment/xl/spack.yaml index 5b842c125..bdcaefa2a 100644 --- a/spack-environment/xl/spack.yaml +++ b/spack-environment/xl/spack.yaml @@ -2,10 +2,49 @@ spack: include: - ../concretizer.yaml - ../config.yaml - - ../packages.yaml - - ../packages_root_with_opengl.yaml + - path: ../packages_root_with_opengl.yaml + when: platform == "linux" + - path: ../packages_root_without_opengl.yaml + when: platform == "darwin" + - path: ../packages_darwin.yaml + when: platform == "darwin" + - path: ../packages_linux.yaml + when: platform == "linux" - ../view.yaml + - ../packages.yaml + definitions: + # Note that in definitions 'when' takes a python expression as argument + # https://spack.readthedocs.io/en/latest/environments.html#spec-list-references + - when: arch.satisfies("platform=linux") + geant4: [geant4 +opengl] + - when: arch.satisfies("platform=darwin") + geant4: [geant4 -opengl] + - when: arch.satisfies("platform=linux") + linux_packages: + - dawncut + - estarlight + - gdb + - gfal2 + - gfal2-util + - graphviz + - herwig3 + - imagemagick + - iwyu + - lager + - lcov + - madx + - mosquitto + - nopayloadclient + - ollama + - py-onnxruntime + - sherpa + - slurm sysconfdir=/etc/slurm + - strace + - valgrind + - when: arch.satisfies("platform=darwin") + linux_packages: [] specs: + - $linux_packages - acts - actsvg - afterburner @@ -23,7 +62,6 @@ spack: - covfie - cppcoro - dawn - - dawncut - dd4hep - doxygen - dusk @@ -36,41 +74,27 @@ spack: - eigen - emacs - epic-generator - - estarlight - fastjet - fjcontrib - fmt - g4occt - - gdb - - geant4 +opengl - - gfal2 - - gfal2-util + - $geant4 - github-copilot - - graphviz - hepmc3 - hepmcmerger - heppdt - - herwig3 - - imagemagick - irt - irt2 - - iwyu - jana2 - just - - lager - - lcov - lhapdf - lhapdfsets - libtool - lzo - - madx - - mosquitto - multitime - nano - ninja - - nopayloadclient - npsim - - ollama - onnx - opencascade - opencode @@ -109,7 +133,6 @@ spack: - py-htgettoken - py-numpy - py-onnx - - py-onnxruntime - py-openpyxl - py-pandas - py-particle @@ -135,15 +158,11 @@ spack: - root - rucio-eic-mcp-server - timeframebuilder - - sherpa - simsipm - - slurm sysconfdir=/etc/slurm - snakemake - spdlog - stow - - strace - uproot-mcp-server - - valgrind - xeyes - xrootd - xrootd-mcp-server diff --git a/spack.sh b/spack.sh index 0dcd622bb..f3406cd56 100644 --- a/spack.sh +++ b/spack.sh @@ -11,7 +11,7 @@ SPACK_VERSION="v1.2.2" read -r -d '' SPACK_CHERRYPICKS <<- \ --- || true 292b0dcaba3b2a5e3f9668d205d39fee2c715721 -678e506a95b319c573ba7e84703b06d7275ab80e +473d1459fba532de0d3d7edc34d27b1432bc90cd 0971885764f8d75ff838b4ab45f692a762888821 --- ## Optional hash table with comma-separated file list @@ -21,5 +21,5 @@ read -r -d '' SPACK_CHERRYPICKS_FILES <<- \ ## Ref: https://github.com/spack/spack/commit/[hash] ## [hash]: [description] ## 292b0dcaba3b2a5e3f9668d205d39fee2c715721: fix: write created time field with OCI buildcache config -## 678e506a95b319c573ba7e84703b06d7275ab80e: fix: don't map prefix to view root for pkgs excluded from view +## 473d1459fba532de0d3d7edc34d27b1432bc90cd: fix: don't map prefix to view root for pkgs excluded from view ## 0971885764f8d75ff838b4ab45f692a762888821: lock.py: fall back to read-only open on EROFS