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
59 changes: 29 additions & 30 deletions .github/actions/ccache-push/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ inputs:
description: 'PR number for tag. Auto-detected from ref_name when empty.'
required: false
default: ''
delta-threshold:
description: 'Skip push if cache file count changed by less than this percentage. Set to 0 to always push.'
miss-threshold:
description: 'Skip push if ccache miss rate is below this percentage. Set to 0 to always push.'
required: false
default: '10'
default: '5'
ref-name:
description: 'Branch name used for tag when not a PR build'
required: false
Expand All @@ -42,7 +42,7 @@ runs:
shell: bash
run: |
cache_path="${{ inputs.cache-path }}"
delta_threshold="${{ inputs.delta-threshold }}"
miss_threshold="${{ inputs.miss-threshold }}"

# Validate cache path has content
if [ ! -d "$cache_path" ] || [ -z "$(ls -A "$cache_path" 2>/dev/null)" ]; then
Expand All @@ -53,35 +53,30 @@ runs:

# Print stats for visibility: prefer _build_stats.txt from Docker
# export, fall back to querying ccache on the host (native builds).
stats_output=""
stats_file="$cache_path/_build_stats.txt"
if [ -f "$stats_file" ]; then
echo "=== ccache stats (from Docker build) ==="
cat "$stats_file"
stats_output=$(cat "$stats_file")
echo "$stats_output"
elif command -v ccache &>/dev/null; then
echo "=== ccache stats ==="
ccache -s 2>/dev/null || true
stats_output=$(ccache -s 2>/dev/null || true)
echo "$stats_output"
fi

# Compute cache delta: compare current file count to the baseline
# recorded at restore time. This measures how much the cache
# actually changed, regardless of whether the build ran natively
# or inside Docker.
restore_count_file="$cache_path/_restore_file_count.txt"
current_count=$(find "$cache_path" -type f | wc -l | tr -d ' ')
if [ -f "$restore_count_file" ]; then
restore_count=$(cat "$restore_count_file" | tr -d ' ')
else
restore_count=0
fi
new_files=$((current_count - restore_count))
if [ "$restore_count" -gt 0 ]; then
delta_pct=$((new_files * 100 / restore_count))
elif [ "$current_count" -gt 0 ]; then
delta_pct=100
else
delta_pct=0
# Use ccache's miss rate to decide whether the cache is worth saving.
# File-count growth is a poor proxy: a small percentage change can still
# represent hundreds of missed compile results in a large cache.
cacheable_calls=$(echo "$stats_output" | awk '/^Cacheable calls:/ {print $3; exit}')
cache_misses=$(echo "$stats_output" | awk '/^ Misses:/ {print $2; exit}')
miss_pct=""
if [[ "$cacheable_calls" =~ ^[0-9]+$ ]] && \
[[ "$cache_misses" =~ ^[0-9]+$ ]] && \
[ "$cacheable_calls" -gt 0 ]; then
miss_pct=$((cache_misses * 100 / cacheable_calls))
echo "Cache miss rate: ${miss_pct}% (${cache_misses} misses / ${cacheable_calls} cacheable calls)"
fi
echo "Cache delta: ${new_files} new files (${delta_pct}% change, ${restore_count} -> ${current_count})"

# Always push on the default branch to keep the baseline fresh.
# This includes merge queue runs, which are the only path into
Expand All @@ -96,10 +91,14 @@ runs:

if $is_default_branch; then
echo "On default branch ($default_branch); always pushing to keep baseline fresh."
elif [ "$delta_threshold" -gt 0 ] 2>/dev/null && [ "$delta_pct" -lt "$delta_threshold" ]; then
echo "Cache changed by ${delta_pct}% (threshold: ${delta_threshold}%); skipping push."
echo "pushed=false" >> $GITHUB_OUTPUT
exit 0
elif [ "$miss_threshold" -gt 0 ] 2>/dev/null; then
if [ -z "$miss_pct" ]; then
echo "No ccache miss rate available; pushing cache."
elif [ "$miss_pct" -lt "$miss_threshold" ]; then
echo "Cache miss rate ${miss_pct}% is below threshold ${miss_threshold}%; skipping push."
echo "pushed=false" >> $GITHUB_OUTPUT
exit 0
fi
fi

# Extract log and stats from cache dir before tarring so they
Expand All @@ -108,7 +107,7 @@ runs:
if [ -f "$cache_path/ccache.log" ]; then
mv "$cache_path/ccache.log" "${GITHUB_WORKSPACE:-$(pwd)}/ccache.log"
fi
rm -f "$stats_file" "$restore_count_file"
rm -f "$stats_file" "$cache_path/_restore_file_count.txt"

# Determine tag
repo_owner=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]')
Expand Down
94 changes: 94 additions & 0 deletions .github/actions/setup-xcode-stable/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
name: Set up latest stable Xcode
description: Select the newest non-beta Xcode installed on the macOS runner.

outputs:
version:
description: Selected Xcode version.
value: ${{ steps.select.outputs.version }}
build:
description: Selected Xcode build number.
value: ${{ steps.select.outputs.build }}
path:
description: Selected Xcode application path.
value: ${{ steps.select.outputs.path }}
developer-dir:
description: Selected Xcode developer directory.
value: ${{ steps.select.outputs.developer-dir }}

runs:
using: composite
steps:
- id: select
shell: bash
run: |
set -euo pipefail

# Mirrors the selection strategy from maxim-lobanov/setup-xcode
# (scan installed Xcode apps, ignore beta releases, choose newest
# stable), but keeps it local to avoid external-action allowlist issues.
selection_file=$(mktemp)
python3 > "$selection_file" <<'PY'
from pathlib import Path
import plistlib
import re
import sys

def semver_key(version):
return tuple(int(part) for part in re.findall(r"\d+", version))

versions = []
for app in Path("/Applications").iterdir():
if not app.is_dir() or app.is_symlink():
continue
if not re.match(r"^Xcode.*\.app$", app.name):
continue

version_plist = app / "Contents" / "version.plist"
license_plist = app / "Contents" / "Resources" / "LicenseInfo.plist"
if not version_plist.exists() or not license_plist.exists():
continue

with version_plist.open("rb") as handle:
version_info = plistlib.load(handle)
with license_plist.open("rb") as handle:
license_info = plistlib.load(handle)

version = str(version_info.get("CFBundleShortVersionString", ""))
build = str(version_info.get("ProductBuildVersion", ""))
license_type = str(license_info.get("licenseType", "")).lower()
if not version or not license_type or "beta" in license_type:
continue

versions.append((semver_key(version), version, build, str(app)))

if not versions:
print("No stable Xcode installation found in /Applications.", file=sys.stderr)
sys.exit(1)

_, version, build, path = sorted(versions, reverse=True)[0]
print(path)
print(version)
print(build)
PY

xcode_path=$(sed -n '1p' "$selection_file")
xcode_version=$(sed -n '2p' "$selection_file")
xcode_build=$(sed -n '3p' "$selection_file")
rm -f "$selection_file"

developer_dir="$xcode_path/Contents/Developer"
if [ ! -d "$developer_dir" ]; then
echo "Could not find Xcode developer directory: $developer_dir"
exit 1
fi

sudo xcode-select -s "$developer_dir"
echo "DEVELOPER_DIR=$developer_dir" >> "$GITHUB_ENV"
echo "MD_APPLE_SDK_ROOT=$xcode_path" >> "$GITHUB_ENV"

echo "path=$xcode_path" >> "$GITHUB_OUTPUT"
echo "version=$xcode_version" >> "$GITHUB_OUTPUT"
echo "build=$xcode_build" >> "$GITHUB_OUTPUT"
echo "developer-dir=$developer_dir" >> "$GITHUB_OUTPUT"

echo "Selected Xcode $xcode_version ($xcode_build) at $xcode_path"
58 changes: 55 additions & 3 deletions .github/workflows/ci_macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,17 @@ jobs:
with:
python-version: '3.12'

- name: Select latest stable Xcode
uses: ./.github/actions/setup-xcode-stable

- name: Log macOS toolchain
run: |
sw_vers
xcodebuild -version
xcrun --show-sdk-path
xcrun --show-sdk-version
clang --version

- name: Set up ccache and ORAS
uses: ./.github/actions/ccache-setup

Expand Down Expand Up @@ -158,7 +169,6 @@ jobs:
run: |
source scripts/set_env_defaults.sh
Python3_EXECUTABLE="$(which python3)" \
LLVM_PROJECTS='clang;lld;mlir;openmp;python-bindings' \
LLVM_SOURCE="$HOME/.llvm-project" \
bash scripts/build_llvm.sh -c Release -v -j $(sysctl -n hw.ncpu)

Expand Down Expand Up @@ -209,6 +219,17 @@ jobs:
with:
python-version: ${{ matrix.python_version }}

- name: Select latest stable Xcode
uses: ./.github/actions/setup-xcode-stable

- name: Log macOS toolchain
run: |
sw_vers
xcodebuild -version
xcrun --show-sdk-path
xcrun --show-sdk-version
clang --version

- name: Set up ccache and ORAS
uses: ./.github/actions/ccache-setup

Expand All @@ -235,7 +256,6 @@ jobs:
run: |
source scripts/set_env_defaults.sh
Python3_EXECUTABLE="$(which python3)" \
LLVM_PROJECTS='clang;lld;mlir;openmp;python-bindings' \
LLVM_SOURCE="$HOME/.llvm-project" \
bash scripts/build_llvm.sh -c Release -v -j $(sysctl -n hw.ncpu)

Expand Down Expand Up @@ -291,6 +311,17 @@ jobs:
with:
python-version: ${{ matrix.python_version }}

- name: Select latest stable Xcode
uses: ./.github/actions/setup-xcode-stable

- name: Log macOS toolchain
run: |
sw_vers
xcodebuild -version
xcrun --show-sdk-path
xcrun --show-sdk-version
clang --version

- name: Download wheel artifact
uses: actions/download-artifact@v4
with:
Expand Down Expand Up @@ -339,6 +370,17 @@ jobs:
with:
python-version: '3.12'

- name: Select latest stable Xcode
uses: ./.github/actions/setup-xcode-stable

- name: Log macOS toolchain
run: |
sw_vers
xcodebuild -version
xcrun --show-sdk-path
xcrun --show-sdk-version
clang --version

- name: Set up ccache and ORAS
uses: ./.github/actions/ccache-setup

Expand Down Expand Up @@ -367,7 +409,6 @@ jobs:
run: |
source scripts/set_env_defaults.sh
Python3_EXECUTABLE="$(which python3)" \
LLVM_PROJECTS='clang;lld;mlir;openmp;python-bindings' \
LLVM_SOURCE="$HOME/.llvm-project" \
bash scripts/build_llvm.sh -c Release -v -j $(sysctl -n hw.ncpu)

Expand Down Expand Up @@ -418,6 +459,17 @@ jobs:
with:
python-version: '3.12'

- name: Select latest stable Xcode
uses: ./.github/actions/setup-xcode-stable

- name: Log macOS toolchain
run: |
sw_vers
xcodebuild -version
xcrun --show-sdk-path
xcrun --show-sdk-version
clang --version

- name: Download installer artifact
uses: actions/download-artifact@v4
with:
Expand Down
21 changes: 18 additions & 3 deletions .github/workflows/dev_environment_macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ jobs:
scripts/install_prerequisites.sh \
scripts/build_llvm.sh \
scripts/set_env_defaults.sh \
cmake/caches/LLVM.cmake \
.github/workflows/dev_environment_macos.yml \
tpls/customizations/llvm/*.diff \
| sha256sum | cut -c1-8)
Expand Down Expand Up @@ -142,17 +143,32 @@ jobs:
- name: Log in to GitHub CR
run: echo "${{ github.token }}" | oras login ghcr.io -u ${{ github.actor }} --password-stdin

- name: Select latest stable Xcode
uses: ./.github/actions/setup-xcode-stable

- name: Log macOS toolchain
run: |
sw_vers
xcodebuild -version
xcrun --show-sdk-path
xcrun --show-sdk-version
clang --version

- name: Compute cache key
id: cache_key
run: |
# Combine build scripts hash with the runner image version to detect
# tool updates (cmake, compilers, etc.) that would invalidate the cache.
# Include the selected Xcode as well because the same runner image can
# contain multiple SDK/compiler releases.
scripts_hash=${{ needs.metadata.outputs.scripts_hash }}
runner_image=${ImageVersion:-unknown}
deps_hash=$(echo "${scripts_hash}-${runner_image}" | shasum -a 256 | cut -c1-8)
developer_dir=${DEVELOPER_DIR:-$(xcode-select -p 2>/dev/null || echo unknown)}
xcode_version=$(xcodebuild -version 2>/dev/null | shasum -a 256 | cut -c1-8 || echo unknown)
deps_hash=$(echo "${scripts_hash}-${runner_image}-${developer_dir}-${xcode_version}" | shasum -a 256 | cut -c1-8)
image_name="${{ needs.metadata.outputs.image_name_base }}${deps_hash}"
echo "image_name=$image_name" >> $GITHUB_OUTPUT
echo "Cache key: scripts_hash=$scripts_hash runner_image=$runner_image deps_hash=$deps_hash"
echo "Cache key: scripts_hash=$scripts_hash runner_image=$runner_image developer_dir=$developer_dir xcode_version=$xcode_version deps_hash=$deps_hash"
echo "Image name: $image_name"

- name: Check for cached image
Expand Down Expand Up @@ -186,7 +202,6 @@ jobs:
git submodule update --init --recursive tpls/nanobind

source scripts/set_env_defaults.sh
export LLVM_PROJECTS='clang;lld;mlir;openmp;python-bindings'

echo "Building prerequisites..."
echo "LLVM_INSTALL_PREFIX=$LLVM_INSTALL_PREFIX"
Expand Down
6 changes: 6 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -755,6 +755,7 @@ if (CUDAQ_ENABLE_PYTHON)
endif()

if(CUDAQ_BUILD_TESTS AND NOT CUDAQ_DISABLE_CPP_FRONTEND)
include(CheckCXXCompilerFlag)
umbrella_lit_testsuite_begin(check-all)
set(INSTALL_GTEST OFF)
add_subdirectory(tpls/googletest-src)
Expand All @@ -773,6 +774,11 @@ if(CUDAQ_BUILD_TESTS AND NOT CUDAQ_DISABLE_CPP_FRONTEND)
endif()
if (CMAKE_CXX_COMPILER_ID MATCHES "^(Apple)?Clang$")
target_compile_options(gtest PUBLIC -Wno-covered-switch-default)
check_cxx_compiler_flag("-Wno-character-conversion"
CUDAQ_SUPPORTS_WNO_CHARACTER_CONVERSION)
if (CUDAQ_SUPPORTS_WNO_CHARACTER_CONVERSION)
target_compile_options(gtest PUBLIC -Wno-character-conversion)
endif()
endif()
include(GoogleTest)
include(CTest)
Expand Down
2 changes: 1 addition & 1 deletion runtime/cudaq/cudaq.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ void __nvqpp_initializer_list_to_vector_bool(std::vector<bool> &result,
char *initList, std::size_t size) {
// result is a sret return value. Make sure it is default initialized. Takes
// advantage of default empty vector being all 0s.
std::memset(reinterpret_cast<void *>(&result), 0, sizeof(result));
std::memset(static_cast<void *>(&result), 0, sizeof(result));
// Allocate space.
result.reserve(size);
// Copy in the initialization list data.
Expand Down
Loading
Loading