[libcxx-commits] [libcxx] [llvm] [libc++] Extract building libc++ into a composite action (PR #224670)
via libcxx-commits
libcxx-commits at lists.llvm.org
Fri Sep 18 09:52:21 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-libcxx
Author: Louis Dionne (ldionne)
<details>
<summary>Changes</summary>
This allows reusing the same build script for the PR benchmark and
the historical benchmarking jobs. This also opens the door to adding
new configurations where the library isn't rebuilt (e.g. where the
artifacts are pulled from a pre-built location, or even testing against
non-libc++.
Assisted by Claude
---
Full diff: https://github.com/llvm/llvm-project/pull/224670.diff
6 Files Affected:
- (modified) .github/workflows/libcxx-benchmark-commit.yml (+23-2)
- (modified) .github/workflows/libcxx-pr-benchmark.yml (+19-7)
- (added) .github/workflows/libcxx/build-at-commit/action.yml (+49)
- (modified) libcxx/utils/ci/lnt/README.md (+4-3)
- (modified) libcxx/utils/ci/lnt/run-benchmarks (+17-32)
- (modified) libcxx/utils/ci/run-buildbot (+1)
``````````diff
diff --git a/.github/workflows/libcxx-benchmark-commit.yml b/.github/workflows/libcxx-benchmark-commit.yml
index 040c0a7536dc4..f1f8965760fdb 100644
--- a/.github/workflows/libcxx-benchmark-commit.yml
+++ b/.github/workflows/libcxx-benchmark-commit.yml
@@ -107,6 +107,9 @@ jobs:
runs-on: ${{ matrix.runner }}
env:
COMPILER: ${{ matrix.cxx }}
+ # Where we install the library. This lives inside the workspace so that actions/checkout
+ # cleans it up between runs on self-hosted runners.
+ INSTALL_DIR: ${{ github.workspace }}/install
steps:
- name: Checkout the LLVM monorepo
uses: actions/checkout at df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
@@ -146,10 +149,22 @@ jobs:
source .venv/bin/activate
pip install -r libcxx/utils/ci/lnt/requirements.txt
+ # Build the library.
+ #
+ # A build failure is tolerated on purpose, since the library doesn't build at every historical commit.
+ # We still submit an empty run for such commits.
+ - name: Build libc++ at ${{ inputs.commit }}
+ continue-on-error: true
+ uses: ./.github/workflows/libcxx/build-at-commit
+ with:
+ commit: ${{ inputs.commit }}
+ install-dir: ${{ env.INSTALL_DIR }}
+ compiler: ${{ matrix.cxx }}
+ cmake-cache: ${{ matrix.cmake-cache }}
+
- name: Run the benchmarks
env:
BENCHMARK_SUITE_VERSION: ${{ matrix.benchmark-suite-version }}
- CMAKE_CACHE: ${{ matrix.cmake-cache }}
COMMIT: ${{ inputs.commit }}
FILTER: ${{ inputs.filter }}
LNT_MACHINE: ${{ matrix.lnt-machine }}
@@ -159,12 +174,18 @@ jobs:
if [ -n "${FILTER}" ]; then
filter_arg=(--filter "${FILTER}")
fi
+
+ # If the library build failed, still create an install directory so we
+ # run the benchmarks. The benchmarks will fail, and we'll submit an empty
+ # LNT report.
+ mkdir -p "${INSTALL_DIR}"
+
libcxx/utils/ci/lnt/run-benchmarks \
--test-suite-commit "${BENCHMARK_SUITE_VERSION}" \
--machine "${LNT_MACHINE}" \
--compiler "${COMPILER}" \
--benchmark-commit "${COMMIT}" \
- --cmake-cache "${CMAKE_CACHE}" \
+ --libcxx-installation "${INSTALL_DIR}" \
"${filter_arg[@]}" \
--output "${COMMIT}.json"
diff --git a/.github/workflows/libcxx-pr-benchmark.yml b/.github/workflows/libcxx-pr-benchmark.yml
index 9c31be387b4d1..b9e540c986d17 100644
--- a/.github/workflows/libcxx-pr-benchmark.yml
+++ b/.github/workflows/libcxx-pr-benchmark.yml
@@ -110,7 +110,6 @@ jobs:
- extract-info
env:
BENCHMARKS: ${{ needs.extract-info.outputs.benchmarks }}
- CMAKE_CACHE: ${{ matrix.cmake-cache }}
COMPILER: ${{ matrix.cxx }}
LNT_MACHINE: ${{ matrix.lnt-machine }}
PR_HEAD: ${{ needs.extract-info.outputs.pr_head }}
@@ -153,12 +152,25 @@ jobs:
source .venv/bin/activate
pip install -r libcxx/utils/requirements.txt
- - name: Build the baseline and the candidate
- run: |
- source .venv/bin/activate
- baseline_commit=$(git merge-base $PR_BASE $PR_HEAD)
- ./libcxx/utils/build-at-commit --commit ${baseline_commit} --install-dir install/baseline -- -DCMAKE_CXX_COMPILER="${COMPILER}" -C "${CMAKE_CACHE}"
- ./libcxx/utils/build-at-commit --commit $PR_HEAD --install-dir install/candidate -- -DCMAKE_CXX_COMPILER="${COMPILER}" -C "${CMAKE_CACHE}"
+ - name: Determine the baseline commit
+ id: baseline
+ run: echo "commit=$(git merge-base "${PR_BASE}" "${PR_HEAD}")" >> "${GITHUB_OUTPUT}"
+
+ - name: Build the baseline
+ uses: ./.github/workflows/libcxx/build-at-commit
+ with:
+ commit: ${{ steps.baseline.outputs.commit }}
+ install-dir: ${{ github.workspace }}/install/baseline
+ compiler: ${{ matrix.cxx }}
+ cmake-cache: ${{ matrix.cmake-cache }}
+
+ - name: Build the candidate
+ uses: ./.github/workflows/libcxx/build-at-commit
+ with:
+ commit: ${{ needs.extract-info.outputs.pr_head }}
+ install-dir: ${{ github.workspace }}/install/candidate
+ compiler: ${{ matrix.cxx }}
+ cmake-cache: ${{ matrix.cmake-cache }}
- name: Run baseline and candidate interleaved
run: |
diff --git a/.github/workflows/libcxx/build-at-commit/action.yml b/.github/workflows/libcxx/build-at-commit/action.yml
new file mode 100644
index 0000000000000..0528a8e37c5fe
--- /dev/null
+++ b/.github/workflows/libcxx/build-at-commit/action.yml
@@ -0,0 +1,49 @@
+# This composite action builds libc++ as of an arbitrary commit and installs it into the
+# specified directory.
+#
+# This allows other workflows to reuse this job for various purposes.
+#
+# This action assumes that the monorepo has been checked out with its full history (arbitrary
+# historical commits must be reachable) and that CMake, Ninja and the compiler are available.
+
+name: 'Build libc++ at a commit'
+description: 'Build and install libc++ as of the given commit.'
+
+inputs:
+ commit:
+ description: 'The libc++ commit to build.'
+ required: true
+ install-dir:
+ description: 'Directory to install the library into.'
+ required: true
+ compiler:
+ description: 'The C++ compiler to build the library with.'
+ required: true
+ cmake-cache:
+ description: >-
+ CMake cache describing how to build the library, as a path relative to the root of the
+ monorepo (e.g. libcxx/utils/ci/lnt/cmake/generic.cmake).
+ required: true
+
+runs:
+ using: composite
+ steps:
+ - name: Build libc++ at ${{ inputs.commit }}
+ shell: bash
+ env:
+ CMAKE_CACHE: ${{ inputs.cmake-cache }}
+ COMMIT: ${{ inputs.commit }}
+ COMPILER: ${{ inputs.compiler }}
+ INSTALL_DIR: ${{ inputs.install-dir }}
+ run: |
+ # Clean up any stale install directory
+ rm -rf "${INSTALL_DIR}"
+
+ # Let build-at-commit use (and clean up) its own temporary source and build directories:
+ # only the installation is of interest to the caller.
+ libcxx/utils/build-at-commit \
+ --commit "${COMMIT}" \
+ --install-dir "${INSTALL_DIR}" \
+ -- \
+ -DCMAKE_CXX_COMPILER="${COMPILER}" \
+ -C "${GITHUB_WORKSPACE}/${CMAKE_CACHE}"
diff --git a/libcxx/utils/ci/lnt/README.md b/libcxx/utils/ci/lnt/README.md
index b82908f077e42..1b40d562d9391 100644
--- a/libcxx/utils/ci/lnt/README.md
+++ b/libcxx/utils/ci/lnt/README.md
@@ -76,12 +76,13 @@ which can be used to benchmark locally:
```
run-benchmarks --test-suite-commit <SHA1> --machine <MACHINE> \
--compiler clang++ --benchmark-commit <SHA2> \
+ --libcxx-installation <PATH> \
--output result.json
```
-This will run the benchmarks (using the test suite at the specified `SHA1`) against libc++
-as-of the specified `SHA2`, and produce a LNT-ready JSON report. The results can then be
-submitted to a LNT instance if desired.
+This will run the benchmarks (using the test suite at the specified `SHA1`) against the installation
+of libc++ at `PATH` (which is assumed to be libc++ as-of `SHA2`), and produce a LNT-ready JSON report.
+The results can then be submitted to a LNT instance if desired.
## Setting up a local LNT instance
diff --git a/libcxx/utils/ci/lnt/run-benchmarks b/libcxx/utils/ci/lnt/run-benchmarks
index 6365e2465892e..8ecc653c8fb79 100755
--- a/libcxx/utils/ci/lnt/run-benchmarks
+++ b/libcxx/utils/ci/lnt/run-benchmarks
@@ -109,6 +109,8 @@ def main(argv):
prog='run-benchmarks',
description='Benchmark libc++ at the given commit and produce a LNT JSON report.',
epilog='This script depends on the modules listed in `libcxx/utils/ci/lnt/requirements.txt`.')
+ parser.add_argument('--libcxx-installation', type=pathlib.Path, required=True,
+ help='Directory where libc++ is installed.')
parser.add_argument('--benchmark-commit', type=str, required=True,
help='The SHA representing the version of the library to benchmark.')
parser.add_argument('--test-suite-commit', type=str, required=True,
@@ -125,14 +127,10 @@ def main(argv):
"running only a subset of the benchmarks.")
parser.add_argument('--spec-dir', type=pathlib.Path, required=False,
help='Optional path to a SPEC installation to use for benchmarking.')
- parser.add_argument('--cmake-cache', type=pathlib.Path, required=False, default=pathlib.Path(__file__).resolve().parent / 'cmake' / 'generic.cmake',
- help='Optional path to a CMake cache to use when building the library. Only used when building '
- 'the library, not when running the test suite. Defaults to '
- 'libcxx/utils/ci/lnt/cmake/generic.cmake, which builds the library with optimizations enabled.')
- parser.add_argument('--build-dir', type=pathlib.Path, required=False,
- help='Optional directory in which to keep build artifacts. By default, a temporary directory '
- 'is used and the build artifacts are discarded after the run. It is an error to specify '
- 'a build directory that already exists.')
+ parser.add_argument('--artifacts-dir', type=pathlib.Path, required=False,
+ help='Optional directory in which to keep the artifacts produced by the run. By default, a '
+ 'temporary directory is used and the artifacts are discarded after the run. It is an '
+ 'error to specify an artifacts directory that already exists.')
parser.add_argument('--git-repo', type=directory_path, default=os.getcwd(),
help='Optional path to the Git repository to use. By default, the current working directory is used.')
parser.add_argument('--dry-run', action='store_true',
@@ -175,39 +173,26 @@ def main(argv):
# check dependencies.
if args.output.exists():
sys.exit(f'error: output report {args.output} already exists; not overwriting it')
- if args.build_dir is not None and args.build_dir.exists():
- sys.exit(f'error: build directory {args.build_dir} already exists; not overwriting it')
- if args.cmake_cache is not None and not args.cmake_cache.is_file():
- sys.exit(f'error: CMake cache {args.cmake_cache} does not exist')
+ if not args.libcxx_installation.exists():
+ sys.exit(f'error: libc++ installation directory {args.libcxx_installation} does not exist')
+ if args.artifacts_dir is not None and args.artifacts_dir.exists():
+ sys.exit(f'error: artifacts directory {args.artifacts_dir} already exists; not overwriting it')
if shutil.which('lnt') is None:
sys.exit('error: cannot find `lnt`; install libcxx/utils/ci/lnt/requirements.txt')
with contextlib.ExitStack() as stack:
- # The build artifacts are kept in --build-dir when it is given, and stored in a temporary directory
- # otherwise.
- if args.build_dir is not None:
- artifacts = args.build_dir
+ # The artifacts are kept in --artifacts-dir when it is given, and stored in a temporary directory otherwise.
+ if args.artifacts_dir is not None:
+ artifacts = args.artifacts_dir
else:
artifacts = pathlib.Path(stack.enter_context(tempfile.TemporaryDirectory()))
if not args.dry_run:
artifacts.mkdir(parents=True, exist_ok=True)
- logging.info(f'Storing build artifacts in {artifacts}')
-
- logging.info(f'Building libc++ at commit {args.benchmark_commit}')
- cmake_args = [f'-DCMAKE_CXX_COMPILER={args.compiler}']
- if args.cmake_cache is not None:
- cmake_args += ['-C', args.cmake_cache.resolve()]
- build_cmd = [args.git_repo / 'libcxx/utils/build-at-commit',
- '--git-repo', args.git_repo,
- '--install-dir', artifacts / 'libcxx-install',
- '--tmp-src-dir', artifacts / 'libcxx-src',
- '--tmp-build-dir', artifacts / 'libcxx-build',
- '--commit', args.benchmark_commit, '--', *cmake_args]
- run(build_cmd, enforce_success=False) # if the build fails, carry on: we'll fail later and submit empty LNT results
-
- logging.info(f'Running benchmarks from {args.test_suite_commit} against libc++ {args.benchmark_commit}')
+ logging.info(f'Storing artifacts in {artifacts}')
+
+ logging.info(f'Running benchmarks from {args.test_suite_commit} against libc++ at {args.libcxx_installation}')
lit_params = ['--param', f'compiler={args.compiler}',
- '--param', f'libcxx_installation={artifacts / "libcxx-install"}',
+ '--param', f'libcxx_installation={args.libcxx_installation}',
'--param', 'optimization=speed',
'--param', 'std=c++26',
'--param', 'enable_werror=False'] # older versions of the library trigger new warnings, don't fail
diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index f2c3d1f67916e..02f9a87d01a07 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -337,6 +337,7 @@ EOF
"${MONOREPO_ROOT}/libcxx/utils/ci/lnt/run-benchmarks" \
--dry-run -vv \
--git-repo "${MONOREPO_ROOT}" \
+ --libcxx-installation "${INSTALL_DIR}" \
--benchmark-commit HEAD \
--test-suite-commit HEAD \
--machine test-tools \
``````````
</details>
https://github.com/llvm/llvm-project/pull/224670
More information about the libcxx-commits
mailing list