diff --git a/.clangd b/.clangd
new file mode 100644
index 00000000..0e4c84bd
--- /dev/null
+++ b/.clangd
@@ -0,0 +1,62 @@
+# https://clangd.llvm.org/config
+
+# Apply a config conditionally to all C files
+If:
+ PathMatch: .*\.(c|h)$
+
+---
+
+# Apply a config conditionally to all C++ files
+If:
+ PathMatch: .*\.(c|h)pp
+
+---
+
+# Apply a config conditionally to all CUDA files
+If:
+ PathMatch: .*\.cuh?
+CompileFlags:
+ Add:
+ # Allow variadic CUDA functions
+ - "-Xclang=-fcuda-allow-variadic-functions"
+
+---
+
+# Tweak the clangd parse settings for all files
+CompileFlags:
+ Compiler: clang++
+ CompilationDatabase: .
+ Add:
+ - -x
+ - cuda
+ # report all errors
+ - "-ferror-limit=0"
+ - "-ftemplate-backtrace-limit=0"
+ - "-std=c++17"
+ Remove:
+ # strip CUDA fatbin args
+ - "-Xfatbin*"
+ - "-Xcompiler*"
+ - "-Xcudafe*"
+ - "-rdc=*"
+ - "-gpu=*"
+ - "--diag_suppress*"
+ # strip CUDA arch flags
+ - "-gencode*"
+ - "--generate-code*"
+ # strip gcc's -fcoroutines
+ - -fcoroutines
+ # strip CUDA flags unknown to clang
+ - "-ccbin*"
+ - "--compiler-options*"
+ - "--expt-extended-lambda"
+ - "--expt-relaxed-constexpr"
+ - "-forward-unknown-to-host-compiler"
+ - "-Werror=cross-execution-space-call"
+Diagnostics:
+ Suppress:
+ - "variadic_device_fn"
+ - "attributes_not_allowed"
+ # The NVHPC version of _NVCXX_EXPAND_PACK macro triggers this clang error.
+ # Temporarily suppressing it, but should probably fix
+ - "template_param_shadow"
diff --git a/.devcontainer/README.md b/.devcontainer/README.md
new file mode 100644
index 00000000..e84b5f39
--- /dev/null
+++ b/.devcontainer/README.md
@@ -0,0 +1,198 @@
+> **Note**
+> The instructions in this README are specific to Linux development environments. Instructions for Windows are coming soon!
+
+[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/NVIDIA/cccl?quickstart=1&devcontainer_path=.devcontainer%2Fdevcontainer.json)
+
+# CCCL Dev Containers
+
+CCCL uses [Development Containers](https://containers.dev/) to provide consistent and convenient development environments for both local development and for CI. This guide covers setup in [Visual Studio Code](#quickstart-vscode-recommended) and [Docker](#quickstart-docker-manual-approach). The guide also provides additional instructions in case you want use WSL.
+
+## Table of Contents
+1. [Quickstart: VSCode (Recommended)](#vscode)
+2. [Quickstart: Docker (Manual Approach)](#docker)
+3. [Quickstart: Using WSL](#wsl)
+
+## Quickstart: VSCode (Recommended)
+
+### Prerequisites
+- [Visual Studio Code](https://code.visualstudio.com/)
+- [Remote - Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)
+- [NVIDIA Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html)
+- [Docker](https://docs.docker.com/engine/install/) - This is only for completeness because it should already be implicitly installed by the Dev Containers extension
+
+### Steps
+
+1. Clone the Repository
+ ```bash
+ git clone https://github.com/nvidia/cccl.git
+ ```
+2. Open the cloned directory in VSCode
+
+3. Launch a Dev Container by clicking the prompt suggesting to "Reopen in Container"
+
+ ![Shows "Reopen in Container" prompt when opening the cccl directory in VScode.](./img/reopen_in_container.png)
+
+ - Alternatively, use the Command Palette to start a Dev Container. Press `Ctrl+Shift+P` to open the Command Palette. Type "Remote-Containers: Reopen in Container" and select it.
+
+ ![Shows "Reopen in Container" in command pallete.](./img/open_in_container_manual.png)
+
+4. Select an environment with the desired CTK and host compiler from the list:
+
+ ![Shows list of available container environments.](./img/container_list.png)
+
+5. VSCode will initialize the selected Dev Container. This can take a few minutes the first time.
+
+6. Once initialized, the local `cccl/` directory is mirrored into the container to ensure any changes are persistent.
+
+7. Done! See the [contributing guide](../CONTRIBUTING.md#building-and-testing) for instructions on how to build and run tests.
+
+### (Optional) Authenticate with GitHub for `sccache`
+
+After starting the container, there will be a prompt to authenticate with GitHub. This grants access to a [`sccache`](https://github.com/mozilla/sccache) server shared with CI and greatly accelerates local build times. This is currently limited to NVIDIA employees belonging to the `NVIDIA` or `rapidsai` GitHub organizations.
+
+Without authentication to the remote server, `sccache` will still accelerate local builds by using a filesystem cache.
+
+Follow the instructions in the prompt as below and enter the one-time code at https://github.com/login/device
+
+ ![Shows authentication with GitHub to access sccache bucket.](./img/github_auth.png)
+
+To manually trigger this authentication, execute the `devcontainer-utils-vault-s3-init` script within the container.
+
+For more information about the sccache configuration and authentication, see the documentation at [`rapidsai/devcontainers`](https://github.com/rapidsai/devcontainers/blob/branch-23.10/USAGE.md#build-caching-with-sccache).
+
+## Quickstart: Docker (Manual Approach)
+
+### Prerequisites
+- [Docker](https://docs.docker.com/desktop/install/linux-install/)
+
+### Steps
+1. Clone the repository and use the [`launch.sh`](./launch.sh) script to launch the default container environment
+ ```bash
+ git clone https://github.com/nvidia/cccl.git
+ cd cccl
+ ./.devcontainer/launch.sh --docker
+ ```
+ This script starts an interactive shell as the `coder` user inside the container with the local `cccl/` directory mirrored into `/home/coder/cccl`.
+
+ For specific environments, use the `--cuda` and `--host` options:
+ ```bassh
+ ./.devcontainer/launch.sh --docker --cuda 12.2 --host gcc10
+ ```
+ See `./.devcontainer/launch.sh --help` for more information.
+
+2. Done. See the [contributing guide](../CONTRIBUTING.md#building-and-testing) for instructions on how to build and run tests.
+
+## Available Environments
+
+CCCL provides environments for both the oldest and newest supported CUDA versions with all compatible host compilers.
+
+Look in the [`.devcontainer/`](.) directory to see the available configurations. The top-level [`devcontainer.json`](./devcontainer.json) serves as the default environment. All `devcontainer.json` files in the `cuda-` sub-directories are variations on this top-level file, with different base images for the different CUDA and host compiler versions.
+
+## VSCode Customization
+
+By default, CCCL's Dev Containers come with certain VSCode settings and extensions configured by default, as can be seen in the [`devcontainer.json`](./devcontainer.json) file. This can be further customized by users without needing to modify the `devcontainer.json` file directly.
+
+For extensions, the [`dev.containers.defaultExtensions` setting](https://code.visualstudio.com/docs/devcontainers/containers#_always-installed-extensions) allows listing extensions that will always be installed.
+
+For more general customizations, VSCode allows using a dotfile repository. See the [VSCode documentation](https://code.visualstudio.com/docs/devcontainers/containers#_personalizing-with-dotfile-repositories) for more information.
+
+## GitHub Codespaces
+
+[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/NVIDIA/cccl?quickstart=1&devcontainer_path=.devcontainer%2Fdevcontainer.json)
+
+One of the benefits of Dev Containers is that they integrate natively with [GitHub Codespaces](https://github.com/features/codespaces). Codespaces provide a VSCode development environment right in your browser running on a machine in the cloud. This provides a truly one-click, turnkey development environment where you can develop, build, and test with no other setup required.
+
+Click the badge above or [click here](https://codespaces.new/NVIDIA/cccl?quickstart=1&devcontainer_path=.devcontainer%2Fdevcontainer.json) to get started with CCCL's Dev Containers on Codespaces. This will start the default Dev Container environment. [Click here](https://github.com/codespaces/new?hide_repo_select=true&ref=main&repo=296416761&skip_quickstart=true) to start a Codespace with a particular environment and hardware configuration as shown:
+
+ ![Shows configuring a Codespace with a custom environment](../docs/images/codespaces.png)
+
+## For Maintainers: The `make_devcontainers.sh` Script
+
+### Overview
+
+[`make_devcontainers.sh`](./make_devcontainers.sh) generates devcontainer configurations for the unique combinations of CUDA Toolkit (CTK) versions and host compilers in [`ci/matrix.yaml`](../ci/matrix.yaml).
+
+### How It Works:
+
+1. Parses the matrix from `ci/matrix.yaml`.
+2. Use the top-level [`.devcontainer/devcontainer.json`](./devcontainer.json) as a template. For each unique combination of CTK version and host compiler, generate a corresponding `devcontainer.json` configuration, adjusting only the base Docker image to match the desired environment.
+3. Place the generated configurations in the `.devcontainer` directory, organizing them into subdirectories following the naming convention `cuda-`.
+
+For more information, see the `.devcontainer/make_devcontainers.sh --help` message.
+
+**Note**: When adding or updating supported environments, modify `matrix.yaml` and then rerun this script to synchronize the `devcontainer` configurations.
+
+## Quickstart: Using WSL
+
+> [!NOTE]
+> _Make sure you have the Nvidia driver installed on your Windows host before moving further_. Type in `nvidia-smi` for verification.
+
+### Install WSL on your Windows host
+
+> [!WARNING]
+> Disclaimer: This guide was developed for WSL 2 on Windows 11.
+
+1. Launch a Windows terminal (_e.g. Powershell_) as an administrator.
+
+2. Install WSL 2 by running:
+```bash
+wsl --install
+```
+This should probably install Ubuntu distro as a default.
+
+3. Restart your computer and run `wsl -l -v` on a Windows terminal to verify installation.
+
+ Install prerequisites and VS Code extensions
+
+4. Launch your WSL/Ubuntu terminal by running `wsl` in Powershell.
+
+5. Install the [WSL extension](ms-vscode-remote.remote-wsl) on VS Code.
+
+ - `Ctrl + Shift + P` and select `WSL: Connect to WSL` (it will prompt you to install the WSL extension).
+
+ - Make sure you are connected to WSL with VS Code by checking the bottom left corner of the VS Code window (should indicate "WSL: Ubuntu" in our case).
+
+6. Install the [Dev Containers extension](ms-vscode-remote.remote-containers) on VS Code.
+
+ - In a vanilla system you should be prompted to install `Docker` at this point, accept it. If it hangs you might have to restart VS Code after that.
+
+7. Install the [NVIDIA Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html). **Make sure you install the WSL 2 version and not the native Linux one**. This builds on top of Docker so make sure you have Docker properly installed (run `docker --version`).
+
+8. Open `/etc/docker/daemon.json` from within your WSL system (if the file does not exist, create it) and add the following:
+
+```json
+{
+ "runtimes": {
+ "nvidia": {
+ "path": "nvidia-container-runtime",
+ "runtimeArgs": []
+ }
+ }
+}
+```
+
+then run `sudo systemctl restart docker.service`.
+
+---
+### Build CCCL in WSL using Dev Containers
+
+9. Still on your WSL terminal run `git clone https://github.com/NVIDIA/cccl.git`
+
+
+10. Open the CCCL cloned repo in VS Code ( `Ctrl + Shift + P `, select `File: Open Folder...` and select the path where your CCCL clone is located).
+
+11. If prompted, choose `Reopen in Container`.
+
+ - If you are not prompted just type `Ctrl + Shift + P` and `Dev Containers: Open Folder in Container ...`.
+
+12. Verify that Dev Container was configured properly by running `nvidia-smi` in your Dev Container terminal. For a proper configuration it is important for the steps in [Install prerequisites and VS Code extensions](#prereqs) to be followed in a precise order.
+
+From that point on, the guide aligns with our [existing Dev Containers native Linux guide](https://github.com/NVIDIA/cccl/blob/main/.devcontainer/README.md) with just one minor potential alteration:
+
+13. If WSL was launched without the X-server enabled, when asked to "authenticate Git with your Github credentials", if you answer **Yes**, the browser might not open automatically, with the following error message.
+
+> Failed opening a web browser at https://github.com/login/device
+ exec: "xdg-open,x-www-browser,www-browser,wslview": executable file not found in $PATH
+ Please try entering the URL in your browser manually
+
+In that case type in the address manually in your web browser https://github.com/login/device and fill in the one-time code.
diff --git a/.devcontainer/cuda11.1-gcc7/devcontainer.json b/.devcontainer/cuda11.1-gcc7/devcontainer.json
new file mode 100644
index 00000000..3e9e2f70
--- /dev/null
+++ b/.devcontainer/cuda11.1-gcc7/devcontainer.json
@@ -0,0 +1,46 @@
+{
+ "shutdownAction": "stopContainer",
+ "image": "rapidsai/devcontainers:24.06-cpp-gcc7-cuda11.1-ubuntu18.04",
+ "hostRequirements": {
+ "gpu": "optional"
+ },
+ "initializeCommand": [
+ "/bin/bash",
+ "-c",
+ "mkdir -m 0755 -p ${localWorkspaceFolder}/.{aws,cache,config}"
+ ],
+ "containerEnv": {
+ "SCCACHE_REGION": "us-east-2",
+ "SCCACHE_BUCKET": "rapids-sccache-devs",
+ "VAULT_HOST": "https://vault.ops.k8s.rapids.ai",
+ "HISTFILE": "${containerWorkspaceFolder}/.cache/._bash_history",
+ "DEVCONTAINER_NAME": "cuda11.1-gcc7",
+ "CCCL_CUDA_VERSION": "11.1",
+ "CCCL_HOST_COMPILER": "gcc",
+ "CCCL_HOST_COMPILER_VERSION": "7",
+ "CCCL_BUILD_INFIX": "cuda11.1-gcc7"
+ },
+ "workspaceFolder": "/home/coder/${localWorkspaceFolderBasename}",
+ "workspaceMount": "source=${localWorkspaceFolder},target=/home/coder/${localWorkspaceFolderBasename},type=bind,consistency=consistent",
+ "mounts": [
+ "source=${localWorkspaceFolder}/.aws,target=/home/coder/.aws,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.cache,target=/home/coder/.cache,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.config,target=/home/coder/.config,type=bind,consistency=consistent"
+ ],
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "llvm-vs-code-extensions.vscode-clangd",
+ "xaver.clang-format"
+ ],
+ "settings": {
+ "editor.defaultFormatter": "xaver.clang-format",
+ "clang-format.executable": "/usr/local/bin/clang-format",
+ "clangd.arguments": [
+ "--compile-commands-dir=${workspaceFolder}"
+ ]
+ }
+ }
+ },
+ "name": "cuda11.1-gcc7"
+}
diff --git a/.devcontainer/cuda11.1-gcc8/devcontainer.json b/.devcontainer/cuda11.1-gcc8/devcontainer.json
new file mode 100644
index 00000000..3862680f
--- /dev/null
+++ b/.devcontainer/cuda11.1-gcc8/devcontainer.json
@@ -0,0 +1,46 @@
+{
+ "shutdownAction": "stopContainer",
+ "image": "rapidsai/devcontainers:24.06-cpp-gcc8-cuda11.1-ubuntu18.04",
+ "hostRequirements": {
+ "gpu": "optional"
+ },
+ "initializeCommand": [
+ "/bin/bash",
+ "-c",
+ "mkdir -m 0755 -p ${localWorkspaceFolder}/.{aws,cache,config}"
+ ],
+ "containerEnv": {
+ "SCCACHE_REGION": "us-east-2",
+ "SCCACHE_BUCKET": "rapids-sccache-devs",
+ "VAULT_HOST": "https://vault.ops.k8s.rapids.ai",
+ "HISTFILE": "${containerWorkspaceFolder}/.cache/._bash_history",
+ "DEVCONTAINER_NAME": "cuda11.1-gcc8",
+ "CCCL_CUDA_VERSION": "11.1",
+ "CCCL_HOST_COMPILER": "gcc",
+ "CCCL_HOST_COMPILER_VERSION": "8",
+ "CCCL_BUILD_INFIX": "cuda11.1-gcc8"
+ },
+ "workspaceFolder": "/home/coder/${localWorkspaceFolderBasename}",
+ "workspaceMount": "source=${localWorkspaceFolder},target=/home/coder/${localWorkspaceFolderBasename},type=bind,consistency=consistent",
+ "mounts": [
+ "source=${localWorkspaceFolder}/.aws,target=/home/coder/.aws,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.cache,target=/home/coder/.cache,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.config,target=/home/coder/.config,type=bind,consistency=consistent"
+ ],
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "llvm-vs-code-extensions.vscode-clangd",
+ "xaver.clang-format"
+ ],
+ "settings": {
+ "editor.defaultFormatter": "xaver.clang-format",
+ "clang-format.executable": "/usr/local/bin/clang-format",
+ "clangd.arguments": [
+ "--compile-commands-dir=${workspaceFolder}"
+ ]
+ }
+ }
+ },
+ "name": "cuda11.1-gcc8"
+}
diff --git a/.devcontainer/cuda11.1-gcc9/devcontainer.json b/.devcontainer/cuda11.1-gcc9/devcontainer.json
new file mode 100644
index 00000000..54f7b888
--- /dev/null
+++ b/.devcontainer/cuda11.1-gcc9/devcontainer.json
@@ -0,0 +1,46 @@
+{
+ "shutdownAction": "stopContainer",
+ "image": "rapidsai/devcontainers:24.06-cpp-gcc9-cuda11.1-ubuntu18.04",
+ "hostRequirements": {
+ "gpu": "optional"
+ },
+ "initializeCommand": [
+ "/bin/bash",
+ "-c",
+ "mkdir -m 0755 -p ${localWorkspaceFolder}/.{aws,cache,config}"
+ ],
+ "containerEnv": {
+ "SCCACHE_REGION": "us-east-2",
+ "SCCACHE_BUCKET": "rapids-sccache-devs",
+ "VAULT_HOST": "https://vault.ops.k8s.rapids.ai",
+ "HISTFILE": "${containerWorkspaceFolder}/.cache/._bash_history",
+ "DEVCONTAINER_NAME": "cuda11.1-gcc9",
+ "CCCL_CUDA_VERSION": "11.1",
+ "CCCL_HOST_COMPILER": "gcc",
+ "CCCL_HOST_COMPILER_VERSION": "9",
+ "CCCL_BUILD_INFIX": "cuda11.1-gcc9"
+ },
+ "workspaceFolder": "/home/coder/${localWorkspaceFolderBasename}",
+ "workspaceMount": "source=${localWorkspaceFolder},target=/home/coder/${localWorkspaceFolderBasename},type=bind,consistency=consistent",
+ "mounts": [
+ "source=${localWorkspaceFolder}/.aws,target=/home/coder/.aws,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.cache,target=/home/coder/.cache,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.config,target=/home/coder/.config,type=bind,consistency=consistent"
+ ],
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "llvm-vs-code-extensions.vscode-clangd",
+ "xaver.clang-format"
+ ],
+ "settings": {
+ "editor.defaultFormatter": "xaver.clang-format",
+ "clang-format.executable": "/usr/local/bin/clang-format",
+ "clangd.arguments": [
+ "--compile-commands-dir=${workspaceFolder}"
+ ]
+ }
+ }
+ },
+ "name": "cuda11.1-gcc9"
+}
diff --git a/.devcontainer/cuda11.1-llvm9/devcontainer.json b/.devcontainer/cuda11.1-llvm9/devcontainer.json
new file mode 100644
index 00000000..d875c9b6
--- /dev/null
+++ b/.devcontainer/cuda11.1-llvm9/devcontainer.json
@@ -0,0 +1,46 @@
+{
+ "shutdownAction": "stopContainer",
+ "image": "rapidsai/devcontainers:24.06-cpp-llvm9-cuda11.1-ubuntu18.04",
+ "hostRequirements": {
+ "gpu": "optional"
+ },
+ "initializeCommand": [
+ "/bin/bash",
+ "-c",
+ "mkdir -m 0755 -p ${localWorkspaceFolder}/.{aws,cache,config}"
+ ],
+ "containerEnv": {
+ "SCCACHE_REGION": "us-east-2",
+ "SCCACHE_BUCKET": "rapids-sccache-devs",
+ "VAULT_HOST": "https://vault.ops.k8s.rapids.ai",
+ "HISTFILE": "${containerWorkspaceFolder}/.cache/._bash_history",
+ "DEVCONTAINER_NAME": "cuda11.1-llvm9",
+ "CCCL_CUDA_VERSION": "11.1",
+ "CCCL_HOST_COMPILER": "llvm",
+ "CCCL_HOST_COMPILER_VERSION": "9",
+ "CCCL_BUILD_INFIX": "cuda11.1-llvm9"
+ },
+ "workspaceFolder": "/home/coder/${localWorkspaceFolderBasename}",
+ "workspaceMount": "source=${localWorkspaceFolder},target=/home/coder/${localWorkspaceFolderBasename},type=bind,consistency=consistent",
+ "mounts": [
+ "source=${localWorkspaceFolder}/.aws,target=/home/coder/.aws,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.cache,target=/home/coder/.cache,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.config,target=/home/coder/.config,type=bind,consistency=consistent"
+ ],
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "llvm-vs-code-extensions.vscode-clangd",
+ "xaver.clang-format"
+ ],
+ "settings": {
+ "editor.defaultFormatter": "xaver.clang-format",
+ "clang-format.executable": "/usr/local/bin/clang-format",
+ "clangd.arguments": [
+ "--compile-commands-dir=${workspaceFolder}"
+ ]
+ }
+ }
+ },
+ "name": "cuda11.1-llvm9"
+}
diff --git a/.devcontainer/cuda11.8-gcc11/devcontainer.json b/.devcontainer/cuda11.8-gcc11/devcontainer.json
new file mode 100644
index 00000000..389d6c7f
--- /dev/null
+++ b/.devcontainer/cuda11.8-gcc11/devcontainer.json
@@ -0,0 +1,46 @@
+{
+ "shutdownAction": "stopContainer",
+ "image": "rapidsai/devcontainers:24.06-cpp-gcc11-cuda11.8-ubuntu22.04",
+ "hostRequirements": {
+ "gpu": "optional"
+ },
+ "initializeCommand": [
+ "/bin/bash",
+ "-c",
+ "mkdir -m 0755 -p ${localWorkspaceFolder}/.{aws,cache,config}"
+ ],
+ "containerEnv": {
+ "SCCACHE_REGION": "us-east-2",
+ "SCCACHE_BUCKET": "rapids-sccache-devs",
+ "VAULT_HOST": "https://vault.ops.k8s.rapids.ai",
+ "HISTFILE": "${containerWorkspaceFolder}/.cache/._bash_history",
+ "DEVCONTAINER_NAME": "cuda11.8-gcc11",
+ "CCCL_CUDA_VERSION": "11.8",
+ "CCCL_HOST_COMPILER": "gcc",
+ "CCCL_HOST_COMPILER_VERSION": "11",
+ "CCCL_BUILD_INFIX": "cuda11.8-gcc11"
+ },
+ "workspaceFolder": "/home/coder/${localWorkspaceFolderBasename}",
+ "workspaceMount": "source=${localWorkspaceFolder},target=/home/coder/${localWorkspaceFolderBasename},type=bind,consistency=consistent",
+ "mounts": [
+ "source=${localWorkspaceFolder}/.aws,target=/home/coder/.aws,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.cache,target=/home/coder/.cache,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.config,target=/home/coder/.config,type=bind,consistency=consistent"
+ ],
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "llvm-vs-code-extensions.vscode-clangd",
+ "xaver.clang-format"
+ ],
+ "settings": {
+ "editor.defaultFormatter": "xaver.clang-format",
+ "clang-format.executable": "/usr/local/bin/clang-format",
+ "clangd.arguments": [
+ "--compile-commands-dir=${workspaceFolder}"
+ ]
+ }
+ }
+ },
+ "name": "cuda11.8-gcc11"
+}
diff --git a/.devcontainer/cuda12.0-gcc10/devcontainer.json b/.devcontainer/cuda12.0-gcc10/devcontainer.json
new file mode 100644
index 00000000..eabc73fc
--- /dev/null
+++ b/.devcontainer/cuda12.0-gcc10/devcontainer.json
@@ -0,0 +1,46 @@
+{
+ "shutdownAction": "stopContainer",
+ "image": "rapidsai/devcontainers:24.06-cpp-gcc10-cuda12.0-ubuntu20.04",
+ "hostRequirements": {
+ "gpu": "optional"
+ },
+ "initializeCommand": [
+ "/bin/bash",
+ "-c",
+ "mkdir -m 0755 -p ${localWorkspaceFolder}/.{aws,cache,config}"
+ ],
+ "containerEnv": {
+ "SCCACHE_REGION": "us-east-2",
+ "SCCACHE_BUCKET": "rapids-sccache-devs",
+ "VAULT_HOST": "https://vault.ops.k8s.rapids.ai",
+ "HISTFILE": "${containerWorkspaceFolder}/.cache/._bash_history",
+ "DEVCONTAINER_NAME": "cuda12.0-gcc10",
+ "CCCL_CUDA_VERSION": "12.0",
+ "CCCL_HOST_COMPILER": "gcc",
+ "CCCL_HOST_COMPILER_VERSION": "10",
+ "CCCL_BUILD_INFIX": "cuda12.0-gcc10"
+ },
+ "workspaceFolder": "/home/coder/${localWorkspaceFolderBasename}",
+ "workspaceMount": "source=${localWorkspaceFolder},target=/home/coder/${localWorkspaceFolderBasename},type=bind,consistency=consistent",
+ "mounts": [
+ "source=${localWorkspaceFolder}/.aws,target=/home/coder/.aws,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.cache,target=/home/coder/.cache,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.config,target=/home/coder/.config,type=bind,consistency=consistent"
+ ],
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "llvm-vs-code-extensions.vscode-clangd",
+ "xaver.clang-format"
+ ],
+ "settings": {
+ "editor.defaultFormatter": "xaver.clang-format",
+ "clang-format.executable": "/usr/local/bin/clang-format",
+ "clangd.arguments": [
+ "--compile-commands-dir=${workspaceFolder}"
+ ]
+ }
+ }
+ },
+ "name": "cuda12.0-gcc10"
+}
diff --git a/.devcontainer/cuda12.0-gcc11/devcontainer.json b/.devcontainer/cuda12.0-gcc11/devcontainer.json
new file mode 100644
index 00000000..df5eddb0
--- /dev/null
+++ b/.devcontainer/cuda12.0-gcc11/devcontainer.json
@@ -0,0 +1,46 @@
+{
+ "shutdownAction": "stopContainer",
+ "image": "rapidsai/devcontainers:24.06-cpp-gcc11-cuda12.0-ubuntu22.04",
+ "hostRequirements": {
+ "gpu": "optional"
+ },
+ "initializeCommand": [
+ "/bin/bash",
+ "-c",
+ "mkdir -m 0755 -p ${localWorkspaceFolder}/.{aws,cache,config}"
+ ],
+ "containerEnv": {
+ "SCCACHE_REGION": "us-east-2",
+ "SCCACHE_BUCKET": "rapids-sccache-devs",
+ "VAULT_HOST": "https://vault.ops.k8s.rapids.ai",
+ "HISTFILE": "${containerWorkspaceFolder}/.cache/._bash_history",
+ "DEVCONTAINER_NAME": "cuda12.0-gcc11",
+ "CCCL_CUDA_VERSION": "12.0",
+ "CCCL_HOST_COMPILER": "gcc",
+ "CCCL_HOST_COMPILER_VERSION": "11",
+ "CCCL_BUILD_INFIX": "cuda12.0-gcc11"
+ },
+ "workspaceFolder": "/home/coder/${localWorkspaceFolderBasename}",
+ "workspaceMount": "source=${localWorkspaceFolder},target=/home/coder/${localWorkspaceFolderBasename},type=bind,consistency=consistent",
+ "mounts": [
+ "source=${localWorkspaceFolder}/.aws,target=/home/coder/.aws,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.cache,target=/home/coder/.cache,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.config,target=/home/coder/.config,type=bind,consistency=consistent"
+ ],
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "llvm-vs-code-extensions.vscode-clangd",
+ "xaver.clang-format"
+ ],
+ "settings": {
+ "editor.defaultFormatter": "xaver.clang-format",
+ "clang-format.executable": "/usr/local/bin/clang-format",
+ "clangd.arguments": [
+ "--compile-commands-dir=${workspaceFolder}"
+ ]
+ }
+ }
+ },
+ "name": "cuda12.0-gcc11"
+}
diff --git a/.devcontainer/cuda12.0-gcc12/devcontainer.json b/.devcontainer/cuda12.0-gcc12/devcontainer.json
new file mode 100644
index 00000000..b0d38aa5
--- /dev/null
+++ b/.devcontainer/cuda12.0-gcc12/devcontainer.json
@@ -0,0 +1,46 @@
+{
+ "shutdownAction": "stopContainer",
+ "image": "rapidsai/devcontainers:24.06-cpp-gcc12-cuda12.0-ubuntu22.04",
+ "hostRequirements": {
+ "gpu": "optional"
+ },
+ "initializeCommand": [
+ "/bin/bash",
+ "-c",
+ "mkdir -m 0755 -p ${localWorkspaceFolder}/.{aws,cache,config}"
+ ],
+ "containerEnv": {
+ "SCCACHE_REGION": "us-east-2",
+ "SCCACHE_BUCKET": "rapids-sccache-devs",
+ "VAULT_HOST": "https://vault.ops.k8s.rapids.ai",
+ "HISTFILE": "${containerWorkspaceFolder}/.cache/._bash_history",
+ "DEVCONTAINER_NAME": "cuda12.0-gcc12",
+ "CCCL_CUDA_VERSION": "12.0",
+ "CCCL_HOST_COMPILER": "gcc",
+ "CCCL_HOST_COMPILER_VERSION": "12",
+ "CCCL_BUILD_INFIX": "cuda12.0-gcc12"
+ },
+ "workspaceFolder": "/home/coder/${localWorkspaceFolderBasename}",
+ "workspaceMount": "source=${localWorkspaceFolder},target=/home/coder/${localWorkspaceFolderBasename},type=bind,consistency=consistent",
+ "mounts": [
+ "source=${localWorkspaceFolder}/.aws,target=/home/coder/.aws,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.cache,target=/home/coder/.cache,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.config,target=/home/coder/.config,type=bind,consistency=consistent"
+ ],
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "llvm-vs-code-extensions.vscode-clangd",
+ "xaver.clang-format"
+ ],
+ "settings": {
+ "editor.defaultFormatter": "xaver.clang-format",
+ "clang-format.executable": "/usr/local/bin/clang-format",
+ "clangd.arguments": [
+ "--compile-commands-dir=${workspaceFolder}"
+ ]
+ }
+ }
+ },
+ "name": "cuda12.0-gcc12"
+}
diff --git a/.devcontainer/cuda12.0-gcc7/devcontainer.json b/.devcontainer/cuda12.0-gcc7/devcontainer.json
new file mode 100644
index 00000000..f62bc14a
--- /dev/null
+++ b/.devcontainer/cuda12.0-gcc7/devcontainer.json
@@ -0,0 +1,46 @@
+{
+ "shutdownAction": "stopContainer",
+ "image": "rapidsai/devcontainers:24.06-cpp-gcc7-cuda12.0-ubuntu20.04",
+ "hostRequirements": {
+ "gpu": "optional"
+ },
+ "initializeCommand": [
+ "/bin/bash",
+ "-c",
+ "mkdir -m 0755 -p ${localWorkspaceFolder}/.{aws,cache,config}"
+ ],
+ "containerEnv": {
+ "SCCACHE_REGION": "us-east-2",
+ "SCCACHE_BUCKET": "rapids-sccache-devs",
+ "VAULT_HOST": "https://vault.ops.k8s.rapids.ai",
+ "HISTFILE": "${containerWorkspaceFolder}/.cache/._bash_history",
+ "DEVCONTAINER_NAME": "cuda12.0-gcc7",
+ "CCCL_CUDA_VERSION": "12.0",
+ "CCCL_HOST_COMPILER": "gcc",
+ "CCCL_HOST_COMPILER_VERSION": "7",
+ "CCCL_BUILD_INFIX": "cuda12.0-gcc7"
+ },
+ "workspaceFolder": "/home/coder/${localWorkspaceFolderBasename}",
+ "workspaceMount": "source=${localWorkspaceFolder},target=/home/coder/${localWorkspaceFolderBasename},type=bind,consistency=consistent",
+ "mounts": [
+ "source=${localWorkspaceFolder}/.aws,target=/home/coder/.aws,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.cache,target=/home/coder/.cache,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.config,target=/home/coder/.config,type=bind,consistency=consistent"
+ ],
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "llvm-vs-code-extensions.vscode-clangd",
+ "xaver.clang-format"
+ ],
+ "settings": {
+ "editor.defaultFormatter": "xaver.clang-format",
+ "clang-format.executable": "/usr/local/bin/clang-format",
+ "clangd.arguments": [
+ "--compile-commands-dir=${workspaceFolder}"
+ ]
+ }
+ }
+ },
+ "name": "cuda12.0-gcc7"
+}
diff --git a/.devcontainer/cuda12.0-gcc8/devcontainer.json b/.devcontainer/cuda12.0-gcc8/devcontainer.json
new file mode 100644
index 00000000..10b3397f
--- /dev/null
+++ b/.devcontainer/cuda12.0-gcc8/devcontainer.json
@@ -0,0 +1,46 @@
+{
+ "shutdownAction": "stopContainer",
+ "image": "rapidsai/devcontainers:24.06-cpp-gcc8-cuda12.0-ubuntu20.04",
+ "hostRequirements": {
+ "gpu": "optional"
+ },
+ "initializeCommand": [
+ "/bin/bash",
+ "-c",
+ "mkdir -m 0755 -p ${localWorkspaceFolder}/.{aws,cache,config}"
+ ],
+ "containerEnv": {
+ "SCCACHE_REGION": "us-east-2",
+ "SCCACHE_BUCKET": "rapids-sccache-devs",
+ "VAULT_HOST": "https://vault.ops.k8s.rapids.ai",
+ "HISTFILE": "${containerWorkspaceFolder}/.cache/._bash_history",
+ "DEVCONTAINER_NAME": "cuda12.0-gcc8",
+ "CCCL_CUDA_VERSION": "12.0",
+ "CCCL_HOST_COMPILER": "gcc",
+ "CCCL_HOST_COMPILER_VERSION": "8",
+ "CCCL_BUILD_INFIX": "cuda12.0-gcc8"
+ },
+ "workspaceFolder": "/home/coder/${localWorkspaceFolderBasename}",
+ "workspaceMount": "source=${localWorkspaceFolder},target=/home/coder/${localWorkspaceFolderBasename},type=bind,consistency=consistent",
+ "mounts": [
+ "source=${localWorkspaceFolder}/.aws,target=/home/coder/.aws,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.cache,target=/home/coder/.cache,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.config,target=/home/coder/.config,type=bind,consistency=consistent"
+ ],
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "llvm-vs-code-extensions.vscode-clangd",
+ "xaver.clang-format"
+ ],
+ "settings": {
+ "editor.defaultFormatter": "xaver.clang-format",
+ "clang-format.executable": "/usr/local/bin/clang-format",
+ "clangd.arguments": [
+ "--compile-commands-dir=${workspaceFolder}"
+ ]
+ }
+ }
+ },
+ "name": "cuda12.0-gcc8"
+}
diff --git a/.devcontainer/cuda12.0-gcc9/devcontainer.json b/.devcontainer/cuda12.0-gcc9/devcontainer.json
new file mode 100644
index 00000000..d739f584
--- /dev/null
+++ b/.devcontainer/cuda12.0-gcc9/devcontainer.json
@@ -0,0 +1,46 @@
+{
+ "shutdownAction": "stopContainer",
+ "image": "rapidsai/devcontainers:24.06-cpp-gcc9-cuda12.0-ubuntu20.04",
+ "hostRequirements": {
+ "gpu": "optional"
+ },
+ "initializeCommand": [
+ "/bin/bash",
+ "-c",
+ "mkdir -m 0755 -p ${localWorkspaceFolder}/.{aws,cache,config}"
+ ],
+ "containerEnv": {
+ "SCCACHE_REGION": "us-east-2",
+ "SCCACHE_BUCKET": "rapids-sccache-devs",
+ "VAULT_HOST": "https://vault.ops.k8s.rapids.ai",
+ "HISTFILE": "${containerWorkspaceFolder}/.cache/._bash_history",
+ "DEVCONTAINER_NAME": "cuda12.0-gcc9",
+ "CCCL_CUDA_VERSION": "12.0",
+ "CCCL_HOST_COMPILER": "gcc",
+ "CCCL_HOST_COMPILER_VERSION": "9",
+ "CCCL_BUILD_INFIX": "cuda12.0-gcc9"
+ },
+ "workspaceFolder": "/home/coder/${localWorkspaceFolderBasename}",
+ "workspaceMount": "source=${localWorkspaceFolder},target=/home/coder/${localWorkspaceFolderBasename},type=bind,consistency=consistent",
+ "mounts": [
+ "source=${localWorkspaceFolder}/.aws,target=/home/coder/.aws,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.cache,target=/home/coder/.cache,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.config,target=/home/coder/.config,type=bind,consistency=consistent"
+ ],
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "llvm-vs-code-extensions.vscode-clangd",
+ "xaver.clang-format"
+ ],
+ "settings": {
+ "editor.defaultFormatter": "xaver.clang-format",
+ "clang-format.executable": "/usr/local/bin/clang-format",
+ "clangd.arguments": [
+ "--compile-commands-dir=${workspaceFolder}"
+ ]
+ }
+ }
+ },
+ "name": "cuda12.0-gcc9"
+}
diff --git a/.devcontainer/cuda12.0-llvm10/devcontainer.json b/.devcontainer/cuda12.0-llvm10/devcontainer.json
new file mode 100644
index 00000000..2ecdfde2
--- /dev/null
+++ b/.devcontainer/cuda12.0-llvm10/devcontainer.json
@@ -0,0 +1,46 @@
+{
+ "shutdownAction": "stopContainer",
+ "image": "rapidsai/devcontainers:24.06-cpp-llvm10-cuda12.0-ubuntu20.04",
+ "hostRequirements": {
+ "gpu": "optional"
+ },
+ "initializeCommand": [
+ "/bin/bash",
+ "-c",
+ "mkdir -m 0755 -p ${localWorkspaceFolder}/.{aws,cache,config}"
+ ],
+ "containerEnv": {
+ "SCCACHE_REGION": "us-east-2",
+ "SCCACHE_BUCKET": "rapids-sccache-devs",
+ "VAULT_HOST": "https://vault.ops.k8s.rapids.ai",
+ "HISTFILE": "${containerWorkspaceFolder}/.cache/._bash_history",
+ "DEVCONTAINER_NAME": "cuda12.0-llvm10",
+ "CCCL_CUDA_VERSION": "12.0",
+ "CCCL_HOST_COMPILER": "llvm",
+ "CCCL_HOST_COMPILER_VERSION": "10",
+ "CCCL_BUILD_INFIX": "cuda12.0-llvm10"
+ },
+ "workspaceFolder": "/home/coder/${localWorkspaceFolderBasename}",
+ "workspaceMount": "source=${localWorkspaceFolder},target=/home/coder/${localWorkspaceFolderBasename},type=bind,consistency=consistent",
+ "mounts": [
+ "source=${localWorkspaceFolder}/.aws,target=/home/coder/.aws,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.cache,target=/home/coder/.cache,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.config,target=/home/coder/.config,type=bind,consistency=consistent"
+ ],
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "llvm-vs-code-extensions.vscode-clangd",
+ "xaver.clang-format"
+ ],
+ "settings": {
+ "editor.defaultFormatter": "xaver.clang-format",
+ "clang-format.executable": "/usr/local/bin/clang-format",
+ "clangd.arguments": [
+ "--compile-commands-dir=${workspaceFolder}"
+ ]
+ }
+ }
+ },
+ "name": "cuda12.0-llvm10"
+}
diff --git a/.devcontainer/cuda12.0-llvm11/devcontainer.json b/.devcontainer/cuda12.0-llvm11/devcontainer.json
new file mode 100644
index 00000000..69d3cd1a
--- /dev/null
+++ b/.devcontainer/cuda12.0-llvm11/devcontainer.json
@@ -0,0 +1,46 @@
+{
+ "shutdownAction": "stopContainer",
+ "image": "rapidsai/devcontainers:24.06-cpp-llvm11-cuda12.0-ubuntu20.04",
+ "hostRequirements": {
+ "gpu": "optional"
+ },
+ "initializeCommand": [
+ "/bin/bash",
+ "-c",
+ "mkdir -m 0755 -p ${localWorkspaceFolder}/.{aws,cache,config}"
+ ],
+ "containerEnv": {
+ "SCCACHE_REGION": "us-east-2",
+ "SCCACHE_BUCKET": "rapids-sccache-devs",
+ "VAULT_HOST": "https://vault.ops.k8s.rapids.ai",
+ "HISTFILE": "${containerWorkspaceFolder}/.cache/._bash_history",
+ "DEVCONTAINER_NAME": "cuda12.0-llvm11",
+ "CCCL_CUDA_VERSION": "12.0",
+ "CCCL_HOST_COMPILER": "llvm",
+ "CCCL_HOST_COMPILER_VERSION": "11",
+ "CCCL_BUILD_INFIX": "cuda12.0-llvm11"
+ },
+ "workspaceFolder": "/home/coder/${localWorkspaceFolderBasename}",
+ "workspaceMount": "source=${localWorkspaceFolder},target=/home/coder/${localWorkspaceFolderBasename},type=bind,consistency=consistent",
+ "mounts": [
+ "source=${localWorkspaceFolder}/.aws,target=/home/coder/.aws,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.cache,target=/home/coder/.cache,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.config,target=/home/coder/.config,type=bind,consistency=consistent"
+ ],
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "llvm-vs-code-extensions.vscode-clangd",
+ "xaver.clang-format"
+ ],
+ "settings": {
+ "editor.defaultFormatter": "xaver.clang-format",
+ "clang-format.executable": "/usr/local/bin/clang-format",
+ "clangd.arguments": [
+ "--compile-commands-dir=${workspaceFolder}"
+ ]
+ }
+ }
+ },
+ "name": "cuda12.0-llvm11"
+}
diff --git a/.devcontainer/cuda12.0-llvm12/devcontainer.json b/.devcontainer/cuda12.0-llvm12/devcontainer.json
new file mode 100644
index 00000000..438cd8f4
--- /dev/null
+++ b/.devcontainer/cuda12.0-llvm12/devcontainer.json
@@ -0,0 +1,46 @@
+{
+ "shutdownAction": "stopContainer",
+ "image": "rapidsai/devcontainers:24.06-cpp-llvm12-cuda12.0-ubuntu20.04",
+ "hostRequirements": {
+ "gpu": "optional"
+ },
+ "initializeCommand": [
+ "/bin/bash",
+ "-c",
+ "mkdir -m 0755 -p ${localWorkspaceFolder}/.{aws,cache,config}"
+ ],
+ "containerEnv": {
+ "SCCACHE_REGION": "us-east-2",
+ "SCCACHE_BUCKET": "rapids-sccache-devs",
+ "VAULT_HOST": "https://vault.ops.k8s.rapids.ai",
+ "HISTFILE": "${containerWorkspaceFolder}/.cache/._bash_history",
+ "DEVCONTAINER_NAME": "cuda12.0-llvm12",
+ "CCCL_CUDA_VERSION": "12.0",
+ "CCCL_HOST_COMPILER": "llvm",
+ "CCCL_HOST_COMPILER_VERSION": "12",
+ "CCCL_BUILD_INFIX": "cuda12.0-llvm12"
+ },
+ "workspaceFolder": "/home/coder/${localWorkspaceFolderBasename}",
+ "workspaceMount": "source=${localWorkspaceFolder},target=/home/coder/${localWorkspaceFolderBasename},type=bind,consistency=consistent",
+ "mounts": [
+ "source=${localWorkspaceFolder}/.aws,target=/home/coder/.aws,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.cache,target=/home/coder/.cache,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.config,target=/home/coder/.config,type=bind,consistency=consistent"
+ ],
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "llvm-vs-code-extensions.vscode-clangd",
+ "xaver.clang-format"
+ ],
+ "settings": {
+ "editor.defaultFormatter": "xaver.clang-format",
+ "clang-format.executable": "/usr/local/bin/clang-format",
+ "clangd.arguments": [
+ "--compile-commands-dir=${workspaceFolder}"
+ ]
+ }
+ }
+ },
+ "name": "cuda12.0-llvm12"
+}
diff --git a/.devcontainer/cuda12.0-llvm13/devcontainer.json b/.devcontainer/cuda12.0-llvm13/devcontainer.json
new file mode 100644
index 00000000..a91fe042
--- /dev/null
+++ b/.devcontainer/cuda12.0-llvm13/devcontainer.json
@@ -0,0 +1,46 @@
+{
+ "shutdownAction": "stopContainer",
+ "image": "rapidsai/devcontainers:24.06-cpp-llvm13-cuda12.0-ubuntu20.04",
+ "hostRequirements": {
+ "gpu": "optional"
+ },
+ "initializeCommand": [
+ "/bin/bash",
+ "-c",
+ "mkdir -m 0755 -p ${localWorkspaceFolder}/.{aws,cache,config}"
+ ],
+ "containerEnv": {
+ "SCCACHE_REGION": "us-east-2",
+ "SCCACHE_BUCKET": "rapids-sccache-devs",
+ "VAULT_HOST": "https://vault.ops.k8s.rapids.ai",
+ "HISTFILE": "${containerWorkspaceFolder}/.cache/._bash_history",
+ "DEVCONTAINER_NAME": "cuda12.0-llvm13",
+ "CCCL_CUDA_VERSION": "12.0",
+ "CCCL_HOST_COMPILER": "llvm",
+ "CCCL_HOST_COMPILER_VERSION": "13",
+ "CCCL_BUILD_INFIX": "cuda12.0-llvm13"
+ },
+ "workspaceFolder": "/home/coder/${localWorkspaceFolderBasename}",
+ "workspaceMount": "source=${localWorkspaceFolder},target=/home/coder/${localWorkspaceFolderBasename},type=bind,consistency=consistent",
+ "mounts": [
+ "source=${localWorkspaceFolder}/.aws,target=/home/coder/.aws,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.cache,target=/home/coder/.cache,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.config,target=/home/coder/.config,type=bind,consistency=consistent"
+ ],
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "llvm-vs-code-extensions.vscode-clangd",
+ "xaver.clang-format"
+ ],
+ "settings": {
+ "editor.defaultFormatter": "xaver.clang-format",
+ "clang-format.executable": "/usr/local/bin/clang-format",
+ "clangd.arguments": [
+ "--compile-commands-dir=${workspaceFolder}"
+ ]
+ }
+ }
+ },
+ "name": "cuda12.0-llvm13"
+}
diff --git a/.devcontainer/cuda12.0-llvm14/devcontainer.json b/.devcontainer/cuda12.0-llvm14/devcontainer.json
new file mode 100644
index 00000000..18b91b2d
--- /dev/null
+++ b/.devcontainer/cuda12.0-llvm14/devcontainer.json
@@ -0,0 +1,46 @@
+{
+ "shutdownAction": "stopContainer",
+ "image": "rapidsai/devcontainers:24.06-cpp-llvm14-cuda12.0-ubuntu20.04",
+ "hostRequirements": {
+ "gpu": "optional"
+ },
+ "initializeCommand": [
+ "/bin/bash",
+ "-c",
+ "mkdir -m 0755 -p ${localWorkspaceFolder}/.{aws,cache,config}"
+ ],
+ "containerEnv": {
+ "SCCACHE_REGION": "us-east-2",
+ "SCCACHE_BUCKET": "rapids-sccache-devs",
+ "VAULT_HOST": "https://vault.ops.k8s.rapids.ai",
+ "HISTFILE": "${containerWorkspaceFolder}/.cache/._bash_history",
+ "DEVCONTAINER_NAME": "cuda12.0-llvm14",
+ "CCCL_CUDA_VERSION": "12.0",
+ "CCCL_HOST_COMPILER": "llvm",
+ "CCCL_HOST_COMPILER_VERSION": "14",
+ "CCCL_BUILD_INFIX": "cuda12.0-llvm14"
+ },
+ "workspaceFolder": "/home/coder/${localWorkspaceFolderBasename}",
+ "workspaceMount": "source=${localWorkspaceFolder},target=/home/coder/${localWorkspaceFolderBasename},type=bind,consistency=consistent",
+ "mounts": [
+ "source=${localWorkspaceFolder}/.aws,target=/home/coder/.aws,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.cache,target=/home/coder/.cache,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.config,target=/home/coder/.config,type=bind,consistency=consistent"
+ ],
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "llvm-vs-code-extensions.vscode-clangd",
+ "xaver.clang-format"
+ ],
+ "settings": {
+ "editor.defaultFormatter": "xaver.clang-format",
+ "clang-format.executable": "/usr/local/bin/clang-format",
+ "clangd.arguments": [
+ "--compile-commands-dir=${workspaceFolder}"
+ ]
+ }
+ }
+ },
+ "name": "cuda12.0-llvm14"
+}
diff --git a/.devcontainer/cuda12.0-llvm9/devcontainer.json b/.devcontainer/cuda12.0-llvm9/devcontainer.json
new file mode 100644
index 00000000..fee929e9
--- /dev/null
+++ b/.devcontainer/cuda12.0-llvm9/devcontainer.json
@@ -0,0 +1,46 @@
+{
+ "shutdownAction": "stopContainer",
+ "image": "rapidsai/devcontainers:24.06-cpp-llvm9-cuda12.0-ubuntu20.04",
+ "hostRequirements": {
+ "gpu": "optional"
+ },
+ "initializeCommand": [
+ "/bin/bash",
+ "-c",
+ "mkdir -m 0755 -p ${localWorkspaceFolder}/.{aws,cache,config}"
+ ],
+ "containerEnv": {
+ "SCCACHE_REGION": "us-east-2",
+ "SCCACHE_BUCKET": "rapids-sccache-devs",
+ "VAULT_HOST": "https://vault.ops.k8s.rapids.ai",
+ "HISTFILE": "${containerWorkspaceFolder}/.cache/._bash_history",
+ "DEVCONTAINER_NAME": "cuda12.0-llvm9",
+ "CCCL_CUDA_VERSION": "12.0",
+ "CCCL_HOST_COMPILER": "llvm",
+ "CCCL_HOST_COMPILER_VERSION": "9",
+ "CCCL_BUILD_INFIX": "cuda12.0-llvm9"
+ },
+ "workspaceFolder": "/home/coder/${localWorkspaceFolderBasename}",
+ "workspaceMount": "source=${localWorkspaceFolder},target=/home/coder/${localWorkspaceFolderBasename},type=bind,consistency=consistent",
+ "mounts": [
+ "source=${localWorkspaceFolder}/.aws,target=/home/coder/.aws,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.cache,target=/home/coder/.cache,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.config,target=/home/coder/.config,type=bind,consistency=consistent"
+ ],
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "llvm-vs-code-extensions.vscode-clangd",
+ "xaver.clang-format"
+ ],
+ "settings": {
+ "editor.defaultFormatter": "xaver.clang-format",
+ "clang-format.executable": "/usr/local/bin/clang-format",
+ "clangd.arguments": [
+ "--compile-commands-dir=${workspaceFolder}"
+ ]
+ }
+ }
+ },
+ "name": "cuda12.0-llvm9"
+}
diff --git a/.devcontainer/cuda12.4-gcc10/devcontainer.json b/.devcontainer/cuda12.4-gcc10/devcontainer.json
new file mode 100644
index 00000000..9968a849
--- /dev/null
+++ b/.devcontainer/cuda12.4-gcc10/devcontainer.json
@@ -0,0 +1,46 @@
+{
+ "shutdownAction": "stopContainer",
+ "image": "rapidsai/devcontainers:24.06-cpp-gcc10-cuda12.4-ubuntu20.04",
+ "hostRequirements": {
+ "gpu": "optional"
+ },
+ "initializeCommand": [
+ "/bin/bash",
+ "-c",
+ "mkdir -m 0755 -p ${localWorkspaceFolder}/.{aws,cache,config}"
+ ],
+ "containerEnv": {
+ "SCCACHE_REGION": "us-east-2",
+ "SCCACHE_BUCKET": "rapids-sccache-devs",
+ "VAULT_HOST": "https://vault.ops.k8s.rapids.ai",
+ "HISTFILE": "${containerWorkspaceFolder}/.cache/._bash_history",
+ "DEVCONTAINER_NAME": "cuda12.4-gcc10",
+ "CCCL_CUDA_VERSION": "12.4",
+ "CCCL_HOST_COMPILER": "gcc",
+ "CCCL_HOST_COMPILER_VERSION": "10",
+ "CCCL_BUILD_INFIX": "cuda12.4-gcc10"
+ },
+ "workspaceFolder": "/home/coder/${localWorkspaceFolderBasename}",
+ "workspaceMount": "source=${localWorkspaceFolder},target=/home/coder/${localWorkspaceFolderBasename},type=bind,consistency=consistent",
+ "mounts": [
+ "source=${localWorkspaceFolder}/.aws,target=/home/coder/.aws,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.cache,target=/home/coder/.cache,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.config,target=/home/coder/.config,type=bind,consistency=consistent"
+ ],
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "llvm-vs-code-extensions.vscode-clangd",
+ "xaver.clang-format"
+ ],
+ "settings": {
+ "editor.defaultFormatter": "xaver.clang-format",
+ "clang-format.executable": "/usr/local/bin/clang-format",
+ "clangd.arguments": [
+ "--compile-commands-dir=${workspaceFolder}"
+ ]
+ }
+ }
+ },
+ "name": "cuda12.4-gcc10"
+}
diff --git a/.devcontainer/cuda12.4-gcc11/devcontainer.json b/.devcontainer/cuda12.4-gcc11/devcontainer.json
new file mode 100644
index 00000000..d2c26622
--- /dev/null
+++ b/.devcontainer/cuda12.4-gcc11/devcontainer.json
@@ -0,0 +1,46 @@
+{
+ "shutdownAction": "stopContainer",
+ "image": "rapidsai/devcontainers:24.06-cpp-gcc11-cuda12.4-ubuntu22.04",
+ "hostRequirements": {
+ "gpu": "optional"
+ },
+ "initializeCommand": [
+ "/bin/bash",
+ "-c",
+ "mkdir -m 0755 -p ${localWorkspaceFolder}/.{aws,cache,config}"
+ ],
+ "containerEnv": {
+ "SCCACHE_REGION": "us-east-2",
+ "SCCACHE_BUCKET": "rapids-sccache-devs",
+ "VAULT_HOST": "https://vault.ops.k8s.rapids.ai",
+ "HISTFILE": "${containerWorkspaceFolder}/.cache/._bash_history",
+ "DEVCONTAINER_NAME": "cuda12.4-gcc11",
+ "CCCL_CUDA_VERSION": "12.4",
+ "CCCL_HOST_COMPILER": "gcc",
+ "CCCL_HOST_COMPILER_VERSION": "11",
+ "CCCL_BUILD_INFIX": "cuda12.4-gcc11"
+ },
+ "workspaceFolder": "/home/coder/${localWorkspaceFolderBasename}",
+ "workspaceMount": "source=${localWorkspaceFolder},target=/home/coder/${localWorkspaceFolderBasename},type=bind,consistency=consistent",
+ "mounts": [
+ "source=${localWorkspaceFolder}/.aws,target=/home/coder/.aws,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.cache,target=/home/coder/.cache,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.config,target=/home/coder/.config,type=bind,consistency=consistent"
+ ],
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "llvm-vs-code-extensions.vscode-clangd",
+ "xaver.clang-format"
+ ],
+ "settings": {
+ "editor.defaultFormatter": "xaver.clang-format",
+ "clang-format.executable": "/usr/local/bin/clang-format",
+ "clangd.arguments": [
+ "--compile-commands-dir=${workspaceFolder}"
+ ]
+ }
+ }
+ },
+ "name": "cuda12.4-gcc11"
+}
diff --git a/.devcontainer/cuda12.4-gcc12/devcontainer.json b/.devcontainer/cuda12.4-gcc12/devcontainer.json
new file mode 100644
index 00000000..fa6e0c5e
--- /dev/null
+++ b/.devcontainer/cuda12.4-gcc12/devcontainer.json
@@ -0,0 +1,46 @@
+{
+ "shutdownAction": "stopContainer",
+ "image": "rapidsai/devcontainers:24.06-cpp-gcc12-cuda12.4-ubuntu22.04",
+ "hostRequirements": {
+ "gpu": "optional"
+ },
+ "initializeCommand": [
+ "/bin/bash",
+ "-c",
+ "mkdir -m 0755 -p ${localWorkspaceFolder}/.{aws,cache,config}"
+ ],
+ "containerEnv": {
+ "SCCACHE_REGION": "us-east-2",
+ "SCCACHE_BUCKET": "rapids-sccache-devs",
+ "VAULT_HOST": "https://vault.ops.k8s.rapids.ai",
+ "HISTFILE": "${containerWorkspaceFolder}/.cache/._bash_history",
+ "DEVCONTAINER_NAME": "cuda12.4-gcc12",
+ "CCCL_CUDA_VERSION": "12.4",
+ "CCCL_HOST_COMPILER": "gcc",
+ "CCCL_HOST_COMPILER_VERSION": "12",
+ "CCCL_BUILD_INFIX": "cuda12.4-gcc12"
+ },
+ "workspaceFolder": "/home/coder/${localWorkspaceFolderBasename}",
+ "workspaceMount": "source=${localWorkspaceFolder},target=/home/coder/${localWorkspaceFolderBasename},type=bind,consistency=consistent",
+ "mounts": [
+ "source=${localWorkspaceFolder}/.aws,target=/home/coder/.aws,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.cache,target=/home/coder/.cache,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.config,target=/home/coder/.config,type=bind,consistency=consistent"
+ ],
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "llvm-vs-code-extensions.vscode-clangd",
+ "xaver.clang-format"
+ ],
+ "settings": {
+ "editor.defaultFormatter": "xaver.clang-format",
+ "clang-format.executable": "/usr/local/bin/clang-format",
+ "clangd.arguments": [
+ "--compile-commands-dir=${workspaceFolder}"
+ ]
+ }
+ }
+ },
+ "name": "cuda12.4-gcc12"
+}
diff --git a/.devcontainer/cuda12.4-gcc7/devcontainer.json b/.devcontainer/cuda12.4-gcc7/devcontainer.json
new file mode 100644
index 00000000..af6fdb10
--- /dev/null
+++ b/.devcontainer/cuda12.4-gcc7/devcontainer.json
@@ -0,0 +1,46 @@
+{
+ "shutdownAction": "stopContainer",
+ "image": "rapidsai/devcontainers:24.06-cpp-gcc7-cuda12.4-ubuntu20.04",
+ "hostRequirements": {
+ "gpu": "optional"
+ },
+ "initializeCommand": [
+ "/bin/bash",
+ "-c",
+ "mkdir -m 0755 -p ${localWorkspaceFolder}/.{aws,cache,config}"
+ ],
+ "containerEnv": {
+ "SCCACHE_REGION": "us-east-2",
+ "SCCACHE_BUCKET": "rapids-sccache-devs",
+ "VAULT_HOST": "https://vault.ops.k8s.rapids.ai",
+ "HISTFILE": "${containerWorkspaceFolder}/.cache/._bash_history",
+ "DEVCONTAINER_NAME": "cuda12.4-gcc7",
+ "CCCL_CUDA_VERSION": "12.4",
+ "CCCL_HOST_COMPILER": "gcc",
+ "CCCL_HOST_COMPILER_VERSION": "7",
+ "CCCL_BUILD_INFIX": "cuda12.4-gcc7"
+ },
+ "workspaceFolder": "/home/coder/${localWorkspaceFolderBasename}",
+ "workspaceMount": "source=${localWorkspaceFolder},target=/home/coder/${localWorkspaceFolderBasename},type=bind,consistency=consistent",
+ "mounts": [
+ "source=${localWorkspaceFolder}/.aws,target=/home/coder/.aws,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.cache,target=/home/coder/.cache,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.config,target=/home/coder/.config,type=bind,consistency=consistent"
+ ],
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "llvm-vs-code-extensions.vscode-clangd",
+ "xaver.clang-format"
+ ],
+ "settings": {
+ "editor.defaultFormatter": "xaver.clang-format",
+ "clang-format.executable": "/usr/local/bin/clang-format",
+ "clangd.arguments": [
+ "--compile-commands-dir=${workspaceFolder}"
+ ]
+ }
+ }
+ },
+ "name": "cuda12.4-gcc7"
+}
diff --git a/.devcontainer/cuda12.4-gcc8/devcontainer.json b/.devcontainer/cuda12.4-gcc8/devcontainer.json
new file mode 100644
index 00000000..46670d42
--- /dev/null
+++ b/.devcontainer/cuda12.4-gcc8/devcontainer.json
@@ -0,0 +1,46 @@
+{
+ "shutdownAction": "stopContainer",
+ "image": "rapidsai/devcontainers:24.06-cpp-gcc8-cuda12.4-ubuntu20.04",
+ "hostRequirements": {
+ "gpu": "optional"
+ },
+ "initializeCommand": [
+ "/bin/bash",
+ "-c",
+ "mkdir -m 0755 -p ${localWorkspaceFolder}/.{aws,cache,config}"
+ ],
+ "containerEnv": {
+ "SCCACHE_REGION": "us-east-2",
+ "SCCACHE_BUCKET": "rapids-sccache-devs",
+ "VAULT_HOST": "https://vault.ops.k8s.rapids.ai",
+ "HISTFILE": "${containerWorkspaceFolder}/.cache/._bash_history",
+ "DEVCONTAINER_NAME": "cuda12.4-gcc8",
+ "CCCL_CUDA_VERSION": "12.4",
+ "CCCL_HOST_COMPILER": "gcc",
+ "CCCL_HOST_COMPILER_VERSION": "8",
+ "CCCL_BUILD_INFIX": "cuda12.4-gcc8"
+ },
+ "workspaceFolder": "/home/coder/${localWorkspaceFolderBasename}",
+ "workspaceMount": "source=${localWorkspaceFolder},target=/home/coder/${localWorkspaceFolderBasename},type=bind,consistency=consistent",
+ "mounts": [
+ "source=${localWorkspaceFolder}/.aws,target=/home/coder/.aws,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.cache,target=/home/coder/.cache,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.config,target=/home/coder/.config,type=bind,consistency=consistent"
+ ],
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "llvm-vs-code-extensions.vscode-clangd",
+ "xaver.clang-format"
+ ],
+ "settings": {
+ "editor.defaultFormatter": "xaver.clang-format",
+ "clang-format.executable": "/usr/local/bin/clang-format",
+ "clangd.arguments": [
+ "--compile-commands-dir=${workspaceFolder}"
+ ]
+ }
+ }
+ },
+ "name": "cuda12.4-gcc8"
+}
diff --git a/.devcontainer/cuda12.4-gcc9/devcontainer.json b/.devcontainer/cuda12.4-gcc9/devcontainer.json
new file mode 100644
index 00000000..4005e723
--- /dev/null
+++ b/.devcontainer/cuda12.4-gcc9/devcontainer.json
@@ -0,0 +1,46 @@
+{
+ "shutdownAction": "stopContainer",
+ "image": "rapidsai/devcontainers:24.06-cpp-gcc9-cuda12.4-ubuntu20.04",
+ "hostRequirements": {
+ "gpu": "optional"
+ },
+ "initializeCommand": [
+ "/bin/bash",
+ "-c",
+ "mkdir -m 0755 -p ${localWorkspaceFolder}/.{aws,cache,config}"
+ ],
+ "containerEnv": {
+ "SCCACHE_REGION": "us-east-2",
+ "SCCACHE_BUCKET": "rapids-sccache-devs",
+ "VAULT_HOST": "https://vault.ops.k8s.rapids.ai",
+ "HISTFILE": "${containerWorkspaceFolder}/.cache/._bash_history",
+ "DEVCONTAINER_NAME": "cuda12.4-gcc9",
+ "CCCL_CUDA_VERSION": "12.4",
+ "CCCL_HOST_COMPILER": "gcc",
+ "CCCL_HOST_COMPILER_VERSION": "9",
+ "CCCL_BUILD_INFIX": "cuda12.4-gcc9"
+ },
+ "workspaceFolder": "/home/coder/${localWorkspaceFolderBasename}",
+ "workspaceMount": "source=${localWorkspaceFolder},target=/home/coder/${localWorkspaceFolderBasename},type=bind,consistency=consistent",
+ "mounts": [
+ "source=${localWorkspaceFolder}/.aws,target=/home/coder/.aws,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.cache,target=/home/coder/.cache,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.config,target=/home/coder/.config,type=bind,consistency=consistent"
+ ],
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "llvm-vs-code-extensions.vscode-clangd",
+ "xaver.clang-format"
+ ],
+ "settings": {
+ "editor.defaultFormatter": "xaver.clang-format",
+ "clang-format.executable": "/usr/local/bin/clang-format",
+ "clangd.arguments": [
+ "--compile-commands-dir=${workspaceFolder}"
+ ]
+ }
+ }
+ },
+ "name": "cuda12.4-gcc9"
+}
diff --git a/.devcontainer/cuda12.4-llvm10/devcontainer.json b/.devcontainer/cuda12.4-llvm10/devcontainer.json
new file mode 100644
index 00000000..6ee57886
--- /dev/null
+++ b/.devcontainer/cuda12.4-llvm10/devcontainer.json
@@ -0,0 +1,46 @@
+{
+ "shutdownAction": "stopContainer",
+ "image": "rapidsai/devcontainers:24.06-cpp-llvm10-cuda12.4-ubuntu20.04",
+ "hostRequirements": {
+ "gpu": "optional"
+ },
+ "initializeCommand": [
+ "/bin/bash",
+ "-c",
+ "mkdir -m 0755 -p ${localWorkspaceFolder}/.{aws,cache,config}"
+ ],
+ "containerEnv": {
+ "SCCACHE_REGION": "us-east-2",
+ "SCCACHE_BUCKET": "rapids-sccache-devs",
+ "VAULT_HOST": "https://vault.ops.k8s.rapids.ai",
+ "HISTFILE": "${containerWorkspaceFolder}/.cache/._bash_history",
+ "DEVCONTAINER_NAME": "cuda12.4-llvm10",
+ "CCCL_CUDA_VERSION": "12.4",
+ "CCCL_HOST_COMPILER": "llvm",
+ "CCCL_HOST_COMPILER_VERSION": "10",
+ "CCCL_BUILD_INFIX": "cuda12.4-llvm10"
+ },
+ "workspaceFolder": "/home/coder/${localWorkspaceFolderBasename}",
+ "workspaceMount": "source=${localWorkspaceFolder},target=/home/coder/${localWorkspaceFolderBasename},type=bind,consistency=consistent",
+ "mounts": [
+ "source=${localWorkspaceFolder}/.aws,target=/home/coder/.aws,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.cache,target=/home/coder/.cache,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.config,target=/home/coder/.config,type=bind,consistency=consistent"
+ ],
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "llvm-vs-code-extensions.vscode-clangd",
+ "xaver.clang-format"
+ ],
+ "settings": {
+ "editor.defaultFormatter": "xaver.clang-format",
+ "clang-format.executable": "/usr/local/bin/clang-format",
+ "clangd.arguments": [
+ "--compile-commands-dir=${workspaceFolder}"
+ ]
+ }
+ }
+ },
+ "name": "cuda12.4-llvm10"
+}
diff --git a/.devcontainer/cuda12.4-llvm11/devcontainer.json b/.devcontainer/cuda12.4-llvm11/devcontainer.json
new file mode 100644
index 00000000..66bd26cf
--- /dev/null
+++ b/.devcontainer/cuda12.4-llvm11/devcontainer.json
@@ -0,0 +1,46 @@
+{
+ "shutdownAction": "stopContainer",
+ "image": "rapidsai/devcontainers:24.06-cpp-llvm11-cuda12.4-ubuntu20.04",
+ "hostRequirements": {
+ "gpu": "optional"
+ },
+ "initializeCommand": [
+ "/bin/bash",
+ "-c",
+ "mkdir -m 0755 -p ${localWorkspaceFolder}/.{aws,cache,config}"
+ ],
+ "containerEnv": {
+ "SCCACHE_REGION": "us-east-2",
+ "SCCACHE_BUCKET": "rapids-sccache-devs",
+ "VAULT_HOST": "https://vault.ops.k8s.rapids.ai",
+ "HISTFILE": "${containerWorkspaceFolder}/.cache/._bash_history",
+ "DEVCONTAINER_NAME": "cuda12.4-llvm11",
+ "CCCL_CUDA_VERSION": "12.4",
+ "CCCL_HOST_COMPILER": "llvm",
+ "CCCL_HOST_COMPILER_VERSION": "11",
+ "CCCL_BUILD_INFIX": "cuda12.4-llvm11"
+ },
+ "workspaceFolder": "/home/coder/${localWorkspaceFolderBasename}",
+ "workspaceMount": "source=${localWorkspaceFolder},target=/home/coder/${localWorkspaceFolderBasename},type=bind,consistency=consistent",
+ "mounts": [
+ "source=${localWorkspaceFolder}/.aws,target=/home/coder/.aws,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.cache,target=/home/coder/.cache,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.config,target=/home/coder/.config,type=bind,consistency=consistent"
+ ],
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "llvm-vs-code-extensions.vscode-clangd",
+ "xaver.clang-format"
+ ],
+ "settings": {
+ "editor.defaultFormatter": "xaver.clang-format",
+ "clang-format.executable": "/usr/local/bin/clang-format",
+ "clangd.arguments": [
+ "--compile-commands-dir=${workspaceFolder}"
+ ]
+ }
+ }
+ },
+ "name": "cuda12.4-llvm11"
+}
diff --git a/.devcontainer/cuda12.4-llvm12/devcontainer.json b/.devcontainer/cuda12.4-llvm12/devcontainer.json
new file mode 100644
index 00000000..8889f147
--- /dev/null
+++ b/.devcontainer/cuda12.4-llvm12/devcontainer.json
@@ -0,0 +1,46 @@
+{
+ "shutdownAction": "stopContainer",
+ "image": "rapidsai/devcontainers:24.06-cpp-llvm12-cuda12.4-ubuntu20.04",
+ "hostRequirements": {
+ "gpu": "optional"
+ },
+ "initializeCommand": [
+ "/bin/bash",
+ "-c",
+ "mkdir -m 0755 -p ${localWorkspaceFolder}/.{aws,cache,config}"
+ ],
+ "containerEnv": {
+ "SCCACHE_REGION": "us-east-2",
+ "SCCACHE_BUCKET": "rapids-sccache-devs",
+ "VAULT_HOST": "https://vault.ops.k8s.rapids.ai",
+ "HISTFILE": "${containerWorkspaceFolder}/.cache/._bash_history",
+ "DEVCONTAINER_NAME": "cuda12.4-llvm12",
+ "CCCL_CUDA_VERSION": "12.4",
+ "CCCL_HOST_COMPILER": "llvm",
+ "CCCL_HOST_COMPILER_VERSION": "12",
+ "CCCL_BUILD_INFIX": "cuda12.4-llvm12"
+ },
+ "workspaceFolder": "/home/coder/${localWorkspaceFolderBasename}",
+ "workspaceMount": "source=${localWorkspaceFolder},target=/home/coder/${localWorkspaceFolderBasename},type=bind,consistency=consistent",
+ "mounts": [
+ "source=${localWorkspaceFolder}/.aws,target=/home/coder/.aws,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.cache,target=/home/coder/.cache,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.config,target=/home/coder/.config,type=bind,consistency=consistent"
+ ],
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "llvm-vs-code-extensions.vscode-clangd",
+ "xaver.clang-format"
+ ],
+ "settings": {
+ "editor.defaultFormatter": "xaver.clang-format",
+ "clang-format.executable": "/usr/local/bin/clang-format",
+ "clangd.arguments": [
+ "--compile-commands-dir=${workspaceFolder}"
+ ]
+ }
+ }
+ },
+ "name": "cuda12.4-llvm12"
+}
diff --git a/.devcontainer/cuda12.4-llvm13/devcontainer.json b/.devcontainer/cuda12.4-llvm13/devcontainer.json
new file mode 100644
index 00000000..76faea9e
--- /dev/null
+++ b/.devcontainer/cuda12.4-llvm13/devcontainer.json
@@ -0,0 +1,46 @@
+{
+ "shutdownAction": "stopContainer",
+ "image": "rapidsai/devcontainers:24.06-cpp-llvm13-cuda12.4-ubuntu20.04",
+ "hostRequirements": {
+ "gpu": "optional"
+ },
+ "initializeCommand": [
+ "/bin/bash",
+ "-c",
+ "mkdir -m 0755 -p ${localWorkspaceFolder}/.{aws,cache,config}"
+ ],
+ "containerEnv": {
+ "SCCACHE_REGION": "us-east-2",
+ "SCCACHE_BUCKET": "rapids-sccache-devs",
+ "VAULT_HOST": "https://vault.ops.k8s.rapids.ai",
+ "HISTFILE": "${containerWorkspaceFolder}/.cache/._bash_history",
+ "DEVCONTAINER_NAME": "cuda12.4-llvm13",
+ "CCCL_CUDA_VERSION": "12.4",
+ "CCCL_HOST_COMPILER": "llvm",
+ "CCCL_HOST_COMPILER_VERSION": "13",
+ "CCCL_BUILD_INFIX": "cuda12.4-llvm13"
+ },
+ "workspaceFolder": "/home/coder/${localWorkspaceFolderBasename}",
+ "workspaceMount": "source=${localWorkspaceFolder},target=/home/coder/${localWorkspaceFolderBasename},type=bind,consistency=consistent",
+ "mounts": [
+ "source=${localWorkspaceFolder}/.aws,target=/home/coder/.aws,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.cache,target=/home/coder/.cache,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.config,target=/home/coder/.config,type=bind,consistency=consistent"
+ ],
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "llvm-vs-code-extensions.vscode-clangd",
+ "xaver.clang-format"
+ ],
+ "settings": {
+ "editor.defaultFormatter": "xaver.clang-format",
+ "clang-format.executable": "/usr/local/bin/clang-format",
+ "clangd.arguments": [
+ "--compile-commands-dir=${workspaceFolder}"
+ ]
+ }
+ }
+ },
+ "name": "cuda12.4-llvm13"
+}
diff --git a/.devcontainer/cuda12.4-llvm14/devcontainer.json b/.devcontainer/cuda12.4-llvm14/devcontainer.json
new file mode 100644
index 00000000..58b00cc3
--- /dev/null
+++ b/.devcontainer/cuda12.4-llvm14/devcontainer.json
@@ -0,0 +1,46 @@
+{
+ "shutdownAction": "stopContainer",
+ "image": "rapidsai/devcontainers:24.06-cpp-llvm14-cuda12.4-ubuntu20.04",
+ "hostRequirements": {
+ "gpu": "optional"
+ },
+ "initializeCommand": [
+ "/bin/bash",
+ "-c",
+ "mkdir -m 0755 -p ${localWorkspaceFolder}/.{aws,cache,config}"
+ ],
+ "containerEnv": {
+ "SCCACHE_REGION": "us-east-2",
+ "SCCACHE_BUCKET": "rapids-sccache-devs",
+ "VAULT_HOST": "https://vault.ops.k8s.rapids.ai",
+ "HISTFILE": "${containerWorkspaceFolder}/.cache/._bash_history",
+ "DEVCONTAINER_NAME": "cuda12.4-llvm14",
+ "CCCL_CUDA_VERSION": "12.4",
+ "CCCL_HOST_COMPILER": "llvm",
+ "CCCL_HOST_COMPILER_VERSION": "14",
+ "CCCL_BUILD_INFIX": "cuda12.4-llvm14"
+ },
+ "workspaceFolder": "/home/coder/${localWorkspaceFolderBasename}",
+ "workspaceMount": "source=${localWorkspaceFolder},target=/home/coder/${localWorkspaceFolderBasename},type=bind,consistency=consistent",
+ "mounts": [
+ "source=${localWorkspaceFolder}/.aws,target=/home/coder/.aws,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.cache,target=/home/coder/.cache,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.config,target=/home/coder/.config,type=bind,consistency=consistent"
+ ],
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "llvm-vs-code-extensions.vscode-clangd",
+ "xaver.clang-format"
+ ],
+ "settings": {
+ "editor.defaultFormatter": "xaver.clang-format",
+ "clang-format.executable": "/usr/local/bin/clang-format",
+ "clangd.arguments": [
+ "--compile-commands-dir=${workspaceFolder}"
+ ]
+ }
+ }
+ },
+ "name": "cuda12.4-llvm14"
+}
diff --git a/.devcontainer/cuda12.4-llvm15/devcontainer.json b/.devcontainer/cuda12.4-llvm15/devcontainer.json
new file mode 100644
index 00000000..9c926538
--- /dev/null
+++ b/.devcontainer/cuda12.4-llvm15/devcontainer.json
@@ -0,0 +1,46 @@
+{
+ "shutdownAction": "stopContainer",
+ "image": "rapidsai/devcontainers:24.06-cpp-llvm15-cuda12.4-ubuntu22.04",
+ "hostRequirements": {
+ "gpu": "optional"
+ },
+ "initializeCommand": [
+ "/bin/bash",
+ "-c",
+ "mkdir -m 0755 -p ${localWorkspaceFolder}/.{aws,cache,config}"
+ ],
+ "containerEnv": {
+ "SCCACHE_REGION": "us-east-2",
+ "SCCACHE_BUCKET": "rapids-sccache-devs",
+ "VAULT_HOST": "https://vault.ops.k8s.rapids.ai",
+ "HISTFILE": "${containerWorkspaceFolder}/.cache/._bash_history",
+ "DEVCONTAINER_NAME": "cuda12.4-llvm15",
+ "CCCL_CUDA_VERSION": "12.4",
+ "CCCL_HOST_COMPILER": "llvm",
+ "CCCL_HOST_COMPILER_VERSION": "15",
+ "CCCL_BUILD_INFIX": "cuda12.4-llvm15"
+ },
+ "workspaceFolder": "/home/coder/${localWorkspaceFolderBasename}",
+ "workspaceMount": "source=${localWorkspaceFolder},target=/home/coder/${localWorkspaceFolderBasename},type=bind,consistency=consistent",
+ "mounts": [
+ "source=${localWorkspaceFolder}/.aws,target=/home/coder/.aws,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.cache,target=/home/coder/.cache,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.config,target=/home/coder/.config,type=bind,consistency=consistent"
+ ],
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "llvm-vs-code-extensions.vscode-clangd",
+ "xaver.clang-format"
+ ],
+ "settings": {
+ "editor.defaultFormatter": "xaver.clang-format",
+ "clang-format.executable": "/usr/local/bin/clang-format",
+ "clangd.arguments": [
+ "--compile-commands-dir=${workspaceFolder}"
+ ]
+ }
+ }
+ },
+ "name": "cuda12.4-llvm15"
+}
diff --git a/.devcontainer/cuda12.4-llvm16/devcontainer.json b/.devcontainer/cuda12.4-llvm16/devcontainer.json
new file mode 100644
index 00000000..9f6fcadf
--- /dev/null
+++ b/.devcontainer/cuda12.4-llvm16/devcontainer.json
@@ -0,0 +1,46 @@
+{
+ "shutdownAction": "stopContainer",
+ "image": "rapidsai/devcontainers:24.06-cpp-llvm16-cuda12.4-ubuntu22.04",
+ "hostRequirements": {
+ "gpu": "optional"
+ },
+ "initializeCommand": [
+ "/bin/bash",
+ "-c",
+ "mkdir -m 0755 -p ${localWorkspaceFolder}/.{aws,cache,config}"
+ ],
+ "containerEnv": {
+ "SCCACHE_REGION": "us-east-2",
+ "SCCACHE_BUCKET": "rapids-sccache-devs",
+ "VAULT_HOST": "https://vault.ops.k8s.rapids.ai",
+ "HISTFILE": "${containerWorkspaceFolder}/.cache/._bash_history",
+ "DEVCONTAINER_NAME": "cuda12.4-llvm16",
+ "CCCL_CUDA_VERSION": "12.4",
+ "CCCL_HOST_COMPILER": "llvm",
+ "CCCL_HOST_COMPILER_VERSION": "16",
+ "CCCL_BUILD_INFIX": "cuda12.4-llvm16"
+ },
+ "workspaceFolder": "/home/coder/${localWorkspaceFolderBasename}",
+ "workspaceMount": "source=${localWorkspaceFolder},target=/home/coder/${localWorkspaceFolderBasename},type=bind,consistency=consistent",
+ "mounts": [
+ "source=${localWorkspaceFolder}/.aws,target=/home/coder/.aws,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.cache,target=/home/coder/.cache,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.config,target=/home/coder/.config,type=bind,consistency=consistent"
+ ],
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "llvm-vs-code-extensions.vscode-clangd",
+ "xaver.clang-format"
+ ],
+ "settings": {
+ "editor.defaultFormatter": "xaver.clang-format",
+ "clang-format.executable": "/usr/local/bin/clang-format",
+ "clangd.arguments": [
+ "--compile-commands-dir=${workspaceFolder}"
+ ]
+ }
+ }
+ },
+ "name": "cuda12.4-llvm16"
+}
diff --git a/.devcontainer/cuda12.4-llvm17/devcontainer.json b/.devcontainer/cuda12.4-llvm17/devcontainer.json
new file mode 100644
index 00000000..7b9f2e54
--- /dev/null
+++ b/.devcontainer/cuda12.4-llvm17/devcontainer.json
@@ -0,0 +1,46 @@
+{
+ "shutdownAction": "stopContainer",
+ "image": "rapidsai/devcontainers:24.06-cpp-llvm17-cuda12.4-ubuntu22.04",
+ "hostRequirements": {
+ "gpu": "optional"
+ },
+ "initializeCommand": [
+ "/bin/bash",
+ "-c",
+ "mkdir -m 0755 -p ${localWorkspaceFolder}/.{aws,cache,config}"
+ ],
+ "containerEnv": {
+ "SCCACHE_REGION": "us-east-2",
+ "SCCACHE_BUCKET": "rapids-sccache-devs",
+ "VAULT_HOST": "https://vault.ops.k8s.rapids.ai",
+ "HISTFILE": "${containerWorkspaceFolder}/.cache/._bash_history",
+ "DEVCONTAINER_NAME": "cuda12.4-llvm17",
+ "CCCL_CUDA_VERSION": "12.4",
+ "CCCL_HOST_COMPILER": "llvm",
+ "CCCL_HOST_COMPILER_VERSION": "17",
+ "CCCL_BUILD_INFIX": "cuda12.4-llvm17"
+ },
+ "workspaceFolder": "/home/coder/${localWorkspaceFolderBasename}",
+ "workspaceMount": "source=${localWorkspaceFolder},target=/home/coder/${localWorkspaceFolderBasename},type=bind,consistency=consistent",
+ "mounts": [
+ "source=${localWorkspaceFolder}/.aws,target=/home/coder/.aws,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.cache,target=/home/coder/.cache,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.config,target=/home/coder/.config,type=bind,consistency=consistent"
+ ],
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "llvm-vs-code-extensions.vscode-clangd",
+ "xaver.clang-format"
+ ],
+ "settings": {
+ "editor.defaultFormatter": "xaver.clang-format",
+ "clang-format.executable": "/usr/local/bin/clang-format",
+ "clangd.arguments": [
+ "--compile-commands-dir=${workspaceFolder}"
+ ]
+ }
+ }
+ },
+ "name": "cuda12.4-llvm17"
+}
diff --git a/.devcontainer/cuda12.4-llvm18/devcontainer.json b/.devcontainer/cuda12.4-llvm18/devcontainer.json
new file mode 100644
index 00000000..ff2c1a78
--- /dev/null
+++ b/.devcontainer/cuda12.4-llvm18/devcontainer.json
@@ -0,0 +1,46 @@
+{
+ "shutdownAction": "stopContainer",
+ "image": "rapidsai/devcontainers:24.06-cpp-llvm18-cuda12.4-ubuntu22.04",
+ "hostRequirements": {
+ "gpu": "optional"
+ },
+ "initializeCommand": [
+ "/bin/bash",
+ "-c",
+ "mkdir -m 0755 -p ${localWorkspaceFolder}/.{aws,cache,config}"
+ ],
+ "containerEnv": {
+ "SCCACHE_REGION": "us-east-2",
+ "SCCACHE_BUCKET": "rapids-sccache-devs",
+ "VAULT_HOST": "https://vault.ops.k8s.rapids.ai",
+ "HISTFILE": "${containerWorkspaceFolder}/.cache/._bash_history",
+ "DEVCONTAINER_NAME": "cuda12.4-llvm18",
+ "CCCL_CUDA_VERSION": "12.4",
+ "CCCL_HOST_COMPILER": "llvm",
+ "CCCL_HOST_COMPILER_VERSION": "18",
+ "CCCL_BUILD_INFIX": "cuda12.4-llvm18"
+ },
+ "workspaceFolder": "/home/coder/${localWorkspaceFolderBasename}",
+ "workspaceMount": "source=${localWorkspaceFolder},target=/home/coder/${localWorkspaceFolderBasename},type=bind,consistency=consistent",
+ "mounts": [
+ "source=${localWorkspaceFolder}/.aws,target=/home/coder/.aws,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.cache,target=/home/coder/.cache,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.config,target=/home/coder/.config,type=bind,consistency=consistent"
+ ],
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "llvm-vs-code-extensions.vscode-clangd",
+ "xaver.clang-format"
+ ],
+ "settings": {
+ "editor.defaultFormatter": "xaver.clang-format",
+ "clang-format.executable": "/usr/local/bin/clang-format",
+ "clangd.arguments": [
+ "--compile-commands-dir=${workspaceFolder}"
+ ]
+ }
+ }
+ },
+ "name": "cuda12.4-llvm18"
+}
diff --git a/.devcontainer/cuda12.4-llvm9/devcontainer.json b/.devcontainer/cuda12.4-llvm9/devcontainer.json
new file mode 100644
index 00000000..d9910e20
--- /dev/null
+++ b/.devcontainer/cuda12.4-llvm9/devcontainer.json
@@ -0,0 +1,46 @@
+{
+ "shutdownAction": "stopContainer",
+ "image": "rapidsai/devcontainers:24.06-cpp-llvm9-cuda12.4-ubuntu20.04",
+ "hostRequirements": {
+ "gpu": "optional"
+ },
+ "initializeCommand": [
+ "/bin/bash",
+ "-c",
+ "mkdir -m 0755 -p ${localWorkspaceFolder}/.{aws,cache,config}"
+ ],
+ "containerEnv": {
+ "SCCACHE_REGION": "us-east-2",
+ "SCCACHE_BUCKET": "rapids-sccache-devs",
+ "VAULT_HOST": "https://vault.ops.k8s.rapids.ai",
+ "HISTFILE": "${containerWorkspaceFolder}/.cache/._bash_history",
+ "DEVCONTAINER_NAME": "cuda12.4-llvm9",
+ "CCCL_CUDA_VERSION": "12.4",
+ "CCCL_HOST_COMPILER": "llvm",
+ "CCCL_HOST_COMPILER_VERSION": "9",
+ "CCCL_BUILD_INFIX": "cuda12.4-llvm9"
+ },
+ "workspaceFolder": "/home/coder/${localWorkspaceFolderBasename}",
+ "workspaceMount": "source=${localWorkspaceFolder},target=/home/coder/${localWorkspaceFolderBasename},type=bind,consistency=consistent",
+ "mounts": [
+ "source=${localWorkspaceFolder}/.aws,target=/home/coder/.aws,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.cache,target=/home/coder/.cache,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.config,target=/home/coder/.config,type=bind,consistency=consistent"
+ ],
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "llvm-vs-code-extensions.vscode-clangd",
+ "xaver.clang-format"
+ ],
+ "settings": {
+ "editor.defaultFormatter": "xaver.clang-format",
+ "clang-format.executable": "/usr/local/bin/clang-format",
+ "clangd.arguments": [
+ "--compile-commands-dir=${workspaceFolder}"
+ ]
+ }
+ }
+ },
+ "name": "cuda12.4-llvm9"
+}
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
new file mode 100644
index 00000000..fa6e0c5e
--- /dev/null
+++ b/.devcontainer/devcontainer.json
@@ -0,0 +1,46 @@
+{
+ "shutdownAction": "stopContainer",
+ "image": "rapidsai/devcontainers:24.06-cpp-gcc12-cuda12.4-ubuntu22.04",
+ "hostRequirements": {
+ "gpu": "optional"
+ },
+ "initializeCommand": [
+ "/bin/bash",
+ "-c",
+ "mkdir -m 0755 -p ${localWorkspaceFolder}/.{aws,cache,config}"
+ ],
+ "containerEnv": {
+ "SCCACHE_REGION": "us-east-2",
+ "SCCACHE_BUCKET": "rapids-sccache-devs",
+ "VAULT_HOST": "https://vault.ops.k8s.rapids.ai",
+ "HISTFILE": "${containerWorkspaceFolder}/.cache/._bash_history",
+ "DEVCONTAINER_NAME": "cuda12.4-gcc12",
+ "CCCL_CUDA_VERSION": "12.4",
+ "CCCL_HOST_COMPILER": "gcc",
+ "CCCL_HOST_COMPILER_VERSION": "12",
+ "CCCL_BUILD_INFIX": "cuda12.4-gcc12"
+ },
+ "workspaceFolder": "/home/coder/${localWorkspaceFolderBasename}",
+ "workspaceMount": "source=${localWorkspaceFolder},target=/home/coder/${localWorkspaceFolderBasename},type=bind,consistency=consistent",
+ "mounts": [
+ "source=${localWorkspaceFolder}/.aws,target=/home/coder/.aws,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.cache,target=/home/coder/.cache,type=bind,consistency=consistent",
+ "source=${localWorkspaceFolder}/.config,target=/home/coder/.config,type=bind,consistency=consistent"
+ ],
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "llvm-vs-code-extensions.vscode-clangd",
+ "xaver.clang-format"
+ ],
+ "settings": {
+ "editor.defaultFormatter": "xaver.clang-format",
+ "clang-format.executable": "/usr/local/bin/clang-format",
+ "clangd.arguments": [
+ "--compile-commands-dir=${workspaceFolder}"
+ ]
+ }
+ }
+ },
+ "name": "cuda12.4-gcc12"
+}
diff --git a/.devcontainer/img/container_list.png b/.devcontainer/img/container_list.png
new file mode 100644
index 00000000..09c4510f
Binary files /dev/null and b/.devcontainer/img/container_list.png differ
diff --git a/.devcontainer/img/github_auth.png b/.devcontainer/img/github_auth.png
new file mode 100644
index 00000000..3f52b3a2
Binary files /dev/null and b/.devcontainer/img/github_auth.png differ
diff --git a/.devcontainer/img/open_in_container_manual.png b/.devcontainer/img/open_in_container_manual.png
new file mode 100644
index 00000000..e09435b8
Binary files /dev/null and b/.devcontainer/img/open_in_container_manual.png differ
diff --git a/.devcontainer/img/reopen_in_container.png b/.devcontainer/img/reopen_in_container.png
new file mode 100644
index 00000000..0e1d82dd
Binary files /dev/null and b/.devcontainer/img/reopen_in_container.png differ
diff --git a/.devcontainer/launch.sh b/.devcontainer/launch.sh
new file mode 100755
index 00000000..0299e0c1
--- /dev/null
+++ b/.devcontainer/launch.sh
@@ -0,0 +1,130 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+# Ensure the script is being executed in the cccl/ root
+cd "$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/..";
+
+print_help() {
+ echo "Usage: $0 [-c|--cuda ] [-H|--host ] [-d|--docker]"
+ echo "Launch a development container. If no CUDA version or Host compiler are specified,"
+ echo "the top-level devcontainer in .devcontainer/devcontainer.json will be used."
+ echo ""
+ echo "Options:"
+ echo " -c, --cuda Specify the CUDA version. E.g., 12.2"
+ echo " -H, --host Specify the host compiler. E.g., gcc12"
+ echo " -d, --docker Launch the development environment in Docker directly without using VSCode."
+ echo " -h, --help Display this help message and exit."
+}
+
+parse_options() {
+ local OPTIONS=c:H:dh
+ local LONG_OPTIONS=cuda:,host:,docker,help
+ local PARSED_OPTIONS=$(getopt -n "$0" -o "${OPTIONS}" --long "${LONG_OPTIONS}" -- "$@")
+
+ if [[ $? -ne 0 ]]; then
+ exit 1
+ fi
+
+ eval set -- "${PARSED_OPTIONS}"
+
+ while true; do
+ case "$1" in
+ -c|--cuda)
+ cuda_version="$2"
+ shift 2
+ ;;
+ -H|--host)
+ host_compiler="$2"
+ shift 2
+ ;;
+ -d|--docker)
+ docker_mode=true
+ shift
+ ;;
+ -h|--help)
+ print_help
+ exit 0
+ ;;
+ --)
+ shift
+ break
+ ;;
+ *)
+ echo "Invalid option: $1"
+ print_help
+ exit 1
+ ;;
+ esac
+ done
+}
+
+launch_docker() {
+ DOCKER_IMAGE=$(grep "image" "${path}/devcontainer.json" | sed 's/.*: "\(.*\)",/\1/')
+ echo "Found image: ${DOCKER_IMAGE}"
+ docker pull ${DOCKER_IMAGE}
+ docker run \
+ -it --rm \
+ --user coder \
+ --workdir /home/coder/cccl \
+ --mount type=bind,src="$(pwd)",dst='/home/coder/cccl' \
+ ${DOCKER_IMAGE} \
+ /bin/bash
+}
+
+launch_vscode() {
+ # Since Visual Studio Code allows only one instance per `devcontainer.json`,
+ # this code prepares a unique temporary directory structure for each launch of a devcontainer.
+ # By doing so, it ensures that multiple instances of the same environment can be run
+ # simultaneously. The script replicates the `devcontainer.json` from the desired CUDA
+ # and compiler environment into this temporary directory, adjusting paths to ensure the
+ # correct workspace is loaded. A special URL is then generated to instruct VSCode to
+ # launch the development container using this temporary configuration.
+ local workspace="$(basename "$(pwd)")"
+ local tmpdir="$(mktemp -d)/${workspace}"
+ mkdir -p "${tmpdir}"
+ mkdir -p "${tmpdir}/.devcontainer"
+ cp -arL "${path}/devcontainer.json" "${tmpdir}/.devcontainer"
+ sed -i 's@\\${localWorkspaceFolder}@$(pwd)@g' "${tmpdir}/.devcontainer/devcontainer.json"
+ local path="${tmpdir}"
+ local hash="$(echo -n "${path}" | xxd -pu - | tr -d '[:space:]')"
+ local url="vscode://vscode-remote/dev-container+${hash}/home/coder/cccl"
+
+ local launch=""
+ if type open >/dev/null 2>&1; then
+ launch="open"
+ elif type xdg-open >/dev/null 2>&1; then
+ launch="xdg-open"
+ fi
+
+ if [ -n "${launch}" ]; then
+ echo "Launching VSCode Dev Container URL: ${url}"
+ code --new-window "${tmpdir}"
+ exec "${launch}" "${url}" >/dev/null 2>&1
+ fi
+}
+
+main() {
+ parse_options "$@"
+
+ # If no CTK/Host compiler are provided, just use the default environment
+ if [[ -z ${cuda_version:-} ]] && [[ -z ${host_compiler:-} ]]; then
+ path=".devcontainer"
+ else
+ path=".devcontainer/cuda${cuda_version}-${host_compiler}"
+ if [[ ! -f "${path}/devcontainer.json" ]]; then
+ echo "Unknown CUDA [${cuda_version}] compiler [${host_compiler}] combination"
+ echo "Requested devcontainer ${path}/devcontainer.json does not exist"
+ exit 1
+ fi
+ fi
+
+ if ${docker_mode:-'false'}; then
+ launch_docker
+ else
+ launch_vscode
+ fi
+}
+
+main "$@"
+
diff --git a/.devcontainer/make_devcontainers.sh b/.devcontainer/make_devcontainers.sh
new file mode 100755
index 00000000..64b92c08
--- /dev/null
+++ b/.devcontainer/make_devcontainers.sh
@@ -0,0 +1,141 @@
+#!/bin/bash
+
+# This script parses the CI matrix.yaml file and generates a devcontainer.json file for each unique combination of
+# CUDA version, compiler name/version, and Ubuntu version. The devcontainer.json files are written to the
+# .devcontainer directory to a subdirectory named after the CUDA version and compiler name/version.
+# GitHub docs on using multiple devcontainer.json files:
+# https://docs.github.com/en/codespaces/setting-up-your-project-for-codespaces/adding-a-dev-container-configuration/introduction-to-dev-containers#devcontainerjson
+
+set -euo pipefail
+
+# Ensure the script is being executed in its containing directory
+cd "$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )";
+
+
+function usage {
+ echo "Usage: $0 [--clean] [-h/--help] [-v/--verbose]"
+ echo " --clean Remove stale devcontainer subdirectories"
+ echo " -h, --help Display this help message"
+ echo " -v, --verbose Enable verbose mode (set -x)"
+ exit 1
+}
+
+# Function to update the devcontainer.json file with the provided parameters
+update_devcontainer() {
+ local input_file="$1"
+ local output_file="$2"
+ local name="$3"
+ local cuda_version="$4"
+ local compiler_name="$5"
+ local compiler_exe="$6"
+ local compiler_version="$7"
+ local os="$8"
+ local devcontainer_version="$9"
+
+ local IMAGE_ROOT="rapidsai/devcontainers:${devcontainer_version}-cpp-"
+ local image="${IMAGE_ROOT}${compiler_name}${compiler_version}-cuda${cuda_version}-${os}"
+
+ jq --arg image "$image" --arg name "$name" \
+ --arg cuda_version "$cuda_version" --arg compiler_name "$compiler_name" \
+ --arg compiler_exe "$compiler_exe" --arg compiler_version "$compiler_version" --arg os "$os" \
+ '.image = $image | .name = $name | .containerEnv.DEVCONTAINER_NAME = $name |
+ .containerEnv.CCCL_BUILD_INFIX = $name |
+ .containerEnv.CCCL_CUDA_VERSION = $cuda_version | .containerEnv.CCCL_HOST_COMPILER = $compiler_name |
+ .containerEnv.CCCL_HOST_COMPILER_VERSION = $compiler_version '\
+ "$input_file" > "$output_file"
+}
+
+make_name() {
+ local cuda_version="$1"
+ local compiler_name="$2"
+ local compiler_version="$3"
+
+ echo "cuda$cuda_version-$compiler_name$compiler_version"
+}
+
+CLEAN=false
+VERBOSE=false
+while [[ $# -gt 0 ]]; do
+ case "$1" in
+ --clean)
+ CLEAN=true
+ ;;
+ -h|--help)
+ usage
+ ;;
+ -v|--verbose)
+ VERBOSE=true
+ ;;
+ *)
+ usage
+ ;;
+ esac
+ shift
+done
+
+MATRIX_FILE="../ci/matrix.yaml"
+
+# Enable verbose mode if requested
+if [ "$VERBOSE" = true ]; then
+ set -x
+ cat ${MATRIX_FILE}
+fi
+
+# Read matrix.yaml and convert it to json
+matrix_json=$(yq -o json ${MATRIX_FILE})
+
+# Exclude Windows environments
+readonly matrix_json=$(echo "$matrix_json" | jq 'del(.pull_request.nvcc[] | select(.os | contains("windows")))')
+
+# Get the devcontainer image version and define image tag root
+readonly DEVCONTAINER_VERSION=$(echo "$matrix_json" | jq -r '.devcontainer_version')
+
+# Get unique combinations of cuda version, compiler name/version, and Ubuntu version
+readonly combinations=$(echo "$matrix_json" | jq -c '[.pull_request.nvcc[] | {cuda: .cuda, compiler_name: .compiler.name, compiler_exe: .compiler.exe, compiler_version: .compiler.version, os: .os}] | unique | .[]')
+
+# Update the base devcontainer with the default values
+# The root devcontainer.json file is used as the default container as well as a template for all
+# other devcontainer.json files by replacing the `image:` field with the appropriate image name
+readonly base_devcontainer_file="./devcontainer.json"
+readonly NEWEST_GCC_CUDA_ENTRY=$(echo "$combinations" | jq -rs '[.[] | select(.compiler_name == "gcc")] | sort_by((.cuda | tonumber), (.compiler_version | tonumber)) | .[-1]')
+readonly DEFAULT_CUDA=$(echo "$NEWEST_GCC_CUDA_ENTRY" | jq -r '.cuda')
+readonly DEFAULT_COMPILER_NAME=$(echo "$NEWEST_GCC_CUDA_ENTRY" | jq -r '.compiler_name')
+readonly DEFAULT_COMPILER_EXE=$(echo "$NEWEST_GCC_CUDA_ENTRY" | jq -r '.compiler_exe')
+readonly DEFAULT_COMPILER_VERSION=$(echo "$NEWEST_GCC_CUDA_ENTRY" | jq -r '.compiler_version')
+readonly DEFAULT_OS=$(echo "$NEWEST_GCC_CUDA_ENTRY" | jq -r '.os')
+readonly DEFAULT_NAME=$(make_name "$DEFAULT_CUDA" "$DEFAULT_COMPILER_NAME" "$DEFAULT_COMPILER_VERSION")
+
+update_devcontainer ${base_devcontainer_file} "./temp_devcontainer.json" "$DEFAULT_NAME" "$DEFAULT_CUDA" "$DEFAULT_COMPILER_NAME" "$DEFAULT_COMPILER_EXE" "$DEFAULT_COMPILER_VERSION" "$DEFAULT_OS" "$DEVCONTAINER_VERSION"
+mv "./temp_devcontainer.json" ${base_devcontainer_file}
+
+# Create an array to keep track of valid subdirectory names
+valid_subdirs=()
+
+# For each unique combination
+for combination in $combinations; do
+ cuda_version=$(echo "$combination" | jq -r '.cuda')
+ compiler_name=$(echo "$combination" | jq -r '.compiler_name')
+ compiler_exe=$(echo "$combination" | jq -r '.compiler_exe')
+ compiler_version=$(echo "$combination" | jq -r '.compiler_version')
+ os=$(echo "$combination" | jq -r '.os')
+
+ name=$(make_name "$cuda_version" "$compiler_name" "$compiler_version")
+ mkdir -p "$name"
+ new_devcontainer_file="$name/devcontainer.json"
+
+ update_devcontainer "$base_devcontainer_file" "$new_devcontainer_file" "$name" "$cuda_version" "$compiler_name" "$compiler_exe" "$compiler_version" "$os" "$DEVCONTAINER_VERSION"
+ echo "Created $new_devcontainer_file"
+
+ # Add the subdirectory name to the valid_subdirs array
+ valid_subdirs+=("$name")
+done
+
+# Clean up stale subdirectories and devcontainer.json files
+if [ "$CLEAN" = true ]; then
+ for subdir in ./*; do
+ if [ -d "$subdir" ] && [[ ! " ${valid_subdirs[@]} " =~ " ${subdir#./} " ]]; then
+ echo "Removing stale subdirectory: $subdir"
+ rm -r "$subdir"
+ fi
+ done
+fi
diff --git a/.devcontainer/verify_devcontainer.sh b/.devcontainer/verify_devcontainer.sh
new file mode 100755
index 00000000..b5934ea2
--- /dev/null
+++ b/.devcontainer/verify_devcontainer.sh
@@ -0,0 +1,89 @@
+#!/bin/bash
+
+function usage {
+ echo "Usage: $0"
+ echo
+ echo "This script is intended to be run within one of CCCL's Dev Containers."
+ echo "It verifies that the expected environment variables and binary versions match what is expected."
+}
+
+check_envvars() {
+ for var_name in "$@"; do
+ if [[ -z "${!var_name:-}" ]]; then
+ echo "::error:: ${var_name} variable is not set."
+ exit 1
+ else
+ echo "$var_name=${!var_name}"
+ fi
+ done
+}
+
+check_host_compiler_version() {
+ local version_output=$($CXX --version)
+
+ if [[ "$CXX" == "g++" ]]; then
+ local actual_version=$(echo "$version_output" | head -n 1 | cut -d ' ' -f 4 | cut -d '.' -f 1)
+ local expected_compiler="gcc"
+ elif [[ "$CXX" == "clang++" ]]; then
+ if [[ $version_output =~ clang\ version\ ([0-9]+) ]]; then
+ actual_version=${BASH_REMATCH[1]}
+ else
+ echo "::error:: Unable to determine clang version."
+ exit 1
+ fi
+ expected_compiler="llvm"
+ elif [[ "$CXX" == "icpc" ]]; then
+ local actual_version=$(echo "$version_output" | head -n 1 | cut -d ' ' -f 3 )
+ # The icpc compiler version of oneAPI release 2023.2.0 is 2021.10.0
+ if [[ "$actual_version" == "2021.10.0" ]]; then
+ actual_version="2023.2.0"
+ fi
+ expected_compiler="oneapi"
+ else
+ echo "::error:: Unexpected CXX value ($CXX)."
+ exit 1
+ fi
+
+ if [[ "$expected_compiler" != "${CCCL_HOST_COMPILER}" || "$actual_version" != "$CCCL_HOST_COMPILER_VERSION" ]]; then
+ echo "::error:: CXX ($CXX) version ($actual_version) does not match the expected compiler (${CCCL_HOST_COMPILER}) and version (${CCCL_HOST_COMPILER_VERSION})."
+ exit 1
+ else
+ echo "Detected host compiler: $CXX version $actual_version"
+ fi
+}
+
+check_cuda_version() {
+ local cuda_version_output=$(nvcc --version)
+ if [[ $cuda_version_output =~ release\ ([0-9]+\.[0-9]+) ]]; then
+ local actual_cuda_version=${BASH_REMATCH[1]}
+ else
+ echo "::error:: Unable to determine CUDA version from nvcc."
+ exit 1
+ fi
+
+ if [[ "$actual_cuda_version" != "$CCCL_CUDA_VERSION" ]]; then
+ echo "::error:: CUDA version ($actual_cuda_version) does not match the expected CUDA version ($CCCL_CUDA_VERSION)."
+ exit 1
+ else
+ echo "Detected CUDA version: $actual_cuda_version"
+ fi
+}
+
+main() {
+ if [[ "$1" == "-h" || "$1" == "--help" ]]; then
+ usage
+ exit 0
+ fi
+
+ set -euo pipefail
+
+ check_envvars DEVCONTAINER_NAME CXX CUDAHOSTCXX CCCL_BUILD_INFIX CCCL_HOST_COMPILER CCCL_CUDA_VERSION CCCL_HOST_COMPILER_VERSION
+
+ check_host_compiler_version
+
+ check_cuda_version
+
+ echo "Dev Container successfully verified!"
+}
+
+main "$@"
diff --git a/.github/actions/compute-matrix/action.yml b/.github/actions/compute-matrix/action.yml
new file mode 100644
index 00000000..b8155e7a
--- /dev/null
+++ b/.github/actions/compute-matrix/action.yml
@@ -0,0 +1,25 @@
+
+name: Compute Matrix
+description: "Compute the matrix for a given matrix type from the specified matrix file"
+
+inputs:
+ matrix_query:
+ description: "The jq query used to specify the desired matrix. e.g., .pull_request.nvcc"
+ required: true
+ matrix_file:
+ description: 'The file containing the matrix'
+ required: true
+outputs:
+ matrix:
+ description: 'The requested matrix'
+ value: ${{ steps.compute-matrix.outputs.MATRIX }}
+
+runs:
+ using: "composite"
+ steps:
+ - name: Compute matrix
+ id: compute-matrix
+ run: |
+ MATRIX=$(./.github/actions/compute-matrix/compute-matrix.sh ${{inputs.matrix_file}} ${{inputs.matrix_query}} )
+ echo "matrix=$MATRIX" | tee -a $GITHUB_OUTPUT
+ shell: bash -euxo pipefail {0}
diff --git a/.github/actions/compute-matrix/compute-matrix.sh b/.github/actions/compute-matrix/compute-matrix.sh
new file mode 100755
index 00000000..cd3946f1
--- /dev/null
+++ b/.github/actions/compute-matrix/compute-matrix.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+
+set -euo pipefail
+
+write_output() {
+ local key="$1"
+ local value="$2"
+ echo "$key=$value" | tee --append "${GITHUB_OUTPUT:-/dev/null}"
+}
+
+extract_matrix() {
+ local file="$1"
+ local type="$2"
+ local matrix=$(yq -o=json "$file" | jq -cr ".$type")
+ write_output "DEVCONTAINER_VERSION" "$(yq -o json "$file" | jq -cr '.devcontainer_version')"
+
+ local nvcc_full_matrix="$(echo "$matrix" | jq -cr '.nvcc')"
+ local per_cuda_compiler_matrix="$(echo "$nvcc_full_matrix" | jq -cr ' group_by(.cuda + .compiler.name) | map({(.[0].cuda + "-" + .[0].compiler.name): .}) | add')"
+ write_output "PER_CUDA_COMPILER_MATRIX" "$per_cuda_compiler_matrix"
+ write_output "PER_CUDA_COMPILER_KEYS" "$(echo "$per_cuda_compiler_matrix" | jq -r 'keys | @json')"
+}
+
+main() {
+ if [ "$1" == "-v" ]; then
+ set -x
+ shift
+ fi
+
+ if [ $# -ne 2 ] || [ "$2" != "pull_request" ]; then
+ echo "Usage: $0 [-v] MATRIX_FILE MATRIX_TYPE"
+ echo " -v : Enable verbose output"
+ echo " MATRIX_FILE : The path to the matrix file."
+ echo " MATRIX_TYPE : The desired matrix. Supported values: 'pull_request'"
+ exit 1
+ fi
+
+ echo "Input matrix file:" >&2
+ cat "$1" >&2
+ echo "Matrix Type: $2" >&2
+
+ extract_matrix "$1" "$2"
+}
+
+main "$@"
diff --git a/.github/actions/configure_cccl_sccache/action.yml b/.github/actions/configure_cccl_sccache/action.yml
new file mode 100644
index 00000000..1b42fc95
--- /dev/null
+++ b/.github/actions/configure_cccl_sccache/action.yml
@@ -0,0 +1,19 @@
+name: Set up AWS credentials and environment variables for sccache
+description: "Set up AWS credentials and environment variables for sccache"
+runs:
+ using: "composite"
+ steps:
+ - name: Get AWS credentials for sccache bucket
+ uses: aws-actions/configure-aws-credentials@v2
+ with:
+ role-to-assume: arn:aws:iam::279114543810:role/gha-oidc-NVIDIA
+ aws-region: us-east-2
+ role-duration-seconds: 43200 # 12 hours)
+ - name: Set environment variables
+ run: |
+ echo "SCCACHE_BUCKET=rapids-sccache-devs" >> $GITHUB_ENV
+ echo "SCCACHE_REGION=us-east-2" >> $GITHUB_ENV
+ echo "SCCACHE_IDLE_TIMEOUT=32768" >> $GITHUB_ENV
+ echo "SCCACHE_S3_USE_SSL=true" >> $GITHUB_ENV
+ echo "SCCACHE_S3_NO_CREDENTIALS=false" >> $GITHUB_ENV
+ shell: bash
diff --git a/.github/copy-pr-bot.yaml b/.github/copy-pr-bot.yaml
new file mode 100644
index 00000000..895ba83e
--- /dev/null
+++ b/.github/copy-pr-bot.yaml
@@ -0,0 +1,4 @@
+# Configuration file for `copy-pr-bot` GitHub App
+# https://docs.gha-runners.nvidia.com/apps/copy-pr-bot/
+
+enabled: true
diff --git a/.github/problem-matchers/problem-matcher.json b/.github/problem-matchers/problem-matcher.json
new file mode 100644
index 00000000..f196a5c8
--- /dev/null
+++ b/.github/problem-matchers/problem-matcher.json
@@ -0,0 +1,14 @@
+{
+ "problemMatcher": [
+ {
+ "owner": "nvcc",
+ "pattern": [
+ {
+ "regexp": "^\\/home\\/coder\\/(.+):(\\d+):(\\d+): (\\w+): \"(.+)\"$",
+ "severity": 4,
+ "message": 5
+ }
+ ]
+ }
+ ]
+}
diff --git a/.github/workflows/build-and-test-linux.yml b/.github/workflows/build-and-test-linux.yml
new file mode 100644
index 00000000..0a9d23e0
--- /dev/null
+++ b/.github/workflows/build-and-test-linux.yml
@@ -0,0 +1,32 @@
+name: build and test
+
+defaults:
+ run:
+ shell: bash -exo pipefail {0}
+
+on:
+ workflow_call:
+ inputs:
+ cpu: {type: string, required: true}
+ test_name: {type: string, required: false}
+ build_script: {type: string, required: false}
+ test_script: {type: string, required: false}
+ container_image: {type: string, required: false}
+ run_tests: {type: boolean, required: false, default: true}
+
+permissions:
+ contents: read
+
+jobs:
+ build-and-test:
+ name: Build/Test ${{inputs.test_name}}
+ permissions:
+ id-token: write
+ contents: read
+ uses: ./.github/workflows/run-as-coder.yml
+ with:
+ name: Build/Test ${{inputs.test_name}}
+ runner: linux-${{inputs.cpu}}-gpu-v100-latest-1
+ image: ${{ inputs.container_image }}
+ command: |
+ ${{ inputs.test_script }}
diff --git a/.github/workflows/build-and-test-windows.yml b/.github/workflows/build-and-test-windows.yml
new file mode 100644
index 00000000..23f3e3fd
--- /dev/null
+++ b/.github/workflows/build-and-test-windows.yml
@@ -0,0 +1,49 @@
+name: Build Windows
+
+on:
+ workflow_call:
+ inputs:
+ test_name: {type: string, required: false}
+ build_script: {type: string, required: false}
+ test_script: {type: string, required: false}
+ container_image: {type: string, required: false}
+
+jobs:
+ prepare:
+ name: Build Only ${{inputs.test_name}}
+ runs-on: windows-amd64-cpu16
+ permissions:
+ id-token: write
+ contents: read
+ env:
+ SCCACHE_BUCKET: rapids-sccache-devs
+ SCCACHE_REGION: us-east-2
+ SCCACHE_IDLE_TIMEOUT: 0
+ SCCACHE_S3_USE_SSL: true
+ SCCACHE_S3_NO_CREDENTIALS: false
+ steps:
+ - name: Get AWS credentials for sccache bucket
+ uses: aws-actions/configure-aws-credentials@v2
+ with:
+ role-to-assume: arn:aws:iam::279114543810:role/gha-oidc-NVIDIA
+ aws-region: us-east-2
+ role-duration-seconds: 43200 # 12 hours
+ - name: Fetch ${{ inputs.container_image }}
+ shell: powershell
+ run: docker pull ${{ inputs.container_image }}
+ - name: Run the tests
+ shell: powershell
+ run: >-
+ docker run ${{ inputs.container_image }} powershell -c "[System.Environment]::SetEnvironmentVariable('AWS_ACCESS_KEY_ID','${{env.AWS_ACCESS_KEY_ID}}')
+ [System.Environment]::SetEnvironmentVariable('AWS_SECRET_ACCESS_KEY','${{env.AWS_SECRET_ACCESS_KEY}}')
+ [System.Environment]::SetEnvironmentVariable('AWS_SESSION_TOKEN','${{env.AWS_SESSION_TOKEN }}')
+ [System.Environment]::SetEnvironmentVariable('SCCACHE_BUCKET','${{env.SCCACHE_BUCKET}}')
+ [System.Environment]::SetEnvironmentVariable('SCCACHE_REGION','${{env.SCCACHE_REGION}}')
+ [System.Environment]::SetEnvironmentVariable('SCCACHE_IDLE_TIMEOUT','${{env.SCCACHE_IDLE_TIMEOUT}}')
+ [System.Environment]::SetEnvironmentVariable('SCCACHE_S3_USE_SSL','${{env.SCCACHE_S3_USE_SSL}}')
+ [System.Environment]::SetEnvironmentVariable('SCCACHE_S3_NO_CREDENTIALS','${{env.SCCACHE_S3_NO_CREDENTIALS}}')
+ git clone https://github.com/NVIDIA/nvbench.git;
+ cd nvbench;
+ git fetch --all;
+ git checkout ${{github.ref_name}};
+ ${{inputs.build_script}};"
diff --git a/.github/workflows/dispatch-build-and-test.yml b/.github/workflows/dispatch-build-and-test.yml
new file mode 100644
index 00000000..72cfb6bf
--- /dev/null
+++ b/.github/workflows/dispatch-build-and-test.yml
@@ -0,0 +1,51 @@
+name: Dispatch build and test
+
+on:
+ workflow_call:
+ inputs:
+ project_name: {type: string, required: true}
+ per_cuda_compiler_matrix: {type: string, required: true}
+ devcontainer_version: {type: string, required: true}
+ is_windows: {type: boolean, required: true}
+
+permissions:
+ contents: read
+
+jobs:
+ # Using a matrix to dispatch to the build-and-test reusable workflow for each build configuration
+ # ensures that the build/test steps can overlap across different configurations. For example,
+ # the build step for CUDA 12.1 + gcc 9.3 can run at the same time as the test step for CUDA 11.0 + clang 11.
+ build_and_test_linux:
+ name: build and test linux
+ permissions:
+ id-token: write
+ contents: read
+ if: ${{ !inputs.is_windows }}
+ uses: ./.github/workflows/build-and-test-linux.yml
+ strategy:
+ fail-fast: false
+ matrix:
+ include: ${{ fromJSON(inputs.per_cuda_compiler_matrix) }}
+ with:
+ cpu: ${{ matrix.cpu }}
+ test_name: ${{matrix.cpu}}/${{matrix.compiler.name}}${{matrix.compiler.version}} ${{matrix.extra_build_args}}
+ build_script: "./ci/build_${{ inputs.project_name }}.sh -cxx ${{matrix.compiler.exe}} ${{matrix.extra_build_args}}"
+ test_script: "./ci/test_${{ inputs.project_name }}.sh -cxx ${{matrix.compiler.exe}} ${{matrix.extra_build_args}}"
+ container_image: rapidsai/devcontainers:${{inputs.devcontainer_version}}-cpp-${{matrix.compiler.name}}${{matrix.compiler.version}}-cuda${{matrix.cuda}}-${{matrix.os}}
+
+ build_and_test_windows:
+ name: build and test windows
+ permissions:
+ id-token: write
+ contents: read
+ if: ${{ inputs.is_windows }}
+ uses: ./.github/workflows/build-and-test-windows.yml
+ strategy:
+ fail-fast: false
+ matrix:
+ include: ${{ fromJSON(inputs.per_cuda_compiler_matrix) }}
+ with:
+ test_name: ${{matrix.cpu}}/${{matrix.compiler.name}}${{matrix.compiler.version}}
+ build_script: "./ci/windows/build_${{ inputs.project_name }}.ps1 ${{matrix.extra_build_args}}"
+ test_script: "./ci/windows/test_${{ inputs.project_name }}.ps1 ${{matrix.extra_build_args}}"
+ container_image: rapidsai/devcontainers:${{inputs.devcontainer_version}}-cuda${{matrix.cuda}}-${{matrix.compiler.name}}${{matrix.compiler.version}}-${{matrix.os}}
diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml
new file mode 100644
index 00000000..6ea22ab8
--- /dev/null
+++ b/.github/workflows/pr.yml
@@ -0,0 +1,95 @@
+# SPDX-FileCopyrightText: Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+# SPDX-License-Identifier: Apache-2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This is the main workflow that runs on every PR and push to main
+name: pr
+
+defaults:
+ run:
+ shell: bash -euo pipefail {0}
+
+on:
+ push:
+ branches:
+ - "pull-request/[0-9]+"
+
+# Only runs one instance of this workflow at a time for a given PR and cancels any in-progress runs when a new one starts.
+concurrency:
+ group: ${{ github.workflow }}-on-${{ github.event_name }}-from-${{ github.ref_name }}
+ cancel-in-progress: true
+
+permissions:
+ contents: read
+ pull-requests: read
+
+jobs:
+ compute-matrix:
+ name: Compute matrix
+ runs-on: ubuntu-latest
+ outputs:
+ DEVCONTAINER_VERSION: ${{steps.set-outputs.outputs.DEVCONTAINER_VERSION}}
+ PER_CUDA_COMPILER_MATRIX: ${{steps.set-outputs.outputs.PER_CUDA_COMPILER_MATRIX}}
+ PER_CUDA_COMPILER_KEYS: ${{steps.set-outputs.outputs.PER_CUDA_COMPILER_KEYS}}
+ steps:
+ - name: Checkout repo
+ uses: actions/checkout@v3
+ - name: Compute matrix outputs
+ id: set-outputs
+ run: |
+ .github/actions/compute-matrix/compute-matrix.sh ci/matrix.yaml pull_request
+
+ nvbench:
+ name: NVBench CUDA${{ matrix.cuda_host_combination }}
+ permissions:
+ id-token: write
+ contents: read
+ needs: compute-matrix
+ uses: ./.github/workflows/dispatch-build-and-test.yml
+ strategy:
+ fail-fast: false
+ matrix:
+ cuda_host_combination: ${{ fromJSON(needs.compute-matrix.outputs.PER_CUDA_COMPILER_KEYS) }}
+ with:
+ project_name: "nvbench"
+ per_cuda_compiler_matrix: ${{ toJSON(fromJSON(needs.compute-matrix.outputs.PER_CUDA_COMPILER_MATRIX)[ matrix.cuda_host_combination ]) }}
+ devcontainer_version: ${{ needs.compute-matrix.outputs.DEVCONTAINER_VERSION }}
+ is_windows: ${{ contains(matrix.cuda_host_combination, 'cl') }}
+
+ verify-devcontainers:
+ name: Verify Dev Containers
+ permissions:
+ id-token: write
+ contents: read
+ uses: ./.github/workflows/verify-devcontainers.yml
+
+ # This job is the final job that runs after all other jobs and is used for branch protection status checks.
+ # See: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/collaborating-on-repositories-with-code-quality-features/about-status-checks
+ # https://github.com/orgs/community/discussions/26822#discussioncomment-5122101
+ ci:
+ runs-on: ubuntu-latest
+ name: CI
+ if: ${{ always() }} # need to use always() instead of !cancelled() because skipped jobs count as success
+ needs:
+ - nvbench
+ - verify-devcontainers
+ steps:
+ - name: Check status of all precursor jobs
+ if: >-
+ ${{
+ contains(needs.*.result, 'failure')
+ || contains(needs.*.result, 'cancelled')
+ || contains(needs.*.result, 'skipped')
+ }}
+ run: exit 1
diff --git a/.github/workflows/run-as-coder.yml b/.github/workflows/run-as-coder.yml
new file mode 100644
index 00000000..29399b66
--- /dev/null
+++ b/.github/workflows/run-as-coder.yml
@@ -0,0 +1,67 @@
+name: Run as coder user
+
+defaults:
+ run:
+ shell: bash -exo pipefail {0}
+
+on:
+ workflow_call:
+ inputs:
+ name: {type: string, required: true}
+ image: {type: string, required: true}
+ runner: {type: string, required: true}
+ command: {type: string, required: true}
+ env: { type: string, required: false, default: "" }
+
+permissions:
+ contents: read
+
+jobs:
+ run-as-coder:
+ name: ${{inputs.name}}
+ permissions:
+ id-token: write
+ contents: read
+ runs-on: ${{inputs.runner}}
+ container:
+ options: -u root
+ image: ${{inputs.image}}
+ env:
+ NVIDIA_VISIBLE_DEVICES: ${{ env.NVIDIA_VISIBLE_DEVICES }}
+ steps:
+ - name: Checkout repo
+ uses: actions/checkout@v3
+ with:
+ path: nvbench
+ persist-credentials: false
+ - name: Move files to coder user home directory
+ run: |
+ cp -R nvbench /home/coder/nvbench
+ chown -R coder:coder /home/coder/
+ - name: Add NVCC problem matcher
+ run: |
+ echo "::add-matcher::nvbench/.github/problem-matchers/problem-matcher.json"
+ - name: Configure credentials and environment variables for sccache
+ uses: ./nvbench/.github/actions/configure_cccl_sccache
+ - name: Run command
+ shell: su coder {0}
+ run: |
+ set -eo pipefail
+ cd ~/nvbench
+ echo -e "\e[1;34mRunning as 'coder' user in $(pwd):\e[0m"
+ echo -e "\e[1;34m${{inputs.command}}\e[0m"
+ eval "${{inputs.command}}" || exit_code=$?
+ if [ ! -z "$exit_code" ]; then
+ echo -e "::group::️❗ \e[1;31mInstructions to Reproduce CI Failure Locally\e[0m"
+ echo "::error:: To replicate this failure locally, follow the steps below:"
+ echo "1. Clone the repository, and navigate to the correct branch and commit:"
+ echo " git clone --branch $GITHUB_REF_NAME --single-branch https://github.com/$GITHUB_REPOSITORY.git && cd $(echo $GITHUB_REPOSITORY | cut -d'/' -f2) && git checkout $GITHUB_SHA"
+ echo ""
+ echo "2. Run the failed command inside the same Docker container used by the CI:"
+ echo " docker run --rm -it --gpus all --pull=always --volume \$PWD:/repo --workdir /repo ${{ inputs.image }} ${{inputs.command}}"
+ echo ""
+ echo "For additional information, see:"
+ echo " - DevContainer Documentation: https://github.com/NVIDIA/cccl/blob/main/.devcontainer/README.md"
+ echo " - Continuous Integration (CI) Overview: https://github.com/NVIDIA/cccl/blob/main/ci-overview.md"
+ exit $exit_code
+ fi
diff --git a/.github/workflows/verify-devcontainers.yml b/.github/workflows/verify-devcontainers.yml
new file mode 100644
index 00000000..6fea8aeb
--- /dev/null
+++ b/.github/workflows/verify-devcontainers.yml
@@ -0,0 +1,94 @@
+name: Verify devcontainers
+
+on:
+ workflow_call:
+
+defaults:
+ run:
+ shell: bash -euo pipefail {0}
+
+permissions:
+ contents: read
+
+jobs:
+ verify-make-devcontainers:
+ name: Verify devcontainer files are up-to-date
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v3
+ - name: Setup jq and yq
+ run: |
+ sudo apt-get update
+ sudo apt-get install jq -y
+ sudo wget -O /usr/local/bin/yq https://github.com/mikefarah/yq/releases/download/v4.34.2/yq_linux_amd64
+ sudo chmod +x /usr/local/bin/yq
+ - name: Run the script to generate devcontainer files
+ run: |
+ ./.devcontainer/make_devcontainers.sh --verbose
+ - name: Check for changes
+ run: |
+ if [[ $(git diff --stat) != '' || $(git status --porcelain | grep '^??') != '' ]]; then
+ git diff --minimal
+ git status --porcelain
+ echo "::error:: Dev Container files are out of date or there are untracked files. Run the .devcontainer/make_devcontainers.sh script and commit the changes."
+ exit 1
+ else
+ echo "::note::Dev Container files are up-to-date."
+ fi
+
+ get-devcontainer-list:
+ needs: verify-make-devcontainers
+ name: Get list of devcontainer.json files
+ runs-on: ubuntu-latest
+ outputs:
+ devcontainers: ${{ steps.get-list.outputs.devcontainers }}
+ steps:
+ - name: Check out the code
+ uses: actions/checkout@v3
+ - name: Get list of devcontainer.json paths and names
+ id: get-list
+ run: |
+ devcontainers=$(find .devcontainer/ -name 'devcontainer.json' | while read -r devcontainer; do
+ jq --arg path "$devcontainer" '{path: $path, name: .name}' "$devcontainer"
+ done | jq -s -c .)
+ echo "devcontainers=${devcontainers}" | tee --append "${GITHUB_OUTPUT}"
+
+ verify-devcontainers:
+ needs: get-devcontainer-list
+ name: ${{matrix.devcontainer.name}}
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ devcontainer: ${{fromJson(needs.get-devcontainer-list.outputs.devcontainers)}}
+ permissions:
+ id-token: write
+ contents: read
+ steps:
+ - name: Check out the code
+ uses: actions/checkout@v3
+ # devcontainer/ci doesn't supported nested devcontainer.json files, so we need to copy the devcontainer.json
+ # file to the top level .devcontainer/ directory
+ - name: Copy devcontainer.json to .devcontainer/
+ run: |
+ src="${{ matrix.devcontainer.path }}"
+ dst=".devcontainer/devcontainer.json"
+ if [[ "$src" != "$dst" ]]; then
+ cp "$src" "$dst"
+ fi
+ # We don't really need sccache configured, but we need the AWS credentials envvars to be set
+ # in order to avoid the devcontainer hanging waiting for GitHub authentication
+ - name: Configure credentials and environment variables for sccache
+ uses: ./.github/actions/configure_cccl_sccache
+ - name: Run in devcontainer
+ uses: devcontainers/ci@v0.3
+ with:
+ push: never
+ env: |
+ SCCACHE_REGION=${{ env.SCCACHE_REGION }}
+ AWS_ACCESS_KEY_ID=${{ env.AWS_ACCESS_KEY_ID }}
+ AWS_SESSION_TOKEN=${{ env.AWS_SESSION_TOKEN }}
+ AWS_SECRET_ACCESS_KEY=${{ env.AWS_SECRET_ACCESS_KEY }}
+ runCmd: |
+ .devcontainer/verify_devcontainer.sh
diff --git a/.gitignore b/.gitignore
index 20d94d8a..50fac98d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,10 @@
build*/
+.aws
+.vscode
.cache
+.config
.idea
cmake-build-*
*~
+compile_commands.json
+CMakeUserPresets.json
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8b4518ff..8eb5f883 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -21,6 +21,11 @@ project(NVBench
nvbench_init_rapids_cmake()
+# Define NVBench_DETECTED_${LANG}_STANDARDS
+include(cmake/DetectSupportedStandards.cmake)
+detect_supported_standards(NVBench CXX 17 20)
+detect_supported_standards(NVBench CUDA 17 20)
+
# See NVIDIA/NVBench#52
find_package(CUDAToolkit REQUIRED)
set(cupti_default ON)
@@ -34,6 +39,7 @@ option(NVBench_ENABLE_NVML "Build with NVML support from the Cuda Toolkit." ON)
option(NVBench_ENABLE_CUPTI "Build NVBench with CUPTI." ${cupti_default})
option(NVBench_ENABLE_TESTING "Build NVBench testing suite." OFF)
+option(NVBench_ENABLE_HEADER_TESTING "Build NVBench testing suite." OFF)
option(NVBench_ENABLE_DEVICE_TESTING
"Include tests that require a GPU (with locked clocks)."
OFF
@@ -41,19 +47,24 @@ option(NVBench_ENABLE_DEVICE_TESTING
option(NVBench_ENABLE_EXAMPLES "Build NVBench examples." OFF)
option(NVBench_ENABLE_INSTALL_RULES "Install NVBench." ${NVBench_TOPLEVEL_PROJECT})
+include(cmake/NVBenchUtilities.cmake) # Must be first
+include(cmake/NVBenchClangdCompileInfo.cmake) # Must be before any targets are created
+
include(cmake/NVBenchConfigTarget.cmake)
include(cmake/NVBenchDependentDlls.cmake)
include(cmake/NVBenchExports.cmake)
include(cmake/NVBenchWriteConfigHeader.cmake)
include(cmake/NVBenchDependencies.cmake)
include(cmake/NVBenchInstallRules.cmake)
-include(cmake/NVBenchUtilities.cmake)
message(STATUS "NVBench CUDA architectures: ${CMAKE_CUDA_ARCHITECTURES}")
add_subdirectory(nvbench)
-if (NVBench_ENABLE_EXAMPLES OR NVBench_ENABLE_TESTING)
+if (NVBench_ENABLE_EXAMPLES OR
+ NVBench_ENABLE_TESTING OR
+ NVBench_ENABLE_HEADER_TESTING)
+ include(CTest)
enable_testing()
endif()
@@ -67,4 +78,8 @@ if (NVBench_ENABLE_TESTING)
add_subdirectory(testing)
endif()
+if (NVBench_ENABLE_HEADER_TESTING)
+ include(cmake/NVBenchHeaderTesting.cmake)
+endif()
+
nvbench_generate_exports()
diff --git a/CMakePresets.json b/CMakePresets.json
new file mode 100644
index 00000000..3e66f9ad
--- /dev/null
+++ b/CMakePresets.json
@@ -0,0 +1,74 @@
+{
+ "version": 3,
+ "cmakeMinimumRequired": {
+ "major": 3,
+ "minor": 23,
+ "patch": 1
+ },
+ "configurePresets": [
+ {
+ "name": "base",
+ "hidden": true,
+ "generator": "Ninja",
+ "binaryDir": "${sourceDir}/build/$env{CCCL_BUILD_INFIX}/${presetName}",
+ "cacheVariables": {
+ "CMAKE_BUILD_TYPE": "Release",
+ "CMAKE_CUDA_ARCHITECTURES": "all-major",
+ "NVBench_ENABLE_CUPTI": true,
+ "NVBench_ENABLE_DEVICE_TESTING": false,
+ "NVBench_ENABLE_EXAMPLES": true,
+ "NVBench_ENABLE_HEADER_TESTING": true,
+ "NVBench_ENABLE_INSTALL_RULES": true,
+ "NVBench_ENABLE_NVML": true,
+ "NVBench_ENABLE_TESTING": true,
+ "NVBench_ENABLE_WERROR": true
+ }
+ },
+ {
+ "name": "nvbench-dev",
+ "displayName": "Developer Build",
+ "inherits": "base",
+ "cacheVariables": {
+ "NVBench_ENABLE_DEVICE_TESTING": true
+ }
+ },
+ {
+ "name": "nvbench-ci",
+ "displayName": "NVBench CI",
+ "inherits": "base"
+ }
+ ],
+ "buildPresets": [
+ {
+ "name": "nvbench-dev",
+ "configurePreset": "nvbench-dev"
+ },
+ {
+ "name": "nvbench-ci",
+ "configurePreset": "nvbench-ci"
+ }
+ ],
+ "testPresets": [
+ {
+ "name": "base",
+ "hidden": true,
+ "output": {
+ "outputOnFailure": true
+ },
+ "execution": {
+ "noTestsAction": "error",
+ "stopOnFailure": false
+ }
+ },
+ {
+ "name": "nvbench-dev",
+ "configurePreset": "nvbench-dev",
+ "inherits": "base"
+ },
+ {
+ "name": "nvbench-ci",
+ "configurePreset": "nvbench-ci",
+ "inherits": "base"
+ }
+ ]
+}
diff --git a/README.md b/README.md
index c1cad5ad..285213f1 100644
--- a/README.md
+++ b/README.md
@@ -26,6 +26,15 @@ features:
* Executes the benchmark multiple times back-to-back and records total time.
* Reports the average execution time (total time / number of executions).
+# Supported Compilers and Tools
+
+- CMake > 2.23.1
+- CUDA Toolkit + nvcc: 11.1 -> 12.4
+- g++: 7 -> 12
+- clang++: 9 -> 18
+- cl.exe: 2019 -> 2022 (19.29, 29.39)
+- Headers are tested with C++17 -> C++20.
+
# Getting Started
## Minimal Benchmark
@@ -34,7 +43,7 @@ A basic kernel benchmark can be created with just a few lines of CUDA C++:
```cpp
void my_benchmark(nvbench::state& state) {
- state.exec([](nvbench::launch& launch) {
+ state.exec([](nvbench::launch& launch) {
my_kernel<<>>();
});
}
@@ -72,7 +81,7 @@ mkdir -p build
cd build
cmake -DNVBench_ENABLE_EXAMPLES=ON -DCMAKE_CUDA_ARCHITECTURES=70 .. && make
```
-Be sure to set `CMAKE_CUDA_ARCHITECTURE` based on the GPU you are running on.
+Be sure to set `CMAKE_CUDA_ARCHITECTURE` based on the GPU you are running on.
Examples are built by default into `build/bin` and are prefixed with `nvbench.example`.
@@ -119,7 +128,7 @@ Pass: Batch: 0.261963ms GPU, 7.18s total GPU, 27394x
## Demo Project
To get started using NVBench with your own kernels, consider trying out
-the [NVBench Demo Project](https://github.com/allisonvacanti/nvbench_demo).
+the [NVBench Demo Project](https://github.com/allisonvacanti/nvbench_demo).
`nvbench_demo` provides a simple CMake project that uses NVBench to build an
example benchmark. It's a great way to experiment with the library without a lot
@@ -129,7 +138,7 @@ of investment.
Contributions are welcome!
-For current issues, see the [issue board](https://github.com/NVIDIA/nvbench/issues). Issues labeled with [![](https://img.shields.io/github/labels/NVIDIA/nvbench/good%20first%20issue)](https://github.com/NVIDIA/nvbench/labels/good%20first%20issue) are good for first time contributors.
+For current issues, see the [issue board](https://github.com/NVIDIA/nvbench/issues). Issues labeled with [![](https://img.shields.io/github/labels/NVIDIA/nvbench/good%20first%20issue)](https://github.com/NVIDIA/nvbench/labels/good%20first%20issue) are good for first time contributors.
## Tests
@@ -146,7 +155,7 @@ To run all tests:
```
make test
```
-or
+or
```
ctest
```
diff --git a/ci/axis/cpu.yml b/ci/axis/cpu.yml
deleted file mode 100644
index 7230b666..00000000
--- a/ci/axis/cpu.yml
+++ /dev/null
@@ -1,38 +0,0 @@
-# Copyright (c) 2018-2020 NVIDIA Corporation
-# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-# Released under the Apache License v2.0 with LLVM Exceptions.
-# See https://llvm.org/LICENSE.txt for license information.
-
-SDK_TYPE:
- - cuda
-
-SDK_VER:
- - 11.5.1-devel
-
-OS_TYPE:
- - ubuntu
-
-OS_VER:
- - 20.04
-
-CXX_TYPE:
- - clang
- - gcc
-
-CXX_VER:
- - 5
- - 6
- - 7
- - 8
- - 9
- - 10
- - 11
- - 12
-
-exclude:
- - CXX_TYPE: clang
- CXX_VER: 5
- - CXX_TYPE: clang
- CXX_VER: 6
- - CXX_TYPE: gcc
- CXX_VER: 12
diff --git a/ci/axis/gpu.yml b/ci/axis/gpu.yml
deleted file mode 100644
index 15310794..00000000
--- a/ci/axis/gpu.yml
+++ /dev/null
@@ -1,30 +0,0 @@
-# Copyright (c) 2018-2020 NVIDIA Corporation
-# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-# Released under the Apache License v2.0 with LLVM Exceptions.
-# See https://llvm.org/LICENSE.txt for license information.
-
-SDK_TYPE:
- - cuda
-
-SDK_VER:
- - 11.5.1-devel
-
-OS_TYPE:
- - ubuntu
-
-OS_VER:
- - 20.04
-
-CXX_TYPE:
- - clang
- - gcc
-
-CXX_VER:
- - 11
- - 12
-
-exclude:
- - CXX_TYPE: clang
- CXX_VER: 11
- - CXX_TYPE: gcc
- CXX_VER: 12
diff --git a/ci/build_common.sh b/ci/build_common.sh
new file mode 100755
index 00000000..ee95b00f
--- /dev/null
+++ b/ci/build_common.sh
@@ -0,0 +1,246 @@
+#!/bin/bash
+
+set -eo pipefail
+
+# Ensure the script is being executed in its containing directory
+cd "$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )";
+
+# Script defaults
+HOST_COMPILER=${CXX:-g++} # $CXX if set, otherwise `g++`
+CXX_STANDARD=17
+CUDA_COMPILER=${CUDACXX:-nvcc} # $CUDACXX if set, otherwise `nvcc`
+CUDA_ARCHS= # Empty, use presets by default.
+GLOBAL_CMAKE_OPTIONS=()
+DISABLE_CUB_BENCHMARKS= # Enable to force-disable building CUB benchmarks.
+
+# Check if the correct number of arguments has been provided
+function usage {
+ echo "Usage: $0 [OPTIONS]"
+ echo
+ echo "The PARALLEL_LEVEL environment variable controls the amount of build parallelism. Default is the number of cores."
+ echo
+ echo "Options:"
+ echo " -v/--verbose: enable shell echo for debugging"
+ echo " -cuda: CUDA compiler (Defaults to \$CUDACXX if set, otherwise nvcc)"
+ echo " -cxx: Host compiler (Defaults to \$CXX if set, otherwise g++)"
+ echo " -std: CUDA/C++ standard (Defaults to 17)"
+ echo " -arch: Target CUDA arches, e.g. \"60-real;70;80-virtual\" (Defaults to value in presets file)"
+ echo " -cmake-options: Additional options to pass to CMake"
+ echo
+ echo "Examples:"
+ echo " $ PARALLEL_LEVEL=8 $0"
+ echo " $ PARALLEL_LEVEL=8 $0 -cxx g++-9"
+ echo " $ $0 -cxx clang++-8"
+ echo " $ $0 -cxx g++-8 -std 20 -arch 80-real -v -cuda /usr/local/bin/nvcc"
+ echo " $ $0 -cmake-options \"-DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS=-Wfatal-errors\""
+ exit 1
+}
+
+# Parse options
+
+# Copy the args into a temporary array, since we will modify them and
+# the parent script may still need them.
+args=("$@")
+while [ "${#args[@]}" -ne 0 ]; do
+ case "${args[0]}" in
+ -v | --verbose) VERBOSE=1; args=("${args[@]:1}");;
+ -cxx) HOST_COMPILER="${args[1]}"; args=("${args[@]:2}");;
+ -std) CXX_STANDARD="${args[1]}"; args=("${args[@]:2}");;
+ -cuda) CUDA_COMPILER="${args[1]}"; args=("${args[@]:2}");;
+ -arch) CUDA_ARCHS="${args[1]}"; args=("${args[@]:2}");;
+ -disable-benchmarks) DISABLE_CUB_BENCHMARKS=1; args=("${args[@]:1}");;
+ -cmake-options)
+ if [ -n "${args[1]}" ]; then
+ IFS=' ' read -ra split_args <<< "${args[1]}"
+ GLOBAL_CMAKE_OPTIONS+=("${split_args[@]}")
+ args=("${args[@]:2}")
+ else
+ echo "Error: No arguments provided for -cmake-options"
+ usage
+ exit 1
+ fi
+ ;;
+ -h | -help | --help) usage ;;
+ *) echo "Unrecognized option: ${args[0]}"; usage ;;
+ esac
+done
+
+# Convert to full paths:
+HOST_COMPILER=$(which ${HOST_COMPILER})
+CUDA_COMPILER=$(which ${CUDA_COMPILER})
+
+if [[ -n "${CUDA_ARCHS}" ]]; then
+ GLOBAL_CMAKE_OPTIONS+=("-DCMAKE_CUDA_ARCHITECTURES=${CUDA_ARCHS}")
+fi
+
+if [ $VERBOSE ]; then
+ set -x
+fi
+
+# Begin processing unsets after option parsing
+set -u
+
+readonly PARALLEL_LEVEL=${PARALLEL_LEVEL:=$(nproc)}
+
+if [ -z ${CCCL_BUILD_INFIX+x} ]; then
+ CCCL_BUILD_INFIX=""
+fi
+
+# Presets will be configured in this directory:
+BUILD_DIR="../build/${CCCL_BUILD_INFIX}"
+
+# The most recent build will always be symlinked to cccl/build/latest
+mkdir -p $BUILD_DIR
+rm -f ../build/latest
+ln -sf $BUILD_DIR ../build/latest
+
+# Now that BUILD_DIR exists, use readlink to canonicalize the path:
+BUILD_DIR=$(readlink -f "${BUILD_DIR}")
+
+# Prepare environment for CMake:
+export CMAKE_BUILD_PARALLEL_LEVEL="${PARALLEL_LEVEL}"
+export CTEST_PARALLEL_LEVEL="1"
+export CXX="${HOST_COMPILER}"
+export CUDACXX="${CUDA_COMPILER}"
+export CUDAHOSTCXX="${HOST_COMPILER}"
+export CXX_STANDARD
+
+source ./pretty_printing.sh
+
+print_environment_details() {
+ begin_group "⚙️ Environment Details"
+
+ echo "pwd=$(pwd)"
+
+ print_var_values \
+ BUILD_DIR \
+ CXX_STANDARD \
+ CXX \
+ CUDACXX \
+ CUDAHOSTCXX \
+ NVCC_VERSION \
+ CMAKE_BUILD_PARALLEL_LEVEL \
+ CTEST_PARALLEL_LEVEL \
+ CCCL_BUILD_INFIX \
+ GLOBAL_CMAKE_OPTIONS
+
+ echo "Current commit is:"
+ git log -1 || echo "Not a repository"
+
+ if command -v nvidia-smi &> /dev/null; then
+ nvidia-smi
+ else
+ echo "nvidia-smi not found"
+ fi
+
+ end_group "⚙️ Environment Details"
+}
+
+fail_if_no_gpu() {
+ if ! nvidia-smi &> /dev/null; then
+ echo "Error: No NVIDIA GPU detected. Please ensure you have an NVIDIA GPU installed and the drivers are properly configured." >&2
+ exit 1
+ fi
+}
+
+function print_test_time_summary()
+{
+ ctest_log=${1}
+
+ if [ -f ${ctest_log} ]; then
+ begin_group "⏱️ Longest Test Steps"
+ # Only print the full output in CI:
+ if [ -n "${GITHUB_ACTIONS:-}" ]; then
+ cmake -DLOGFILE=${ctest_log} -P ../cmake/PrintCTestRunTimes.cmake
+ else
+ cmake -DLOGFILE=${ctest_log} -P ../cmake/PrintCTestRunTimes.cmake | head -n 15
+ fi
+ end_group "⏱️ Longest Test Steps"
+ fi
+}
+
+function configure_preset()
+{
+ local BUILD_NAME=$1
+ local PRESET=$2
+ local CMAKE_OPTIONS=$3
+ local GROUP_NAME="🛠️ CMake Configure ${BUILD_NAME}"
+
+ pushd .. > /dev/null
+ run_command "$GROUP_NAME" cmake --preset=$PRESET --log-level=VERBOSE "${GLOBAL_CMAKE_OPTIONS[@]}" $CMAKE_OPTIONS
+ status=$?
+ popd > /dev/null
+ return $status
+}
+
+function build_preset() {
+ local BUILD_NAME=$1
+ local PRESET=$2
+ local green="1;32"
+ local red="1;31"
+ local GROUP_NAME="🏗️ Build ${BUILD_NAME}"
+
+ source "./sccache_stats.sh" "start"
+
+ pushd .. > /dev/null
+ run_command "$GROUP_NAME" cmake --build --preset=$PRESET -v
+ status=$?
+ popd > /dev/null
+
+ minimal_sccache_stats=$(source "./sccache_stats.sh" "end")
+
+ # Only print detailed stats in actions workflow
+ if [ -n "${GITHUB_ACTIONS:-}" ]; then
+ begin_group "💲 sccache stats"
+ echo "${minimal_sccache_stats}"
+ sccache -s
+ end_group
+
+ begin_group "🥷 ninja build times"
+ echo "The "weighted" time is the elapsed time of each build step divided by the number
+ of tasks that were running in parallel. This makes it an excellent approximation
+ of how "important" a slow step was. A link that is entirely or mostly serialized
+ will have a weighted time that is the same or similar to its elapsed time. A
+ compile that runs in parallel with 999 other compiles will have a weighted time
+ that is tiny."
+ ./ninja_summary.py -C ${BUILD_DIR}/${PRESET}
+ end_group
+ else
+ echo $minimal_sccache_stats
+ fi
+
+ return $status
+}
+
+function test_preset()
+{
+ local BUILD_NAME=$1
+ local PRESET=$2
+ local GROUP_NAME="🚀 Test ${BUILD_NAME}"
+
+ fail_if_no_gpu
+
+
+ ctest_log_dir="${BUILD_DIR}/log/ctest"
+ ctest_log="${ctest_log_dir}/${PRESET}"
+ mkdir -p "${ctest_log_dir}"
+
+ pushd .. > /dev/null
+ run_command "$GROUP_NAME" ctest --output-log "${ctest_log}" --preset=$PRESET
+ status=$?
+ popd > /dev/null
+
+ print_test_time_summary ${ctest_log}
+
+ return $status
+}
+
+function configure_and_build_preset()
+{
+ local BUILD_NAME=$1
+ local PRESET=$2
+ local CMAKE_OPTIONS=$3
+
+ configure_preset "$BUILD_NAME" "$PRESET" "$CMAKE_OPTIONS"
+ build_preset "$BUILD_NAME" "$PRESET"
+}
diff --git a/ci/build_nvbench.sh b/ci/build_nvbench.sh
new file mode 100755
index 00000000..cc245d3a
--- /dev/null
+++ b/ci/build_nvbench.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+source "$(dirname "$0")/build_common.sh"
+
+print_environment_details
+
+PRESET="nvbench-ci"
+
+CMAKE_OPTIONS=""
+
+configure_and_build_preset "NVBench" "$PRESET" "$CMAKE_OPTIONS"
+
+print_time_summary
diff --git a/ci/common/build.bash b/ci/common/build.bash
deleted file mode 100755
index 61b3654c..00000000
--- a/ci/common/build.bash
+++ /dev/null
@@ -1,231 +0,0 @@
-#! /usr/bin/env bash
-
-# Copyright (c) 2018-2020 NVIDIA Corporation
-# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-# Released under the Apache License v2.0 with LLVM Exceptions.
-# See https://llvm.org/LICENSE.txt for license information.
-
-################################################################################
-# NVBench build script for gpuCI
-################################################################################
-
-set -e
-
-# append variable value
-# Appends ${value} to ${variable}, adding a space before ${value} if
-# ${variable} is not empty.
-function append {
- tmp="${!1:+${!1} }${2}"
- eval "${1}=\${tmp}"
-}
-
-# log args...
-# Prints out ${args[*]} with a gpuCI log prefix and a newline before and after.
-function log() {
- printf "\n>>>> %s\n\n" "${*}"
-}
-
-# print_with_trailing_blank_line args...
-# Prints ${args[*]} with one blank line following, preserving newlines within
-# ${args[*]} but stripping any preceding ${args[*]}.
-function print_with_trailing_blank_line {
- printf "%s\n\n" "${*}"
-}
-
-# echo_and_run name args...
-# Echo ${args[@]}, then execute ${args[@]}
-function echo_and_run {
- echo "${1}: ${@:2}"
- ${@:2}
-}
-
-# echo_and_run_timed name args...
-# Echo ${args[@]}, then execute ${args[@]} and report how long it took,
-# including ${name} in the output of the time.
-function echo_and_run_timed {
- echo "${@:2}"
- TIMEFORMAT=$'\n'"${1} Time: %lR"
- time ${@:2}
-}
-
-# join_delimit [value [value [...]]]
-# Combine all values into a single string, separating each by a single character
-# delimiter. Eg:
-# foo=(bar baz kramble)
-# joined_foo=$(join_delimit "|" "${foo[@]}")
-# echo joined_foo # "bar|baz|kramble"
-function join_delimit {
- local IFS="${1}"
- shift
- echo "${*}"
-}
-
-################################################################################
-# VARIABLES - Set up bash and environmental variables.
-################################################################################
-
-# Get the variables the Docker container set up for us: ${CXX}, ${CUDACXX}, etc.
-source /etc/cccl.bashrc
-
-# Set path.
-export PATH=/usr/local/cuda/bin:${PATH}
-
-# Set home to the job's workspace.
-export HOME=${WORKSPACE}
-
-# Switch to the build directory.
-cd ${WORKSPACE}
-mkdir -p build
-cd build
-
-# Remove any old .ninja_log file so the PrintNinjaBuildTimes step is accurate:
-rm -f .ninja_log
-
-if [[ -z "${CMAKE_BUILD_TYPE}" ]]; then
- CMAKE_BUILD_TYPE="Release"
-fi
-
-CMAKE_BUILD_FLAGS="--"
-
-# The Docker image sets up `${CXX}` and `${CUDACXX}`.
-append CMAKE_FLAGS "-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}"
-append CMAKE_FLAGS "-DCMAKE_CUDA_COMPILER='${CUDACXX}'"
-
-if [[ "${CXX_TYPE}" == "nvcxx" ]]; then
- echo "nvc++ not supported."
- exit 1
-else
- if [[ "${CXX_TYPE}" == "icc" ]]; then
- echo "icc not supported."
- exit 1
- fi
- # We're using NVCC so we need to set the host compiler.
- append CMAKE_FLAGS "-DCMAKE_CXX_COMPILER='${CXX}'"
- append CMAKE_FLAGS "-DCMAKE_CUDA_HOST_COMPILER='${CXX}'"
- append CMAKE_FLAGS "-G Ninja"
- # Don't stop on build failures.
- append CMAKE_BUILD_FLAGS "-k0"
-fi
-
-if [[ -n "${PARALLEL_LEVEL}" ]]; then
- DETERMINE_PARALLELISM_FLAGS="-j ${PARALLEL_LEVEL}"
-fi
-
-WSL=0
-if [[ $(grep -i microsoft /proc/version) ]]; then
- echo "Windows Subsystem for Linux detected."
- WSL=1
-fi
-export WSL
-
-#append CMAKE_FLAGS "-DCMAKE_CUDA_ARCHITECTURES=all"
-
-append CMAKE_FLAGS "-DNVBench_ENABLE_EXAMPLES=ON"
-append CMAKE_FLAGS "-DNVBench_ENABLE_TESTING=ON"
-append CMAKE_FLAGS "-DNVBench_ENABLE_CUPTI=ON"
-append CMAKE_FLAGS "-DNVBench_ENABLE_WERROR=ON"
-
-# These consume a lot of time and don't currently have
-# any value as regression tests.
-append CMAKE_FLAGS "-DNVBench_ENABLE_DEVICE_TESTING=OFF"
-
-# NVML doesn't work under WSL
-if [[ ${WSL} -eq 0 ]]; then
- append CMAKE_FLAGS "-DNVBench_ENABLE_NVML=ON"
-else
- append CMAKE_FLAGS "-DNVBench_ENABLE_NVML=OFF"
-fi
-
-if [[ -n "${@}" ]]; then
- append CMAKE_BUILD_FLAGS "${@}"
-fi
-
-append CTEST_FLAGS "--output-on-failure"
-
-# Export variables so they'll show up in the logs when we report the environment.
-export CMAKE_FLAGS
-export CMAKE_BUILD_FLAGS
-export CTEST_FLAGS
-
-################################################################################
-# ENVIRONMENT - Configure and print out information about the environment.
-################################################################################
-
-log "Determine system topology..."
-
-# Set `${PARALLEL_LEVEL}` if it is unset; otherwise, this just reports the
-# system topology.
-source ${WORKSPACE}/ci/common/determine_build_parallelism.bash ${DETERMINE_PARALLELISM_FLAGS}
-
-log "Get environment..."
-
-env | sort
-
-log "Check versions..."
-
-# We use sed and echo below to ensure there is always one and only trailing
-# line following the output from each tool.
-
-${CXX} --version 2>&1 | sed -Ez '$ s/\n*$/\n/'
-
-echo
-
-${CUDACXX} --version 2>&1 | sed -Ez '$ s/\n*$/\n/'
-
-echo
-
-cmake --version 2>&1 | sed -Ez '$ s/\n*$/\n/'
-
-echo
-
-if [[ "${BUILD_TYPE}" == "gpu" ]]; then
- nvidia-smi 2>&1 | sed -Ez '$ s/\n*$/\n/'
-fi
-
-################################################################################
-# BUILD
-################################################################################
-
-log "Configure..."
-
-echo_and_run_timed "Configure" cmake .. --log-level=VERBOSE ${CMAKE_FLAGS}
-configure_status=$?
-
-log "Build..."
-
-# ${PARALLEL_LEVEL} needs to be passed after we run
-# determine_build_parallelism.bash, so it can't be part of ${CMAKE_BUILD_FLAGS}.
-set +e # Don't stop on build failures.
-echo_and_run_timed "Build" cmake --build . ${CMAKE_BUILD_FLAGS} -j ${PARALLEL_LEVEL}
-build_status=$?
-set -e
-
-################################################################################
-# TEST - Run examples and tests.
-################################################################################
-
-log "Test..."
-
-(
- # Make sure test_status captures ctest, not tee:
- # https://stackoverflow.com/a/999259/11130318
- set -o pipefail
- echo_and_run_timed "Test" ctest ${CTEST_FLAGS} -j ${PARALLEL_LEVEL} | tee ctest_log
-)
-
-test_status=$?
-
-################################################################################
-# SUMMARY - Print status of each step and exit with failure if needed.
-################################################################################
-
-log "Summary:"
-echo "- Configure Error Code: ${configure_status}"
-echo "- Build Error Code: ${build_status}"
-echo "- Test Error Code: ${test_status}"
-
-if [[ "${configure_status}" != "0" ]] || \
- [[ "${build_status}" != "0" ]] || \
- [[ "${test_status}" != "0" ]]; then
- exit 1
-fi
diff --git a/ci/common/determine_build_parallelism.bash b/ci/common/determine_build_parallelism.bash
deleted file mode 100755
index 1a1cf4c7..00000000
--- a/ci/common/determine_build_parallelism.bash
+++ /dev/null
@@ -1,119 +0,0 @@
-#! /usr/bin/env bash
-
-# Copyright (c) 2018-2020 NVIDIA Corporation
-# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-# Released under the Apache License v2.0 with LLVM Exceptions.
-# See https://llvm.org/LICENSE.txt for license information.
-
-function usage {
- echo "Usage: ${0} [flags...]"
- echo
- echo "Examine the system topology to determine a reasonable amount of build"
- echo "parallelism."
- echo
- echo "Exported variables:"
- echo " \${LOGICAL_CPUS} : Logical processors (e.g. threads)."
- echo " \${PHYSICAL_CPUS} : Physical processors (e.g. cores)."
- echo " \${TOTAL_MEM} : Total system memory [GB]."
- echo " \${MAX_THREADS_PER_CORE} : Maximum threads per core allowed."
- echo " \${MIN_MEMORY_PER_THREAD} : Minimum memory [GB] per thread allowed."
- echo " \${CPU_BOUND_THREADS} : # of build threads constrained by processors."
- echo " \${MEM_BOUND_THREADS} : # of build threads constrained by memory [GB]."
- echo " \${PARALLEL_LEVEL} : Determined # of build threads."
- echo " \${MEM_PER_THREAD} : Memory [GB] per build thread."
- echo
- echo "-h, -help, --help"
- echo " Print this message."
- echo
- echo "-q, --quiet"
- echo " Print nothing and only export variables."
- echo
- echo "-j , --jobs "
- echo " Explicitly set the number of build threads to use."
- echo
- echo "--max-threads-per-core "
- echo " Specify the maximum threads per core allowed (default: ${MAX_THREADS_PER_CORE} [threads/core])."
- echo
- echo "--min-memory-per-thread "
- echo " Specify the minimum memory per thread allowed (default: ${MIN_MEMORY_PER_THREAD} [GBs/thread])."
-
- exit -3
-}
-
-QUIET=0
-
-export MAX_THREADS_PER_CORE=2
-export MIN_MEMORY_PER_THREAD=1 # [GB]
-
-while test ${#} != 0
-do
- case "${1}" in
- -h) ;&
- -help) ;&
- --help) usage ;;
- -q) ;&
- --quiet) QUIET=1 ;;
- -j) ;&
- --jobs)
- shift # The next argument is the number of threads.
- PARALLEL_LEVEL="${1}"
- ;;
- --max-threads-per-core)
- shift # The next argument is the number of threads per core.
- MAX_THREADS_PER_CORE="${1}"
- ;;
- --min-memory-per-thread)
- shift # The next argument is the amount of memory per thread.
- MIN_MEMORY_PER_THREAD="${1}"
- ;;
- esac
- shift
-done
-
-# https://stackoverflow.com/a/23378780
-if [ $(uname) == "Darwin" ]; then
- export LOGICAL_CPUS=$(sysctl -n hw.logicalcpu_max)
- export PHYSICAL_CPUS=$(sysctl -n hw.physicalcpu_max)
-else
- export LOGICAL_CPUS=$(lscpu -p | egrep -v '^#' | wc -l)
- export PHYSICAL_CPUS=$(lscpu -p | egrep -v '^#' | sort -u -t, -k 2,4 | wc -l)
-fi
-
-export TOTAL_MEM=$(awk "BEGIN { printf \"%0.4g\", $(grep MemTotal /proc/meminfo | awk '{ print $2 }') / (1024 * 1024) }")
-
-export CPU_BOUND_THREADS=$(awk "BEGIN { printf \"%.04g\", int(${PHYSICAL_CPUS} * ${MAX_THREADS_PER_CORE}) }")
-export MEM_BOUND_THREADS=$(awk "BEGIN { printf \"%.04g\", int(${TOTAL_MEM} / ${MIN_MEMORY_PER_THREAD}) }")
-
-if [[ -z "${PARALLEL_LEVEL}" ]]; then
- # Pick the smaller of the two as the default.
- if [[ "${MEM_BOUND_THREADS}" -lt "${CPU_BOUND_THREADS}" ]]; then
- export PARALLEL_LEVEL=${MEM_BOUND_THREADS}
- else
- export PARALLEL_LEVEL=${CPU_BOUND_THREADS}
- fi
-else
- EXPLICIT_PARALLEL_LEVEL=1
-fi
-
-# This can be a floating point number.
-export MEM_PER_THREAD=$(awk "BEGIN { printf \"%.04g\", ${TOTAL_MEM} / ${PARALLEL_LEVEL} }")
-
-if [[ "${QUIET}" == 0 ]]; then
- echo "Logical CPUs: ${LOGICAL_CPUS} [threads]"
- echo "Physical CPUs: ${PHYSICAL_CPUS} [cores]"
- echo "Total Mem: ${TOTAL_MEM} [GBs]"
- echo "Max Threads Per Core: ${MAX_THREADS_PER_CORE} [threads/core]"
- echo "Min Memory Per Threads: ${MIN_MEMORY_PER_THREAD} [GBs/thread]"
- echo "CPU Bound Threads: ${CPU_BOUND_THREADS} [threads]"
- echo "Mem Bound Threads: ${MEM_BOUND_THREADS} [threads]"
-
- echo -n "Parallel Level: ${PARALLEL_LEVEL} [threads]"
- if [[ -n "${EXPLICIT_PARALLEL_LEVEL}" ]]; then
- echo " (explicitly set)"
- else
- echo
- fi
-
- echo "Mem Per Thread: ${MEM_PER_THREAD} [GBs/thread]"
-fi
-
diff --git a/ci/cpu/build.bash b/ci/cpu/build.bash
deleted file mode 100755
index edf1ba31..00000000
--- a/ci/cpu/build.bash
+++ /dev/null
@@ -1,14 +0,0 @@
-#! /usr/bin/env bash
-
-# Copyright (c) 2018-2020 NVIDIA Corporation
-# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-# Released under the Apache License v2.0 with LLVM Exceptions.
-# See https://llvm.org/LICENSE.txt for license information.
-
-################################################################################
-# NVBench build script for gpuCI (CPU-only)
-################################################################################
-
-export PARALLEL_LEVEL=${PARALLEL_LEVEL:-4}
-
-source ${WORKSPACE}/ci/common/build.bash
diff --git a/ci/gpu/build.bash b/ci/gpu/build.bash
deleted file mode 100755
index 9f6fc01f..00000000
--- a/ci/gpu/build.bash
+++ /dev/null
@@ -1,14 +0,0 @@
-#! /usr/bin/env bash
-
-# Copyright (c) 2018-2020 NVIDIA Corporation
-# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-# Released under the Apache License v2.0 with LLVM Exceptions.
-# See https://llvm.org/LICENSE.txt for license information.
-
-################################################################################
-# NVBench build script for gpuCI (heterogeneous)
-################################################################################
-
-export PARALLEL_LEVEL=${PARALLEL_LEVEL:-4}
-
-source ${WORKSPACE}/ci/common/build.bash
diff --git a/ci/local/build.bash b/ci/local/build.bash
deleted file mode 100755
index 60d22dea..00000000
--- a/ci/local/build.bash
+++ /dev/null
@@ -1,215 +0,0 @@
-#! /usr/bin/env bash
-
-# Copyright (c) 2018-2020 NVIDIA Corporation
-# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-# Released under the Apache License v2.0 with LLVM Exceptions.
-# See https://llvm.org/LICENSE.txt for license information.
-
-################################################################################
-# NVBench local containerized build script
-################################################################################
-
-function usage {
- echo "Usage: ${0} [flags...] [cmake-targets...]"
- echo
- echo "Build and test your local repository using a gpuCI Docker image."
- echo "If CMake targets are specified, only those targets are built and tested."
- echo "Otherwise, everything is built and tested."
- echo
- echo "-h, -help, --help"
- echo " Print this message."
- echo
- echo "-r , --repository "
- echo " Path to the repository (default: ${REPOSITORY_PATH})."
- echo
- echo "-i , --image "
- echo " Docker image to use (default: ${IMAGE})"
- echo
- echo "-l, --local-image"
- echo " Use the local version of the image instead of pulling from Docker hub."
- echo
- echo "-s, --shell-only"
- echo " Skip building and testing and launch an interactive shell instead."
- echo
- echo "-d, --disable-gpus"
- echo " Don't start the container with the NVIDIA runtime and GPUs attached."
- echo
- echo "-c, --clean"
- echo " If the build directory already exists, delete it."
- echo
- echo "-j , --jobs "
- echo " Number of threads to use when building (default: inferred)."
- echo
- echo "-b , --cmake-build-type "
- echo " CMake build type to use, either Release, RelWithDebInfo, or Debug"
- echo " (default: ${CMAKE_BUILD_TYPE})."
- echo
-
- exit -3
-}
-
-SCRIPT_PATH=$(cd $(dirname ${0}); pwd -P)
-
-REPOSITORY_PATH=$(realpath ${SCRIPT_PATH}/../..)
-
-################################################################################
-# FLAGS - Process command line flags.
-################################################################################
-
-IMAGE="gpuci/cccl:cuda11.5.1-devel-ubuntu20.04-gcc9"
-
-LOCAL_IMAGE=0
-
-SHELL_ONLY=0
-
-BUILD_TYPE="gpu"
-
-CLEAN=0
-
-PARALLEL_LEVEL=""
-
-CMAKE_BUILD_TYPE="Release"
-
-TARGETS=""
-
-while test ${#} != 0
-do
- case "${1}" in
- -h) ;&
- -help) ;&
- --help) usage ;;
- -r) ;&
- --repository)
- shift # The next argument is the path.
- REPOSITORY_PATH="${1}"
- ;;
- -i) ;&
- --image)
- shift # The next argument is the image.
- IMAGE="${1}"
- ;;
- -l) ;&
- --local-image) LOCAL_IMAGE=1 ;;
- -s) ;&
- --shell-only) SHELL_ONLY=1 ;;
- -d) ;&
- --disable-gpus) BUILD_TYPE="cpu" ;;
- -c) ;&
- --clean) CLEAN=1 ;;
- -j) ;&
- --jobs)
- shift # The next argument is the number of threads.
- PARALLEL_LEVEL="${1}"
- ;;
- -b) ;&
- --cmake-build-type)
- shift # The next argument is the build type.
- CMAKE_BUILD_TYPE="${1}"
- ;;
- *)
- TARGETS="${TARGETS:+${TARGETS} }${1}"
- ;;
- esac
- shift
-done
-
-################################################################################
-# PATHS - Setup paths for the container.
-################################################################################
-
-# ${REPOSITORY_PATH} is the local filesystem path to the Git repository being
-# built and tested. It can be set with the --repository flag.
-#
-# ${BUILD_PATH} is the local filesystem path that will be used for the build. It
-# is named after the image name, allowing multiple image builds to coexist on
-# the local filesystem.
-#
-# ${REPOSITORY_PATH_IN_CONTAINER} is the location of ${REPOSITORY_PATH} inside
-# the container.
-#
-# ${BUILD_PATH_IN_CONTAINER} is the location of ${BUILD_PATH} inside the
-# container.
-
-BUILD_PATH=${REPOSITORY_PATH}/build_$(echo "$(basename "${IMAGE}")" | sed -e 's/:/_/g' | sed -e 's/-/_/g')
-
-if [[ "${CLEAN}" != 0 ]]; then
- rm -rf ${BUILD_PATH}
-fi
-
-mkdir -p ${BUILD_PATH}
-
-BASE_PATH_IN_CONTAINER="/cccl"
-
-REPOSITORY_PATH_IN_CONTAINER="${BASE_PATH_IN_CONTAINER}/$(basename "${REPOSITORY_PATH}")"
-
-BUILD_PATH_IN_CONTAINER="${BASE_PATH_IN_CONTAINER}/$(basename "${REPOSITORY_PATH}")/build"
-
-################################################################################
-# ENVIRONMENT - Setup the thunk build script that will be run by the container.
-################################################################################
-
-# We have to run `ldconfig` to rebuild `ld.so.cache` to work around this
-# failure on Debian: https://github.com/NVIDIA/nvidia-docker/issues/1399
-
-COMMAND="sudo ldconfig; sudo ldconfig"
-if [[ "${SHELL_ONLY}" != 0 ]]; then
- COMMAND="${COMMAND}; bash"
-else
- COMMAND="${COMMAND}; ${REPOSITORY_PATH_IN_CONTAINER}/ci/common/build.bash ${TARGETS} || bash"
-fi
-
-################################################################################
-# GPU - Setup GPUs.
-################################################################################
-
-# Note: We always start docker with --gpus, even for cpu builds. Otherwise
-# libcuda.so.1 is not present and no NVBench tests are able to run.
-
-# Limit GPUs available to the container based on ${CUDA_VISIBLE_DEVICES}.
-if [[ -z "${CUDA_VISIBLE_DEVICES}" ]]; then
- VISIBLE_DEVICES="all"
-else
- VISIBLE_DEVICES="${CUDA_VISIBLE_DEVICES}"
-fi
-
-DOCKER_MAJOR_VER=$(docker -v | sed 's/[^[0-9]*\([0-9]*\).*/\1/')
-GPU_OPTS="--gpus device=${VISIBLE_DEVICES}"
-if [[ "${DOCKER_MAJOR_VER}" -lt 19 ]]
-then
- GPU_OPTS="--runtime=nvidia -e NVIDIA_VISIBLE_DEVICES='${VISIBLE_DEVICES}'"
-fi
-
-################################################################################
-# LAUNCH - Pull and launch the container.
-################################################################################
-
-#NVIDIA_DOCKER_INSTALLED=$(docker info 2>&1 | grep -i runtime | grep -c nvidia)
-NVIDIA_DOCKER_INSTALLED=1 # Broken on WSL
-if [[ "${NVIDIA_DOCKER_INSTALLED}" == 0 ]]; then
- echo "NVIDIA Docker not found, please install it: https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html#installing-docker-ce"
- exit -4
-fi
-
-if [[ "${LOCAL_IMAGE}" == 0 ]]; then
- docker pull "${IMAGE}"
-fi
-
-docker run --rm -it ${GPU_OPTS} \
- --cap-add=SYS_PTRACE \
- --user "$(id -u)":"$(id -g)" \
- -v "${REPOSITORY_PATH}":"${REPOSITORY_PATH_IN_CONTAINER}" \
- -v "${BUILD_PATH}":"${BUILD_PATH_IN_CONTAINER}" \
- -v /etc/passwd:/etc/passwd:ro \
- -v /etc/group:/etc/group:ro \
- -v /etc/subuid:/etc/subuid:ro \
- -v /etc/subgid:/etc/subgid:ro \
- -v /etc/shadow:/etc/shadow:ro \
- -v /etc/gshadow:/etc/gshadow:ro \
- -e "WORKSPACE=${REPOSITORY_PATH_IN_CONTAINER}" \
- -e "BUILD_TYPE=${BUILD_TYPE}" \
- -e "CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}" \
- -e "COVERAGE_PLAN=${COVERAGE_PLAN}" \
- -e "PARALLEL_LEVEL=${PARALLEL_LEVEL}" \
- -w "${BUILD_PATH_IN_CONTAINER}" \
- "${IMAGE}" bash -c "${COMMAND}"
-
diff --git a/ci/matrix.yaml b/ci/matrix.yaml
new file mode 100644
index 00000000..99594730
--- /dev/null
+++ b/ci/matrix.yaml
@@ -0,0 +1,86 @@
+
+cuda_prev_min: &cuda_prev_min '11.1' # Does not support the CUPTI APIs we use (added in 11.3)
+cuda_prev_max: &cuda_prev_max '11.8'
+cuda_curr_min: &cuda_curr_min '12.0'
+cuda_curr_max: &cuda_curr_max '12.4'
+
+# The version of the devcontainer images to use from https://hub.docker.com/r/rapidsai/devcontainers
+devcontainer_version: '24.06'
+
+# gcc compiler configurations
+gcc7: &gcc7 { name: 'gcc', version: '7', exe: 'g++' }
+gcc8: &gcc8 { name: 'gcc', version: '8', exe: 'g++' }
+gcc9: &gcc9 { name: 'gcc', version: '9', exe: 'g++' }
+gcc10: &gcc10 { name: 'gcc', version: '10', exe: 'g++' }
+gcc11: &gcc11 { name: 'gcc', version: '11', exe: 'g++' }
+gcc12: &gcc12 { name: 'gcc', version: '12', exe: 'g++' }
+gcc12: &gcc13 { name: 'gcc', version: '13', exe: 'g++' }
+
+# LLVM Compiler configurations
+llvm9: &llvm9 { name: 'llvm', version: '9', exe: 'clang++' }
+llvm10: &llvm10 { name: 'llvm', version: '10', exe: 'clang++' }
+llvm11: &llvm11 { name: 'llvm', version: '11', exe: 'clang++' }
+llvm12: &llvm12 { name: 'llvm', version: '12', exe: 'clang++' }
+llvm13: &llvm13 { name: 'llvm', version: '13', exe: 'clang++' }
+llvm14: &llvm14 { name: 'llvm', version: '14', exe: 'clang++' }
+llvm15: &llvm15 { name: 'llvm', version: '15', exe: 'clang++' }
+llvm16: &llvm16 { name: 'llvm', version: '16', exe: 'clang++' }
+llvm16: &llvm17 { name: 'llvm', version: '17', exe: 'clang++' }
+llvm16: &llvm18 { name: 'llvm', version: '18', exe: 'clang++' }
+
+# MSVC configs
+msvc2019: &msvc2019 { name: 'cl', version: '14.29', exe: 'cl++' }
+msvc2022: &msvc2022 { name: 'cl', version: '14.39', exe: 'cl++' }
+
+# Each environment below will generate a unique build/test job
+# See the "compute-matrix" job in the workflow for how this is parsed and used
+# cuda: The CUDA Toolkit version
+# os: The operating system used
+# cpu: The CPU architecture
+# compiler: The compiler to use
+# name: The compiler name
+# version: The compiler version
+# exe: The unverionsed compiler binary name
+# std: The C++ standards to build for
+# This field is unique as it will generate an independent build/test job for each value
+
+# Configurations that will run for every PR
+pull_request:
+ nvcc:
+ - {cuda: *cuda_prev_min, os: 'ubuntu18.04', cpu: 'amd64', compiler: *gcc7, extra_build_args: "-cmake-options '-DNVBench_ENABLE_CUPTI=OFF'"}
+ - {cuda: *cuda_prev_min, os: 'ubuntu18.04', cpu: 'amd64', compiler: *gcc8, extra_build_args: "-cmake-options '-DNVBench_ENABLE_CUPTI=OFF'"}
+ - {cuda: *cuda_prev_min, os: 'ubuntu18.04', cpu: 'amd64', compiler: *gcc9, extra_build_args: "-cmake-options '-DNVBench_ENABLE_CUPTI=OFF'"}
+ - {cuda: *cuda_prev_min, os: 'ubuntu18.04', cpu: 'amd64', compiler: *llvm9, extra_build_args: "-cmake-options '-DNVBench_ENABLE_CUPTI=OFF'"}
+ - {cuda: *cuda_prev_max, os: 'ubuntu22.04', cpu: 'amd64', compiler: *gcc11}
+ - {cuda: *cuda_curr_min, os: 'ubuntu20.04', cpu: 'amd64', compiler: *gcc7}
+ - {cuda: *cuda_curr_min, os: 'ubuntu20.04', cpu: 'amd64', compiler: *gcc8}
+ - {cuda: *cuda_curr_min, os: 'ubuntu20.04', cpu: 'amd64', compiler: *gcc9}
+ - {cuda: *cuda_curr_min, os: 'ubuntu20.04', cpu: 'amd64', compiler: *gcc10}
+ - {cuda: *cuda_curr_min, os: 'ubuntu22.04', cpu: 'amd64', compiler: *gcc11}
+ - {cuda: *cuda_curr_min, os: 'ubuntu22.04', cpu: 'amd64', compiler: *gcc12}
+ - {cuda: *cuda_curr_min, os: 'ubuntu20.04', cpu: 'amd64', compiler: *llvm9}
+ - {cuda: *cuda_curr_min, os: 'ubuntu20.04', cpu: 'amd64', compiler: *llvm10}
+ - {cuda: *cuda_curr_min, os: 'ubuntu20.04', cpu: 'amd64', compiler: *llvm11}
+ - {cuda: *cuda_curr_min, os: 'ubuntu20.04', cpu: 'amd64', compiler: *llvm12}
+ - {cuda: *cuda_curr_min, os: 'ubuntu20.04', cpu: 'amd64', compiler: *llvm13}
+ - {cuda: *cuda_curr_min, os: 'ubuntu20.04', cpu: 'amd64', compiler: *llvm14}
+ - {cuda: *cuda_curr_max, os: 'ubuntu20.04', cpu: 'amd64', compiler: *gcc7}
+ - {cuda: *cuda_curr_max, os: 'ubuntu20.04', cpu: 'amd64', compiler: *gcc8}
+ - {cuda: *cuda_curr_max, os: 'ubuntu20.04', cpu: 'amd64', compiler: *gcc9}
+ - {cuda: *cuda_curr_max, os: 'ubuntu20.04', cpu: 'amd64', compiler: *gcc10}
+ - {cuda: *cuda_curr_max, os: 'ubuntu22.04', cpu: 'amd64', compiler: *gcc11}
+ - {cuda: *cuda_curr_max, os: 'ubuntu22.04', cpu: 'amd64', compiler: *gcc12}
+ # Fails to compile simple input on CTK12.4. Try to add later.
+ # {cuda: *cuda_curr_max, os: 'ubuntu22.04', cpu: 'amd64', compiler: *gcc13}
+ - {cuda: *cuda_curr_max, os: 'ubuntu20.04', cpu: 'amd64', compiler: *llvm9}
+ - {cuda: *cuda_curr_max, os: 'ubuntu20.04', cpu: 'amd64', compiler: *llvm10}
+ - {cuda: *cuda_curr_max, os: 'ubuntu20.04', cpu: 'amd64', compiler: *llvm11}
+ - {cuda: *cuda_curr_max, os: 'ubuntu20.04', cpu: 'amd64', compiler: *llvm12}
+ - {cuda: *cuda_curr_max, os: 'ubuntu20.04', cpu: 'amd64', compiler: *llvm13}
+ - {cuda: *cuda_curr_max, os: 'ubuntu20.04', cpu: 'amd64', compiler: *llvm14}
+ - {cuda: *cuda_curr_max, os: 'ubuntu22.04', cpu: 'amd64', compiler: *llvm15}
+ - {cuda: *cuda_curr_max, os: 'ubuntu22.04', cpu: 'amd64', compiler: *llvm16}
+ - {cuda: *cuda_curr_max, os: 'ubuntu22.04', cpu: 'amd64', compiler: *llvm17}
+ - {cuda: *cuda_curr_max, os: 'ubuntu22.04', cpu: 'amd64', compiler: *llvm18, extra_build_args: "-cmake-options '-DCMAKE_CUDA_FLAGS=-allow-unsupported-compiler'"}
+ - {cuda: *cuda_curr_max, os: 'windows2022', cpu: 'amd64', compiler: *msvc2019, extra_build_args: "-cmake-options '-DNVBench_ENABLE_CUPTI=OFF -DNVBench_ENABLE_NVML=OFF'"}
+ - {cuda: *cuda_curr_max, os: 'windows2022', cpu: 'amd64', compiler: *msvc2022, extra_build_args: "-cmake-options '-DNVBench_ENABLE_CUPTI=OFF -DNVBench_ENABLE_NVML=OFF'"}
diff --git a/ci/ninja_summary.py b/ci/ninja_summary.py
new file mode 100755
index 00000000..f496db53
--- /dev/null
+++ b/ci/ninja_summary.py
@@ -0,0 +1,381 @@
+#!/usr/bin/env python3
+# Copyright (c) 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+r"""Summarize the last ninja build, invoked with ninja's -C syntax.
+
+This script is designed to be automatically run after each ninja build in
+order to summarize the build's performance. Making build performance information
+more visible should make it easier to notice anomalies and opportunities. To use
+this script on Windows just set NINJA_SUMMARIZE_BUILD=1 and run autoninja.bat.
+
+On Linux you can get autoninja to invoke this script using this syntax:
+
+$ NINJA_SUMMARIZE_BUILD=1 autoninja -C out/Default/ chrome
+
+You can also call this script directly using ninja's syntax to specify the
+output directory of interest:
+
+> python3 post_build_ninja_summary.py -C out/Default
+
+Typical output looks like this:
+
+>ninja -C out\debug_component base
+ninja.exe -C out\debug_component base -j 960 -l 48 -d keeprsp
+ninja: Entering directory `out\debug_component'
+[1 processes, 1/1 @ 0.3/s : 3.092s ] Regenerating ninja files
+Longest build steps:
+ 0.1 weighted s to build obj/base/base/trace_log.obj (6.7 s elapsed time)
+ 0.2 weighted s to build nasm.exe, nasm.exe.pdb (0.2 s elapsed time)
+ 0.3 weighted s to build obj/base/base/win_util.obj (12.4 s elapsed time)
+ 1.2 weighted s to build base.dll, base.dll.lib (1.2 s elapsed time)
+Time by build-step type:
+ 0.0 s weighted time to generate 6 .lib files (0.3 s elapsed time sum)
+ 0.1 s weighted time to generate 25 .stamp files (1.2 s elapsed time sum)
+ 0.2 s weighted time to generate 20 .o files (2.8 s elapsed time sum)
+ 1.7 s weighted time to generate 4 PEFile (linking) files (2.0 s elapsed
+time sum)
+ 23.9 s weighted time to generate 770 .obj files (974.8 s elapsed time sum)
+26.1 s weighted time (982.9 s elapsed time sum, 37.7x parallelism)
+839 build steps completed, average of 32.17/s
+
+If no gn clean has been done then results will be for the last non-NULL
+invocation of ninja. Ideas for future statistics, and implementations are
+appreciated.
+
+The "weighted" time is the elapsed time of each build step divided by the number
+of tasks that were running in parallel. This makes it an excellent approximation
+of how "important" a slow step was. A link that is entirely or mostly serialized
+will have a weighted time that is the same or similar to its elapsed time. A
+compile that runs in parallel with 999 other compiles will have a weighted time
+that is tiny."""
+
+import argparse
+import errno
+import fnmatch
+import os
+import subprocess
+import sys
+
+# The number of long build times to report:
+long_count = 10
+# The number of long times by extension to report
+long_ext_count = 10
+
+
+class Target:
+ """Represents a single line read for a .ninja_log file."""
+ def __init__(self, start, end):
+ """Creates a target object by passing in the start/end times in seconds
+ as a float."""
+ self.start = start
+ self.end = end
+ # A list of targets, appended to by the owner of this object.
+ self.targets = []
+ self.weighted_duration = 0.0
+
+ def Duration(self):
+ """Returns the task duration in seconds as a float."""
+ return self.end - self.start
+
+ def SetWeightedDuration(self, weighted_duration):
+ """Sets the duration, in seconds, passed in as a float."""
+ self.weighted_duration = weighted_duration
+
+ def WeightedDuration(self):
+ """Returns the task's weighted duration in seconds as a float.
+
+ Weighted_duration takes the elapsed time of the task and divides it
+ by how many other tasks were running at the same time. Thus, it
+ represents the approximate impact of this task on the total build time,
+ with serialized or serializing steps typically ending up with much
+ longer weighted durations.
+ weighted_duration should always be the same or shorter than duration.
+ """
+ # Allow for modest floating-point errors
+ epsilon = 0.000002
+ if (self.weighted_duration > self.Duration() + epsilon):
+ print('%s > %s?' % (self.weighted_duration, self.Duration()))
+ assert (self.weighted_duration <= self.Duration() + epsilon)
+ return self.weighted_duration
+
+ def DescribeTargets(self):
+ """Returns a printable string that summarizes the targets."""
+ # Some build steps generate dozens of outputs - handle them sanely.
+ # The max_length was chosen so that it can fit most of the long
+ # single-target names, while minimizing word wrapping.
+ result = ', '.join(self.targets)
+ max_length = 65
+ if len(result) > max_length:
+ result = result[:max_length] + '...'
+ return result
+
+
+# Copied with some modifications from ninjatracing
+def ReadTargets(log, show_all):
+ """Reads all targets from .ninja_log file |log_file|, sorted by duration.
+
+ The result is a list of Target objects."""
+ header = log.readline()
+ # Handle empty ninja_log gracefully by silently returning an empty list of
+ # targets.
+ if not header:
+ return []
+ assert header == '# ninja log v5\n', \
+ 'unrecognized ninja log version %r' % header
+ targets_dict = {}
+ last_end_seen = 0.0
+ for line in log:
+ parts = line.strip().split('\t')
+ if len(parts) != 5:
+ # If ninja.exe is rudely halted then the .ninja_log file may be
+ # corrupt. Silently continue.
+ continue
+ start, end, _, name, cmdhash = parts # Ignore restat.
+ # Convert from integral milliseconds to float seconds.
+ start = int(start) / 1000.0
+ end = int(end) / 1000.0
+ if not show_all and end < last_end_seen:
+ # An earlier time stamp means that this step is the first in a new
+ # build, possibly an incremental build. Throw away the previous
+ # data so that this new build will be displayed independently.
+ # This has to be done by comparing end times because records are
+ # written to the .ninja_log file when commands complete, so end
+ # times are guaranteed to be in order, but start times are not.
+ targets_dict = {}
+ target = None
+ if cmdhash in targets_dict:
+ target = targets_dict[cmdhash]
+ if not show_all and (target.start != start or target.end != end):
+ # If several builds in a row just run one or two build steps
+ # then the end times may not go backwards so the last build may
+ # not be detected as such. However in many cases there will be a
+ # build step repeated in the two builds and the changed
+ # start/stop points for that command, identified by the hash,
+ # can be used to detect and reset the target dictionary.
+ targets_dict = {}
+ target = None
+ if not target:
+ targets_dict[cmdhash] = target = Target(start, end)
+ last_end_seen = end
+ target.targets.append(name)
+ return list(targets_dict.values())
+
+
+def GetExtension(target, extra_patterns):
+ """Return the file extension that best represents a target.
+
+ For targets that generate multiple outputs it is important to return a
+ consistent 'canonical' extension. Ultimately the goal is to group build steps
+ by type."""
+ for output in target.targets:
+ if extra_patterns:
+ for fn_pattern in extra_patterns.split(';'):
+ if fnmatch.fnmatch(output, '*' + fn_pattern + '*'):
+ return fn_pattern
+ # Not a true extension, but a good grouping.
+ if output.endswith('type_mappings'):
+ extension = 'type_mappings'
+ break
+
+ # Capture two extensions if present. For example: file.javac.jar should
+ # be distinguished from file.interface.jar.
+ root, ext1 = os.path.splitext(output)
+ _, ext2 = os.path.splitext(root)
+ extension = ext2 + ext1 # Preserve the order in the file name.
+
+ if len(extension) == 0:
+ extension = '(no extension found)'
+
+ if ext1 in ['.pdb', '.dll', '.exe']:
+ extension = 'PEFile (linking)'
+ # Make sure that .dll and .exe are grouped together and that the
+ # .dll.lib files don't cause these to be listed as libraries
+ break
+ if ext1 in ['.so', '.TOC']:
+ extension = '.so (linking)'
+ # Attempt to identify linking, avoid identifying as '.TOC'
+ break
+ # Make sure .obj files don't get categorized as mojo files
+ if ext1 in ['.obj', '.o']:
+ break
+ # Jars are the canonical output of java targets.
+ if ext1 == '.jar':
+ break
+ # Normalize all mojo related outputs to 'mojo'.
+ if output.count('.mojom') > 0:
+ extension = 'mojo'
+ break
+ return extension
+
+
+def SummarizeEntries(entries, extra_step_types, elapsed_time_sorting):
+ """Print a summary of the passed in list of Target objects."""
+
+ # Create a list that is in order by time stamp and has entries for the
+ # beginning and ending of each build step (one time stamp may have multiple
+ # entries due to multiple steps starting/stopping at exactly the same time).
+ # Iterate through this list, keeping track of which tasks are running at all
+ # times. At each time step calculate a running total for weighted time so
+ # that when each task ends its own weighted time can easily be calculated.
+ task_start_stop_times = []
+
+ earliest = -1
+ latest = 0
+ total_cpu_time = 0
+ for target in entries:
+ if earliest < 0 or target.start < earliest:
+ earliest = target.start
+ if target.end > latest:
+ latest = target.end
+ total_cpu_time += target.Duration()
+ task_start_stop_times.append((target.start, 'start', target))
+ task_start_stop_times.append((target.end, 'stop', target))
+ length = latest - earliest
+ weighted_total = 0.0
+
+ # Sort by the time/type records and ignore |target|
+ task_start_stop_times.sort(key=lambda times: times[:2])
+ # Now we have all task start/stop times sorted by when they happen. If a
+ # task starts and stops on the same time stamp then the start will come
+ # first because of the alphabet, which is important for making this work
+ # correctly.
+ # Track the tasks which are currently running.
+ running_tasks = {}
+ # Record the time we have processed up to so we know how to calculate time
+ # deltas.
+ last_time = task_start_stop_times[0][0]
+ # Track the accumulated weighted time so that it can efficiently be added
+ # to individual tasks.
+ last_weighted_time = 0.0
+ # Scan all start/stop events.
+ for event in task_start_stop_times:
+ time, action_name, target = event
+ # Accumulate weighted time up to now.
+ num_running = len(running_tasks)
+ if num_running > 0:
+ # Update the total weighted time up to this moment.
+ last_weighted_time += (time - last_time) / float(num_running)
+ if action_name == 'start':
+ # Record the total weighted task time when this task starts.
+ running_tasks[target] = last_weighted_time
+ if action_name == 'stop':
+ # Record the change in the total weighted task time while this task
+ # ran.
+ weighted_duration = last_weighted_time - running_tasks[target]
+ target.SetWeightedDuration(weighted_duration)
+ weighted_total += weighted_duration
+ del running_tasks[target]
+ last_time = time
+ assert (len(running_tasks) == 0)
+
+ # Warn if the sum of weighted times is off by more than half a second.
+ if abs(length - weighted_total) > 500:
+ print('Warning: Possible corrupt ninja log, results may be '
+ 'untrustworthy. Length = %.3f, weighted total = %.3f' %
+ (length, weighted_total))
+
+ # Print the slowest build steps:
+ print(' Longest build steps:')
+ if elapsed_time_sorting:
+ entries.sort(key=lambda x: x.Duration())
+ else:
+ entries.sort(key=lambda x: x.WeightedDuration())
+ for target in entries[-long_count:]:
+ print(' %8.1f weighted s to build %s (%.1f s elapsed time)' %
+ (target.WeightedDuration(), target.DescribeTargets(),
+ target.Duration()))
+
+ # Sum up the time by file extension/type of the output file
+ count_by_ext = {}
+ time_by_ext = {}
+ weighted_time_by_ext = {}
+ # Scan through all of the targets to build up per-extension statistics.
+ for target in entries:
+ extension = GetExtension(target, extra_step_types)
+ time_by_ext[extension] = time_by_ext.get(extension,
+ 0) + target.Duration()
+ weighted_time_by_ext[extension] = weighted_time_by_ext.get(
+ extension, 0) + target.WeightedDuration()
+ count_by_ext[extension] = count_by_ext.get(extension, 0) + 1
+
+ print(' Time by build-step type:')
+ # Copy to a list with extension name and total time swapped, to (time, ext)
+ if elapsed_time_sorting:
+ weighted_time_by_ext_sorted = sorted(
+ (y, x) for (x, y) in time_by_ext.items())
+ else:
+ weighted_time_by_ext_sorted = sorted(
+ (y, x) for (x, y) in weighted_time_by_ext.items())
+ # Print the slowest build target types:
+ for time, extension in weighted_time_by_ext_sorted[-long_ext_count:]:
+ print(
+ ' %8.1f s weighted time to generate %d %s files '
+ '(%1.1f s elapsed time sum)' %
+ (time, count_by_ext[extension], extension, time_by_ext[extension]))
+
+ print(' %.1f s weighted time (%.1f s elapsed time sum, %1.1fx '
+ 'parallelism)' %
+ (length, total_cpu_time, total_cpu_time * 1.0 / length))
+ print(' %d build steps completed, average of %1.2f/s' %
+ (len(entries), len(entries) / (length)))
+
+
+def main():
+ log_file = '.ninja_log'
+ metrics_file = 'siso_metrics.json'
+ parser = argparse.ArgumentParser()
+ parser.add_argument('-C', dest='build_directory', help='Build directory.')
+ parser.add_argument(
+ '-s',
+ '--step-types',
+ help='semicolon separated fnmatch patterns for build-step grouping')
+ parser.add_argument(
+ '-e',
+ '--elapsed_time_sorting',
+ default=False,
+ action='store_true',
+ help='Sort output by elapsed time instead of weighted time')
+ parser.add_argument('--log-file',
+ help="specific ninja log file to analyze.")
+ args, _extra_args = parser.parse_known_args()
+ if args.build_directory:
+ log_file = os.path.join(args.build_directory, log_file)
+ metrics_file = os.path.join(args.build_directory, metrics_file)
+ if args.log_file:
+ log_file = args.log_file
+ if not args.step_types:
+ # Offer a convenient way to add extra step types automatically,
+ # including when this script is run by autoninja. get() returns None if
+ # the variable isn't set.
+ args.step_types = os.environ.get('chromium_step_types')
+ if args.step_types:
+ # Make room for the extra build types.
+ global long_ext_count
+ long_ext_count += len(args.step_types.split(';'))
+
+ if os.path.exists(metrics_file):
+ # Automatically handle summarizing siso builds.
+ cmd = ['siso.bat' if 'win32' in sys.platform else 'siso']
+ cmd.extend(['metrics', 'summary'])
+ if args.build_directory:
+ cmd.extend(['-C', args.build_directory])
+ if args.step_types:
+ cmd.extend(['--step_types', args.step_types])
+ if args.elapsed_time_sorting:
+ cmd.append('--elapsed_time_sorting')
+ subprocess.run(cmd)
+ else:
+ try:
+ with open(log_file, 'r') as log:
+ entries = ReadTargets(log, False)
+ if entries:
+ SummarizeEntries(entries, args.step_types,
+ args.elapsed_time_sorting)
+ except IOError:
+ print('Log file %r not found, no build summary created.' % log_file)
+ return errno.ENOENT
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/ci/pretty_printing.sh b/ci/pretty_printing.sh
new file mode 100644
index 00000000..5bea1af9
--- /dev/null
+++ b/ci/pretty_printing.sh
@@ -0,0 +1,105 @@
+# Print "ARG=${ARG}" for all args.
+function print_var_values() {
+ # Iterate through the arguments
+ for var_name in "$@"; do
+ if [ -z "$var_name" ]; then
+ echo "Usage: print_var_values ..."
+ return 1
+ fi
+
+ # Dereference the variable and print the result
+ echo "$var_name=${!var_name:-(undefined)}"
+ done
+}
+
+# begin_group: Start a named section of log output, possibly with color.
+# Usage: begin_group "Group Name" [Color]
+# Group Name: A string specifying the name of the group.
+# Color (optional): ANSI color code to set text color. Default is blue (1;34).
+function begin_group() {
+ # See options for colors here: https://gist.github.com/JBlond/2fea43a3049b38287e5e9cefc87b2124
+ local blue="34"
+ local name="${1:-}"
+ local color="${2:-$blue}"
+
+ if [ -n "${GITHUB_ACTIONS:-}" ]; then
+ echo -e "::group::\e[${color}m${name}\e[0m"
+ else
+ echo -e "\e[${color}m================== ${name} ======================\e[0m"
+ fi
+}
+
+# end_group: End a named section of log output and print status based on exit status.
+# Usage: end_group "Group Name" [Exit Status]
+# Group Name: A string specifying the name of the group.
+# Exit Status (optional): The exit status of the command run within the group. Default is 0.
+function end_group() {
+ local name="${1:-}"
+ local build_status="${2:-0}"
+ local duration="${3:-}"
+ local red="31"
+ local blue="34"
+
+ if [ -n "${GITHUB_ACTIONS:-}" ]; then
+ echo "::endgroup::"
+
+ if [ "$build_status" -ne 0 ]; then
+ echo -e "::error::\e[${red}m ${name} - Failed (⬆️ click above for full log ⬆️)\e[0m"
+ fi
+ else
+ if [ "$build_status" -ne 0 ]; then
+ echo -e "\e[${red}m================== End ${name} - Failed${duration:+ - Duration: ${duration}s} ==================\e[0m"
+ else
+ echo -e "\e[${blue}m================== End ${name} - Success${duration:+ - Duration: ${duration}s} ==================\n\e[0m"
+ fi
+ fi
+}
+
+declare -A command_durations
+
+# Runs a command within a named group, handles the exit status, and prints appropriate messages based on the result.
+# Usage: run_command "Group Name" command [arguments...]
+function run_command() {
+ local group_name="${1:-}"
+ shift
+ local command=("$@")
+ local status
+
+ begin_group "$group_name"
+ set +e
+ local start_time=$(date +%s)
+ "${command[@]}"
+ status=$?
+ local end_time=$(date +%s)
+ set -e
+ local duration=$((end_time - start_time))
+ end_group "$group_name" $status $duration
+ command_durations["$group_name"]=$duration
+ return $status
+}
+
+function string_width() {
+ local str="$1"
+ echo "$str" | awk '{print length}'
+}
+
+function print_time_summary() {
+ local max_length=0
+ local group
+
+ # Find the longest group name for formatting
+ for group in "${!command_durations[@]}"; do
+ local group_length=$(echo "$group" | awk '{print length}')
+ if [ "$group_length" -gt "$max_length" ]; then
+ max_length=$group_length
+ fi
+ done
+
+ echo "Time Summary:"
+ for group in "${!command_durations[@]}"; do
+ printf "%-${max_length}s : %s seconds\n" "$group" "${command_durations[$group]}"
+ done
+
+ # Clear the array of timing info
+ declare -gA command_durations=()
+}
diff --git a/ci/sccache_hit_rate.sh b/ci/sccache_hit_rate.sh
new file mode 100755
index 00000000..de8ae465
--- /dev/null
+++ b/ci/sccache_hit_rate.sh
@@ -0,0 +1,41 @@
+#!/bin/bash
+
+set -euo pipefail
+
+# Ensure two arguments are provided
+if [ $# -ne 2 ]; then
+ echo "Usage: $0 " >&2
+ exit 1
+fi
+
+# Print the contents of the before file
+echo "=== Contents of $1 ===" >&2
+cat $1 >&2
+echo "=== End of $1 ===" >&2
+
+# Print the contents of the after file
+echo "=== Contents of $2 ===" >&2
+cat $2 >&2
+echo "=== End of $2 ===" >&2
+
+# Extract compile requests and cache hits from the before and after files
+requests_before=$(awk '/^[ \t]*Compile requests[ \t]+[0-9]+/ {print $3}' "$1")
+hits_before=$(awk '/^[ \t]*Cache hits[ \t]+[0-9]+/ {print $3}' "$1")
+requests_after=$(awk '/^[ \t]*Compile requests[ \t]+[0-9]+/ {print $3}' "$2")
+hits_after=$(awk '/^[ \t]*Cache hits[ \t]+[0-9]+/ {print $3}' "$2")
+
+# Calculate the differences to find out how many new requests and hits
+requests_diff=$((requests_after - requests_before))
+hits_diff=$((hits_after - hits_before))
+
+echo "New Compile Requests: $requests_diff" >&2
+echo "New Hits: $hits_diff" >&2
+
+# Calculate and print the hit rate
+if [ $requests_diff -eq 0 ]; then
+ echo "No new compile requests, hit rate is not applicable"
+else
+ hit_rate=$(awk -v hits=$hits_diff -v requests=$requests_diff 'BEGIN {printf "%.2f", hits/requests * 100}')
+ echo "sccache hit rate: $hit_rate%" >&2
+ echo "$hit_rate"
+fi
diff --git a/ci/sccache_stats.sh b/ci/sccache_stats.sh
new file mode 100755
index 00000000..3a3ebc42
--- /dev/null
+++ b/ci/sccache_stats.sh
@@ -0,0 +1,52 @@
+#!/bin/bash
+
+# This script prints the sccache hit rate between two calls to sccache --show-stats.
+# It should be sourced in your script before and after the operations you want to profile,
+# with the 'start' or 'end' argument respectively.
+
+mode=$1
+
+if [[ "$mode" != "start" && "$mode" != "end" ]]; then
+ echo "Invalid mode: $mode"
+ echo "Usage: $0 {start|end}"
+ exit 1
+fi
+
+# Check if sccache is available
+if ! command -v sccache &> /dev/null; then
+ echo "Notice: sccache is not available. Skipping..."
+ exit 0
+fi
+
+case $mode in
+ start)
+ export SCCACHE_START_HITS=$(sccache --show-stats | awk '/^[ \t]*Cache hits[ \t]+[0-9]+/ {print $3}')
+ export SCCACHE_START_MISSES=$(sccache --show-stats | awk '/^[ \t]*Cache misses[ \t]+[0-9]+/ {print $3}')
+ ;;
+ end)
+ if [[ -z ${SCCACHE_START_HITS+x} || -z ${SCCACHE_START_MISSES+x} ]]; then
+ echo "Error: start stats not collected. Did you call this script with 'start' before your operations?"
+ exit 1
+ fi
+
+ final_hits=$(sccache --show-stats | awk '/^[ \t]*Cache hits[ \t]+[0-9]+/ {print $3}')
+ final_misses=$(sccache --show-stats | awk '/^[ \t]*Cache misses[ \t]+[0-9]+/ {print $3}')
+ hits=$((final_hits - SCCACHE_START_HITS))
+ misses=$((final_misses - SCCACHE_START_MISSES))
+ total=$((hits + misses))
+
+ prefix=""
+ if [ ${GITHUB_ACTIONS:-false} = "true" ]; then
+ prefix="::notice::"
+ fi
+
+ if (( total > 0 )); then
+ hit_rate=$(awk -v hits="$hits" -v total="$total" 'BEGIN { printf "%.2f", (hits / total) * 100 }')
+ echo ${prefix}"sccache hits: $hits | misses: $misses | hit rate: $hit_rate%"
+ else
+ echo ${prefix}"sccache stats: N/A No new compilation requests"
+ fi
+ unset SCCACHE_START_HITS
+ unset SCCACHE_START_MISSES
+ ;;
+esac
diff --git a/ci/test_nvbench.sh b/ci/test_nvbench.sh
new file mode 100755
index 00000000..40559eda
--- /dev/null
+++ b/ci/test_nvbench.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+source "$(dirname "$0")/build_common.sh"
+
+# Run NVBench tests with high parallelism. If any need to be
+# serialized, define the `RUN_SERIAL` CMake property on the
+# test.
+export CTEST_PARALLEL_LEVEL=${PARALLEL_LEVEL}
+
+print_environment_details
+
+./build_nvbench.sh "$@"
+
+PRESET="nvbench-ci"
+
+test_preset "NVBench" ${PRESET}
+
+print_time_summary
diff --git a/ci/windows/build_common.psm1 b/ci/windows/build_common.psm1
new file mode 100644
index 00000000..1edea634
--- /dev/null
+++ b/ci/windows/build_common.psm1
@@ -0,0 +1,207 @@
+
+Param(
+ [Parameter(Mandatory = $true)]
+ [Alias("std")]
+ [ValidateNotNullOrEmpty()]
+ [ValidateSet(17)]
+ [int]$CXX_STANDARD = 17
+)
+
+# We need the full path to cl because otherwise cmake will replace CMAKE_CXX_COMPILER with the full path
+# and keep CMAKE_CUDA_HOST_COMPILER at "cl" which breaks our cmake script
+$script:HOST_COMPILER = (Get-Command "cl").source -replace '\\','/'
+$script:PARALLEL_LEVEL = (Get-WmiObject -class Win32_processor).NumberOfLogicalProcessors
+
+# Extract the CL version for export to build scripts:
+$script:CL_VERSION_STRING = & cl.exe /?
+if ($script:CL_VERSION_STRING -match "Version (\d+\.\d+)\.\d+") {
+ $CL_VERSION = [version]$matches[1]
+ Write-Host "Detected cl.exe version: $CL_VERSION"
+}
+
+if (-not $env:CCCL_BUILD_INFIX) {
+ $env:CCCL_BUILD_INFIX = ""
+}
+
+# Presets will be configured in this directory:
+$BUILD_DIR = "../build/$env:CCCL_BUILD_INFIX"
+
+If(!(test-path -PathType container "../build")) {
+ New-Item -ItemType Directory -Path "../build"
+}
+
+# The most recent build will always be symlinked to cccl/build/latest
+New-Item -ItemType Directory -Path "$BUILD_DIR" -Force
+
+# Prepare environment for CMake:
+$env:CMAKE_BUILD_PARALLEL_LEVEL = $PARALLEL_LEVEL
+$env:CTEST_PARALLEL_LEVEL = 1
+$env:CUDAHOSTCXX = $HOST_COMPILER.FullName
+$env:CXX = $HOST_COMPILER.FullName
+
+Write-Host "========================================"
+Write-Host "Begin build"
+Write-Host "pwd=$pwd"
+Write-Host "BUILD_DIR=$BUILD_DIR"
+Write-Host "CXX_STANDARD=$CXX_STANDARD"
+Write-Host "CXX=$env:CXX"
+Write-Host "CUDACXX=$env:CUDACXX"
+Write-Host "CUDAHOSTCXX=$env:CUDAHOSTCXX"
+Write-Host "NVCC_VERSION=$NVCC_VERSION"
+Write-Host "CMAKE_BUILD_PARALLEL_LEVEL=$env:CMAKE_BUILD_PARALLEL_LEVEL"
+Write-Host "CTEST_PARALLEL_LEVEL=$env:CTEST_PARALLEL_LEVEL"
+Write-Host "CCCL_BUILD_INFIX=$env:CCCL_BUILD_INFIX"
+Write-Host "Current commit is:"
+Write-Host "$(git log -1)"
+Write-Host "========================================"
+
+function configure_preset {
+ Param(
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [string]$BUILD_NAME,
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [string]$PRESET,
+ [Parameter(Mandatory = $true)]
+ [AllowEmptyString()]
+ [string]$CMAKE_OPTIONS
+ )
+
+ $step = "$BUILD_NAME (configure)"
+
+ # CMake must be invoked in the same directory as the presets file:
+ pushd ".."
+
+ $cmake_command = "cmake --preset $PRESET $CMAKE_OPTIONS --log-level VERBOSE"
+ echo "$cmake_command"
+ Invoke-Expression $cmake_command
+ $test_result = $LastExitCode
+
+ If ($test_result -ne 0) {
+ throw "$step Failed"
+ }
+
+ popd
+ Write-Host "$step complete."
+}
+
+function build_preset {
+ Param(
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [string]$BUILD_NAME,
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [string]$PRESET
+ )
+
+ $step = "$BUILD_NAME (build)"
+
+ # CMake must be invoked in the same directory as the presets file:
+ pushd ".."
+
+ sccache_stats('Start')
+
+ cmake --build --preset $PRESET -v
+ $test_result = $LastExitCode
+
+ sccache_stats('Stop')
+
+ echo "$step complete"
+
+ If ($test_result -ne 0) {
+ throw "$step Failed"
+ }
+
+ popd
+}
+
+function test_preset {
+ Param(
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [string]$BUILD_NAME,
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [string]$PRESET
+ )
+
+ $step = "$BUILD_NAME (test)"
+
+ # CTest must be invoked in the same directory as the presets file:
+ pushd ".."
+
+ sccache_stats('Start')
+
+ ctest --preset $PRESET
+ $test_result = $LastExitCode
+
+ sccache_stats('Stop')
+
+ echo "$step complete"
+
+ If ($test_result -ne 0) {
+ throw "$step Failed"
+ }
+
+ popd
+}
+
+function configure_and_build_preset {
+ Param(
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [string]$BUILD_NAME,
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [string]$PRESET,
+ [Parameter(Mandatory = $true)]
+ [AllowEmptyString()]
+ [string]$CMAKE_OPTIONS
+ )
+
+ configure_preset "$BUILD_NAME" "$PRESET" "$CMAKE_OPTIONS"
+ build_preset "$BUILD_NAME" "$PRESET"
+}
+
+function sccache_stats {
+ Param (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [ValidateSet('Start','Stop')]
+ [string]$MODE
+ )
+
+ $sccache_stats = sccache -s
+ If($MODE -eq 'Start') {
+ [int]$script:sccache_compile_requests = ($sccache_stats[0] -replace '[^\d]+')
+ [int]$script:sccache_cache_hits_cpp = ($sccache_stats[2] -replace '[^\d]+')
+ [int]$script:sccache_cache_hits_cuda = ($sccache_stats[3] -replace '[^\d]+')
+ [int]$script:sccache_cache_miss_cpp = ($sccache_stats[5] -replace '[^\d]+')
+ [int]$script:sccache_cache_miss_cuda = ($sccache_stats[6] -replace '[^\d]+')
+ } else {
+ [int]$final_sccache_compile_requests = ($sccache_stats[0] -replace '[^\d]+')
+ [int]$final_sccache_cache_hits_cpp = ($sccache_stats[2] -replace '[^\d]+')
+ [int]$final_sccache_cache_hits_cuda = ($sccache_stats[3] -replace '[^\d]+')
+ [int]$final_sccache_cache_miss_cpp = ($sccache_stats[5] -replace '[^\d]+')
+ [int]$final_sccache_cache_miss_cuda = ($sccache_stats[6] -replace '[^\d]+')
+
+ [int]$total_requests = $final_sccache_compile_requests - $script:sccache_compile_requests
+ [int]$total_hits_cpp = $final_sccache_cache_hits_cpp - $script:sccache_cache_hits_cpp
+ [int]$total_hits_cuda = $final_sccache_cache_hits_cuda - $script:sccache_cache_hits_cuda
+ [int]$total_miss_cpp = $final_sccache_cache_miss_cpp - $script:sccache_cache_miss_cpp
+ [int]$total_miss_cuda = $final_sccache_cache_miss_cuda - $script:sccache_cache_miss_cuda
+ If ( $total_requests -gt 0 ) {
+ [int]$hit_rate_cpp = $total_hits_cpp / $total_requests * 100;
+ [int]$hit_rate_cuda = $total_hits_cuda / $total_requests * 100;
+ echo "sccache hits cpp: $total_hits_cpp `t| misses: $total_miss_cpp `t| hit rate: $hit_rate_cpp%"
+ echo "sccache hits cuda: $total_hits_cuda `t| misses: $total_miss_cuda `t| hit rate: $hit_rate_cuda%"
+ } else {
+ echo "sccache stats: N/A No new compilation requests"
+ }
+ }
+}
+
+Export-ModuleMember -Function configure_preset, build_preset, test_preset, configure_and_build_preset, sccache_stats
+Export-ModuleMember -Variable BUILD_DIR, CL_VERSION
diff --git a/ci/windows/build_nvbench.ps1 b/ci/windows/build_nvbench.ps1
new file mode 100644
index 00000000..1ac8bd16
--- /dev/null
+++ b/ci/windows/build_nvbench.ps1
@@ -0,0 +1,30 @@
+
+Param(
+ [Parameter(Mandatory = $false)]
+ [Alias("cmake-options")]
+ [ValidateNotNullOrEmpty()]
+ [string]$ARG_CMAKE_OPTIONS = ""
+)
+
+$CURRENT_PATH = Split-Path $pwd -leaf
+If($CURRENT_PATH -ne "ci") {
+ Write-Host "Moving to ci folder"
+ pushd "$PSScriptRoot/.."
+}
+
+Remove-Module -Name build_common
+Import-Module $PSScriptRoot/build_common.psm1 -ArgumentList 17
+
+$PRESET = "nvbench-ci"
+$CMAKE_OPTIONS = ""
+
+# Append any arguments pass in on the command line
+If($ARG_CMAKE_OPTIONS -ne "") {
+ $CMAKE_OPTIONS += "$ARG_CMAKE_OPTIONS"
+}
+
+configure_and_build_preset "NVBench" "$PRESET" "$CMAKE_OPTIONS"
+
+If($CURRENT_PATH -ne "ci") {
+ popd
+}
diff --git a/ci/windows/test_nvbench.ps1 b/ci/windows/test_nvbench.ps1
new file mode 100644
index 00000000..bcd9f2c9
--- /dev/null
+++ b/ci/windows/test_nvbench.ps1
@@ -0,0 +1,31 @@
+
+Param(
+ [Parameter(Mandatory = $false)]
+ [Alias("cmake-options")]
+ [ValidateNotNullOrEmpty()]
+ [string]$ARG_CMAKE_OPTIONS = ""
+)
+
+$CURRENT_PATH = Split-Path $pwd -leaf
+If($CURRENT_PATH -ne "ci") {
+ Write-Host "Moving to ci folder"
+ pushd "$PSScriptRoot/.."
+}
+
+Remove-Module -Name build_common
+Import-Module $PSScriptRoot/build_common.psm1 -ArgumentList 17
+
+$PRESET = "nvbench-ci"
+$CMAKE_OPTIONS = ""
+
+# Append any arguments pass in on the command line
+If($ARG_CMAKE_OPTIONS -ne "") {
+ $CMAKE_OPTIONS += "$ARG_CMAKE_OPTIONS"
+}
+
+configure_and_build_preset "NVBench" "$PRESET" "$CMAKE_OPTIONS"
+test_preset "NVBench" "$PRESET"
+
+If($CURRENT_PATH -ne "ci") {
+ popd
+}
diff --git a/cmake/DetectSupportedStandards.cmake b/cmake/DetectSupportedStandards.cmake
new file mode 100644
index 00000000..6a86d6ac
--- /dev/null
+++ b/cmake/DetectSupportedStandards.cmake
@@ -0,0 +1,65 @@
+# Detect the langauge standards supported by the current compilers.
+#
+# Usage: detect_supported_cxx_standards( )
+#
+# - var_prefix: Used to name result variables,
+# e.g. ${var_prefix}_${lang}_XX_SUPPORTED will be TRUE or FALSE. Defined for
+# each XX in ${standards}.
+# - lang: The language to test: C, CXX, or CUDA.
+# - standards: List of any standard versions.
+#
+# Example: detect_supported_standards(PROJ CXX 11 14 17)
+# - Sets the following variables in the parent scope to TRUE or FALSE:
+# - PROJ_CXX_11_SUPPORTED
+# - PROJ_CXX_14_SUPPORTED
+# - PROJ_CXX_17_SUPPORTED
+# - Sets `PROJ_DETECTED_CXX_STANDARDS` to a list of supported standards (e.g. "11;14;17").
+function(detect_supported_standards prefix lang)
+ string(TOLOWER "${lang}_std" feature_prefix)
+ set(all_stds)
+ foreach(standard IN LISTS ARGN)
+ set(var_name "${prefix}_${lang}_${standard}_SUPPORTED")
+ if ("${feature_prefix}_${standard}" IN_LIST CMAKE_${lang}_COMPILE_FEATURES)
+ set(${var_name} TRUE)
+ else()
+ set(${var_name} FALSE)
+ endif()
+
+ # Special cases:
+ if (standard EQUAL 17 AND
+ (lang STREQUAL "CXX" OR lang STREQUAL "CUDA") AND
+ ((CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND
+ CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7) OR
+ (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND
+ CMAKE_CXX_COMPILER_VERSION VERSION_LESS 8)))
+ # gcc < 7 and clang < 8 don't fully support C++17.
+ # They accept the flag and have partial support, but nvcc will refuse
+ # to enable it and falls back to the default dialect for the current
+ # CXX compiler version. This breaks our CI.
+ # CMake's COMPILE_FEATURES var reports that these compilers support C++17,
+ # but we can't rely on it, so manually disable the dialect in these cases.
+ set(${var_name} FALSE)
+ endif()
+
+ if (standard EQUAL 20 AND
+ (lang STREQUAL "CXX" OR lang STREQUAL "CUDA") AND
+ ((CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND
+ CMAKE_CXX_COMPILER_VERSION VERSION_LESS 10) OR
+ (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND
+ CMAKE_CXX_COMPILER_VERSION VERSION_LESS 10) OR
+ (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" AND
+ CMAKE_CXX_COMPILER_VERSION VERSION_LESS 1930)))
+ # Similar to the above, but for C++20.
+ set(${var_name} FALSE)
+ endif()
+
+ if (${var_name})
+ list(APPEND all_stds ${standard})
+ endif()
+
+ message(STATUS "Testing ${lang}${standard} Support: ${${var_name}}")
+ set(${var_name} ${${var_name}} PARENT_SCOPE)
+ endforeach()
+
+ set(${prefix}_DETECTED_${lang}_STANDARDS "${all_stds}" PARENT_SCOPE)
+endfunction()
diff --git a/cmake/NVBenchClangdCompileInfo.cmake b/cmake/NVBenchClangdCompileInfo.cmake
new file mode 100644
index 00000000..a4b9c5e7
--- /dev/null
+++ b/cmake/NVBenchClangdCompileInfo.cmake
@@ -0,0 +1,28 @@
+# SPDX-FileCopyrightText: Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+# SPDX-License-Identifier: Apache-2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Tell cmake to generate a json file of compile commands for clangd:
+set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
+
+# Symlink the compile command output to the source dir, where clangd will find it.
+set(compile_commands_file "${CMAKE_BINARY_DIR}/compile_commands.json")
+set(compile_commands_link "${CMAKE_SOURCE_DIR}/compile_commands.json")
+message(STATUS "Creating symlink from ${compile_commands_link} to ${compile_commands_file}...")
+nvbench_execute_non_fatal_process(COMMAND
+ "${CMAKE_COMMAND}" -E rm -f "${compile_commands_link}")
+nvbench_execute_non_fatal_process(COMMAND
+ "${CMAKE_COMMAND}" -E touch "${compile_commands_file}")
+nvbench_execute_non_fatal_process(COMMAND
+ "${CMAKE_COMMAND}" -E create_symlink "${compile_commands_file}" "${compile_commands_link}")
diff --git a/cmake/NVBenchConfigTarget.cmake b/cmake/NVBenchConfigTarget.cmake
index 5e5e2701..bef95fcf 100644
--- a/cmake/NVBenchConfigTarget.cmake
+++ b/cmake/NVBenchConfigTarget.cmake
@@ -29,7 +29,6 @@ function(nvbench_add_cxx_flag target_name type flag)
target_compile_options(${target_name} ${type}
$<$:${flag}>
$<$:-Xcompiler=${flag}>
- # FIXME nvc++ case
)
endif()
endfunction()
@@ -64,8 +63,8 @@ else()
endif()
endif()
-# GCC-specific flags
-if (CMAKE_CXX_COMPILER_ID STREQUAL GNU)
+# Experimental filesystem library
+if (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang)
target_link_libraries(nvbench.build_interface INTERFACE stdc++fs)
endif()
diff --git a/cmake/NVBenchDependencies.cmake b/cmake/NVBenchDependencies.cmake
index 78fa758f..8ba07fe6 100644
--- a/cmake/NVBenchDependencies.cmake
+++ b/cmake/NVBenchDependencies.cmake
@@ -24,24 +24,16 @@ endif()
# Following recipe from
# http://github.com/cpm-cmake/CPM.cmake/blob/master/examples/json/CMakeLists.txt
# Download the zips because the repo takes an excessively long time to clone.
-rapids_cpm_find(nlohmann_json 3.9.1
- # Release:
+rapids_cpm_find(nlohmann_json 3.11.3
CPM_ARGS
- URL https://github.com/nlohmann/json/releases/download/v3.9.1/include.zip
- URL_HASH SHA256=6bea5877b1541d353bd77bdfbdb2696333ae5ed8f9e8cc22df657192218cad91
- PATCH_COMMAND
- # Work around compiler bug in nvcc 11.0, see NVIDIA/NVBench#18
- ${CMAKE_COMMAND} -E copy
- "${CMAKE_CURRENT_SOURCE_DIR}/cmake/patches/nlohmann_json.hpp"
- "./include/nlohmann/json.hpp"
-
- # Development version:
- # I'm waiting for https://github.com/nlohmann/json/issues/2676 to be fixed,
- # leave this in to simplify testing patches as they come out.
- # CPM_ARGS
- # VERSION develop
- # URL https://github.com/nlohmann/json/archive/refs/heads/develop.zip
- # OPTIONS JSON_MultipleHeaders ON
+ URL https://github.com/nlohmann/json/releases/download/v3.11.3/include.zip
+ URL_HASH SHA256=a22461d13119ac5c78f205d3df1db13403e58ce1bb1794edc9313677313f4a9d
+ PATCH_COMMAND
+ ${CMAKE_COMMAND}
+ -D "CUDA_VERSION=${CMAKE_CUDA_COMPILER_VERSION}"
+ -D "CXX_VERSION=${CMAKE_CXX_COMPILER_VERSION}"
+ -D "CXX_ID=${CMAKE_CXX_COMPILER_ID}"
+ -P "${CMAKE_CURRENT_SOURCE_DIR}/cmake/patches/json_unordered_map_ice.cmake"
)
add_library(nvbench_json INTERFACE IMPORTED)
diff --git a/cmake/NVBenchHeaderTesting.cmake b/cmake/NVBenchHeaderTesting.cmake
new file mode 100644
index 00000000..354ec84d
--- /dev/null
+++ b/cmake/NVBenchHeaderTesting.cmake
@@ -0,0 +1,40 @@
+# For every public header, build a translation unit containing `#include `
+# with some various checks.
+
+set(excluded_headers_regexes
+ # Should never be used externally.
+ "^detail"
+ "^internal"
+)
+
+# Meta target for all configs' header builds:
+add_custom_target(nvbench.headers.all)
+add_dependencies(nvbench.all nvbench.headers.all)
+
+file(GLOB_RECURSE header_files
+ RELATIVE "${NVBench_SOURCE_DIR}/nvbench/"
+ CONFIGURE_DEPENDS
+ "${NVBench_SOURCE_DIR}/nvbench/*.cuh"
+)
+
+foreach (exclusion IN LISTS excluded_headers_regexes)
+ list(FILTER header_files EXCLUDE REGEX "${exclusion}")
+endforeach()
+
+function (nvbench_add_header_target target_name cuda_std)
+ foreach (header IN LISTS header_files)
+ set(headertest_src "headers/${target_name}/${header}.cu")
+ set(header_str "nvbench/${header}") # Substitution used by configure_file:
+ configure_file("${NVBench_SOURCE_DIR}/cmake/header_test.in.cxx" "${headertest_src}")
+ list(APPEND headertest_srcs "${headertest_src}")
+ endforeach()
+
+ add_library(${target_name} OBJECT ${headertest_srcs})
+ target_link_libraries(${target_name} PUBLIC nvbench::nvbench)
+ set_target_properties(${target_name} PROPERTIES COMPILE_FEATURES cuda_std_${cuda_std})
+ add_dependencies(nvbench.headers.all ${target_name})
+endfunction()
+
+foreach (std IN LISTS NVBench_DETECTED_CUDA_STANDARDS)
+ nvbench_add_header_target(nvbench.headers.cpp${std} ${std})
+endforeach()
diff --git a/cmake/NVBenchUtilities.cmake b/cmake/NVBenchUtilities.cmake
index 36684203..caa79b8b 100644
--- a/cmake/NVBenchUtilities.cmake
+++ b/cmake/NVBenchUtilities.cmake
@@ -1,3 +1,48 @@
+# SPDX-FileCopyrightText: Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+# SPDX-License-Identifier: Apache-2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Passes all args directly to execute_process while setting up the following
+# results variables and propogating them to the caller's scope:
+#
+# - nvbench_process_exit_code
+# - nvbench_process_stdout
+# - nvbench_process_stderr
+#
+# If the command is not successful (e.g. the last command does not return zero),
+# a non-fatal warning is printed.
+function(nvbench_execute_non_fatal_process)
+ execute_process(${ARGN}
+ RESULT_VARIABLE nvbench_process_exit_code
+ OUTPUT_VARIABLE nvbench_process_stdout
+ ERROR_VARIABLE nvbench_process_stderr
+ )
+
+ if (NOT nvbench_process_exit_code EQUAL 0)
+ message(WARNING
+ "execute_process failed with non-zero exit code: ${nvbench_process_exit_code}\n"
+ "${ARGN}\n"
+ "stdout:\n${nvbench_process_stdout}\n"
+ "stderr:\n${nvbench_process_stderr}\n"
+ )
+ endif()
+
+ set(nvbench_process_exit_code "${nvbench_process_exit_code}" PARENT_SCOPE)
+ set(nvbench_process_stdout "${nvbench_process_stdout}" PARENT_SCOPE)
+ set(nvbench_process_stderr "${nvbench_process_stderr}" PARENT_SCOPE)
+endfunction()
+
# Writes CMAKE_CUDA_ARCHITECTURES to out_var, but using escaped semicolons
# as delimiters
function(nvbench_escaped_cuda_arches out_var)
diff --git a/cmake/PrintCTestRunTimes.cmake b/cmake/PrintCTestRunTimes.cmake
new file mode 100644
index 00000000..f4ac7d90
--- /dev/null
+++ b/cmake/PrintCTestRunTimes.cmake
@@ -0,0 +1,127 @@
+## This CMake script parses the output of ctest and prints a formatted list
+## of individual test runtimes, sorted longest first.
+##
+## ctest > ctest_log
+## cmake -DLOGFILE=ctest_log \
+## -DMINSEC=10 \
+## -P PrintCTestRunTimes.cmake
+##
+################################################################################
+
+cmake_minimum_required(VERSION 3.15)
+
+# Prepend the string with "0" until the string length equals the specified width
+function(pad_string_with_zeros string_var width)
+ set(local_string "${${string_var}}")
+ string(LENGTH "${local_string}" size)
+ while(size LESS width)
+ string(PREPEND local_string "0")
+ string(LENGTH "${local_string}" size)
+ endwhile()
+ set(${string_var} "${local_string}" PARENT_SCOPE)
+endfunction()
+
+################################################################################
+
+if (NOT LOGFILE)
+ message(FATAL_ERROR "Missing -DLOGFILE= argument.")
+endif()
+
+if (NOT DEFINED MINSEC)
+ set(MINSEC 10)
+endif()
+
+set(num_below_thresh 0)
+
+# Check if logfile exists
+if (NOT EXISTS "${LOGFILE}")
+ message(FATAL_ERROR "LOGFILE does not exist ('${LOGFILE}').")
+endif()
+
+string(JOIN "" regex
+ "[0-9]+/[0-9]+[ ]+Test[ ]+#"
+ "([0-9]+)" # Test ID
+ ":[ ]+"
+ "([^ ]+)" # Test Name
+ "[ ]*\\.+[ ]*\\**[ ]*"
+ "([^ ]+)" # Result
+ "[ ]+"
+ "([0-9]+)" # Seconds
+ "\\.[0-9]+[ ]+sec"
+)
+
+message(DEBUG "LOGFILE: ${LOGFILE}")
+message(DEBUG "MINSEC: ${MINSEC}")
+message(DEBUG "regex: ${regex}")
+
+# Read the logfile and generate a map / keylist
+set(keys)
+file(STRINGS "${LOGFILE}" lines)
+foreach(line ${lines})
+
+ # Parse each build time
+ string(REGEX MATCH "${regex}" _DUMMY "${line}")
+
+ if (CMAKE_MATCH_COUNT EQUAL 4)
+ set(test_id "${CMAKE_MATCH_1}")
+ set(test_name "${CMAKE_MATCH_2}")
+ set(test_result "${CMAKE_MATCH_3}")
+ set(tmp "${CMAKE_MATCH_4}") # floor(runtime_seconds)
+
+ if (tmp LESS MINSEC)
+ math(EXPR num_below_thresh "${num_below_thresh} + 1")
+ continue()
+ endif()
+
+ # Compute human readable time
+ math(EXPR days "${tmp} / (60 * 60 * 24)")
+ math(EXPR tmp "${tmp} - (${days} * 60 * 60 * 24)")
+ math(EXPR hours "${tmp} / (60 * 60)")
+ math(EXPR tmp "${tmp} - (${hours} * 60 * 60)")
+ math(EXPR minutes "${tmp} / (60)")
+ math(EXPR tmp "${tmp} - (${minutes} * 60)")
+ math(EXPR seconds "${tmp}")
+
+ # Format time components
+ pad_string_with_zeros(days 3)
+ pad_string_with_zeros(hours 2)
+ pad_string_with_zeros(minutes 2)
+ pad_string_with_zeros(seconds 2)
+
+ # Construct table entry
+ # Later values in the file for the same command overwrite earlier entries
+ string(MAKE_C_IDENTIFIER "${test_id}" key)
+ string(JOIN " | " ENTRY_${key}
+ "${days}d ${hours}h ${minutes}m ${seconds}s"
+ "${test_result}"
+ "${test_id}: ${test_name}"
+ )
+
+ # Record the key:
+ list(APPEND keys "${key}")
+ endif()
+endforeach()
+
+list(REMOVE_DUPLICATES keys)
+
+# Build the entry list:
+set(entries)
+foreach(key ${keys})
+ list(APPEND entries "${ENTRY_${key}}")
+endforeach()
+
+if (NOT entries)
+ message(STATUS "LOGFILE contained no test times ('${LOGFILE}').")
+endif()
+
+# Sort in descending order:
+list(SORT entries ORDER DESCENDING)
+
+# Dump table:
+foreach(entry ${entries})
+ message(STATUS ${entry})
+endforeach()
+
+if (num_below_thresh GREATER 0)
+ message(STATUS "${num_below_thresh} additional tests took < ${MINSEC}s each.")
+endif()
diff --git a/cmake/PrintNinjaBuildTimes.cmake b/cmake/PrintNinjaBuildTimes.cmake
new file mode 100644
index 00000000..65d243d3
--- /dev/null
+++ b/cmake/PrintNinjaBuildTimes.cmake
@@ -0,0 +1,101 @@
+## This CMake script parses a .ninja_log file (LOGFILE) and prints a list of
+## build/link times, sorted longest first.
+##
+## cmake -DLOGFILE=<.ninja_log file> \
+## -P PrintNinjaBuildTimes.cmake
+##
+## If LOGFILE is omitted, the current directory's .ninja_log file is used.
+################################################################################
+
+cmake_minimum_required(VERSION 3.15)
+
+# Prepend the string with "0" until the string length equals the specified width
+function(pad_string_with_zeros string_var width)
+ set(local_string "${${string_var}}")
+ string(LENGTH "${local_string}" size)
+ while(size LESS width)
+ string(PREPEND local_string "0")
+ string(LENGTH "${local_string}" size)
+ endwhile()
+ set(${string_var} "${local_string}" PARENT_SCOPE)
+endfunction()
+
+################################################################################
+
+if (NOT LOGFILE)
+ set(LOGFILE ".ninja_log")
+endif()
+
+# Check if logfile exists
+if (NOT EXISTS "${LOGFILE}")
+ message(FATAL_ERROR "LOGFILE does not exist ('${LOGFILE}').")
+endif()
+
+# Read the logfile and generate a map / keylist
+set(keys)
+file(STRINGS "${LOGFILE}" lines)
+foreach(line ${lines})
+
+ # Parse each build time
+ string(REGEX MATCH
+ "^([0-9]+)\t([0-9]+)\t[0-9]+\t([^\t]+)+\t[0-9a-fA-F]+$" _DUMMY "${line}")
+
+ if (CMAKE_MATCH_COUNT EQUAL 3)
+ set(start_ms ${CMAKE_MATCH_1})
+ set(end_ms ${CMAKE_MATCH_2})
+ set(command "${CMAKE_MATCH_3}")
+ math(EXPR runtime_ms "${end_ms} - ${start_ms}")
+
+ # Compute human readable time
+ math(EXPR days "${runtime_ms} / (1000 * 60 * 60 * 24)")
+ math(EXPR runtime_ms "${runtime_ms} - (${days} * 1000 * 60 * 60 * 24)")
+ math(EXPR hours "${runtime_ms} / (1000 * 60 * 60)")
+ math(EXPR runtime_ms "${runtime_ms} - (${hours} * 1000 * 60 * 60)")
+ math(EXPR minutes "${runtime_ms} / (1000 * 60)")
+ math(EXPR runtime_ms "${runtime_ms} - (${minutes} * 1000 * 60)")
+ math(EXPR seconds "${runtime_ms} / 1000")
+ math(EXPR milliseconds "${runtime_ms} - (${seconds} * 1000)")
+
+ # Format time components
+ pad_string_with_zeros(days 3)
+ pad_string_with_zeros(hours 2)
+ pad_string_with_zeros(minutes 2)
+ pad_string_with_zeros(seconds 2)
+ pad_string_with_zeros(milliseconds 3)
+
+ # Construct table entry
+ # Later values in the file for the same command overwrite earlier entries
+ string(MAKE_C_IDENTIFIER "${command}" key)
+ set(ENTRY_${key}
+ "${days}d ${hours}h ${minutes}m ${seconds}s ${milliseconds}ms | ${command}"
+ )
+
+ # Record the key:
+ list(APPEND keys "${key}")
+ endif()
+endforeach()
+
+list(REMOVE_DUPLICATES keys)
+
+# Build the entry list:
+set(entries)
+foreach(key ${keys})
+ list(APPEND entries "${ENTRY_${key}}")
+endforeach()
+
+if (NOT entries)
+ message(FATAL_ERROR "LOGFILE contained no build entries ('${LOGFILE}').")
+endif()
+
+# Sort in descending order:
+list(SORT entries)
+list(REVERSE entries)
+
+# Dump table:
+message(STATUS "-----------------------+----------------------------")
+message(STATUS "Time | Command ")
+message(STATUS "-----------------------+----------------------------")
+
+foreach(entry ${entries})
+ message(STATUS ${entry})
+endforeach()
diff --git a/cmake/header_test.in.cxx b/cmake/header_test.in.cxx
new file mode 100644
index 00000000..c26753e1
--- /dev/null
+++ b/cmake/header_test.in.cxx
@@ -0,0 +1,57 @@
+// This source file checks that:
+// 1) Header <${header_str}> compiles without error.
+// 2) Common macro collisions with platform/system headers are avoided.
+
+// Turn off failures for certain configurations:
+#ifndef NVBench_IGNORE_MACRO_CHECKS
+
+// Define NVBench_MACRO_CHECK(macro, header), which emits a diagnostic indicating
+// a potential macro collision and halts.
+//
+// Hacky way to build a string, but it works on all tested platforms.
+#define NVBench_MACRO_CHECK(MACRO, HEADER) \
+ NVBench_MACRO_CHECK_IMPL(Identifier MACRO should not be used from NVBench \
+ headers due to conflicts with HEADER macros.)
+
+// Use raw platform checks instead of the NVBench_HOST_COMPILER macros since we
+// don't want to #include any headers other than the one being tested.
+//
+// This is only implemented for MSVC/GCC/Clang.
+#if defined(_MSC_VER) // MSVC
+
+// Fake up an error for MSVC
+#define NVBench_MACRO_CHECK_IMPL(msg) \
+ /* Print message that looks like an error: */ \
+ __pragma(message(__FILE__ ":" NVBench_MACRO_CHECK_IMPL0(__LINE__) \
+ ": error: " #msg)) \
+ /* abort compilation due to static_assert or syntax error: */ \
+ static_assert(false, #msg);
+#define NVBench_MACRO_CHECK_IMPL0(x) NVBench_MACRO_CHECK_IMPL1(x)
+#define NVBench_MACRO_CHECK_IMPL1(x) #x
+
+#elif defined(__clang__) || defined(__GNUC__)
+
+// GCC/clang are easy:
+#define NVBench_MACRO_CHECK_IMPL(msg) NVBench_MACRO_CHECK_IMPL0(GCC error #msg)
+#define NVBench_MACRO_CHECK_IMPL0(expr) _Pragma(#expr)
+
+#endif
+
+// complex.h conflicts
+#define I NVBench_MACRO_CHECK('I', complex.h)
+
+// windows.h conflicts
+#define small NVBench_MACRO_CHECK('small', windows.h)
+// We can't enable these checks without breaking some builds -- some standard
+// library implementations unconditionally `#undef` these macros, which then
+// causes random failures later.
+// Leaving these commented out as a warning: Here be dragons.
+//#define min(...) NVBench_MACRO_CHECK('min', windows.h)
+//#define max(...) NVBench_MACRO_CHECK('max', windows.h)
+
+// termios.h conflicts (NVIDIA/thrust#1547)
+#define B0 NVBench_MACRO_CHECK("B0", termios.h)
+
+#endif // NVBench_IGNORE_MACRO_CHECKS
+
+#include <${header_str}>
diff --git a/cmake/patches/json_unordered_map_ice.cmake b/cmake/patches/json_unordered_map_ice.cmake
new file mode 100644
index 00000000..44f37c3b
--- /dev/null
+++ b/cmake/patches/json_unordered_map_ice.cmake
@@ -0,0 +1,22 @@
+# NVCC 11.1 and GCC 9 need a patch to build, otherwise:
+#
+# nlohmann/ordered_map.hpp(29): error #3316:
+# Internal Compiler Error (codegen): "internal error during structure layout!"
+#
+# Usage:
+# ${CMAKE_COMMAND}
+# -D "CUDA_VERSION=${CMAKE_CUDA_COMPILER_VERSION}"
+# -D "CXX_VERSION=${CMAKE_CXX_COMPILER_VERSION}"
+# -D "CXX_ID=${CMAKE_CXX_COMPILER_ID}"
+# -P "json_unordered_map_ice.cmake"
+
+if(CUDA_VERSION VERSION_GREATER 11.8 OR NOT CXX_ID STREQUAL "GNU" OR CXX_VERSION VERSION_LESS 9.0)
+ return()
+endif()
+
+# Read the file and replace the string "JSON_NO_UNIQUE_ADDRESS" with
+# "/* JSON_NO_UNIQUE_ADDRESS */".
+file(READ "include/nlohmann/ordered_map.hpp" NLOHMANN_ORDERED_MAP_HPP)
+string(REPLACE "JSON_NO_UNIQUE_ADDRESS" "/* [NVBench Patch] JSON_NO_UNIQUE_ADDRESS */"
+ NLOHMANN_ORDERED_MAP_HPP "${NLOHMANN_ORDERED_MAP_HPP}")
+file(WRITE "include/nlohmann/ordered_map.hpp" "${NLOHMANN_ORDERED_MAP_HPP}")
diff --git a/cmake/patches/nlohmann_json.hpp b/cmake/patches/nlohmann_json.hpp
deleted file mode 100644
index 9a3a0ccb..00000000
--- a/cmake/patches/nlohmann_json.hpp
+++ /dev/null
@@ -1,8799 +0,0 @@
-/*
- __ _____ _____ _____
- __| | __| | | | JSON for Modern C++
-| | |__ | | | | | | version 3.9.1
-|_____|_____|_____|_|___| https://github.com/nlohmann/json
-
-Licensed under the MIT License .
-SPDX-License-Identifier: MIT
-Copyright (c) 2013-2019 Niels Lohmann .
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-
-#ifndef INCLUDE_NLOHMANN_JSON_HPP_
-#define INCLUDE_NLOHMANN_JSON_HPP_
-
-#define NLOHMANN_JSON_VERSION_MAJOR 3
-#define NLOHMANN_JSON_VERSION_MINOR 9
-#define NLOHMANN_JSON_VERSION_PATCH 1
-
-#include // all_of, find, for_each
-#include // nullptr_t, ptrdiff_t, size_t
-#include // hash, less
-#include // initializer_list
-#include // istream, ostream
-#include // random_access_iterator_tag
-#include // unique_ptr
-#include // accumulate
-#include // string, stoi, to_string
-#include // declval, forward, move, pair, swap
-#include // vector
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-/*!
-@brief namespace for Niels Lohmann
-@see https://github.com/nlohmann
-@since version 1.0.0
-*/
-namespace nlohmann
-{
-
-/*!
-@brief a class to store JSON values
-
-@tparam ObjectType type for JSON objects (`std::map` by default; will be used
-in @ref object_t)
-@tparam ArrayType type for JSON arrays (`std::vector` by default; will be used
-in @ref array_t)
-@tparam StringType type for JSON strings and object keys (`std::string` by
-default; will be used in @ref string_t)
-@tparam BooleanType type for JSON booleans (`bool` by default; will be used
-in @ref boolean_t)
-@tparam NumberIntegerType type for JSON integer numbers (`int64_t` by
-default; will be used in @ref number_integer_t)
-@tparam NumberUnsignedType type for JSON unsigned integer numbers (@c
-`uint64_t` by default; will be used in @ref number_unsigned_t)
-@tparam NumberFloatType type for JSON floating-point numbers (`double` by
-default; will be used in @ref number_float_t)
-@tparam BinaryType type for packed binary data for compatibility with binary
-serialization formats (`std::vector` by default; will be used in
-@ref binary_t)
-@tparam AllocatorType type of the allocator to use (`std::allocator` by
-default)
-@tparam JSONSerializer the serializer to resolve internal calls to `to_json()`
-and `from_json()` (@ref adl_serializer by default)
-
-@requirement The class satisfies the following concept requirements:
-- Basic
- - [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible):
- JSON values can be default constructed. The result will be a JSON null
- value.
- - [MoveConstructible](https://en.cppreference.com/w/cpp/named_req/MoveConstructible):
- A JSON value can be constructed from an rvalue argument.
- - [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible):
- A JSON value can be copy-constructed from an lvalue expression.
- - [MoveAssignable](https://en.cppreference.com/w/cpp/named_req/MoveAssignable):
- A JSON value van be assigned from an rvalue argument.
- - [CopyAssignable](https://en.cppreference.com/w/cpp/named_req/CopyAssignable):
- A JSON value can be copy-assigned from an lvalue expression.
- - [Destructible](https://en.cppreference.com/w/cpp/named_req/Destructible):
- JSON values can be destructed.
-- Layout
- - [StandardLayoutType](https://en.cppreference.com/w/cpp/named_req/StandardLayoutType):
- JSON values have
- [standard layout](https://en.cppreference.com/w/cpp/language/data_members#Standard_layout):
- All non-static data members are private and standard layout types, the
- class has no virtual functions or (virtual) base classes.
-- Library-wide
- - [EqualityComparable](https://en.cppreference.com/w/cpp/named_req/EqualityComparable):
- JSON values can be compared with `==`, see @ref
- operator==(const_reference,const_reference).
- - [LessThanComparable](https://en.cppreference.com/w/cpp/named_req/LessThanComparable):
- JSON values can be compared with `<`, see @ref
- operator<(const_reference,const_reference).
- - [Swappable](https://en.cppreference.com/w/cpp/named_req/Swappable):
- Any JSON lvalue or rvalue of can be swapped with any lvalue or rvalue of
- other compatible types, using unqualified function call @ref swap().
- - [NullablePointer](https://en.cppreference.com/w/cpp/named_req/NullablePointer):
- JSON values can be compared against `std::nullptr_t` objects which are used
- to model the `null` value.
-- Container
- - [Container](https://en.cppreference.com/w/cpp/named_req/Container):
- JSON values can be used like STL containers and provide iterator access.
- - [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer);
- JSON values can be used like STL containers and provide reverse iterator
- access.
-
-@invariant The member variables @a m_value and @a m_type have the following
-relationship:
-- If `m_type == value_t::object`, then `m_value.object != nullptr`.
-- If `m_type == value_t::array`, then `m_value.array != nullptr`.
-- If `m_type == value_t::string`, then `m_value.string != nullptr`.
-The invariants are checked by member function assert_invariant().
-
-@internal
-@note ObjectType trick from https://stackoverflow.com/a/9860911
-@endinternal
-
-@see [RFC 7159: The JavaScript Object Notation (JSON) Data Interchange
-Format](http://rfc7159.net/rfc7159)
-
-@since version 1.0.0
-
-@nosubgrouping
-*/
-NLOHMANN_BASIC_JSON_TPL_DECLARATION
-class basic_json
-{
- private:
- template friend struct detail::external_constructor;
- friend ::nlohmann::json_pointer;
-
- template
- friend class ::nlohmann::detail::parser;
- friend ::nlohmann::detail::serializer;
- template
- friend class ::nlohmann::detail::iter_impl;
- template
- friend class ::nlohmann::detail::binary_writer;
- template
- friend class ::nlohmann::detail::binary_reader;
- template
- friend class ::nlohmann::detail::json_sax_dom_parser;
- template
- friend class ::nlohmann::detail::json_sax_dom_callback_parser;
-
- /// workaround type for MSVC
- using basic_json_t = NLOHMANN_BASIC_JSON_TPL;
-
- // convenience aliases for types residing in namespace detail;
- using lexer = ::nlohmann::detail::lexer_base;
-
- template
- static ::nlohmann::detail::parser parser(
- InputAdapterType adapter,
- detail::parser_callback_tcb = nullptr,
- const bool allow_exceptions = true,
- const bool ignore_comments = false
- )
- {
- return ::nlohmann::detail::parser(std::move(adapter),
- std::move(cb), allow_exceptions, ignore_comments);
- }
-
- using primitive_iterator_t = ::nlohmann::detail::primitive_iterator_t;
- template
- using internal_iterator = ::nlohmann::detail::internal_iterator;
- template
- using iter_impl = ::nlohmann::detail::iter_impl;
- template
- using iteration_proxy = ::nlohmann::detail::iteration_proxy;
- template using json_reverse_iterator = ::nlohmann::detail::json_reverse_iterator;
-
- template
- using output_adapter_t = ::nlohmann::detail::output_adapter_t;
-
- template
- using binary_reader = ::nlohmann::detail::binary_reader;
- template using binary_writer = ::nlohmann::detail::binary_writer;
-
- using serializer = ::nlohmann::detail::serializer;
-
- public:
- using value_t = detail::value_t;
- /// JSON Pointer, see @ref nlohmann::json_pointer
- using json_pointer = ::nlohmann::json_pointer;
- template
- using json_serializer = JSONSerializer;
- /// how to treat decoding errors
- using error_handler_t = detail::error_handler_t;
- /// how to treat CBOR tags
- using cbor_tag_handler_t = detail::cbor_tag_handler_t;
- /// helper type for initializer lists of basic_json values
- using initializer_list_t = std::initializer_list>;
-
- using input_format_t = detail::input_format_t;
- /// SAX interface type, see @ref nlohmann::json_sax
- using json_sax_t = json_sax;
-
- ////////////////
- // exceptions //
- ////////////////
-
- /// @name exceptions
- /// Classes to implement user-defined exceptions.
- /// @{
-
- /// @copydoc detail::exception
- using exception = detail::exception;
- /// @copydoc detail::parse_error
- using parse_error = detail::parse_error;
- /// @copydoc detail::invalid_iterator
- using invalid_iterator = detail::invalid_iterator;
- /// @copydoc detail::type_error
- using type_error = detail::type_error;
- /// @copydoc detail::out_of_range
- using out_of_range = detail::out_of_range;
- /// @copydoc detail::other_error
- using other_error = detail::other_error;
-
- /// @}
-
-
- /////////////////////
- // container types //
- /////////////////////
-
- /// @name container types
- /// The canonic container types to use @ref basic_json like any other STL
- /// container.
- /// @{
-
- /// the type of elements in a basic_json container
- using value_type = basic_json;
-
- /// the type of an element reference
- using reference = value_type&;
- /// the type of an element const reference
- using const_reference = const value_type&;
-
- /// a type to represent differences between iterators
- using difference_type = std::ptrdiff_t;
- /// a type to represent container sizes
- using size_type = std::size_t;
-
- /// the allocator type
- using allocator_type = AllocatorType;
-
- /// the type of an element pointer
- using pointer = typename std::allocator_traits::pointer;
- /// the type of an element const pointer
- using const_pointer = typename std::allocator_traits::const_pointer;
-
- /// an iterator for a basic_json container
- using iterator = iter_impl;
- /// a const iterator for a basic_json container
- using const_iterator = iter_impl;
- /// a reverse iterator for a basic_json container
- using reverse_iterator = json_reverse_iterator;
- /// a const reverse iterator for a basic_json container
- using const_reverse_iterator = json_reverse_iterator;
-
- /// @}
-
-
- /*!
- @brief returns the allocator associated with the container
- */
- static allocator_type get_allocator()
- {
- return allocator_type();
- }
-
- /*!
- @brief returns version information on the library
-
- This function returns a JSON object with information about the library,
- including the version number and information on the platform and compiler.
-
- @return JSON object holding version information
- key | description
- ----------- | ---------------
- `compiler` | Information on the used compiler. It is an object with the following keys: `c++` (the used C++ standard), `family` (the compiler family; possible values are `clang`, `icc`, `gcc`, `ilecpp`, `msvc`, `pgcpp`, `sunpro`, and `unknown`), and `version` (the compiler version).
- `copyright` | The copyright line for the library as string.
- `name` | The name of the library as string.
- `platform` | The used platform as string. Possible values are `win32`, `linux`, `apple`, `unix`, and `unknown`.
- `url` | The URL of the project as string.
- `version` | The version of the library. It is an object with the following keys: `major`, `minor`, and `patch` as defined by [Semantic Versioning](http://semver.org), and `string` (the version string).
-
- @liveexample{The following code shows an example output of the `meta()`
- function.,meta}
-
- @exceptionsafety Strong guarantee: if an exception is thrown, there are no
- changes to any JSON value.
-
- @complexity Constant.
-
- @since 2.1.0
- */
- JSON_HEDLEY_WARN_UNUSED_RESULT
- static basic_json meta()
- {
- basic_json result;
-
- result["copyright"] = "(C) 2013-2020 Niels Lohmann";
- result["name"] = "JSON for Modern C++";
- result["url"] = "https://github.com/nlohmann/json";
- result["version"]["string"] =
- std::to_string(NLOHMANN_JSON_VERSION_MAJOR) + "." +
- std::to_string(NLOHMANN_JSON_VERSION_MINOR) + "." +
- std::to_string(NLOHMANN_JSON_VERSION_PATCH);
- result["version"]["major"] = NLOHMANN_JSON_VERSION_MAJOR;
- result["version"]["minor"] = NLOHMANN_JSON_VERSION_MINOR;
- result["version"]["patch"] = NLOHMANN_JSON_VERSION_PATCH;
-
-#ifdef _WIN32
- result["platform"] = "win32";
-#elif defined __linux__
- result["platform"] = "linux";
-#elif defined __APPLE__
- result["platform"] = "apple";
-#elif defined __unix__
- result["platform"] = "unix";
-#else
- result["platform"] = "unknown";
-#endif
-
-#if defined(__ICC) || defined(__INTEL_COMPILER)
- result["compiler"] = {{"family", "icc"}, {"version", __INTEL_COMPILER}};
-#elif defined(__clang__)
- result["compiler"] = {{"family", "clang"}, {"version", __clang_version__}};
-#elif defined(__GNUC__) || defined(__GNUG__)
- result["compiler"] = {{"family", "gcc"}, {"version", std::to_string(__GNUC__) + "." + std::to_string(__GNUC_MINOR__) + "." + std::to_string(__GNUC_PATCHLEVEL__)}};
-#elif defined(__HP_cc) || defined(__HP_aCC)
- result["compiler"] = "hp"
-#elif defined(__IBMCPP__)
- result["compiler"] = {{"family", "ilecpp"}, {"version", __IBMCPP__}};
-#elif defined(_MSC_VER)
- result["compiler"] = {{"family", "msvc"}, {"version", _MSC_VER}};
-#elif defined(__PGI)
- result["compiler"] = {{"family", "pgcpp"}, {"version", __PGI}};
-#elif defined(__SUNPRO_CC)
- result["compiler"] = {{"family", "sunpro"}, {"version", __SUNPRO_CC}};
-#else
- result["compiler"] = {{"family", "unknown"}, {"version", "unknown"}};
-#endif
-
-#ifdef __cplusplus
- result["compiler"]["c++"] = std::to_string(__cplusplus);
-#else
- result["compiler"]["c++"] = "unknown";
-#endif
- return result;
- }
-
-
- ///////////////////////////
- // JSON value data types //
- ///////////////////////////
-
- /// @name JSON value data types
- /// The data types to store a JSON value. These types are derived from
- /// the template arguments passed to class @ref basic_json.
- /// @{
-
-#if defined(JSON_HAS_CPP_14)
- // Use transparent comparator if possible, combined with perfect forwarding
- // on find() and count() calls prevents unnecessary string construction.
- using object_comparator_t = std::less<>;
-#else
- using object_comparator_t = std::less;
-#endif
-
- /*!
- @brief a type for an object
-
- [RFC 7159](http://rfc7159.net/rfc7159) describes JSON objects as follows:
- > An object is an unordered collection of zero or more name/value pairs,
- > where a name is a string and a value is a string, number, boolean, null,
- > object, or array.
-
- To store objects in C++, a type is defined by the template parameters
- described below.
-
- @tparam ObjectType the container to store objects (e.g., `std::map` or
- `std::unordered_map`)
- @tparam StringType the type of the keys or names (e.g., `std::string`).
- The comparison function `std::less` is used to order elements
- inside the container.
- @tparam AllocatorType the allocator to use for objects (e.g.,
- `std::allocator`)
-
- #### Default type
-
- With the default values for @a ObjectType (`std::map`), @a StringType
- (`std::string`), and @a AllocatorType (`std::allocator`), the default
- value for @a object_t is:
-
- @code {.cpp}
- std::map<
- std::string, // key_type
- basic_json, // value_type
- std::less, // key_compare
- std::allocator> // allocator_type
- >
- @endcode
-
- #### Behavior
-
- The choice of @a object_t influences the behavior of the JSON class. With
- the default type, objects have the following behavior:
-
- - When all names are unique, objects will be interoperable in the sense
- that all software implementations receiving that object will agree on
- the name-value mappings.
- - When the names within an object are not unique, it is unspecified which
- one of the values for a given key will be chosen. For instance,
- `{"key": 2, "key": 1}` could be equal to either `{"key": 1}` or
- `{"key": 2}`.
- - Internally, name/value pairs are stored in lexicographical order of the
- names. Objects will also be serialized (see @ref dump) in this order.
- For instance, `{"b": 1, "a": 2}` and `{"a": 2, "b": 1}` will be stored
- and serialized as `{"a": 2, "b": 1}`.
- - When comparing objects, the order of the name/value pairs is irrelevant.
- This makes objects interoperable in the sense that they will not be
- affected by these differences. For instance, `{"b": 1, "a": 2}` and
- `{"a": 2, "b": 1}` will be treated as equal.
-
- #### Limits
-
- [RFC 7159](http://rfc7159.net/rfc7159) specifies:
- > An implementation may set limits on the maximum depth of nesting.
-
- In this class, the object's limit of nesting is not explicitly constrained.
- However, a maximum depth of nesting may be introduced by the compiler or
- runtime environment. A theoretical limit can be queried by calling the
- @ref max_size function of a JSON object.
-
- #### Storage
-
- Objects are stored as pointers in a @ref basic_json type. That is, for any
- access to object values, a pointer of type `object_t*` must be
- dereferenced.
-
- @sa @ref array_t -- type for an array value
-
- @since version 1.0.0
-
- @note The order name/value pairs are added to the object is *not*
- preserved by the library. Therefore, iterating an object may return
- name/value pairs in a different order than they were originally stored. In
- fact, keys will be traversed in alphabetical order as `std::map` with
- `std::less` is used by default. Please note this behavior conforms to [RFC
- 7159](http://rfc7159.net/rfc7159), because any order implements the
- specified "unordered" nature of JSON objects.
- */
- using object_t = ObjectType>>;
-
- /*!
- @brief a type for an array
-
- [RFC 7159](http://rfc7159.net/rfc7159) describes JSON arrays as follows:
- > An array is an ordered sequence of zero or more values.
-
- To store objects in C++, a type is defined by the template parameters
- explained below.
-
- @tparam ArrayType container type to store arrays (e.g., `std::vector` or
- `std::list`)
- @tparam AllocatorType allocator to use for arrays (e.g., `std::allocator`)
-
- #### Default type
-
- With the default values for @a ArrayType (`std::vector`) and @a
- AllocatorType (`std::allocator`), the default value for @a array_t is:
-
- @code {.cpp}
- std::vector<
- basic_json, // value_type
- std::allocator // allocator_type
- >
- @endcode
-
- #### Limits
-
- [RFC 7159](http://rfc7159.net/rfc7159) specifies:
- > An implementation may set limits on the maximum depth of nesting.
-
- In this class, the array's limit of nesting is not explicitly constrained.
- However, a maximum depth of nesting may be introduced by the compiler or
- runtime environment. A theoretical limit can be queried by calling the
- @ref max_size function of a JSON array.
-
- #### Storage
-
- Arrays are stored as pointers in a @ref basic_json type. That is, for any
- access to array values, a pointer of type `array_t*` must be dereferenced.
-
- @sa @ref object_t -- type for an object value
-
- @since version 1.0.0
- */
- using array_t = ArrayType>;
-
- /*!
- @brief a type for a string
-
- [RFC 7159](http://rfc7159.net/rfc7159) describes JSON strings as follows:
- > A string is a sequence of zero or more Unicode characters.
-
- To store objects in C++, a type is defined by the template parameter
- described below. Unicode values are split by the JSON class into
- byte-sized characters during deserialization.
-
- @tparam StringType the container to store strings (e.g., `std::string`).
- Note this container is used for keys/names in objects, see @ref object_t.
-
- #### Default type
-
- With the default values for @a StringType (`std::string`), the default
- value for @a string_t is:
-
- @code {.cpp}
- std::string
- @endcode
-
- #### Encoding
-
- Strings are stored in UTF-8 encoding. Therefore, functions like
- `std::string::size()` or `std::string::length()` return the number of
- bytes in the string rather than the number of characters or glyphs.
-
- #### String comparison
-
- [RFC 7159](http://rfc7159.net/rfc7159) states:
- > Software implementations are typically required to test names of object
- > members for equality. Implementations that transform the textual
- > representation into sequences of Unicode code units and then perform the
- > comparison numerically, code unit by code unit, are interoperable in the
- > sense that implementations will agree in all cases on equality or
- > inequality of two strings. For example, implementations that compare
- > strings with escaped characters unconverted may incorrectly find that
- > `"a\\b"` and `"a\u005Cb"` are not equal.
-
- This implementation is interoperable as it does compare strings code unit
- by code unit.
-
- #### Storage
-
- String values are stored as pointers in a @ref basic_json type. That is,
- for any access to string values, a pointer of type `string_t*` must be
- dereferenced.
-
- @since version 1.0.0
- */
- using string_t = StringType;
-
- /*!
- @brief a type for a boolean
-
- [RFC 7159](http://rfc7159.net/rfc7159) implicitly describes a boolean as a
- type which differentiates the two literals `true` and `false`.
-
- To store objects in C++, a type is defined by the template parameter @a
- BooleanType which chooses the type to use.
-
- #### Default type
-
- With the default values for @a BooleanType (`bool`), the default value for
- @a boolean_t is:
-
- @code {.cpp}
- bool
- @endcode
-
- #### Storage
-
- Boolean values are stored directly inside a @ref basic_json type.
-
- @since version 1.0.0
- */
- using boolean_t = BooleanType;
-
- /*!
- @brief a type for a number (integer)
-
- [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows:
- > The representation of numbers is similar to that used in most
- > programming languages. A number is represented in base 10 using decimal
- > digits. It contains an integer component that may be prefixed with an
- > optional minus sign, which may be followed by a fraction part and/or an
- > exponent part. Leading zeros are not allowed. (...) Numeric values that
- > cannot be represented in the grammar below (such as Infinity and NaN)
- > are not permitted.
-
- This description includes both integer and floating-point numbers.
- However, C++ allows more precise storage if it is known whether the number
- is a signed integer, an unsigned integer or a floating-point number.
- Therefore, three different types, @ref number_integer_t, @ref
- number_unsigned_t and @ref number_float_t are used.
-
- To store integer numbers in C++, a type is defined by the template
- parameter @a NumberIntegerType which chooses the type to use.
-
- #### Default type
-
- With the default values for @a NumberIntegerType (`int64_t`), the default
- value for @a number_integer_t is:
-
- @code {.cpp}
- int64_t
- @endcode
-
- #### Default behavior
-
- - The restrictions about leading zeros is not enforced in C++. Instead,
- leading zeros in integer literals lead to an interpretation as octal
- number. Internally, the value will be stored as decimal number. For
- instance, the C++ integer literal `010` will be serialized to `8`.
- During deserialization, leading zeros yield an error.
- - Not-a-number (NaN) values will be serialized to `null`.
-
- #### Limits
-
- [RFC 7159](http://rfc7159.net/rfc7159) specifies:
- > An implementation may set limits on the range and precision of numbers.
-
- When the default type is used, the maximal integer number that can be
- stored is `9223372036854775807` (INT64_MAX) and the minimal integer number
- that can be stored is `-9223372036854775808` (INT64_MIN). Integer numbers
- that are out of range will yield over/underflow when used in a
- constructor. During deserialization, too large or small integer numbers
- will be automatically be stored as @ref number_unsigned_t or @ref
- number_float_t.
-
- [RFC 7159](http://rfc7159.net/rfc7159) further states:
- > Note that when such software is used, numbers that are integers and are
- > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense
- > that implementations will agree exactly on their numeric values.
-
- As this range is a subrange of the exactly supported range [INT64_MIN,
- INT64_MAX], this class's integer type is interoperable.
-
- #### Storage
-
- Integer number values are stored directly inside a @ref basic_json type.
-
- @sa @ref number_float_t -- type for number values (floating-point)
-
- @sa @ref number_unsigned_t -- type for number values (unsigned integer)
-
- @since version 1.0.0
- */
- using number_integer_t = NumberIntegerType;
-
- /*!
- @brief a type for a number (unsigned)
-
- [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows:
- > The representation of numbers is similar to that used in most
- > programming languages. A number is represented in base 10 using decimal
- > digits. It contains an integer component that may be prefixed with an
- > optional minus sign, which may be followed by a fraction part and/or an
- > exponent part. Leading zeros are not allowed. (...) Numeric values that
- > cannot be represented in the grammar below (such as Infinity and NaN)
- > are not permitted.
-
- This description includes both integer and floating-point numbers.
- However, C++ allows more precise storage if it is known whether the number
- is a signed integer, an unsigned integer or a floating-point number.
- Therefore, three different types, @ref number_integer_t, @ref
- number_unsigned_t and @ref number_float_t are used.
-
- To store unsigned integer numbers in C++, a type is defined by the
- template parameter @a NumberUnsignedType which chooses the type to use.
-
- #### Default type
-
- With the default values for @a NumberUnsignedType (`uint64_t`), the
- default value for @a number_unsigned_t is:
-
- @code {.cpp}
- uint64_t
- @endcode
-
- #### Default behavior
-
- - The restrictions about leading zeros is not enforced in C++. Instead,
- leading zeros in integer literals lead to an interpretation as octal
- number. Internally, the value will be stored as decimal number. For
- instance, the C++ integer literal `010` will be serialized to `8`.
- During deserialization, leading zeros yield an error.
- - Not-a-number (NaN) values will be serialized to `null`.
-
- #### Limits
-
- [RFC 7159](http://rfc7159.net/rfc7159) specifies:
- > An implementation may set limits on the range and precision of numbers.
-
- When the default type is used, the maximal integer number that can be
- stored is `18446744073709551615` (UINT64_MAX) and the minimal integer
- number that can be stored is `0`. Integer numbers that are out of range
- will yield over/underflow when used in a constructor. During
- deserialization, too large or small integer numbers will be automatically
- be stored as @ref number_integer_t or @ref number_float_t.
-
- [RFC 7159](http://rfc7159.net/rfc7159) further states:
- > Note that when such software is used, numbers that are integers and are
- > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense
- > that implementations will agree exactly on their numeric values.
-
- As this range is a subrange (when considered in conjunction with the
- number_integer_t type) of the exactly supported range [0, UINT64_MAX],
- this class's integer type is interoperable.
-
- #### Storage
-
- Integer number values are stored directly inside a @ref basic_json type.
-
- @sa @ref number_float_t -- type for number values (floating-point)
- @sa @ref number_integer_t -- type for number values (integer)
-
- @since version 2.0.0
- */
- using number_unsigned_t = NumberUnsignedType;
-
- /*!
- @brief a type for a number (floating-point)
-
- [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows:
- > The representation of numbers is similar to that used in most
- > programming languages. A number is represented in base 10 using decimal
- > digits. It contains an integer component that may be prefixed with an
- > optional minus sign, which may be followed by a fraction part and/or an
- > exponent part. Leading zeros are not allowed. (...) Numeric values that
- > cannot be represented in the grammar below (such as Infinity and NaN)
- > are not permitted.
-
- This description includes both integer and floating-point numbers.
- However, C++ allows more precise storage if it is known whether the number
- is a signed integer, an unsigned integer or a floating-point number.
- Therefore, three different types, @ref number_integer_t, @ref
- number_unsigned_t and @ref number_float_t are used.
-
- To store floating-point numbers in C++, a type is defined by the template
- parameter @a NumberFloatType which chooses the type to use.
-
- #### Default type
-
- With the default values for @a NumberFloatType (`double`), the default
- value for @a number_float_t is:
-
- @code {.cpp}
- double
- @endcode
-
- #### Default behavior
-
- - The restrictions about leading zeros is not enforced in C++. Instead,
- leading zeros in floating-point literals will be ignored. Internally,
- the value will be stored as decimal number. For instance, the C++
- floating-point literal `01.2` will be serialized to `1.2`. During
- deserialization, leading zeros yield an error.
- - Not-a-number (NaN) values will be serialized to `null`.
-
- #### Limits
-
- [RFC 7159](http://rfc7159.net/rfc7159) states:
- > This specification allows implementations to set limits on the range and
- > precision of numbers accepted. Since software that implements IEEE
- > 754-2008 binary64 (double precision) numbers is generally available and
- > widely used, good interoperability can be achieved by implementations
- > that expect no more precision or range than these provide, in the sense
- > that implementations will approximate JSON numbers within the expected
- > precision.
-
- This implementation does exactly follow this approach, as it uses double
- precision floating-point numbers. Note values smaller than
- `-1.79769313486232e+308` and values greater than `1.79769313486232e+308`
- will be stored as NaN internally and be serialized to `null`.
-
- #### Storage
-
- Floating-point number values are stored directly inside a @ref basic_json
- type.
-
- @sa @ref number_integer_t -- type for number values (integer)
-
- @sa @ref number_unsigned_t -- type for number values (unsigned integer)
-
- @since version 1.0.0
- */
- using number_float_t = NumberFloatType;
-
- /*!
- @brief a type for a packed binary type
-
- This type is a type designed to carry binary data that appears in various
- serialized formats, such as CBOR's Major Type 2, MessagePack's bin, and
- BSON's generic binary subtype. This type is NOT a part of standard JSON and
- exists solely for compatibility with these binary types. As such, it is
- simply defined as an ordered sequence of zero or more byte values.
-
- Additionally, as an implementation detail, the subtype of the binary data is
- carried around as a `std::uint8_t`, which is compatible with both of the
- binary data formats that use binary subtyping, (though the specific
- numbering is incompatible with each other, and it is up to the user to
- translate between them).
-
- [CBOR's RFC 7049](https://tools.ietf.org/html/rfc7049) describes this type
- as:
- > Major type 2: a byte string. The string's length in bytes is represented
- > following the rules for positive integers (major type 0).
-
- [MessagePack's documentation on the bin type
- family](https://github.com/msgpack/msgpack/blob/master/spec.md#bin-format-family)
- describes this type as:
- > Bin format family stores an byte array in 2, 3, or 5 bytes of extra bytes
- > in addition to the size of the byte array.
-
- [BSON's specifications](http://bsonspec.org/spec.html) describe several
- binary types; however, this type is intended to represent the generic binary
- type which has the description:
- > Generic binary subtype - This is the most commonly used binary subtype and
- > should be the 'default' for drivers and tools.
-
- None of these impose any limitations on the internal representation other
- than the basic unit of storage be some type of array whose parts are
- decomposable into bytes.
-
- The default representation of this binary format is a
- `std::vector`, which is a very common way to represent a byte
- array in modern C++.
-
- #### Default type
-
- The default values for @a BinaryType is `std::vector`
-
- #### Storage
-
- Binary Arrays are stored as pointers in a @ref basic_json type. That is,
- for any access to array values, a pointer of the type `binary_t*` must be
- dereferenced.
-
- #### Notes on subtypes
-
- - CBOR
- - Binary values are represented as byte strings. No subtypes are
- supported and will be ignored when CBOR is written.
- - MessagePack
- - If a subtype is given and the binary array contains exactly 1, 2, 4, 8,
- or 16 elements, the fixext family (fixext1, fixext2, fixext4, fixext8)
- is used. For other sizes, the ext family (ext8, ext16, ext32) is used.
- The subtype is then added as singed 8-bit integer.
- - If no subtype is given, the bin family (bin8, bin16, bin32) is used.
- - BSON
- - If a subtype is given, it is used and added as unsigned 8-bit integer.
- - If no subtype is given, the generic binary subtype 0x00 is used.
-
- @sa @ref binary -- create a binary array
-
- @since version 3.8.0
- */
- using binary_t = nlohmann::byte_container_with_subtype;
- /// @}
-
- private:
-
- /// helper for exception-safe object creation
- template
- JSON_HEDLEY_RETURNS_NON_NULL
- static T* create(Args&& ... args)
- {
- AllocatorType alloc;
- using AllocatorTraits = std::allocator_traits>;
-
- auto deleter = [&](T * object)
- {
- AllocatorTraits::deallocate(alloc, object, 1);
- };
- std::unique_ptr object(AllocatorTraits::allocate(alloc, 1), deleter);
- AllocatorTraits::construct(alloc, object.get(), std::forward(args)...);
- JSON_ASSERT(object != nullptr);
- return object.release();
- }
-
- ////////////////////////
- // JSON value storage //
- ////////////////////////
-
- /*!
- @brief a JSON value
-
- The actual storage for a JSON value of the @ref basic_json class. This
- union combines the different storage types for the JSON value types
- defined in @ref value_t.
-
- JSON type | value_t type | used type
- --------- | --------------- | ------------------------
- object | object | pointer to @ref object_t
- array | array | pointer to @ref array_t
- string | string | pointer to @ref string_t
- boolean | boolean | @ref boolean_t
- number | number_integer | @ref number_integer_t
- number | number_unsigned | @ref number_unsigned_t
- number | number_float | @ref number_float_t
- binary | binary | pointer to @ref binary_t
- null | null | *no value is stored*
-
- @note Variable-length types (objects, arrays, and strings) are stored as
- pointers. The size of the union should not exceed 64 bits if the default
- value types are used.
-
- @since version 1.0.0
- */
- union json_value
- {
- /// object (stored with pointer to save storage)
- object_t* object;
- /// array (stored with pointer to save storage)
- array_t* array;
- /// string (stored with pointer to save storage)
- string_t* string;
- /// binary (stored with pointer to save storage)
- binary_t* binary;
- /// boolean
- boolean_t boolean;
- /// number (integer)
- number_integer_t number_integer;
- /// number (unsigned integer)
- number_unsigned_t number_unsigned;
- /// number (floating-point)
- number_float_t number_float;
-
- /// default constructor (for null values)
- json_value() = default;
- /// constructor for booleans
- json_value(boolean_t v) noexcept : boolean(v) {}
- /// constructor for numbers (integer)
- json_value(number_integer_t v) noexcept : number_integer(v) {}
- /// constructor for numbers (unsigned)
- json_value(number_unsigned_t v) noexcept : number_unsigned(v) {}
- /// constructor for numbers (floating-point)
- json_value(number_float_t v) noexcept : number_float(v) {}
- /// constructor for empty values of a given type
- json_value(value_t t)
- {
- switch (t)
- {
- case value_t::object:
- {
- object = create();
- break;
- }
-
- case value_t::array:
- {
- array = create();
- break;
- }
-
- case value_t::string:
- {
- string = create("");
- break;
- }
-
- case value_t::binary:
- {
- binary = create();
- break;
- }
-
- case value_t::boolean:
- {
- boolean = boolean_t(false);
- break;
- }
-
- case value_t::number_integer:
- {
- number_integer = number_integer_t(0);
- break;
- }
-
- case value_t::number_unsigned:
- {
- number_unsigned = number_unsigned_t(0);
- break;
- }
-
- case value_t::number_float:
- {
- number_float = number_float_t(0.0);
- break;
- }
-
- case value_t::null:
- {
- object = nullptr; // silence warning, see #821
- break;
- }
-
- default:
- {
- object = nullptr; // silence warning, see #821
- if (JSON_HEDLEY_UNLIKELY(t == value_t::null))
- {
- JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.9.1")); // LCOV_EXCL_LINE
- }
- break;
- }
- }
- }
-
- /// constructor for strings
- json_value(const string_t& value)
- {
- string = create(value);
- }
-
- /// constructor for rvalue strings
- json_value(string_t&& value)
- {
- string = create(std::move(value));
- }
-
- /// constructor for objects
- json_value(const object_t& value)
- {
- object = create(value);
- }
-
- /// constructor for rvalue objects
- json_value(object_t&& value)
- {
- object = create(std::move(value));
- }
-
- /// constructor for arrays
- json_value(const array_t& value)
- {
- array = create(value);
- }
-
- /// constructor for rvalue arrays
- json_value(array_t&& value)
- {
- array = create(std::move(value));
- }
-
- /// constructor for binary arrays
- json_value(const typename binary_t::container_type& value)
- {
- binary = create(value);
- }
-
- /// constructor for rvalue binary arrays
- json_value(typename binary_t::container_type&& value)
- {
- binary = create(std::move(value));
- }
-
- /// constructor for binary arrays (internal type)
- json_value(const binary_t& value)
- {
- binary = create(value);
- }
-
- /// constructor for rvalue binary arrays (internal type)
- json_value(binary_t&& value)
- {
- binary = create(std::move(value));
- }
-
- void destroy(value_t t) noexcept
- {
- // flatten the current json_value to a heap-allocated stack
- std::vector stack;
-
- // move the top-level items to stack
- if (t == value_t::array)
- {
- stack.reserve(array->size());
- std::move(array->begin(), array->end(), std::back_inserter(stack));
- }
- else if (t == value_t::object)
- {
- stack.reserve(object->size());
- for (auto&& it : *object)
- {
- stack.push_back(std::move(it.second));
- }
- }
-
- while (!stack.empty())
- {
- // move the last item to local variable to be processed
- basic_json current_item(std::move(stack.back()));
- stack.pop_back();
-
- // if current_item is array/object, move
- // its children to the stack to be processed later
- if (current_item.is_array())
- {
- std::move(current_item.m_value.array->begin(), current_item.m_value.array->end(),
- std::back_inserter(stack));
-
- current_item.m_value.array->clear();
- }
- else if (current_item.is_object())
- {
- for (auto&& it : *current_item.m_value.object)
- {
- stack.push_back(std::move(it.second));
- }
-
- current_item.m_value.object->clear();
- }
-
- // it's now safe that current_item get destructed
- // since it doesn't have any children
- }
-
- switch (t)
- {
- case value_t::object:
- {
- AllocatorType alloc;
- std::allocator_traits::destroy(alloc, object);
- std::allocator_traits::deallocate(alloc, object, 1);
- break;
- }
-
- case value_t::array:
- {
- AllocatorType alloc;
- std::allocator_traits::destroy(alloc, array);
- std::allocator_traits::deallocate(alloc, array, 1);
- break;
- }
-
- case value_t::string:
- {
- AllocatorType alloc;
- std::allocator_traits::destroy(alloc, string);
- std::allocator_traits::deallocate(alloc, string, 1);
- break;
- }
-
- case value_t::binary:
- {
- AllocatorType alloc;
- std::allocator_traits::destroy(alloc, binary);
- std::allocator_traits::deallocate(alloc, binary, 1);
- break;
- }
-
- default:
- {
- break;
- }
- }
- }
- };
-
- /*!
- @brief checks the class invariants
-
- This function asserts the class invariants. It needs to be called at the
- end of every constructor to make sure that created objects respect the
- invariant. Furthermore, it has to be called each time the type of a JSON
- value is changed, because the invariant expresses a relationship between
- @a m_type and @a m_value.
- */
- void assert_invariant() const noexcept
- {
- JSON_ASSERT(m_type != value_t::object || m_value.object != nullptr);
- JSON_ASSERT(m_type != value_t::array || m_value.array != nullptr);
- JSON_ASSERT(m_type != value_t::string || m_value.string != nullptr);
- JSON_ASSERT(m_type != value_t::binary || m_value.binary != nullptr);
- }
-
- public:
- //////////////////////////
- // JSON parser callback //
- //////////////////////////
-
- /*!
- @brief parser event types
-
- The parser callback distinguishes the following events:
- - `object_start`: the parser read `{` and started to process a JSON object
- - `key`: the parser read a key of a value in an object
- - `object_end`: the parser read `}` and finished processing a JSON object
- - `array_start`: the parser read `[` and started to process a JSON array
- - `array_end`: the parser read `]` and finished processing a JSON array
- - `value`: the parser finished reading a JSON value
-
- @image html callback_events.png "Example when certain parse events are triggered"
-
- @sa @ref parser_callback_t for more information and examples
- */
- using parse_event_t = detail::parse_event_t;
-
- /*!
- @brief per-element parser callback type
-
- With a parser callback function, the result of parsing a JSON text can be
- influenced. When passed to @ref parse, it is called on certain events
- (passed as @ref parse_event_t via parameter @a event) with a set recursion
- depth @a depth and context JSON value @a parsed. The return value of the
- callback function is a boolean indicating whether the element that emitted
- the callback shall be kept or not.
-
- We distinguish six scenarios (determined by the event type) in which the
- callback function can be called. The following table describes the values
- of the parameters @a depth, @a event, and @a parsed.
-
- parameter @a event | description | parameter @a depth | parameter @a parsed
- ------------------ | ----------- | ------------------ | -------------------
- parse_event_t::object_start | the parser read `{` and started to process a JSON object | depth of the parent of the JSON object | a JSON value with type discarded
- parse_event_t::key | the parser read a key of a value in an object | depth of the currently parsed JSON object | a JSON string containing the key
- parse_event_t::object_end | the parser read `}` and finished processing a JSON object | depth of the parent of the JSON object | the parsed JSON object
- parse_event_t::array_start | the parser read `[` and started to process a JSON array | depth of the parent of the JSON array | a JSON value with type discarded
- parse_event_t::array_end | the parser read `]` and finished processing a JSON array | depth of the parent of the JSON array | the parsed JSON array
- parse_event_t::value | the parser finished reading a JSON value | depth of the value | the parsed JSON value
-
- @image html callback_events.png "Example when certain parse events are triggered"
-
- Discarding a value (i.e., returning `false`) has different effects
- depending on the context in which function was called:
-
- - Discarded values in structured types are skipped. That is, the parser
- will behave as if the discarded value was never read.
- - In case a value outside a structured type is skipped, it is replaced
- with `null`. This case happens if the top-level element is skipped.
-
- @param[in] depth the depth of the recursion during parsing
-
- @param[in] event an event of type parse_event_t indicating the context in
- the callback function has been called
-
- @param[in,out] parsed the current intermediate parse result; note that
- writing to this value has no effect for parse_event_t::key events
-
- @return Whether the JSON value which called the function during parsing
- should be kept (`true`) or not (`false`). In the latter case, it is either
- skipped completely or replaced by an empty discarded object.
-
- @sa @ref parse for examples
-
- @since version 1.0.0
- */
- using parser_callback_t = detail::parser_callback_t;
-
- //////////////////
- // constructors //
- //////////////////
-
- /// @name constructors and destructors
- /// Constructors of class @ref basic_json, copy/move constructor, copy
- /// assignment, static functions creating objects, and the destructor.
- /// @{
-
- /*!
- @brief create an empty value with a given type
-
- Create an empty JSON value with a given type. The value will be default
- initialized with an empty value which depends on the type:
-
- Value type | initial value
- ----------- | -------------
- null | `null`
- boolean | `false`
- string | `""`
- number | `0`
- object | `{}`
- array | `[]`
- binary | empty array
-
- @param[in] v the type of the value to create
-
- @complexity Constant.
-
- @exceptionsafety Strong guarantee: if an exception is thrown, there are no
- changes to any JSON value.
-
- @liveexample{The following code shows the constructor for different @ref
- value_t values,basic_json__value_t}
-
- @sa @ref clear() -- restores the postcondition of this constructor
-
- @since version 1.0.0
- */
- basic_json(const value_t v)
- : m_type(v), m_value(v)
- {
- assert_invariant();
- }
-
- /*!
- @brief create a null object
-
- Create a `null` JSON value. It either takes a null pointer as parameter
- (explicitly creating `null`) or no parameter (implicitly creating `null`).
- The passed null pointer itself is not read -- it is only used to choose
- the right constructor.
-
- @complexity Constant.
-
- @exceptionsafety No-throw guarantee: this constructor never throws
- exceptions.
-
- @liveexample{The following code shows the constructor with and without a
- null pointer parameter.,basic_json__nullptr_t}
-
- @since version 1.0.0
- */
- basic_json(std::nullptr_t = nullptr) noexcept
- : basic_json(value_t::null)
- {
- assert_invariant();
- }
-
- /*!
- @brief create a JSON value
-
- This is a "catch all" constructor for all compatible JSON types; that is,
- types for which a `to_json()` method exists. The constructor forwards the
- parameter @a val to that method (to `json_serializer::to_json` method
- with `U = uncvref_t`, to be exact).
-
- Template type @a CompatibleType includes, but is not limited to, the
- following types:
- - **arrays**: @ref array_t and all kinds of compatible containers such as
- `std::vector`, `std::deque`, `std::list`, `std::forward_list`,
- `std::array`, `std::valarray`, `std::set`, `std::unordered_set`,
- `std::multiset`, and `std::unordered_multiset` with a `value_type` from
- which a @ref basic_json value can be constructed.
- - **objects**: @ref object_t and all kinds of compatible associative
- containers such as `std::map`, `std::unordered_map`, `std::multimap`,
- and `std::unordered_multimap` with a `key_type` compatible to
- @ref string_t and a `value_type` from which a @ref basic_json value can
- be constructed.
- - **strings**: @ref string_t, string literals, and all compatible string
- containers can be used.
- - **numbers**: @ref number_integer_t, @ref number_unsigned_t,
- @ref number_float_t, and all convertible number types such as `int`,
- `size_t`, `int64_t`, `float` or `double` can be used.
- - **boolean**: @ref boolean_t / `bool` can be used.
- - **binary**: @ref binary_t / `std::vector` may be used,
- unfortunately because string literals cannot be distinguished from binary
- character arrays by the C++ type system, all types compatible with `const
- char*` will be directed to the string constructor instead. This is both
- for backwards compatibility, and due to the fact that a binary type is not
- a standard JSON type.
-
- See the examples below.
-
- @tparam CompatibleType a type such that:
- - @a CompatibleType is not derived from `std::istream`,
- - @a CompatibleType is not @ref basic_json (to avoid hijacking copy/move
- constructors),
- - @a CompatibleType is not a different @ref basic_json type (i.e. with different template arguments)
- - @a CompatibleType is not a @ref basic_json nested type (e.g.,
- @ref json_pointer, @ref iterator, etc ...)
- - @ref @ref json_serializer has a
- `to_json(basic_json_t&, CompatibleType&&)` method
-
- @tparam U = `uncvref_t`
-
- @param[in] val the value to be forwarded to the respective constructor
-
- @complexity Usually linear in the size of the passed @a val, also
- depending on the implementation of the called `to_json()`
- method.
-
- @exceptionsafety Depends on the called constructor. For types directly
- supported by the library (i.e., all types for which no `to_json()` function
- was provided), strong guarantee holds: if an exception is thrown, there are
- no changes to any JSON value.
-
- @liveexample{The following code shows the constructor with several
- compatible types.,basic_json__CompatibleType}
-
- @since version 2.1.0
- */
- template < typename CompatibleType,
- typename U = detail::uncvref_t,
- detail::enable_if_t <
- !detail::is_basic_json::value && detail::is_compatible_type::value, int > = 0 >
- basic_json(CompatibleType && val) noexcept(noexcept(
- JSONSerializer::to_json(std::declval(),
- std::forward(val))))
- {
- JSONSerializer::to_json(*this, std::forward(val));
- assert_invariant();
- }
-
- /*!
- @brief create a JSON value from an existing one
-
- This is a constructor for existing @ref basic_json types.
- It does not hijack copy/move constructors, since the parameter has different
- template arguments than the current ones.
-
- The constructor tries to convert the internal @ref m_value of the parameter.
-
- @tparam BasicJsonType a type such that:
- - @a BasicJsonType is a @ref basic_json type.
- - @a BasicJsonType has different template arguments than @ref basic_json_t.
-
- @param[in] val the @ref basic_json value to be converted.
-
- @complexity Usually linear in the size of the passed @a val, also
- depending on the implementation of the called `to_json()`
- method.
-
- @exceptionsafety Depends on the called constructor. For types directly
- supported by the library (i.e., all types for which no `to_json()` function
- was provided), strong guarantee holds: if an exception is thrown, there are
- no changes to any JSON value.
-
- @since version 3.2.0
- */
- template < typename BasicJsonType,
- detail::enable_if_t <
- detail::is_basic_json::value&& !std::is_same::value, int > = 0 >
- basic_json(const BasicJsonType& val)
- {
- using other_boolean_t = typename BasicJsonType::boolean_t;
- using other_number_float_t = typename BasicJsonType::number_float_t;
- using other_number_integer_t = typename BasicJsonType::number_integer_t;
- using other_number_unsigned_t = typename BasicJsonType::number_unsigned_t;
- using other_string_t = typename BasicJsonType::string_t;
- using other_object_t = typename BasicJsonType::object_t;
- using other_array_t = typename BasicJsonType::array_t;
- using other_binary_t = typename BasicJsonType::binary_t;
-
- switch (val.type())
- {
- case value_t::boolean:
- JSONSerializer::to_json(*this, val.template get());
- break;
- case value_t::number_float:
- JSONSerializer::to_json(*this, val.template get());
- break;
- case value_t::number_integer:
- JSONSerializer::to_json(*this, val.template get());
- break;
- case value_t::number_unsigned:
- JSONSerializer::to_json(*this, val.template get());
- break;
- case value_t::string:
- JSONSerializer::to_json(*this, val.template get_ref());
- break;
- case value_t::object:
- JSONSerializer::to_json(*this, val.template get_ref());
- break;
- case value_t::array:
- JSONSerializer::to_json(*this, val.template get_ref());
- break;
- case value_t::binary:
- JSONSerializer::to_json(*this, val.template get_ref());
- break;
- case value_t::null:
- *this = nullptr;
- break;
- case value_t::discarded:
- m_type = value_t::discarded;
- break;
- default: // LCOV_EXCL_LINE
- JSON_ASSERT(false); // LCOV_EXCL_LINE
- }
- assert_invariant();
- }
-
- /*!
- @brief create a container (array or object) from an initializer list
-
- Creates a JSON value of type array or object from the passed initializer
- list @a init. In case @a type_deduction is `true` (default), the type of
- the JSON value to be created is deducted from the initializer list @a init
- according to the following rules:
-
- 1. If the list is empty, an empty JSON object value `{}` is created.
- 2. If the list consists of pairs whose first element is a string, a JSON
- object value is created where the first elements of the pairs are
- treated as keys and the second elements are as values.
- 3. In all other cases, an array is created.
-
- The rules aim to create the best fit between a C++ initializer list and
- JSON values. The rationale is as follows:
-
- 1. The empty initializer list is written as `{}` which is exactly an empty
- JSON object.
- 2. C++ has no way of describing mapped types other than to list a list of
- pairs. As JSON requires that keys must be of type string, rule 2 is the
- weakest constraint one can pose on initializer lists to interpret them
- as an object.
- 3. In all other cases, the initializer list could not be interpreted as
- JSON object type, so interpreting it as JSON array type is safe.
-
- With the rules described above, the following JSON values cannot be
- expressed by an initializer list:
-
- - the empty array (`[]`): use @ref array(initializer_list_t)
- with an empty initializer list in this case
- - arrays whose elements satisfy rule 2: use @ref
- array(initializer_list_t) with the same initializer list
- in this case
-
- @note When used without parentheses around an empty initializer list, @ref
- basic_json() is called instead of this function, yielding the JSON null
- value.
-
- @param[in] init initializer list with JSON values
-
- @param[in] type_deduction internal parameter; when set to `true`, the type
- of the JSON value is deducted from the initializer list @a init; when set
- to `false`, the type provided via @a manual_type is forced. This mode is
- used by the functions @ref array(initializer_list_t) and
- @ref object(initializer_list_t).
-
- @param[in] manual_type internal parameter; when @a type_deduction is set
- to `false`, the created JSON value will use the provided type (only @ref
- value_t::array and @ref value_t::object are valid); when @a type_deduction
- is set to `true`, this parameter has no effect
-
- @throw type_error.301 if @a type_deduction is `false`, @a manual_type is
- `value_t::object`, but @a init contains an element which is not a pair
- whose first element is a string. In this case, the constructor could not
- create an object. If @a type_deduction would have be `true`, an array
- would have been created. See @ref object(initializer_list_t)
- for an example.
-
- @complexity Linear in the size of the initializer list @a init.
-
- @exceptionsafety Strong guarantee: if an exception is thrown, there are no
- changes to any JSON value.
-
- @liveexample{The example below shows how JSON values are created from
- initializer lists.,basic_json__list_init_t}
-
- @sa @ref array(initializer_list_t) -- create a JSON array
- value from an initializer list
- @sa @ref object(initializer_list_t) -- create a JSON object
- value from an initializer list
-
- @since version 1.0.0
- */
- basic_json(initializer_list_t init,
- bool type_deduction = true,
- value_t manual_type = value_t::array)
- {
- // check if each element is an array with two elements whose first
- // element is a string
- bool is_an_object = std::all_of(init.begin(), init.end(),
- [](const detail::json_ref& element_ref)
- {
- return element_ref->is_array() && element_ref->size() == 2 && (*element_ref)[0].is_string();
- });
-
- // adjust type if type deduction is not wanted
- if (!type_deduction)
- {
- // if array is wanted, do not create an object though possible
- if (manual_type == value_t::array)
- {
- is_an_object = false;
- }
-
- // if object is wanted but impossible, throw an exception
- if (JSON_HEDLEY_UNLIKELY(manual_type == value_t::object && !is_an_object))
- {
- JSON_THROW(type_error::create(301, "cannot create object from initializer list"));
- }
- }
-
- if (is_an_object)
- {
- // the initializer list is a list of pairs -> create object
- m_type = value_t::object;
- m_value = value_t::object;
-
- std::for_each(init.begin(), init.end(), [this](const detail::json_ref& element_ref)
- {
- auto element = element_ref.moved_or_copied();
- m_value.object->emplace(
- std::move(*((*element.m_value.array)[0].m_value.string)),
- std::move((*element.m_value.array)[1]));
- });
- }
- else
- {
- // the initializer list describes an array -> create array
- m_type = value_t::array;
- m_value.array = create(init.begin(), init.end());
- }
-
- assert_invariant();
- }
-
- /*!
- @brief explicitly create a binary array (without subtype)
-
- Creates a JSON binary array value from a given binary container. Binary
- values are part of various binary formats, such as CBOR, MessagePack, and
- BSON. This constructor is used to create a value for serialization to those
- formats.
-
- @note Note, this function exists because of the difficulty in correctly
- specifying the correct template overload in the standard value ctor, as both
- JSON arrays and JSON binary arrays are backed with some form of a
- `std::vector`. Because JSON binary arrays are a non-standard extension it
- was decided that it would be best to prevent automatic initialization of a
- binary array type, for backwards compatibility and so it does not happen on
- accident.
-
- @param[in] init container containing bytes to use as binary type
-
- @return JSON binary array value
-
- @complexity Linear in the size of @a init.
-
- @exceptionsafety Strong guarantee: if an exception is thrown, there are no
- changes to any JSON value.
-
- @since version 3.8.0
- */
- JSON_HEDLEY_WARN_UNUSED_RESULT
- static basic_json binary(const typename binary_t::container_type& init)
- {
- auto res = basic_json();
- res.m_type = value_t::binary;
- res.m_value = init;
- return res;
- }
-
- /*!
- @brief explicitly create a binary array (with subtype)
-
- Creates a JSON binary array value from a given binary container. Binary
- values are part of various binary formats, such as CBOR, MessagePack, and
- BSON. This constructor is used to create a value for serialization to those
- formats.
-
- @note Note, this function exists because of the difficulty in correctly
- specifying the correct template overload in the standard value ctor, as both
- JSON arrays and JSON binary arrays are backed with some form of a
- `std::vector`. Because JSON binary arrays are a non-standard extension it
- was decided that it would be best to prevent automatic initialization of a
- binary array type, for backwards compatibility and so it does not happen on
- accident.
-
- @param[in] init container containing bytes to use as binary type
- @param[in] subtype subtype to use in MessagePack and BSON
-
- @return JSON binary array value
-
- @complexity Linear in the size of @a init.
-
- @exceptionsafety Strong guarantee: if an exception is thrown, there are no
- changes to any JSON value.
-
- @since version 3.8.0
- */
- JSON_HEDLEY_WARN_UNUSED_RESULT
- static basic_json binary(const typename binary_t::container_type& init, std::uint8_t subtype)
- {
- auto res = basic_json();
- res.m_type = value_t::binary;
- res.m_value = binary_t(init, subtype);
- return res;
- }
-
- /// @copydoc binary(const typename binary_t::container_type&)
- JSON_HEDLEY_WARN_UNUSED_RESULT
- static basic_json binary(typename binary_t::container_type&& init)
- {
- auto res = basic_json();
- res.m_type = value_t::binary;
- res.m_value = std::move(init);
- return res;
- }
-
- /// @copydoc binary(const typename binary_t::container_type&, std::uint8_t)
- JSON_HEDLEY_WARN_UNUSED_RESULT
- static basic_json binary(typename binary_t::container_type&& init, std::uint8_t subtype)
- {
- auto res = basic_json();
- res.m_type = value_t::binary;
- res.m_value = binary_t(std::move(init), subtype);
- return res;
- }
-
- /*!
- @brief explicitly create an array from an initializer list
-
- Creates a JSON array value from a given initializer list. That is, given a
- list of values `a, b, c`, creates the JSON value `[a, b, c]`. If the
- initializer list is empty, the empty array `[]` is created.
-
- @note This function is only needed to express two edge cases that cannot
- be realized with the initializer list constructor (@ref
- basic_json(initializer_list_t, bool, value_t)). These cases
- are:
- 1. creating an array whose elements are all pairs whose first element is a
- string -- in this case, the initializer list constructor would create an
- object, taking the first elements as keys
- 2. creating an empty array -- passing the empty initializer list to the
- initializer list constructor yields an empty object
-
- @param[in] init initializer list with JSON values to create an array from
- (optional)
-
- @return JSON array value
-
- @complexity Linear in the size of @a init.
-
- @exceptionsafety Strong guarantee: if an exception is thrown, there are no
- changes to any JSON value.
-
- @liveexample{The following code shows an example for the `array`
- function.,array}
-
- @sa @ref basic_json(initializer_list_t, bool, value_t) --
- create a JSON value from an initializer list
- @sa @ref object(initializer_list_t) -- create a JSON object
- value from an initializer list
-
- @since version 1.0.0
- */
- JSON_HEDLEY_WARN_UNUSED_RESULT
- static basic_json array(initializer_list_t init = {})
- {
- return basic_json(init, false, value_t::array);
- }
-
- /*!
- @brief explicitly create an object from an initializer list
-
- Creates a JSON object value from a given initializer list. The initializer
- lists elements must be pairs, and their first elements must be strings. If
- the initializer list is empty, the empty object `{}` is created.
-
- @note This function is only added for symmetry reasons. In contrast to the
- related function @ref array(initializer_list_t), there are
- no cases which can only be expressed by this function. That is, any
- initializer list @a init can also be passed to the initializer list
- constructor @ref basic_json(initializer_list_t, bool, value_t).
-
- @param[in] init initializer list to create an object from (optional)
-
- @return JSON object value
-
- @throw type_error.301 if @a init is not a list of pairs whose first
- elements are strings. In this case, no object can be created. When such a
- value is passed to @ref basic_json(initializer_list_t, bool, value_t),
- an array would have been created from the passed initializer list @a init.
- See example below.
-
- @complexity Linear in the size of @a init.
-
- @exceptionsafety Strong guarantee: if an exception is thrown, there are no
- changes to any JSON value.
-
- @liveexample{The following code shows an example for the `object`
- function.,object}
-
- @sa @ref basic_json(initializer_list_t, bool, value_t) --
- create a JSON value from an initializer list
- @sa @ref array(initializer_list_t) -- create a JSON array
- value from an initializer list
-
- @since version 1.0.0
- */
- JSON_HEDLEY_WARN_UNUSED_RESULT
- static basic_json object(initializer_list_t init = {})
- {
- return basic_json(init, false, value_t::object);
- }
-
- /*!
- @brief construct an array with count copies of given value
-
- Constructs a JSON array value by creating @a cnt copies of a passed value.
- In case @a cnt is `0`, an empty array is created.
-
- @param[in] cnt the number of JSON copies of @a val to create
- @param[in] val the JSON value to copy
-
- @post `std::distance(begin(),end()) == cnt` holds.
-
- @complexity Linear in @a cnt.
-
- @exceptionsafety Strong guarantee: if an exception is thrown, there are no
- changes to any JSON value.
-
- @liveexample{The following code shows examples for the @ref
- basic_json(size_type\, const basic_json&)
- constructor.,basic_json__size_type_basic_json}
-
- @since version 1.0.0
- */
- basic_json(size_type cnt, const basic_json& val)
- : m_type(value_t::array)
- {
- m_value.array = create(cnt, val);
- assert_invariant();
- }
-
- /*!
- @brief construct a JSON container given an iterator range
-
- Constructs the JSON value with the contents of the range `[first, last)`.
- The semantics depends on the different types a JSON value can have:
- - In case of a null type, invalid_iterator.206 is thrown.
- - In case of other primitive types (number, boolean, or string), @a first
- must be `begin()` and @a last must be `end()`. In this case, the value is
- copied. Otherwise, invalid_iterator.204 is thrown.
- - In case of structured types (array, object), the constructor behaves as
- similar versions for `std::vector` or `std::map`; that is, a JSON array
- or object is constructed from the values in the range.
-
- @tparam InputIT an input iterator type (@ref iterator or @ref
- const_iterator)
-
- @param[in] first begin of the range to copy from (included)
- @param[in] last end of the range to copy from (excluded)
-
- @pre Iterators @a first and @a last must be initialized. **This
- precondition is enforced with an assertion (see warning).** If
- assertions are switched off, a violation of this precondition yields
- undefined behavior.
-
- @pre Range `[first, last)` is valid. Usually, this precondition cannot be
- checked efficiently. Only certain edge cases are detected; see the
- description of the exceptions below. A violation of this precondition
- yields undefined behavior.
-
- @warning A precondition is enforced with a runtime assertion that will
- result in calling `std::abort` if this precondition is not met.
- Assertions can be disabled by defining `NDEBUG` at compile time.
- See https://en.cppreference.com/w/cpp/error/assert for more
- information.
-
- @throw invalid_iterator.201 if iterators @a first and @a last are not
- compatible (i.e., do not belong to the same JSON value). In this case,
- the range `[first, last)` is undefined.
- @throw invalid_iterator.204 if iterators @a first and @a last belong to a
- primitive type (number, boolean, or string), but @a first does not point
- to the first element any more. In this case, the range `[first, last)` is
- undefined. See example code below.
- @throw invalid_iterator.206 if iterators @a first and @a last belong to a
- null value. In this case, the range `[first, last)` is undefined.
-
- @complexity Linear in distance between @a first and @a last.
-
- @exceptionsafety Strong guarantee: if an exception is thrown, there are no
- changes to any JSON value.
-
- @liveexample{The example below shows several ways to create JSON values by
- specifying a subrange with iterators.,basic_json__InputIt_InputIt}
-
- @since version 1.0.0
- */
- template < class InputIT, typename std::enable_if <
- std::is_same::value ||
- std::is_same::value, int >::type = 0 >
- basic_json(InputIT first, InputIT last)
- {
- JSON_ASSERT(first.m_object != nullptr);
- JSON_ASSERT(last.m_object != nullptr);
-
- // make sure iterator fits the current value
- if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
- {
- JSON_THROW(invalid_iterator::create(201, "iterators are not compatible"));
- }
-
- // copy type from first iterator
- m_type = first.m_object->m_type;
-
- // check if iterator range is complete for primitive values
- switch (m_type)
- {
- case value_t::boolean:
- case value_t::number_float:
- case value_t::number_integer:
- case value_t::number_unsigned:
- case value_t::string:
- {
- if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin()
- || !last.m_it.primitive_iterator.is_end()))
- {
- JSON_THROW(invalid_iterator::create(204, "iterators out of range"));
- }
- break;
- }
-
- default:
- break;
- }
-
- switch (m_type)
- {
- case value_t::number_integer:
- {
- m_value.number_integer = first.m_object->m_value.number_integer;
- break;
- }
-
- case value_t::number_unsigned:
- {
- m_value.number_unsigned = first.m_object->m_value.number_unsigned;
- break;
- }
-
- case value_t::number_float:
- {
- m_value.number_float = first.m_object->m_value.number_float;
- break;
- }
-
- case value_t::boolean:
- {
- m_value.boolean = first.m_object->m_value.boolean;
- break;
- }
-
- case value_t::string:
- {
- m_value = *first.m_object->m_value.string;
- break;
- }
-
- case value_t::object:
- {
- m_value.object = create(first.m_it.object_iterator,
- last.m_it.object_iterator);
- break;
- }
-
- case value_t::array:
- {
- m_value.array = create(first.m_it.array_iterator,
- last.m_it.array_iterator);
- break;
- }
-
- case value_t::binary:
- {
- m_value = *first.m_object->m_value.binary;
- break;
- }
-
- default:
- JSON_THROW(invalid_iterator::create(206, "cannot construct with iterators from " +
- std::string(first.m_object->type_name())));
- }
-
- assert_invariant();
- }
-
-
- ///////////////////////////////////////
- // other constructors and destructor //
- ///////////////////////////////////////
-
- template,
- std::is_same>::value, int> = 0 >
- basic_json(const JsonRef& ref) : basic_json(ref.moved_or_copied()) {}
-
- /*!
- @brief copy constructor
-
- Creates a copy of a given JSON value.
-
- @param[in] other the JSON value to copy
-
- @post `*this == other`
-
- @complexity Linear in the size of @a other.
-
- @exceptionsafety Strong guarantee: if an exception is thrown, there are no
- changes to any JSON value.
-
- @requirement This function helps `basic_json` satisfying the
- [Container](https://en.cppreference.com/w/cpp/named_req/Container)
- requirements:
- - The complexity is linear.
- - As postcondition, it holds: `other == basic_json(other)`.
-
- @liveexample{The following code shows an example for the copy
- constructor.,basic_json__basic_json}
-
- @since version 1.0.0
- */
- basic_json(const basic_json& other)
- : m_type(other.m_type)
- {
- // check of passed value is valid
- other.assert_invariant();
-
- switch (m_type)
- {
- case value_t::object:
- {
- m_value = *other.m_value.object;
- break;
- }
-
- case value_t::array:
- {
- m_value = *other.m_value.array;
- break;
- }
-
- case value_t::string:
- {
- m_value = *other.m_value.string;
- break;
- }
-
- case value_t::boolean:
- {
- m_value = other.m_value.boolean;
- break;
- }
-
- case value_t::number_integer:
- {
- m_value = other.m_value.number_integer;
- break;
- }
-
- case value_t::number_unsigned:
- {
- m_value = other.m_value.number_unsigned;
- break;
- }
-
- case value_t::number_float:
- {
- m_value = other.m_value.number_float;
- break;
- }
-
- case value_t::binary:
- {
- m_value = *other.m_value.binary;
- break;
- }
-
- default:
- break;
- }
-
- assert_invariant();
- }
-
- /*!
- @brief move constructor
-
- Move constructor. Constructs a JSON value with the contents of the given
- value @a other using move semantics. It "steals" the resources from @a
- other and leaves it as JSON null value.
-
- @param[in,out] other value to move to this object
-
- @post `*this` has the same value as @a other before the call.
- @post @a other is a JSON null value.
-
- @complexity Constant.
-
- @exceptionsafety No-throw guarantee: this constructor never throws
- exceptions.
-
- @requirement This function helps `basic_json` satisfying the
- [MoveConstructible](https://en.cppreference.com/w/cpp/named_req/MoveConstructible)
- requirements.
-
- @liveexample{The code below shows the move constructor explicitly called
- via std::move.,basic_json__moveconstructor}
-
- @since version 1.0.0
- */
- basic_json(basic_json&& other) noexcept
- : m_type(std::move(other.m_type)),
- m_value(std::move(other.m_value))
- {
- // check that passed value is valid
- other.assert_invariant();
-
- // invalidate payload
- other.m_type = value_t::null;
- other.m_value = {};
-
- assert_invariant();
- }
-
- /*!
- @brief copy assignment
-
- Copy assignment operator. Copies a JSON value via the "copy and swap"
- strategy: It is expressed in terms of the copy constructor, destructor,
- and the `swap()` member function.
-
- @param[in] other value to copy from
-
- @complexity Linear.
-
- @requirement This function helps `basic_json` satisfying the
- [Container](https://en.cppreference.com/w/cpp/named_req/Container)
- requirements:
- - The complexity is linear.
-
- @liveexample{The code below shows and example for the copy assignment. It
- creates a copy of value `a` which is then swapped with `b`. Finally\, the
- copy of `a` (which is the null value after the swap) is
- destroyed.,basic_json__copyassignment}
-
- @since version 1.0.0
- */
- basic_json& operator=(basic_json other) noexcept (
- std::is_nothrow_move_constructible::value&&
- std::is_nothrow_move_assignable::value&&
- std::is_nothrow_move_constructible::value&&
- std::is_nothrow_move_assignable::value
- )
- {
- // check that passed value is valid
- other.assert_invariant();
-
- using std::swap;
- swap(m_type, other.m_type);
- swap(m_value, other.m_value);
-
- assert_invariant();
- return *this;
- }
-
- /*!
- @brief destructor
-
- Destroys the JSON value and frees all allocated memory.
-
- @complexity Linear.
-
- @requirement This function helps `basic_json` satisfying the
- [Container](https://en.cppreference.com/w/cpp/named_req/Container)
- requirements:
- - The complexity is linear.
- - All stored elements are destroyed and all memory is freed.
-
- @since version 1.0.0
- */
- ~basic_json() noexcept
- {
- assert_invariant();
- m_value.destroy(m_type);
- }
-
- /// @}
-
- public:
- ///////////////////////
- // object inspection //
- ///////////////////////
-
- /// @name object inspection
- /// Functions to inspect the type of a JSON value.
- /// @{
-
- /*!
- @brief serialization
-
- Serialization function for JSON values. The function tries to mimic
- Python's `json.dumps()` function, and currently supports its @a indent
- and @a ensure_ascii parameters.
-
- @param[in] indent If indent is nonnegative, then array elements and object
- members will be pretty-printed with that indent level. An indent level of
- `0` will only insert newlines. `-1` (the default) selects the most compact
- representation.
- @param[in] indent_char The character to use for indentation if @a indent is
- greater than `0`. The default is ` ` (space).
- @param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters
- in the output are escaped with `\uXXXX` sequences, and the result consists
- of ASCII characters only.
- @param[in] error_handler how to react on decoding errors; there are three
- possible values: `strict` (throws and exception in case a decoding error
- occurs; default), `replace` (replace invalid UTF-8 sequences with U+FFFD),
- and `ignore` (ignore invalid UTF-8 sequences during serialization; all
- bytes are copied to the output unchanged).
-
- @return string containing the serialization of the JSON value
-
- @throw type_error.316 if a string stored inside the JSON value is not
- UTF-8 encoded and @a error_handler is set to strict
-
- @note Binary values are serialized as object containing two keys:
- - "bytes": an array of bytes as integers
- - "subtype": the subtype as integer or "null" if the binary has no subtype
-
- @complexity Linear.
-
- @exceptionsafety Strong guarantee: if an exception is thrown, there are no
- changes in the JSON value.
-
- @liveexample{The following example shows the effect of different @a indent\,
- @a indent_char\, and @a ensure_ascii parameters to the result of the
- serialization.,dump}
-
- @see https://docs.python.org/2/library/json.html#json.dump
-
- @since version 1.0.0; indentation character @a indent_char, option
- @a ensure_ascii and exceptions added in version 3.0.0; error
- handlers added in version 3.4.0; serialization of binary values added
- in version 3.8.0.
- */
- string_t dump(const int indent = -1,
- const char indent_char = ' ',
- const bool ensure_ascii = false,
- const error_handler_t error_handler = error_handler_t::strict) const
- {
- string_t result;
- serializer s(detail::output_adapter(result), indent_char, error_handler);
-
- if (indent >= 0)
- {
- s.dump(*this, true, ensure_ascii, static_cast(indent));
- }
- else
- {
- s.dump(*this, false, ensure_ascii, 0);
- }
-
- return result;
- }
-
- /*!
- @brief return the type of the JSON value (explicit)
-
- Return the type of the JSON value as a value from the @ref value_t
- enumeration.
-
- @return the type of the JSON value
- Value type | return value
- ------------------------- | -------------------------
- null | value_t::null
- boolean | value_t::boolean
- string | value_t::string
- number (integer) | value_t::number_integer
- number (unsigned integer) | value_t::number_unsigned
- number (floating-point) | value_t::number_float
- object | value_t::object
- array | value_t::array
- binary | value_t::binary
- discarded | value_t::discarded
-
- @complexity Constant.
-
- @exceptionsafety No-throw guarantee: this member function never throws
- exceptions.
-
- @liveexample{The following code exemplifies `type()` for all JSON
- types.,type}
-
- @sa @ref operator value_t() -- return the type of the JSON value (implicit)
- @sa @ref type_name() -- return the type as string
-
- @since version 1.0.0
- */
- constexpr value_t type() const noexcept
- {
- return m_type;
- }
-
- /*!
- @brief return whether type is primitive
-
- This function returns true if and only if the JSON type is primitive
- (string, number, boolean, or null).
-
- @return `true` if type is primitive (string, number, boolean, or null),
- `false` otherwise.
-
- @complexity Constant.
-
- @exceptionsafety No-throw guarantee: this member function never throws
- exceptions.
-
- @liveexample{The following code exemplifies `is_primitive()` for all JSON
- types.,is_primitive}
-
- @sa @ref is_structured() -- returns whether JSON value is structured
- @sa @ref is_null() -- returns whether JSON value is `null`
- @sa @ref is_string() -- returns whether JSON value is a string
- @sa @ref is_boolean() -- returns whether JSON value is a boolean
- @sa @ref is_number() -- returns whether JSON value is a number
- @sa @ref is_binary() -- returns whether JSON value is a binary array
-
- @since version 1.0.0
- */
- constexpr bool is_primitive() const noexcept
- {
- return is_null() || is_string() || is_boolean() || is_number() || is_binary();
- }
-
- /*!
- @brief return whether type is structured
-
- This function returns true if and only if the JSON type is structured
- (array or object).
-
- @return `true` if type is structured (array or object), `false` otherwise.
-
- @complexity Constant.
-
- @exceptionsafety No-throw guarantee: this member function never throws
- exceptions.
-
- @liveexample{The following code exemplifies `is_structured()` for all JSON
- types.,is_structured}
-
- @sa @ref is_primitive() -- returns whether value is primitive
- @sa @ref is_array() -- returns whether value is an array
- @sa @ref is_object() -- returns whether value is an object
-
- @since version 1.0.0
- */
- constexpr bool is_structured() const noexcept
- {
- return is_array() || is_object();
- }
-
- /*!
- @brief return whether value is null
-
- This function returns true if and only if the JSON value is null.
-
- @return `true` if type is null, `false` otherwise.
-
- @complexity Constant.
-
- @exceptionsafety No-throw guarantee: this member function never throws
- exceptions.
-
- @liveexample{The following code exemplifies `is_null()` for all JSON
- types.,is_null}
-
- @since version 1.0.0
- */
- constexpr bool is_null() const noexcept
- {
- return m_type == value_t::null;
- }
-
- /*!
- @brief return whether value is a boolean
-
- This function returns true if and only if the JSON value is a boolean.
-
- @return `true` if type is boolean, `false` otherwise.
-
- @complexity Constant.
-
- @exceptionsafety No-throw guarantee: this member function never throws
- exceptions.
-
- @liveexample{The following code exemplifies `is_boolean()` for all JSON
- types.,is_boolean}
-
- @since version 1.0.0
- */
- constexpr bool is_boolean() const noexcept
- {
- return m_type == value_t::boolean;
- }
-
- /*!
- @brief return whether value is a number
-
- This function returns true if and only if the JSON value is a number. This
- includes both integer (signed and unsigned) and floating-point values.
-
- @return `true` if type is number (regardless whether integer, unsigned
- integer or floating-type), `false` otherwise.
-
- @complexity Constant.
-
- @exceptionsafety No-throw guarantee: this member function never throws
- exceptions.
-
- @liveexample{The following code exemplifies `is_number()` for all JSON
- types.,is_number}
-
- @sa @ref is_number_integer() -- check if value is an integer or unsigned
- integer number
- @sa @ref is_number_unsigned() -- check if value is an unsigned integer
- number
- @sa @ref is_number_float() -- check if value is a floating-point number
-
- @since version 1.0.0
- */
- constexpr bool is_number() const noexcept
- {
- return is_number_integer() || is_number_float();
- }
-
- /*!
- @brief return whether value is an integer number
-
- This function returns true if and only if the JSON value is a signed or
- unsigned integer number. This excludes floating-point values.
-
- @return `true` if type is an integer or unsigned integer number, `false`
- otherwise.
-
- @complexity Constant.
-
- @exceptionsafety No-throw guarantee: this member function never throws
- exceptions.
-
- @liveexample{The following code exemplifies `is_number_integer()` for all
- JSON types.,is_number_integer}
-
- @sa @ref is_number() -- check if value is a number
- @sa @ref is_number_unsigned() -- check if value is an unsigned integer
- number
- @sa @ref is_number_float() -- check if value is a floating-point number
-
- @since version 1.0.0
- */
- constexpr bool is_number_integer() const noexcept
- {
- return m_type == value_t::number_integer || m_type == value_t::number_unsigned;
- }
-
- /*!
- @brief return whether value is an unsigned integer number
-
- This function returns true if and only if the JSON value is an unsigned
- integer number. This excludes floating-point and signed integer values.
-
- @return `true` if type is an unsigned integer number, `false` otherwise.
-
- @complexity Constant.
-
- @exceptionsafety No-throw guarantee: this member function never throws
- exceptions.
-
- @liveexample{The following code exemplifies `is_number_unsigned()` for all
- JSON types.,is_number_unsigned}
-
- @sa @ref is_number() -- check if value is a number
- @sa @ref is_number_integer() -- check if value is an integer or unsigned
- integer number
- @sa @ref is_number_float() -- check if value is a floating-point number
-
- @since version 2.0.0
- */
- constexpr bool is_number_unsigned() const noexcept
- {
- return m_type == value_t::number_unsigned;
- }
-
- /*!
- @brief return whether value is a floating-point number
-
- This function returns true if and only if the JSON value is a
- floating-point number. This excludes signed and unsigned integer values.
-
- @return `true` if type is a floating-point number, `false` otherwise.
-
- @complexity Constant.
-
- @exceptionsafety No-throw guarantee: this member function never throws
- exceptions.
-
- @liveexample{The following code exemplifies `is_number_float()` for all
- JSON types.,is_number_float}
-
- @sa @ref is_number() -- check if value is number
- @sa @ref is_number_integer() -- check if value is an integer number
- @sa @ref is_number_unsigned() -- check if value is an unsigned integer
- number
-
- @since version 1.0.0
- */
- constexpr bool is_number_float() const noexcept
- {
- return m_type == value_t::number_float;
- }
-
- /*!
- @brief return whether value is an object
-
- This function returns true if and only if the JSON value is an object.
-
- @return `true` if type is object, `false` otherwise.
-
- @complexity Constant.
-
- @exceptionsafety No-throw guarantee: this member function never throws
- exceptions.
-
- @liveexample{The following code exemplifies `is_object()` for all JSON
- types.,is_object}
-
- @since version 1.0.0
- */
- constexpr bool is_object() const noexcept
- {
- return m_type == value_t::object;
- }
-
- /*!
- @brief return whether value is an array
-
- This function returns true if and only if the JSON value is an array.
-
- @return `true` if type is array, `false` otherwise.
-
- @complexity Constant.
-
- @exceptionsafety No-throw guarantee: this member function never throws
- exceptions.
-
- @liveexample{The following code exemplifies `is_array()` for all JSON
- types.,is_array}
-
- @since version 1.0.0
- */
- constexpr bool is_array() const noexcept
- {
- return m_type == value_t::array;
- }
-
- /*!
- @brief return whether value is a string
-
- This function returns true if and only if the JSON value is a string.
-
- @return `true` if type is string, `false` otherwise.
-
- @complexity Constant.
-
- @exceptionsafety No-throw guarantee: this member function never throws
- exceptions.
-
- @liveexample{The following code exemplifies `is_string()` for all JSON
- types.,is_string}
-
- @since version 1.0.0
- */
- constexpr bool is_string() const noexcept
- {
- return m_type == value_t::string;
- }
-
- /*!
- @brief return whether value is a binary array
-
- This function returns true if and only if the JSON value is a binary array.
-
- @return `true` if type is binary array, `false` otherwise.
-
- @complexity Constant.
-
- @exceptionsafety No-throw guarantee: this member function never throws
- exceptions.
-
- @liveexample{The following code exemplifies `is_binary()` for all JSON
- types.,is_binary}
-
- @since version 3.8.0
- */
- constexpr bool is_binary() const noexcept
- {
- return m_type == value_t::binary;
- }
-
- /*!
- @brief return whether value is discarded
-
- This function returns true if and only if the JSON value was discarded
- during parsing with a callback function (see @ref parser_callback_t).
-
- @note This function will always be `false` for JSON values after parsing.
- That is, discarded values can only occur during parsing, but will be
- removed when inside a structured value or replaced by null in other cases.
-
- @return `true` if type is discarded, `false` otherwise.
-
- @complexity Constant.
-
- @exceptionsafety No-throw guarantee: this member function never throws
- exceptions.
-
- @liveexample{The following code exemplifies `is_discarded()` for all JSON
- types.,is_discarded}
-
- @since version 1.0.0
- */
- constexpr bool is_discarded() const noexcept
- {
- return m_type == value_t::discarded;
- }
-
- /*!
- @brief return the type of the JSON value (implicit)
-
- Implicitly return the type of the JSON value as a value from the @ref
- value_t enumeration.
-
- @return the type of the JSON value
-
- @complexity Constant.
-
- @exceptionsafety No-throw guarantee: this member function never throws
- exceptions.
-
- @liveexample{The following code exemplifies the @ref value_t operator for
- all JSON types.,operator__value_t}
-
- @sa @ref type() -- return the type of the JSON value (explicit)
- @sa @ref type_name() -- return the type as string
-
- @since version 1.0.0
- */
- constexpr operator value_t() const noexcept
- {
- return m_type;
- }
-
- /// @}
-
- private:
- //////////////////
- // value access //
- //////////////////
-
- /// get a boolean (explicit)
- boolean_t get_impl(boolean_t* /*unused*/) const
- {
- if (JSON_HEDLEY_LIKELY(is_boolean()))
- {
- return m_value.boolean;
- }
-
- JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(type_name())));
- }
-
- /// get a pointer to the value (object)
- object_t* get_impl_ptr(object_t* /*unused*/) noexcept
- {
- return is_object() ? m_value.object : nullptr;
- }
-
- /// get a pointer to the value (object)
- constexpr const object_t* get_impl_ptr(const object_t* /*unused*/) const noexcept
- {
- return is_object() ? m_value.object : nullptr;
- }
-
- /// get a pointer to the value (array)
- array_t* get_impl_ptr(array_t* /*unused*/) noexcept
- {
- return is_array() ? m_value.array : nullptr;
- }
-
- /// get a pointer to the value (array)
- constexpr const array_t* get_impl_ptr(const array_t* /*unused*/) const noexcept
- {
- return is_array() ? m_value.array : nullptr;
- }
-
- /// get a pointer to the value (string)
- string_t* get_impl_ptr(string_t* /*unused*/) noexcept
- {
- return is_string() ? m_value.string : nullptr;
- }
-
- /// get a pointer to the value (string)
- constexpr const string_t* get_impl_ptr(const string_t* /*unused*/) const noexcept
- {
- return is_string() ? m_value.string : nullptr;
- }
-
- /// get a pointer to the value (boolean)
- boolean_t* get_impl_ptr(boolean_t* /*unused*/) noexcept
- {
- return is_boolean() ? &m_value.boolean : nullptr;
- }
-
- /// get a pointer to the value (boolean)
- constexpr const boolean_t* get_impl_ptr(const boolean_t* /*unused*/) const noexcept
- {
- return is_boolean() ? &m_value.boolean : nullptr;
- }
-
- /// get a pointer to the value (integer number)
- number_integer_t* get_impl_ptr(number_integer_t* /*unused*/) noexcept
- {
- return is_number_integer() ? &m_value.number_integer : nullptr;
- }
-
- /// get a pointer to the value (integer number)
- constexpr const number_integer_t* get_impl_ptr(const number_integer_t* /*unused*/) const noexcept
- {
- return is_number_integer() ? &m_value.number_integer : nullptr;
- }
-
- /// get a pointer to the value (unsigned number)
- number_unsigned_t* get_impl_ptr(number_unsigned_t* /*unused*/) noexcept
- {
- return is_number_unsigned() ? &m_value.number_unsigned : nullptr;
- }
-
- /// get a pointer to the value (unsigned number)
- constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t* /*unused*/) const noexcept
- {
- return is_number_unsigned() ? &m_value.number_unsigned : nullptr;
- }
-
- /// get a pointer to the value (floating-point number)
- number_float_t* get_impl_ptr(number_float_t* /*unused*/) noexcept
- {
- return is_number_float() ? &m_value.number_float : nullptr;
- }
-
- /// get a pointer to the value (floating-point number)
- constexpr const number_float_t* get_impl_ptr(const number_float_t* /*unused*/) const noexcept
- {
- return is_number_float() ? &m_value.number_float : nullptr;
- }
-
- /// get a pointer to the value (binary)
- binary_t* get_impl_ptr(binary_t* /*unused*/) noexcept
- {
- return is_binary() ? m_value.binary : nullptr;
- }
-
- /// get a pointer to the value (binary)
- constexpr const binary_t* get_impl_ptr(const binary_t* /*unused*/) const noexcept
- {
- return is_binary() ? m_value.binary : nullptr;
- }
-
- /*!
- @brief helper function to implement get_ref()
-
- This function helps to implement get_ref() without code duplication for
- const and non-const overloads
-
- @tparam ThisType will be deduced as `basic_json` or `const basic_json`
-
- @throw type_error.303 if ReferenceType does not match underlying value
- type of the current JSON
- */
- template
- static ReferenceType get_ref_impl(ThisType& obj)
- {
- // delegate the call to get_ptr<>()
- auto ptr = obj.template get_ptr::type>();
-
- if (JSON_HEDLEY_LIKELY(ptr != nullptr))
- {
- return *ptr;
- }
-
- JSON_THROW(type_error::create(303, "incompatible ReferenceType for get_ref, actual type is " + std::string(obj.type_name())));
- }
-
- public:
- /// @name value access
- /// Direct access to the stored value of a JSON value.
- /// @{
-
- /*!
- @brief get special-case overload
-
- This overloads avoids a lot of template boilerplate, it can be seen as the
- identity method
-
- @tparam BasicJsonType == @ref basic_json
-
- @return a copy of *this
-
- @complexity Constant.
-
- @since version 2.1.0
- */
- template::type, basic_json_t>::value,
- int> = 0>
- basic_json get() const
- {
- return *this;
- }
-
- /*!
- @brief get special-case overload
-
- This overloads converts the current @ref basic_json in a different
- @ref basic_json type
-
- @tparam BasicJsonType == @ref basic_json
-
- @return a copy of *this, converted into @tparam BasicJsonType
-
- @complexity Depending on the implementation of the called `from_json()`
- method.
-
- @since version 3.2.0
- */
- template < typename BasicJsonType, detail::enable_if_t <
- !std::is_same::value&&
- detail::is_basic_json::value, int > = 0 >
- BasicJsonType get() const
- {
- return *this;
- }
-
- /*!
- @brief get a value (explicit)
-
- Explicit type conversion between the JSON value and a compatible value
- which is [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible)
- and [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible).
- The value is converted by calling the @ref json_serializer
- `from_json()` method.
-
- The function is equivalent to executing
- @code {.cpp}
- ValueType ret;
- JSONSerializer::from_json(*this, ret);
- return ret;
- @endcode
-
- This overloads is chosen if:
- - @a ValueType is not @ref basic_json,
- - @ref json_serializer has a `from_json()` method of the form
- `void from_json(const basic_json&, ValueType&)`, and
- - @ref json_serializer does not have a `from_json()` method of
- the form `ValueType from_json(const basic_json&)`
-
- @tparam ValueTypeCV the provided value type
- @tparam ValueType the returned value type
-
- @return copy of the JSON value, converted to @a ValueType
-
- @throw what @ref json_serializer `from_json()` method throws
-
- @liveexample{The example below shows several conversions from JSON values
- to other types. There a few things to note: (1) Floating-point numbers can
- be converted to integers\, (2) A JSON array can be converted to a standard
- `std::vector`\, (3) A JSON object can be converted to C++
- associative containers such as `std::unordered_map`.,get__ValueType_const}
-
- @since version 2.1.0
- */
- template < typename ValueTypeCV, typename ValueType = detail::uncvref_t,
- detail::enable_if_t <
- !detail::is_basic_json::value &&
- detail::has_from_json::value &&
- !detail::has_non_default_from_json::value,
- int > = 0 >
- ValueType get() const noexcept(noexcept(
- JSONSerializer::from_json(std::declval(), std::declval())))
- {
- // we cannot static_assert on ValueTypeCV being non-const, because
- // there is support for get(), which is why we
- // still need the uncvref
- static_assert(!std::is_reference::value,
- "get() cannot be used with reference types, you might want to use get_ref()");
- static_assert(std::is_default_constructible::value,
- "types must be DefaultConstructible when used with get()");
-
- ValueType ret;
- JSONSerializer::from_json(*this, ret);
- return ret;
- }
-
- /*!
- @brief get a value (explicit); special case
-
- Explicit type conversion between the JSON value and a compatible value
- which is **not** [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible)
- and **not** [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible).
- The value is converted by calling the @ref json_serializer
- `from_json()` method.
-
- The function is equivalent to executing
- @code {.cpp}
- return JSONSerializer::from_json(*this);
- @endcode
-
- This overloads is chosen if:
- - @a ValueType is not @ref basic_json and
- - @ref json_serializer has a `from_json()` method of the form
- `ValueType from_json(const basic_json&)`
-
- @note If @ref json_serializer has both overloads of
- `from_json()`, this one is chosen.
-
- @tparam ValueTypeCV the provided value type
- @tparam ValueType the returned value type
-
- @return copy of the JSON value, converted to @a ValueType
-
- @throw what @ref json_serializer `from_json()` method throws
-
- @since version 2.1.0
- */
- template < typename ValueTypeCV, typename ValueType = detail::uncvref_t,
- detail::enable_if_t < !std::is_same::value &&
- detail::has_non_default_from_json::value,
- int > = 0 >
- ValueType get() const noexcept(noexcept(
- JSONSerializer::from_json(std::declval())))
- {
- static_assert(!std::is_reference::value,
- "get() cannot be used with reference types, you might want to use get_ref()");
- return JSONSerializer::from_json(*this);
- }
-
- /*!
- @brief get a value (explicit)
-
- Explicit type conversion between the JSON value and a compatible value.
- The value is filled into the input parameter by calling the @ref json_serializer
- `from_json()` method.
-
- The function is equivalent to executing
- @code {.cpp}
- ValueType v;
- JSONSerializer::from_json(*this, v);
- @endcode
-
- This overloads is chosen if:
- - @a ValueType is not @ref basic_json,
- - @ref json_serializer has a `from_json()` method of the form
- `void from_json(const basic_json&, ValueType&)`, and
-
- @tparam ValueType the input parameter type.
-
- @return the input parameter, allowing chaining calls.
-
- @throw what @ref json_serializer `from_json()` method throws
-
- @liveexample{The example below shows several conversions from JSON values
- to other types. There a few things to note: (1) Floating-point numbers can
- be converted to integers\, (2) A JSON array can be converted to a standard
- `std::vector`\, (3) A JSON object can be converted to C++
- associative containers such as `std::unordered_map`.,get_to}
-
- @since version 3.3.0
- */
- template < typename ValueType,
- detail::enable_if_t <
- !detail::is_basic_json::value&&
- detail::has_from_json::value,
- int > = 0 >
- ValueType & get_to(ValueType& v) const noexcept(noexcept(
- JSONSerializer::from_json(std::declval(), v)))
- {
- JSONSerializer::from_json(*this, v);
- return v;
- }
-
- // specialization to allow to call get_to with a basic_json value
- // see https://github.com/nlohmann/json/issues/2175
- template::value,
- int> = 0>
- ValueType & get_to(ValueType& v) const
- {
- v = *this;
- return v;
- }
-
- template <
- typename T, std::size_t N,
- typename Array = T (&)[N],
- detail::enable_if_t <
- detail::has_from_json::value, int > = 0 >
- Array get_to(T (&v)[N]) const
- noexcept(noexcept(JSONSerializer::from_json(
- std::declval(), v)))
- {
- JSONSerializer::from_json(*this, v);
- return v;
- }
-
-
- /*!
- @brief get a pointer value (implicit)
-
- Implicit pointer access to the internally stored JSON value. No copies are
- made.
-
- @warning Writing data to the pointee of the result yields an undefined
- state.
-
- @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref
- object_t, @ref string_t, @ref boolean_t, @ref number_integer_t,
- @ref number_unsigned_t, or @ref number_float_t. Enforced by a static
- assertion.
-
- @return pointer to the internally stored JSON value if the requested
- pointer type @a PointerType fits to the JSON value; `nullptr` otherwise
-
- @complexity Constant.
-
- @liveexample{The example below shows how pointers to internal values of a
- JSON value can be requested. Note that no type conversions are made and a
- `nullptr` is returned if the value and the requested pointer type does not
- match.,get_ptr}
-
- @since version 1.0.0
- */
- template::value, int>::type = 0>
- auto get_ptr() noexcept -> decltype(std::declval().get_impl_ptr(std::declval