feat(cccl): integrate missing CCCL directories — python/, ci/, .agent/, docs/, test/
Sparse-checkout from NVIDIA/cccl main branch to complete cccl_upstream: Added: - python/cuda_cccl/ (226 files) — Python bindings for device-level algorithms Critical for muh toolchain: cuda.compute.reduce_into, scan, radix_sort, etc. Includes 204 .py files with full test coverage for all 27 algorithms - ci/ (163 files) — Build/test infrastructure build_cub.sh, test_cub.sh, build_and_test_targets.sh, matrix.yaml Directly maps to our [INFRA-CI] and [INFRA-BUILD] items - .agent/skills/ (7 files) — NVIDIA's own agent skills for CCCL cccl-style/SKILL.md, cccl-test/SKILL.md, sass-diff/SKILL.md - docs/ (491 files) — Official CCCL documentation CI references, CMake guides, Python compute docs, libcudacxx PTX docs - test/ (12 files) — Top-level integration tests (cuda_smoke, stdpar) - Root configs: .clang-format, .clang-tidy, CONTRIBUTING.md, pyproject.toml - CLAUDE.md symlink → AGENTS.md (NVIDIA's standard) cccl_upstream now mirrors full NVIDIA/cccl structure: Before: 42M (cub + thrust + libcudacxx + cudax + c + examples + benchmarks) After: 53M (+python +ci +docs +.agent +test +configs) This completes the CCCL base needed for: - [muh-bench] items: ci/util/build_and_test_targets.sh for targeted builds - [CCCL-verify] items: python/cuda_cccl/tests/ as reference implementations - [CCCL-test] items: ci/test_cub.sh, ci/test_thrust.sh - Agent workflow: .agent/skills/ for consistent style and test patterns
This commit is contained in:
323
cccl_upstream/ci/util/git_bisect.sh
Executable file
323
cccl_upstream/ci/util/git_bisect.sh
Executable file
@@ -0,0 +1,323 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ci_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
cd "$ci_dir/.."
|
||||
|
||||
usage() {
|
||||
cat <<USAGE
|
||||
Usage: $0 [--preset NAME | --configure-override CMD] [options]
|
||||
|
||||
Generic Options:
|
||||
|
||||
-h, --help Show this help and exit
|
||||
|
||||
Bisection Options:
|
||||
|
||||
--good-ref STR Good ref/sha/tag/branch. Defaults to latest release tag.
|
||||
Accepts '-Nd' (e.g., '-14d') to mean 'origin/main as of N days ago'.
|
||||
--bad-ref STR Bad ref/sha/tag/branch. Defaults to origin/main.
|
||||
Accepts '-Nd' (e.g., '-14d') to mean 'origin/main as of N days ago'.
|
||||
--summary-file PATH Markdown summary output path (optional)
|
||||
No summary file will be generated if this is omitted.
|
||||
Build / Test Options:
|
||||
|
||||
--preset NAME CMake preset
|
||||
--cmake-options STR Extra options passed to CMake preset configure (optional)
|
||||
--configure-override CMD Command to run for configuration instead of cmake preset
|
||||
If set, --preset and --cmake-options will be ignored
|
||||
--build-targets STR Space separated ninja build targets (optional)
|
||||
If omitted, no targets will be built -- explicitly specify 'all' if needed.
|
||||
--ctest-targets STR Space separated CTest -R regex patterns (optional)
|
||||
If omitted, no tests will be run -- explicitly specify '.' to run all.
|
||||
--lit-precompile-tests STR Space-separated libcudacxx lit test paths to precompile without execution (optional)
|
||||
e.g. 'cuda/utility/basic_any.pass.cpp'
|
||||
--lit-tests STR Space-separated libcudacxx lit test paths to execute (optional)
|
||||
e.g. 'cuda/utility/basic_any.pass.cpp'
|
||||
--custom-test-cmd CMD Custom command run after build and tests (optional)
|
||||
--repeat N Re-run the build/test for passing commits N times (default: 1)
|
||||
USAGE
|
||||
}
|
||||
|
||||
start_timestamp=${SECONDS}
|
||||
|
||||
function elapsed_time {
|
||||
local duration=$(( SECONDS - start_timestamp ))
|
||||
local minutes=$(( duration / 60 ))
|
||||
local seconds=$(( duration % 60 ))
|
||||
printf "%dm%02ds" "$minutes" "$seconds"
|
||||
}
|
||||
|
||||
GOOD_REF=""
|
||||
BAD_REF=""
|
||||
PRESET=""
|
||||
BUILD_TARGETS=""
|
||||
CTEST_TARGETS=""
|
||||
LIT_PRECOMPILE_TESTS=""
|
||||
LIT_TESTS=""
|
||||
SUMMARY_FILE=""
|
||||
CMAKE_OPTIONS=""
|
||||
CONFIGURE_OVERRIDE=""
|
||||
CUSTOM_TEST_CMD=""
|
||||
REPEAT=1
|
||||
|
||||
# Basic arg parser (keep simple, no extras)
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-h|--help) usage; exit 0 ;;
|
||||
--preset) PRESET="${2:-}"; shift 2 ;;
|
||||
--good-ref) GOOD_REF="${2:-}"; shift 2 ;;
|
||||
--bad-ref) BAD_REF="${2:-}"; shift 2 ;;
|
||||
--build-targets) BUILD_TARGETS="${2:-}"; shift 2 ;;
|
||||
--ctest-targets) CTEST_TARGETS="${2:-}"; shift 2 ;;
|
||||
--lit-precompile-tests) LIT_PRECOMPILE_TESTS="${2:-}"; shift 2 ;;
|
||||
--lit-tests) LIT_TESTS="${2:-}"; shift 2 ;;
|
||||
--cmake-options) CMAKE_OPTIONS="${2:-}"; shift 2 ;;
|
||||
--configure-override) CONFIGURE_OVERRIDE="${2:-}"; shift 2 ;;
|
||||
--summary-file) SUMMARY_FILE="${2:-}"; shift 2 ;;
|
||||
--custom-test-cmd) CUSTOM_TEST_CMD="${2:-}"; shift 2 ;;
|
||||
--repeat) REPEAT="${2:-}"; shift 2 ;;
|
||||
*) echo "Unknown argument: $1" >&2; usage; exit 2 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "${PRESET}" && -z "${CONFIGURE_OVERRIDE}" ]]; then
|
||||
echo "::error:: --preset or --configure-override is required" >&2
|
||||
usage
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [[ -n "${CONFIGURE_OVERRIDE}" ]]; then
|
||||
if [[ -n "${PRESET}" ]]; then
|
||||
echo "::warning:: --preset ignored due to --configure-override" >&2
|
||||
fi
|
||||
if [[ -n "${CMAKE_OPTIONS}" ]]; then
|
||||
echo "::warning:: --cmake-options ignored due to --configure-override" >&2
|
||||
fi
|
||||
fi
|
||||
|
||||
# Ensure the checkout has complete history and tags:
|
||||
git fetch --unshallow > /dev/null 2>&1 || :
|
||||
git fetch --tags > /dev/null 2>&1 || :
|
||||
|
||||
# Resolve good and bad refs
|
||||
good_ref="${GOOD_REF}"
|
||||
bad_ref="${BAD_REF}"
|
||||
|
||||
# Helper to resolve '-Nd' (N days ago on origin/main) to a SHA
|
||||
_resolve_days_ago() {
|
||||
local spec="$1"
|
||||
local base_branch="origin/main"
|
||||
local n="${spec#-}"
|
||||
n="${n%d}"
|
||||
if [[ -z "$n" || ! "$n" =~ ^[0-9]+$ ]]; then
|
||||
return 1
|
||||
fi
|
||||
local when
|
||||
when=$(date -u -d "$n days ago" '+%Y-%m-%d %H:%M:%S %z')
|
||||
git rev-list -n 1 --before="$when" "$base_branch"
|
||||
}
|
||||
|
||||
# Resolve good_ref
|
||||
if [[ -z "$good_ref" ]]; then
|
||||
good_ref=$(git tag --list 'v*' | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -n 1 || :)
|
||||
echo "Good ref defaulted to last release: $good_ref"
|
||||
fi
|
||||
if [[ "$good_ref" =~ ^-[0-9]+d$ ]]; then
|
||||
good_sha=$(_resolve_days_ago "$good_ref")
|
||||
if [[ -z "$good_sha" ]]; then
|
||||
echo "::error::Unable to resolve good_ref '$good_ref' to a commit on origin/main" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "Resolved good_ref '$good_ref' to origin/main @ $good_sha"
|
||||
else
|
||||
if [[ -z "$good_ref" ]]; then
|
||||
echo "::error::Unable to determine good ref" >&2
|
||||
exit 1
|
||||
fi
|
||||
good_sha=$(git rev-parse "$good_ref")
|
||||
fi
|
||||
|
||||
# Resolve bad_ref
|
||||
if [[ -z "$bad_ref" ]]; then
|
||||
bad_ref="origin/main"
|
||||
echo "Bad ref defaulted to origin/main: $bad_ref"
|
||||
fi
|
||||
if [[ "$bad_ref" =~ ^-[0-9]+d$ ]]; then
|
||||
bad_sha=$(_resolve_days_ago "$bad_ref")
|
||||
if [[ -z "$bad_sha" ]]; then
|
||||
echo "::error::Unable to resolve bad_ref '$bad_ref' to a commit on origin/main" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "Resolved bad_ref '$bad_ref' to origin/main @ $bad_sha"
|
||||
else
|
||||
bad_sha=$(git rev-parse "$bad_ref")
|
||||
fi
|
||||
|
||||
# Copy the build-and-test runner to a temp file so it remains available as HEAD changes:
|
||||
tmp_runner="$(mktemp /tmp/build-and-test-XXXXXX.sh)"
|
||||
cp "${ci_dir}/util/build_and_test_targets.sh" "${tmp_runner}"
|
||||
chmod +x "${tmp_runner}"
|
||||
|
||||
# If --repeat > 1, wrap the runner to repeat successful runs to detect flakiness.
|
||||
bisect_runner="${tmp_runner}"
|
||||
if [[ "${REPEAT}" =~ ^[0-9]+$ ]] && [[ "${REPEAT}" -gt 1 ]]; then
|
||||
tmp_repeat="$(mktemp /tmp/build-and-test-repeat-XXXXXX.sh)"
|
||||
cat > "${tmp_repeat}" <<EOF
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
attempts=${REPEAT}
|
||||
for ((i=1; i<=attempts; ++i)); do
|
||||
echo "::group::✅ build_and_test attempt \${i}/${REPEAT}"
|
||||
"${tmp_runner}" "\$@" || {
|
||||
echo "Attempt \${i} failed; marking commit as bad."
|
||||
exit 1
|
||||
}
|
||||
echo "::endgroup::"
|
||||
done
|
||||
exit 0
|
||||
EOF
|
||||
chmod +x "${tmp_repeat}"
|
||||
bisect_runner="${tmp_repeat}"
|
||||
echo "Repeating successful runs ${REPEAT} times to check for flakiness."
|
||||
fi
|
||||
|
||||
# Writer that always prints to stdout and tees to file if provided
|
||||
write_summary() {
|
||||
if [[ -n "${SUMMARY_FILE}" ]]; then
|
||||
tee -a "${SUMMARY_FILE}"
|
||||
else
|
||||
cat
|
||||
fi
|
||||
}
|
||||
|
||||
echo "Starting bisect with:"
|
||||
echo " BAD_SHA: $bad_sha"
|
||||
echo " GOOD_SHA: $good_sha"
|
||||
|
||||
echo "::group::⚙️ Starting git bisect"
|
||||
(set -x; git bisect start "$bad_sha" "$good_sha")
|
||||
echo "::endgroup::"
|
||||
|
||||
bisect_log="$(mktemp /tmp/git-bisect-log-XXXXXX.log)"
|
||||
bisect_output="$(mktemp /tmp/git-bisect-output-XXXXXX.log)"
|
||||
(
|
||||
set -x
|
||||
git bisect run "${bisect_runner}" \
|
||||
--preset "${PRESET}" \
|
||||
--build-targets "${BUILD_TARGETS}" \
|
||||
--ctest-targets "${CTEST_TARGETS}" \
|
||||
--lit-precompile-tests "${LIT_PRECOMPILE_TESTS}" \
|
||||
--lit-tests "${LIT_TESTS}" \
|
||||
--cmake-options "${CMAKE_OPTIONS}" \
|
||||
--configure-override "${CONFIGURE_OVERRIDE}" \
|
||||
--custom-test-cmd "${CUSTOM_TEST_CMD}" \
|
||||
| tee "${bisect_output}" || :
|
||||
git bisect log | tee "${bisect_log}" || :
|
||||
git bisect reset || :
|
||||
)
|
||||
|
||||
if grep -q " is the first bad commit" "${bisect_output}"; then
|
||||
bad_commit=$(awk '/ is the first bad commit/ {print $1}' "${bisect_output}")
|
||||
echo -e "\e[1;32mFound bad commit in $(elapsed_time): $bad_commit\e[0m"
|
||||
found=true
|
||||
else
|
||||
echo -e "\e[1;31mNo bad commit found ($(elapsed_time)).\e[0m"
|
||||
found=false
|
||||
fi
|
||||
|
||||
function print_repro {
|
||||
echo "### ♻️ Reproduction Steps"
|
||||
echo
|
||||
echo '```bash'
|
||||
if [[ -n "${LAUNCH_ARGS:-}" ]]; then
|
||||
echo " .devcontainer/launch.sh \\"
|
||||
echo " ${LAUNCH_ARGS} \\"
|
||||
echo " -- \\"
|
||||
fi
|
||||
echo " ./ci/util/build_and_test_targets.sh \\"
|
||||
|
||||
declare -a build_test_vars=(
|
||||
PRESET
|
||||
CMAKE_OPTIONS
|
||||
CONFIGURE_OVERRIDE
|
||||
BUILD_TARGETS
|
||||
CTEST_TARGETS
|
||||
LIT_PRECOMPILE_TESTS
|
||||
LIT_TESTS
|
||||
CUSTOM_TEST_CMD
|
||||
)
|
||||
|
||||
# only print the above vars if they're non-empty.
|
||||
first=true
|
||||
for var in "${build_test_vars[@]}"; do
|
||||
if [[ -n "${!var:-}" ]]; then
|
||||
flag="--${var,,}"
|
||||
flag=${flag//_/-} # replace _ with -
|
||||
if ! $first; then
|
||||
echo " \\" # Trailing "\" to escape newlines
|
||||
fi
|
||||
echo -n " ${flag} \"${!var}\""
|
||||
first=false
|
||||
fi
|
||||
done
|
||||
echo # Final newline for last argument
|
||||
echo '```'
|
||||
}
|
||||
|
||||
if [[ "${found}" == "true" ]]; then
|
||||
commit_info=$(git log "$bad_commit" -1 --pretty=format:'%h %s')
|
||||
pr_ref=$(echo "$commit_info" | grep -oE '#[0-9]+' | head -n1 || :)
|
||||
(
|
||||
echo "## 🔎 Bisect Result"
|
||||
echo
|
||||
echo "- Culprit Commit: $commit_info"
|
||||
if [[ -n "$pr_ref" ]]; then
|
||||
pr_num=${pr_ref#\#}
|
||||
echo "- Culprit PR: https://github.com/NVIDIA/cccl/pull/$pr_num"
|
||||
fi
|
||||
echo "- Commit SHA: $bad_commit"
|
||||
echo "- Commit URL: https://github.com/NVIDIA/cccl/commit/${bad_commit}"
|
||||
if [[ -n "${GHA_LOG_URL:-}" ]]; then
|
||||
echo "- Bisection Logs: [GHA Job](${GHA_LOG_URL})"
|
||||
fi
|
||||
if [[ -n "${STEP_SUMMARY_URL:-}" ]]; then
|
||||
echo "- Bisection Summary: [GHA Report](${STEP_SUMMARY_URL})"
|
||||
fi
|
||||
echo
|
||||
print_repro
|
||||
echo
|
||||
echo "### ℹ️ Commit Details"
|
||||
echo
|
||||
echo '```'
|
||||
git show "$bad_commit" --stat
|
||||
echo '```'
|
||||
echo
|
||||
echo "### 🪵 Bisect Log"
|
||||
echo
|
||||
echo '```'
|
||||
cat "${bisect_log}"
|
||||
echo '```'
|
||||
) | write_summary
|
||||
else
|
||||
(
|
||||
echo "## ‼️ Bisect Failed"
|
||||
echo
|
||||
echo "git bisect did not resolve to a single commit."
|
||||
echo
|
||||
if [[ -n "${GHA_LOG_URL:-}" ]]; then
|
||||
echo "- Bisection Logs: [GHA Job](${GHA_LOG_URL})"
|
||||
echo "- Bisection Summary: [GHA Report](${STEP_SUMMARY_URL})"
|
||||
fi
|
||||
echo
|
||||
print_repro
|
||||
echo
|
||||
echo "### 🪵 Bisect Log"
|
||||
echo
|
||||
echo '```'
|
||||
cat "${bisect_log}"
|
||||
echo '```'
|
||||
) | write_summary
|
||||
exit 1
|
||||
fi
|
||||
Reference in New Issue
Block a user