Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cc31370a78 | ||
|
|
ce7498b54c | ||
|
|
e3e46253ed | ||
|
|
755bb16d7a |
No files matched your search
@@ -154,15 +154,6 @@ and each page's title (H1) and declaration by
|
|||||||
[`docs/docset/generate_docset.py`](https://github.com/nlohmann/json/blob/develop/docs/docset/generate_docset.py);
|
[`docs/docset/generate_docset.py`](https://github.com/nlohmann/json/blob/develop/docs/docset/generate_docset.py);
|
||||||
`make build` reports API pages that cannot be classified.
|
`make build` reports API pages that cannot be classified.
|
||||||
|
|
||||||
A pull request that changes only the documentation, Markdown files, GitHub metadata, or license files skips the
|
|
||||||
compiler workflows (Ubuntu, macOS, Windows); the documentation and license checks still run. See
|
|
||||||
[`tools/check_path_filters`](https://github.com/nlohmann/json/tree/develop/tools/check_path_filters) for the list of
|
|
||||||
skipped files and what to do when a compiler job starts reading one of them.
|
|
||||||
|
|
||||||
Most compiler jobs run only once a maintainer adds the label "🚀 ready to merge" to a pull request; until then, a
|
|
||||||
smaller set of jobs runs on every push. See
|
|
||||||
[How CI runs](https://json.nlohmann.me/community/quality_assurance/#how-ci-runs) for the sets and when they run.
|
|
||||||
|
|
||||||
### Amalgamate the source code
|
### Amalgamate the source code
|
||||||
|
|
||||||
The single-header files
|
The single-header files
|
||||||
|
|||||||
@@ -7,25 +7,6 @@ on:
|
|||||||
- master
|
- master
|
||||||
- release/*
|
- release/*
|
||||||
pull_request:
|
pull_request:
|
||||||
# Skip pull requests that change only files no job here reads. The list is checked by
|
|
||||||
# tools/check_path_filters/check_path_filters.py (repository_checks.yml); change both together.
|
|
||||||
paths:
|
|
||||||
- '**'
|
|
||||||
- '!docs/**'
|
|
||||||
- '!**.md'
|
|
||||||
- '!.github/*'
|
|
||||||
- '!.github/ISSUE_TEMPLATE/**'
|
|
||||||
- '!.github/external_ci/**'
|
|
||||||
- '!.github/workflows/**'
|
|
||||||
- '!.reuse/**'
|
|
||||||
- '!LICENSES/**'
|
|
||||||
- '!CITATION.cff'
|
|
||||||
- '!REUSE.toml'
|
|
||||||
# read by tools/check_build_options in the ci_meson_install job (ubuntu.yml)
|
|
||||||
- 'docs/mkdocs/docs/integration/package_managers.md'
|
|
||||||
# built by the swiftpm job (macos.yml)
|
|
||||||
- 'docs/mkdocs/docs/integration/swift/**'
|
|
||||||
- '.github/workflows/macos.yml'
|
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
|
|||||||
@@ -1,83 +0,0 @@
|
|||||||
name: Repository checks
|
|
||||||
|
|
||||||
# Checks that concern files the compiler workflows (ubuntu.yml, macos.yml,
|
|
||||||
# windows.yml) skip with their path filter: the documentation, the license
|
|
||||||
# information, and the CI configuration itself. This workflow has no path filter.
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- develop
|
|
||||||
- master
|
|
||||||
- release/*
|
|
||||||
pull_request:
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.ref || github.run_id }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
check_ci_configuration:
|
|
||||||
runs-on: ubuntu-26.04
|
|
||||||
steps:
|
|
||||||
- name: Harden Runner
|
|
||||||
uses: step-security/harden-runner@dc2769abcc0fcfbadfc1df54081c1608579ef6b2 # v2.22.2
|
|
||||||
with:
|
|
||||||
egress-policy: audit
|
|
||||||
|
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
- name: Check the path filters of the compiler workflows
|
|
||||||
run: python3 tools/check_path_filters/check_path_filters.py
|
|
||||||
- name: Check the job sets of the compiler workflows and their documentation
|
|
||||||
run: |
|
|
||||||
python3 -m venv venv
|
|
||||||
venv/bin/pip install --quiet -r tools/ci_jobs/requirements.txt
|
|
||||||
venv/bin/python tools/ci_jobs/ci_jobs.py
|
|
||||||
|
|
||||||
ci_reuse_compliance:
|
|
||||||
runs-on: ubuntu-26.04
|
|
||||||
steps:
|
|
||||||
- name: Harden Runner
|
|
||||||
uses: step-security/harden-runner@dc2769abcc0fcfbadfc1df54081c1608579ef6b2 # v2.22.2
|
|
||||||
with:
|
|
||||||
egress-policy: audit
|
|
||||||
|
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
- name: Get latest CMake and ninja
|
|
||||||
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
|
||||||
- name: Run CMake
|
|
||||||
run: cmake -S . -B build -DJSON_CI=On
|
|
||||||
- name: Build
|
|
||||||
run: cmake --build build --target ci_reuse_compliance
|
|
||||||
|
|
||||||
ci_test_documentation:
|
|
||||||
runs-on: ubuntu-26.04
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
target: [ci_test_examples, ci_test_build_documentation, ci_test_documentation_mermaid]
|
|
||||||
steps:
|
|
||||||
- name: Harden Runner
|
|
||||||
uses: step-security/harden-runner@dc2769abcc0fcfbadfc1df54081c1608579ef6b2 # v2.22.2
|
|
||||||
with:
|
|
||||||
egress-policy: audit
|
|
||||||
|
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
# the git-revision-date-localized plugin needs the history; a shallow clone makes the strict build fail
|
|
||||||
fetch-depth: ${{ matrix.target == 'ci_test_build_documentation' && '0' || '1' }}
|
|
||||||
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
|
|
||||||
if: matrix.target == 'ci_test_documentation_mermaid'
|
|
||||||
with:
|
|
||||||
node-version: 24
|
|
||||||
- name: Run CMake
|
|
||||||
run: cmake -S . -B build -DJSON_CI=On
|
|
||||||
- name: Build
|
|
||||||
run: cmake --build build --target ${{ matrix.target }}
|
|
||||||
+262
-422
@@ -7,62 +7,17 @@ on:
|
|||||||
- master
|
- master
|
||||||
- release/*
|
- release/*
|
||||||
pull_request:
|
pull_request:
|
||||||
# labeled: the "🚀 ready to merge" label starts the full set (see below)
|
|
||||||
types: [opened, synchronize, reopened, labeled]
|
|
||||||
# Skip pull requests that change only files no job here reads. The list is checked by
|
|
||||||
# tools/check_path_filters/check_path_filters.py (repository_checks.yml); change both together.
|
|
||||||
paths:
|
|
||||||
- '**'
|
|
||||||
- '!docs/**'
|
|
||||||
- '!**.md'
|
|
||||||
- '!.github/*'
|
|
||||||
- '!.github/ISSUE_TEMPLATE/**'
|
|
||||||
- '!.github/external_ci/**'
|
|
||||||
- '!.github/workflows/**'
|
|
||||||
- '!.reuse/**'
|
|
||||||
- '!LICENSES/**'
|
|
||||||
- '!CITATION.cff'
|
|
||||||
- '!REUSE.toml'
|
|
||||||
# read by tools/check_build_options in the ci_meson_install job (ubuntu.yml)
|
|
||||||
- 'docs/mkdocs/docs/integration/package_managers.md'
|
|
||||||
# built by the swiftpm job (macos.yml)
|
|
||||||
- 'docs/mkdocs/docs/integration/swift/**'
|
|
||||||
- '.github/workflows/ubuntu.yml'
|
|
||||||
schedule:
|
|
||||||
# the nightly set (see below), on develop
|
|
||||||
- cron: '0 2 * * *'
|
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
# A run triggered by any other label gets a group of its own, so it does not
|
|
||||||
# cancel the run of the last push; its jobs are skipped.
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.event_name == 'schedule' && 'nightly' || github.ref || github.run_id }}${{ github.event.action == 'labeled' && github.event.label.name != '🚀 ready to merge' && format('-{0}', github.run_id) || '' }}
|
group: ${{ github.workflow }}-${{ github.ref || github.run_id }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
# The jobs are split into three sets (see "How CI runs" in
|
|
||||||
# docs/mkdocs/docs/community/quality_assurance.md):
|
|
||||||
#
|
|
||||||
# - PR set: every pull request push and every push; chosen because these jobs
|
|
||||||
# caught 92 % of the failing commits in Sep/Oct 2026.
|
|
||||||
# - full set: after the PR set passed, on pushes and on pull requests with the
|
|
||||||
# "🚀 ready to merge" label; every remaining compiler, version and standard.
|
|
||||||
# - nightly set: slow tools and rare compilers, nightly on develop, on pushes
|
|
||||||
# to master and release/*, and when started by hand.
|
|
||||||
#
|
|
||||||
# A matrix whose entries belong to different sets is split into one job per
|
|
||||||
# set; the jobs share their steps through YAML anchors. tools/ci_jobs checks
|
|
||||||
# that every job belongs to exactly one set.
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
###########################################################################
|
|
||||||
# PR set
|
|
||||||
###########################################################################
|
|
||||||
|
|
||||||
ci_test_gcc:
|
ci_test_gcc:
|
||||||
if: &pr_set ${{ github.event_name != 'schedule' && (github.event.action != 'labeled' || github.event.label.name == '🚀 ready to merge') }}
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container: gcc:latest
|
container: gcc:latest
|
||||||
steps:
|
steps:
|
||||||
@@ -77,7 +32,6 @@ jobs:
|
|||||||
run: cmake --build build --target ci_test_gcc
|
run: cmake --build build --target ci_test_gcc
|
||||||
|
|
||||||
ci_meson_install:
|
ci_meson_install:
|
||||||
if: *pr_set
|
|
||||||
runs-on: ubuntu-26.04
|
runs-on: ubuntu-26.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
@@ -122,342 +76,7 @@ jobs:
|
|||||||
cmake -S tests/cmake_import/project -B build-import-split -DCMAKE_PREFIX_PATH=${{ github.workspace }}/install-split
|
cmake -S tests/cmake_import/project -B build-import-split -DCMAKE_PREFIX_PATH=${{ github.workspace }}/install-split
|
||||||
cmake --build build-import-split
|
cmake --build build-import-split
|
||||||
|
|
||||||
ci_static_analysis_ubuntu:
|
|
||||||
if: *pr_set
|
|
||||||
runs-on: ubuntu-26.04
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
target: [ci_cpplint]
|
|
||||||
steps: &static_analysis_ubuntu_steps
|
|
||||||
- name: Harden Runner
|
|
||||||
uses: step-security/harden-runner@dc2769abcc0fcfbadfc1df54081c1608579ef6b2 # v2.22.2
|
|
||||||
with:
|
|
||||||
egress-policy: audit
|
|
||||||
|
|
||||||
- name: Install Valgrind
|
|
||||||
if: matrix.target == 'ci_test_valgrind'
|
|
||||||
run: sudo apt-get update ; sudo apt-get install -y valgrind
|
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
- name: Get latest CMake and ninja
|
|
||||||
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
|
||||||
- name: Run CMake
|
|
||||||
run: cmake -S . -B build -DJSON_CI=On
|
|
||||||
- name: Build
|
|
||||||
run: cmake --build build --target ${{ matrix.target }}
|
|
||||||
|
|
||||||
ci_static_analysis_clang:
|
|
||||||
if: *pr_set
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
container: silkeh/clang:22
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
target: [ci_test_clang, ci_clang_tidy, ci_test_clang_sanitizer, ci_single_binaries]
|
|
||||||
steps: &static_analysis_clang_steps
|
|
||||||
- name: Install git, clang-tools, iwyu (ci_single_binaries), and unzip
|
|
||||||
run: apt-get update ; apt-get install -y git clang-tools iwyu unzip
|
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
- name: Get latest CMake and ninja
|
|
||||||
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
|
||||||
- name: Run CMake
|
|
||||||
run: cmake -S . -B build -DJSON_CI=On
|
|
||||||
- name: Build
|
|
||||||
run: cmake --build build --target ${{ matrix.target }}
|
|
||||||
|
|
||||||
ci_cmake_options:
|
|
||||||
if: *pr_set
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
container: ubuntu:24.04
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
target: [ci_test_diagnostics, ci_test_diagnostic_positions, ci_test_noexceptions, ci_test_noglobaludls]
|
|
||||||
steps: &cmake_options_steps
|
|
||||||
- name: Install build-essential
|
|
||||||
run: apt-get update ; apt-get install -y build-essential unzip wget git
|
|
||||||
# the CMake 3.5.0 release binary that ci_cmake_flags downloads links
|
|
||||||
# against libidn.so.11, which Ubuntu 24.04 no longer ships
|
|
||||||
- name: Install libidn11 for CMake 3.5.0
|
|
||||||
if: matrix.target == 'ci_cmake_flags'
|
|
||||||
run: |
|
|
||||||
wget -q http://archive.ubuntu.com/ubuntu/pool/main/libi/libidn/libidn11_1.33-2.2ubuntu2_amd64.deb
|
|
||||||
dpkg -i libidn11_1.33-2.2ubuntu2_amd64.deb
|
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
- name: Get latest CMake and ninja
|
|
||||||
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
|
||||||
- name: Run CMake
|
|
||||||
run: cmake -S . -B build -DJSON_CI=On
|
|
||||||
- name: Build
|
|
||||||
run: cmake --build build --target ${{ matrix.target }}
|
|
||||||
|
|
||||||
ci_test_compilers_gcc_old:
|
|
||||||
if: *pr_set
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
compiler: ['4.8']
|
|
||||||
# official gcc:4.8/4.9/5/6 images fail to check out code (too old for
|
|
||||||
# actions/checkout); install the old compilers on top of official ubuntu:20.04
|
|
||||||
# instead, mirroring what the (now retired) custom json-ci image did.
|
|
||||||
container: ubuntu:20.04
|
|
||||||
steps: &compilers_gcc_old_steps
|
|
||||||
- name: Install g++-${{ matrix.compiler }}
|
|
||||||
run: |
|
|
||||||
export DEBIAN_FRONTEND=noninteractive
|
|
||||||
apt-get update
|
|
||||||
apt-get install -y --no-install-recommends software-properties-common ca-certificates gnupg make git
|
|
||||||
# add-apt-repository resolves the PPA through the Launchpad API,
|
|
||||||
# which intermittently times out or fails the team lookup (the plain
|
|
||||||
# "deb ..." sources below never hit Launchpad and never flake).
|
|
||||||
# Retry with backoff so a transient Launchpad blip does not fail CI.
|
|
||||||
for attempt in 1 2 3 4 5; do
|
|
||||||
add-apt-repository -y ppa:ubuntu-toolchain-r/test && break
|
|
||||||
echo "::warning::add-apt-repository ppa:ubuntu-toolchain-r/test failed (attempt ${attempt}/5); retrying"
|
|
||||||
sleep $((attempt * 10))
|
|
||||||
done
|
|
||||||
apt-add-repository -y "deb http://archive.ubuntu.com/ubuntu/ bionic main"
|
|
||||||
apt-add-repository -y "deb http://archive.ubuntu.com/ubuntu/ bionic universe"
|
|
||||||
apt-add-repository -y "deb http://archive.ubuntu.com/ubuntu/ xenial main"
|
|
||||||
apt-add-repository -y "deb http://archive.ubuntu.com/ubuntu/ xenial universe"
|
|
||||||
apt-add-repository -y "deb http://archive.ubuntu.com/ubuntu/ xenial-updates main"
|
|
||||||
apt-add-repository -y "deb http://archive.ubuntu.com/ubuntu/ xenial-updates universe"
|
|
||||||
apt-get update
|
|
||||||
apt-get install -y --no-install-recommends g++-${{ matrix.compiler }}
|
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
- name: Get latest CMake and ninja
|
|
||||||
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
|
||||||
- name: Run CMake
|
|
||||||
run: CXX=g++-${{ matrix.compiler }} cmake -S . -B build -DJSON_CI=On
|
|
||||||
- name: Build
|
|
||||||
run: cmake --build build --target ci_test_compiler_g++-${{ matrix.compiler }}
|
|
||||||
|
|
||||||
ci_test_compilers_clang:
|
|
||||||
if: *pr_set
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
compiler: ['3.4']
|
|
||||||
container: silkeh/clang:${{ matrix.compiler }}
|
|
||||||
steps: &compilers_clang_steps
|
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
- name: Get latest CMake and ninja
|
|
||||||
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
|
||||||
- name: Disable filesystem support (std::filesystem is unusable) for clang 7 / 8 / 9 / 10
|
|
||||||
run: echo "JSON_FORCED_GLOBAL_COMPILE_OPTIONS=-DJSON_HAS_FILESYSTEM=0;-DJSON_HAS_EXPERIMENTAL_FILESYSTEM=0" >> "$GITHUB_ENV"
|
|
||||||
if: ${{ matrix.compiler == '7' || matrix.compiler == '8' || matrix.compiler == '9' || matrix.compiler == '10' }}
|
|
||||||
- name: Run CMake
|
|
||||||
run: cmake -S . -B build -DJSON_CI=On
|
|
||||||
- name: Build
|
|
||||||
run: cmake --build build --target ci_test_compiler_default
|
|
||||||
|
|
||||||
ci_test_standards_gcc:
|
|
||||||
if: *pr_set
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
container: gcc:latest
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
standard: [26]
|
|
||||||
steps: &standards_gcc_steps
|
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
- name: Get latest CMake and ninja
|
|
||||||
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
|
||||||
- name: Run CMake
|
|
||||||
run: cmake -S . -B build -DJSON_CI=On
|
|
||||||
- name: Build
|
|
||||||
run: cmake --build build --target ci_test_gcc_cxx${{ matrix.standard }}
|
|
||||||
|
|
||||||
ci_test_standards_clang:
|
|
||||||
if: *pr_set
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
container: silkeh/clang:latest
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
standard: [26]
|
|
||||||
stdlib: [libcxx]
|
|
||||||
steps: &standards_clang_steps
|
|
||||||
- name: Install git and unzip
|
|
||||||
run: apt-get update ; apt-get install -y git unzip
|
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
- name: Get latest CMake and ninja
|
|
||||||
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
|
||||||
- name: Run CMake
|
|
||||||
run: cmake -S . -B build -DJSON_CI=On
|
|
||||||
- name: Build with libc++
|
|
||||||
run: cmake --build build --target ci_test_clang_libcxx_cxx${{ matrix.standard }}
|
|
||||||
if: ${{ matrix.stdlib == 'libcxx' }}
|
|
||||||
- name: Build with libstdc++
|
|
||||||
run: cmake --build build --target ci_test_clang_cxx${{ matrix.standard }}
|
|
||||||
if: ${{ matrix.stdlib == 'libstdcxx' }}
|
|
||||||
|
|
||||||
ci_emscripten:
|
|
||||||
if: *pr_set
|
|
||||||
runs-on: ubuntu-26.04
|
|
||||||
steps:
|
|
||||||
- name: Harden Runner
|
|
||||||
uses: step-security/harden-runner@dc2769abcc0fcfbadfc1df54081c1608579ef6b2 # v2.22.2
|
|
||||||
with:
|
|
||||||
egress-policy: audit
|
|
||||||
|
|
||||||
- name: Install emscripten
|
|
||||||
uses: mymindstorm/setup-emsdk@4528d102f7230f0e7b276855c01ea1159be0e984 # v16
|
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
- name: Get latest CMake and ninja
|
|
||||||
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
|
||||||
- name: Run CMake
|
|
||||||
run: cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE=$EMSDK/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake -GNinja
|
|
||||||
- name: Build
|
|
||||||
run: cmake --build build
|
|
||||||
|
|
||||||
###########################################################################
|
|
||||||
# full set: starts after the PR set passed
|
|
||||||
###########################################################################
|
|
||||||
|
|
||||||
ci_static_analysis_ubuntu_full:
|
|
||||||
needs: &pr_set_jobs [ci_test_gcc, ci_meson_install, ci_static_analysis_ubuntu, ci_static_analysis_clang, ci_cmake_options, ci_test_compilers_gcc_old, ci_test_compilers_clang, ci_test_standards_gcc, ci_test_standards_clang, ci_emscripten]
|
|
||||||
if: &full_set ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' || contains(github.event.pull_request.labels.*.name, '🚀 ready to merge') }}
|
|
||||||
runs-on: ubuntu-26.04
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
target: [ci_test_single_header, ci_cppcheck, ci_reproducible_tests, ci_non_git_tests, ci_offline_testdata]
|
|
||||||
steps: *static_analysis_ubuntu_steps
|
|
||||||
|
|
||||||
ci_static_analysis_clang_full:
|
|
||||||
needs: *pr_set_jobs
|
|
||||||
if: *full_set
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
container: silkeh/clang:22
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
target: [ci_clang_analyze]
|
|
||||||
steps: *static_analysis_clang_steps
|
|
||||||
|
|
||||||
ci_cmake_options_full:
|
|
||||||
needs: *pr_set_jobs
|
|
||||||
if: *full_set
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
container: ubuntu:24.04
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
target: [ci_cmake_flags, ci_test_noimplicitconversions, ci_test_legacycomparison, ci_test_disableenumserialization, ci_test_disabletuplereferenceconversion, ci_test_skiplibraryversioncheck, ci_test_simdutf, ci_test_strict_nul_handling, ci_test_delete_deprecated_functions, ci_test_no_thread_local]
|
|
||||||
steps: *cmake_options_steps
|
|
||||||
|
|
||||||
ci_test_compilers_gcc_old_full:
|
|
||||||
needs: *pr_set_jobs
|
|
||||||
if: *full_set
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
compiler: ['4.9', '5', '6']
|
|
||||||
container: ubuntu:20.04
|
|
||||||
steps: *compilers_gcc_old_steps
|
|
||||||
|
|
||||||
ci_test_compilers_gcc_full:
|
|
||||||
needs: *pr_set_jobs
|
|
||||||
if: *full_set
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
# older GCC docker images (4, 5, 6) fail to check out code
|
|
||||||
compiler: ['7', '8', '9', '10', '11', '12', '13', '14', '15', '16', 'latest']
|
|
||||||
container: gcc:${{ matrix.compiler }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
- name: Get latest CMake and ninja
|
|
||||||
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
|
||||||
- name: Run CMake
|
|
||||||
run: cmake -S . -B build -DJSON_CI=On
|
|
||||||
- name: Build
|
|
||||||
run: cmake --build build --target ci_test_compiler_default
|
|
||||||
|
|
||||||
ci_test_compilers_clang_full:
|
|
||||||
needs: *pr_set_jobs
|
|
||||||
if: *full_set
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
compiler: ['3.5', '3.6', '3.7', '3.8', '3.9', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15-bullseye', '16', '17', '18', '19', '20', '21', '22', 'latest']
|
|
||||||
container: silkeh/clang:${{ matrix.compiler }}
|
|
||||||
steps: *compilers_clang_steps
|
|
||||||
|
|
||||||
ci_test_standards_gcc_full:
|
|
||||||
needs: *pr_set_jobs
|
|
||||||
if: *full_set
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
container: gcc:latest
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
standard: [11, 14, 17, 20, 23]
|
|
||||||
steps: *standards_gcc_steps
|
|
||||||
|
|
||||||
ci_test_standards_clang_full:
|
|
||||||
needs: *pr_set_jobs
|
|
||||||
if: *full_set
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
container: silkeh/clang:latest
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
standard: [11, 14, 17, 20, 23, 26]
|
|
||||||
stdlib: [libcxx, libstdcxx]
|
|
||||||
exclude:
|
|
||||||
# in the PR set
|
|
||||||
- standard: 26
|
|
||||||
stdlib: libcxx
|
|
||||||
steps: *standards_clang_steps
|
|
||||||
|
|
||||||
ci_module_cpp20:
|
|
||||||
needs: *pr_set_jobs
|
|
||||||
if: *full_set
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
container: ['gcc:latest', 'silkeh/clang:latest']
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
container: ${{ matrix.container }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
# The module test uses `import std;`, which needs CMake's experimental
|
|
||||||
# import-std support. Its opt-in token is CMake-version-specific, so pin
|
|
||||||
# CMake to the version whose token is set in tests/module_cpp20/CMakeLists.txt.
|
|
||||||
- name: Get pinned CMake and ninja
|
|
||||||
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
|
||||||
with:
|
|
||||||
cmakeVersion: 4.3.4
|
|
||||||
# Clang: the std library module is provided by libc++ (the image's libstdc++
|
|
||||||
# ships none), and the image's libc++ module manifest has a broken relative
|
|
||||||
# path — repoint it at the real module sources.
|
|
||||||
- name: Use libc++ and fix its module manifest path (Clang)
|
|
||||||
if: matrix.container == 'silkeh/clang:latest'
|
|
||||||
run: |
|
|
||||||
echo "CXXFLAGS=-stdlib=libc++" >> "$GITHUB_ENV"
|
|
||||||
mkdir -p /usr/lib/share && ln -sf /usr/lib/llvm-*/share/libc++ /usr/lib/share/libc++
|
|
||||||
- name: Run CMake
|
|
||||||
run: cmake -S . -B build -DJSON_CI=On
|
|
||||||
- name: Build
|
|
||||||
run: cmake --build build --target ci_module_cpp20
|
|
||||||
|
|
||||||
###########################################################################
|
|
||||||
# nightly set
|
|
||||||
###########################################################################
|
|
||||||
|
|
||||||
ci_infer:
|
ci_infer:
|
||||||
if: &nightly_set ${{ github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || (github.event_name == 'push' && github.ref != 'refs/heads/develop') }}
|
|
||||||
runs-on: ubuntu-26.04
|
runs-on: ubuntu-26.04
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
@@ -486,16 +105,76 @@ jobs:
|
|||||||
path: ${{ github.workspace }}/build/build_infer/infer-out/report.txt
|
path: ${{ github.workspace }}/build/build_infer/infer-out/report.txt
|
||||||
if-no-files-found: ignore
|
if-no-files-found: ignore
|
||||||
|
|
||||||
ci_static_analysis_ubuntu_nightly:
|
ci_static_analysis_ubuntu:
|
||||||
if: *nightly_set
|
|
||||||
runs-on: ubuntu-26.04
|
runs-on: ubuntu-26.04
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
target: [ci_test_valgrind]
|
target: [ci_test_single_header, ci_cppcheck, ci_cpplint, ci_reproducible_tests, ci_non_git_tests, ci_offline_testdata, ci_reuse_compliance, ci_test_valgrind]
|
||||||
steps: *static_analysis_ubuntu_steps
|
steps:
|
||||||
|
- name: Harden Runner
|
||||||
|
uses: step-security/harden-runner@dc2769abcc0fcfbadfc1df54081c1608579ef6b2 # v2.22.2
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- name: Install Valgrind
|
||||||
|
if: matrix.target == 'ci_test_valgrind'
|
||||||
|
run: sudo apt-get update ; sudo apt-get install -y valgrind
|
||||||
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
- name: Get latest CMake and ninja
|
||||||
|
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
||||||
|
- name: Run CMake
|
||||||
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
|
- name: Build
|
||||||
|
run: cmake --build build --target ${{ matrix.target }}
|
||||||
|
|
||||||
|
ci_static_analysis_clang:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container: silkeh/clang:22
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
target: [ci_test_clang, ci_clang_tidy, ci_test_clang_sanitizer, ci_clang_analyze, ci_single_binaries]
|
||||||
|
steps:
|
||||||
|
- name: Install git, clang-tools, iwyu (ci_single_binaries), and unzip
|
||||||
|
run: apt-get update ; apt-get install -y git clang-tools iwyu unzip
|
||||||
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
- name: Get latest CMake and ninja
|
||||||
|
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
||||||
|
- name: Run CMake
|
||||||
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
|
- name: Build
|
||||||
|
run: cmake --build build --target ${{ matrix.target }}
|
||||||
|
|
||||||
|
ci_cmake_options:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container: ubuntu:24.04
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
target: [ci_cmake_flags, ci_test_diagnostics, ci_test_diagnostic_positions, ci_test_noexceptions, ci_test_noimplicitconversions, ci_test_legacycomparison, ci_test_noglobaludls, ci_test_disableenumserialization, ci_test_disabletuplereferenceconversion, ci_test_skiplibraryversioncheck, ci_test_simdutf, ci_test_strict_nul_handling, ci_test_delete_deprecated_functions, ci_test_no_thread_local]
|
||||||
|
steps:
|
||||||
|
- name: Install build-essential
|
||||||
|
run: apt-get update ; apt-get install -y build-essential unzip wget git
|
||||||
|
# the CMake 3.5.0 release binary that ci_cmake_flags downloads links
|
||||||
|
# against libidn.so.11, which Ubuntu 24.04 no longer ships
|
||||||
|
- name: Install libidn11 for CMake 3.5.0
|
||||||
|
if: matrix.target == 'ci_cmake_flags'
|
||||||
|
run: |
|
||||||
|
wget -q http://archive.ubuntu.com/ubuntu/pool/main/libi/libidn/libidn11_1.33-2.2ubuntu2_amd64.deb
|
||||||
|
dpkg -i libidn11_1.33-2.2ubuntu2_amd64.deb
|
||||||
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
- name: Get latest CMake and ninja
|
||||||
|
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
||||||
|
- name: Run CMake
|
||||||
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
|
- name: Build
|
||||||
|
run: cmake --build build --target ${{ matrix.target }}
|
||||||
|
|
||||||
ci_test_coverage:
|
ci_test_coverage:
|
||||||
if: *nightly_set
|
|
||||||
runs-on: ubuntu-26.04
|
runs-on: ubuntu-26.04
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
@@ -529,8 +208,128 @@ jobs:
|
|||||||
path-to-lcov: ${{ github.workspace }}/build/json.info.filtered.noexcept
|
path-to-lcov: ${{ github.workspace }}/build/json.info.filtered.noexcept
|
||||||
fail-on-error: false
|
fail-on-error: false
|
||||||
|
|
||||||
|
ci_test_compilers_gcc_old:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
compiler: ['4.8', '4.9', '5', '6']
|
||||||
|
# official gcc:4.8/4.9/5/6 images fail to check out code (too old for
|
||||||
|
# actions/checkout); install the old compilers on top of official ubuntu:20.04
|
||||||
|
# instead, mirroring what the (now retired) custom json-ci image did.
|
||||||
|
container: ubuntu:20.04
|
||||||
|
steps:
|
||||||
|
- name: Install g++-${{ matrix.compiler }}
|
||||||
|
run: |
|
||||||
|
export DEBIAN_FRONTEND=noninteractive
|
||||||
|
apt-get update
|
||||||
|
apt-get install -y --no-install-recommends software-properties-common ca-certificates gnupg make git
|
||||||
|
# add-apt-repository resolves the PPA through the Launchpad API,
|
||||||
|
# which intermittently times out or fails the team lookup (the plain
|
||||||
|
# "deb ..." sources below never hit Launchpad and never flake).
|
||||||
|
# Retry with backoff so a transient Launchpad blip does not fail CI.
|
||||||
|
for attempt in 1 2 3 4 5; do
|
||||||
|
add-apt-repository -y ppa:ubuntu-toolchain-r/test && break
|
||||||
|
echo "::warning::add-apt-repository ppa:ubuntu-toolchain-r/test failed (attempt ${attempt}/5); retrying"
|
||||||
|
sleep $((attempt * 10))
|
||||||
|
done
|
||||||
|
apt-add-repository -y "deb http://archive.ubuntu.com/ubuntu/ bionic main"
|
||||||
|
apt-add-repository -y "deb http://archive.ubuntu.com/ubuntu/ bionic universe"
|
||||||
|
apt-add-repository -y "deb http://archive.ubuntu.com/ubuntu/ xenial main"
|
||||||
|
apt-add-repository -y "deb http://archive.ubuntu.com/ubuntu/ xenial universe"
|
||||||
|
apt-add-repository -y "deb http://archive.ubuntu.com/ubuntu/ xenial-updates main"
|
||||||
|
apt-add-repository -y "deb http://archive.ubuntu.com/ubuntu/ xenial-updates universe"
|
||||||
|
apt-get update
|
||||||
|
apt-get install -y --no-install-recommends g++-${{ matrix.compiler }}
|
||||||
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
- name: Get latest CMake and ninja
|
||||||
|
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
||||||
|
- name: Run CMake
|
||||||
|
run: CXX=g++-${{ matrix.compiler }} cmake -S . -B build -DJSON_CI=On
|
||||||
|
- name: Build
|
||||||
|
run: cmake --build build --target ci_test_compiler_g++-${{ matrix.compiler }}
|
||||||
|
|
||||||
|
ci_test_compilers_gcc:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
# older GCC docker images (4, 5, 6) fail to check out code
|
||||||
|
compiler: ['7', '8', '9', '10', '11', '12', '13', '14', '15', '16', 'latest']
|
||||||
|
container: gcc:${{ matrix.compiler }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
- name: Get latest CMake and ninja
|
||||||
|
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
||||||
|
- name: Run CMake
|
||||||
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
|
- name: Build
|
||||||
|
run: cmake --build build --target ci_test_compiler_default
|
||||||
|
|
||||||
|
ci_test_compilers_clang:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
compiler: ['3.4', '3.5', '3.6', '3.7', '3.8', '3.9', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15-bullseye', '16', '17', '18', '19', '20', '21', '22', 'latest']
|
||||||
|
container: silkeh/clang:${{ matrix.compiler }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
- name: Get latest CMake and ninja
|
||||||
|
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
||||||
|
- name: Disable filesystem support (std::filesystem is unusable) for clang 7 / 8 / 9 / 10
|
||||||
|
run: echo "JSON_FORCED_GLOBAL_COMPILE_OPTIONS=-DJSON_HAS_FILESYSTEM=0;-DJSON_HAS_EXPERIMENTAL_FILESYSTEM=0" >> "$GITHUB_ENV"
|
||||||
|
if: ${{ matrix.compiler == '7' || matrix.compiler == '8' || matrix.compiler == '9' || matrix.compiler == '10' }}
|
||||||
|
- name: Run CMake
|
||||||
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
|
- name: Build
|
||||||
|
run: cmake --build build --target ci_test_compiler_default
|
||||||
|
|
||||||
|
ci_test_standards_gcc:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container: gcc:latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
standard: [11, 14, 17, 20, 23, 26]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
- name: Get latest CMake and ninja
|
||||||
|
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
||||||
|
- name: Run CMake
|
||||||
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
|
- name: Build
|
||||||
|
run: cmake --build build --target ci_test_gcc_cxx${{ matrix.standard }}
|
||||||
|
|
||||||
|
ci_test_standards_clang:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container: silkeh/clang:latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
standard: [11, 14, 17, 20, 23, 26]
|
||||||
|
stdlib: [libcxx, libstdcxx]
|
||||||
|
steps:
|
||||||
|
- name: Install git and unzip
|
||||||
|
run: apt-get update ; apt-get install -y git unzip
|
||||||
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
- name: Get latest CMake and ninja
|
||||||
|
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
||||||
|
- name: Run CMake
|
||||||
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
|
- name: Build with libc++
|
||||||
|
run: cmake --build build --target ci_test_clang_libcxx_cxx${{ matrix.standard }}
|
||||||
|
if: ${{ matrix.stdlib == 'libcxx' }}
|
||||||
|
- name: Build with libstdc++
|
||||||
|
run: cmake --build build --target ci_test_clang_cxx${{ matrix.standard }}
|
||||||
|
if: ${{ matrix.stdlib == 'libstdcxx' }}
|
||||||
|
|
||||||
ci_cuda_example:
|
ci_cuda_example:
|
||||||
if: *nightly_set
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
@@ -553,8 +352,37 @@ jobs:
|
|||||||
- name: Build
|
- name: Build
|
||||||
run: cmake --build build --target ci_cuda_example
|
run: cmake --build build --target ci_cuda_example
|
||||||
|
|
||||||
|
ci_module_cpp20:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
container: ['gcc:latest', 'silkeh/clang:latest']
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container: ${{ matrix.container }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
# The module test uses `import std;`, which needs CMake's experimental
|
||||||
|
# import-std support. Its opt-in token is CMake-version-specific, so pin
|
||||||
|
# CMake to the version whose token is set in tests/module_cpp20/CMakeLists.txt.
|
||||||
|
- name: Get pinned CMake and ninja
|
||||||
|
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
||||||
|
with:
|
||||||
|
cmakeVersion: 4.3.4
|
||||||
|
# Clang: the std library module is provided by libc++ (the image's libstdc++
|
||||||
|
# ships none), and the image's libc++ module manifest has a broken relative
|
||||||
|
# path — repoint it at the real module sources.
|
||||||
|
- name: Use libc++ and fix its module manifest path (Clang)
|
||||||
|
if: matrix.container == 'silkeh/clang:latest'
|
||||||
|
run: |
|
||||||
|
echo "CXXFLAGS=-stdlib=libc++" >> "$GITHUB_ENV"
|
||||||
|
mkdir -p /usr/lib/share && ln -sf /usr/lib/llvm-*/share/libc++ /usr/lib/share/libc++
|
||||||
|
- name: Run CMake
|
||||||
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
|
- name: Build
|
||||||
|
run: cmake --build build --target ci_module_cpp20
|
||||||
|
|
||||||
ci_icpc:
|
ci_icpc:
|
||||||
if: *nightly_set
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
# Intel discontinued the classic icc/icpc compiler in oneAPI 2024.0; this is
|
# Intel discontinued the classic icc/icpc compiler in oneAPI 2024.0; this is
|
||||||
# Intel's own last officially published image that still includes it.
|
# Intel's own last officially published image that still includes it.
|
||||||
@@ -574,7 +402,6 @@ jobs:
|
|||||||
run: cmake --build build --target ci_icpc
|
run: cmake --build build --target ci_icpc
|
||||||
|
|
||||||
ci_icpx:
|
ci_icpx:
|
||||||
if: *nightly_set
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container: intel/oneapi-hpckit:latest
|
container: intel/oneapi-hpckit:latest
|
||||||
steps:
|
steps:
|
||||||
@@ -589,7 +416,6 @@ jobs:
|
|||||||
run: cmake --build build --target ci_icpx
|
run: cmake --build build --target ci_icpx
|
||||||
|
|
||||||
ci_nvhpc:
|
ci_nvhpc:
|
||||||
if: *nightly_set
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container: nvcr.io/nvidia/nvhpc:25.5-devel-cuda12.9-ubuntu22.04
|
container: nvcr.io/nvidia/nvhpc:25.5-devel-cuda12.9-ubuntu22.04
|
||||||
steps:
|
steps:
|
||||||
@@ -603,10 +429,54 @@ jobs:
|
|||||||
- name: Build
|
- name: Build
|
||||||
run: cmake --build build --target ci_nvhpc
|
run: cmake --build build --target ci_nvhpc
|
||||||
|
|
||||||
|
ci_emscripten:
|
||||||
|
runs-on: ubuntu-26.04
|
||||||
|
steps:
|
||||||
|
- name: Harden Runner
|
||||||
|
uses: step-security/harden-runner@dc2769abcc0fcfbadfc1df54081c1608579ef6b2 # v2.22.2
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- name: Install emscripten
|
||||||
|
uses: mymindstorm/setup-emsdk@4528d102f7230f0e7b276855c01ea1159be0e984 # v16
|
||||||
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
- name: Get latest CMake and ninja
|
||||||
|
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
||||||
|
- name: Run CMake
|
||||||
|
run: cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE=$EMSDK/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake -GNinja
|
||||||
|
- name: Build
|
||||||
|
run: cmake --build build
|
||||||
|
|
||||||
|
ci_test_documentation:
|
||||||
|
runs-on: ubuntu-26.04
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
target: [ci_test_examples, ci_test_build_documentation, ci_test_documentation_mermaid]
|
||||||
|
steps:
|
||||||
|
- name: Harden Runner
|
||||||
|
uses: step-security/harden-runner@dc2769abcc0fcfbadfc1df54081c1608579ef6b2 # v2.22.2
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
# the git-revision-date-localized plugin needs the history; a shallow clone makes the strict build fail
|
||||||
|
fetch-depth: ${{ matrix.target == 'ci_test_build_documentation' && '0' || '1' }}
|
||||||
|
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
|
||||||
|
if: matrix.target == 'ci_test_documentation_mermaid'
|
||||||
|
with:
|
||||||
|
node-version: 24
|
||||||
|
- name: Run CMake
|
||||||
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
|
- name: Build
|
||||||
|
run: cmake --build build --target ${{ matrix.target }}
|
||||||
|
|
||||||
# Native Linux ARM64 runner with the latest GCC. The ubuntu-24.04-arm label is
|
# Native Linux ARM64 runner with the latest GCC. The ubuntu-24.04-arm label is
|
||||||
# only available for public repositories.
|
# only available for public repositories.
|
||||||
ci_test_arm64:
|
ci_test_arm64:
|
||||||
if: *nightly_set
|
|
||||||
runs-on: ubuntu-24.04-arm
|
runs-on: ubuntu-24.04-arm
|
||||||
container: gcc:latest
|
container: gcc:latest
|
||||||
steps:
|
steps:
|
||||||
@@ -621,33 +491,3 @@ jobs:
|
|||||||
run: cmake -S . -B build -DJSON_CI=On
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
- name: Build
|
- name: Build
|
||||||
run: cmake --build build --target ci_test_compiler_default
|
run: cmake --build build --target ci_test_compiler_default
|
||||||
|
|
||||||
# A failure of a scheduled run opens an issue, or comments on the open one.
|
|
||||||
nightly_report:
|
|
||||||
needs: [ci_infer, ci_static_analysis_ubuntu_nightly, ci_test_coverage, ci_cuda_example, ci_icpc, ci_icpx, ci_nvhpc, ci_test_arm64]
|
|
||||||
if: ${{ always() && github.event_name == 'schedule' && contains(needs.*.result, 'failure') }}
|
|
||||||
runs-on: ubuntu-26.04
|
|
||||||
permissions:
|
|
||||||
issues: write
|
|
||||||
steps:
|
|
||||||
- name: Harden Runner
|
|
||||||
uses: step-security/harden-runner@dc2769abcc0fcfbadfc1df54081c1608579ef6b2 # v2.22.2
|
|
||||||
with:
|
|
||||||
egress-policy: audit
|
|
||||||
|
|
||||||
- name: Open or update the nightly failure issue
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ github.token }}
|
|
||||||
GH_REPO: ${{ github.repository }}
|
|
||||||
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
|
||||||
WORKFLOW: ${{ github.workflow }}
|
|
||||||
SHA: ${{ github.sha }}
|
|
||||||
run: |
|
|
||||||
title="Nightly CI failure (${WORKFLOW})"
|
|
||||||
body="The nightly run of ${WORKFLOW} on ${SHA:0:10} failed: ${RUN_URL}"
|
|
||||||
number=$(gh issue list --state open --search "in:title \"${title}\"" --json number,title --jq "map(select(.title == \"${title}\")) | .[0].number // empty")
|
|
||||||
if [ -n "$number" ]; then
|
|
||||||
gh issue comment "$number" --body "$body"
|
|
||||||
else
|
|
||||||
gh issue create --title "$title" --body "$body"
|
|
||||||
fi
|
|
||||||
@@ -7,25 +7,6 @@ on:
|
|||||||
- master
|
- master
|
||||||
- release/*
|
- release/*
|
||||||
pull_request:
|
pull_request:
|
||||||
# Skip pull requests that change only files no job here reads. The list is checked by
|
|
||||||
# tools/check_path_filters/check_path_filters.py (repository_checks.yml); change both together.
|
|
||||||
paths:
|
|
||||||
- '**'
|
|
||||||
- '!docs/**'
|
|
||||||
- '!**.md'
|
|
||||||
- '!.github/*'
|
|
||||||
- '!.github/ISSUE_TEMPLATE/**'
|
|
||||||
- '!.github/external_ci/**'
|
|
||||||
- '!.github/workflows/**'
|
|
||||||
- '!.reuse/**'
|
|
||||||
- '!LICENSES/**'
|
|
||||||
- '!CITATION.cff'
|
|
||||||
- '!REUSE.toml'
|
|
||||||
# read by tools/check_build_options in the ci_meson_install job (ubuntu.yml)
|
|
||||||
- 'docs/mkdocs/docs/integration/package_managers.md'
|
|
||||||
# built by the swiftpm job (macos.yml)
|
|
||||||
- 'docs/mkdocs/docs/integration/swift/**'
|
|
||||||
- '.github/workflows/windows.yml'
|
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
|
|||||||
@@ -13,72 +13,6 @@ describe each of them.
|
|||||||
|
|
||||||
<p class="nj-caption" markdown>The checks grouped by the stage at which they run, from push or pull request through the merge into develop to a release. The badge on each card says whether the check runs on every push or PR, on a schedule, or continuously.</p>
|
<p class="nj-caption" markdown>The checks grouped by the stage at which they run, from push or pull request through the merge into develop to a release. The badge on each card says whether the check runs on every push or PR, on a schedule, or continuously.</p>
|
||||||
|
|
||||||
## How CI runs
|
|
||||||
|
|
||||||
Not every job runs for every change. The compiler jobs on Ubuntu are split into three sets, so a pull request gets fast
|
|
||||||
feedback and the long jobs run when they matter:
|
|
||||||
|
|
||||||
| Set | When it runs | What it contains |
|
|
||||||
|-------------|-------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
||||||
| PR set | every push to a pull request, and every push to `develop`, `master`, and `release/*` | GCC and Clang with all warnings, Clang-Tidy, sanitizers, IWYU, cpplint, the oldest and newest GCC and Clang, common CMake options, Meson, Emscripten; with the checks that always run, they would have caught at least 92 % of the failing commits of September and October 2026 |
|
|
||||||
| full set | after the PR set passed: on pull requests with the label :rocket: ready to merge, and on every push | every other compiler, version, C++ standard, and CMake option, the Clang Static Analyzer, Cppcheck, and the C++20 module tests |
|
|
||||||
| nightly set | every night on `develop`, on every push to `master` and `release/*`, and when started by hand | Infer, Valgrind, coverage, the Intel and NVIDIA compilers, CUDA, and ARM64; a failure opens an issue |
|
|
||||||
|
|
||||||
The jobs on macOS and Windows, the documentation and license checks, and the amalgamation check run on every push and
|
|
||||||
pull request. A pull request that changes only the documentation, Markdown files, GitHub metadata, or license files
|
|
||||||
skips the compiler workflows (see [`tools/check_path_filters`](https://github.com/nlohmann/json/tree/develop/tools/check_path_filters)).
|
|
||||||
|
|
||||||
A maintainer adds the label :rocket: ready to merge when a pull request is ready; the full set then runs, and again on
|
|
||||||
every later push to that pull request. A pull request is merged only when the full set passed.
|
|
||||||
|
|
||||||
??? abstract "Jobs of the compiler workflows and their sets"
|
|
||||||
|
|
||||||
The table is generated by [`tools/ci_jobs`](https://github.com/nlohmann/json/tree/develop/tools/ci_jobs), which
|
|
||||||
also checks in CI that it is up to date and that every job belongs to exactly one set.
|
|
||||||
|
|
||||||
<!-- ci_jobs: begin -->
|
|
||||||
| Set | Workflow | Job | Configurations |
|
|
||||||
|-----|----------|-----|----------------|
|
|
||||||
| PR set | ubuntu.yml | `ci_test_gcc` | - |
|
|
||||||
| PR set | ubuntu.yml | `ci_meson_install` | - |
|
|
||||||
| PR set | ubuntu.yml | `ci_static_analysis_ubuntu` | ci_cpplint |
|
|
||||||
| PR set | ubuntu.yml | `ci_static_analysis_clang` | ci_test_clang; ci_clang_tidy; ci_test_clang_sanitizer; ci_single_binaries |
|
|
||||||
| PR set | ubuntu.yml | `ci_cmake_options` | ci_test_diagnostics; ci_test_diagnostic_positions; ci_test_noexceptions; ci_test_noglobaludls |
|
|
||||||
| PR set | ubuntu.yml | `ci_test_compilers_gcc_old` | 4.8 |
|
|
||||||
| PR set | ubuntu.yml | `ci_test_compilers_clang` | 3.4 |
|
|
||||||
| PR set | ubuntu.yml | `ci_test_standards_gcc` | 26 |
|
|
||||||
| PR set | ubuntu.yml | `ci_test_standards_clang` | 26, libcxx |
|
|
||||||
| PR set | ubuntu.yml | `ci_emscripten` | - |
|
|
||||||
| full set | ubuntu.yml | `ci_static_analysis_ubuntu_full` | ci_test_single_header; ci_cppcheck; ci_reproducible_tests; ci_non_git_tests; ci_offline_testdata |
|
|
||||||
| full set | ubuntu.yml | `ci_static_analysis_clang_full` | ci_clang_analyze |
|
|
||||||
| full set | ubuntu.yml | `ci_cmake_options_full` | ci_cmake_flags; ci_test_noimplicitconversions; ci_test_legacycomparison; ci_test_disableenumserialization; ci_test_disabletuplereferenceconversion; ci_test_skiplibraryversioncheck; ci_test_simdutf; ci_test_strict_nul_handling; ci_test_delete_deprecated_functions; ci_test_no_thread_local |
|
|
||||||
| full set | ubuntu.yml | `ci_test_compilers_gcc_old_full` | 4.9; 5; 6 |
|
|
||||||
| full set | ubuntu.yml | `ci_test_compilers_gcc_full` | 7; 8; 9; 10; 11; 12; 13; 14; 15; 16; latest |
|
|
||||||
| full set | ubuntu.yml | `ci_test_compilers_clang_full` | 3.5; 3.6; 3.7; 3.8; 3.9; 4; 5; 6; 7; 8; 9; 10; 11; 12; 13; 14; 15-bullseye; 16; 17; 18; 19; 20; 21; 22; latest |
|
|
||||||
| full set | ubuntu.yml | `ci_test_standards_gcc_full` | 11; 14; 17; 20; 23 |
|
|
||||||
| full set | ubuntu.yml | `ci_test_standards_clang_full` | 11, libcxx; 11, libstdcxx; 14, libcxx; 14, libstdcxx; 17, libcxx; 17, libstdcxx; 20, libcxx; 20, libstdcxx; 23, libcxx; 23, libstdcxx; 26, libstdcxx |
|
|
||||||
| full set | ubuntu.yml | `ci_module_cpp20` | gcc:latest; silkeh/clang:latest |
|
|
||||||
| nightly set | ubuntu.yml | `ci_infer` | - |
|
|
||||||
| nightly set | ubuntu.yml | `ci_static_analysis_ubuntu_nightly` | ci_test_valgrind |
|
|
||||||
| nightly set | ubuntu.yml | `ci_test_coverage` | - |
|
|
||||||
| nightly set | ubuntu.yml | `ci_cuda_example` | 11.8.0; 12.1.1; 12.6.3 |
|
|
||||||
| nightly set | ubuntu.yml | `ci_icpc` | - |
|
|
||||||
| nightly set | ubuntu.yml | `ci_icpx` | - |
|
|
||||||
| nightly set | ubuntu.yml | `ci_nvhpc` | - |
|
|
||||||
| nightly set | ubuntu.yml | `ci_test_arm64` | - |
|
|
||||||
| every push and PR | macos.yml | `macos-15` | 16.0; 16.1; 16.2; 16.3; 16.4; 26.0.1; 26.1.1; 26.2; 26.3 |
|
|
||||||
| every push and PR | macos.yml | `macos-26` | 26.4.1; 26.5; 26.6 |
|
|
||||||
| every push and PR | macos.yml | `xcode_standards` | 11; 14; 17; 20; 23; 26 |
|
|
||||||
| every push and PR | macos.yml | `swiftpm` | - |
|
|
||||||
| every push and PR | windows.yml | `mingw` | x64; x86 |
|
|
||||||
| every push and PR | windows.yml | `msvc` | Debug, Win32, default; Debug, Win32, latest; Debug, x64, default; Debug, x64, latest; Release, Win32, default; Release, Win32, latest; Release, x64, default; Release, x64, latest |
|
|
||||||
| every push and PR | windows.yml | `msvc-vs2026` | Debug, Win32, default; Debug, Win32, latest; Debug, x64, default; Debug, x64, latest; Release, Win32, default; Release, Win32, latest; Release, x64, default; Release, x64, latest |
|
|
||||||
| every push and PR | windows.yml | `msvc-arm64` | Debug; Release |
|
|
||||||
| every push and PR | windows.yml | `clang` | 11.0.1; 12.0.1; 13.0.1; 14.0.6; 15.0.7; 16.0.6; 18.1.8; 19.1.7; 20.1.8 |
|
|
||||||
| every push and PR | windows.yml | `clang-cl` | Win32; x64 |
|
|
||||||
| every push and PR | windows.yml | `ci_module_cpp20` | - |
|
|
||||||
<!-- ci_jobs: end -->
|
|
||||||
|
|
||||||
## C++ language compliance and compiler compatibility
|
## C++ language compliance and compiler compatibility
|
||||||
|
|
||||||
!!! success "Requirement: Compiler support"
|
!!! success "Requirement: Compiler support"
|
||||||
|
|||||||
@@ -1,31 +1,26 @@
|
|||||||
<svg class="nj-fig nj-exceptions-qa nj-exceptions-qa--wide" viewBox="0 0 760 862" role="img" aria-labelledby="nj-qa-title-w nj-qa-desc-w" xmlns="http://www.w3.org/2000/svg">
|
<svg class="nj-fig nj-exceptions-qa nj-exceptions-qa--wide" viewBox="0 0 760 808" role="img" aria-labelledby="nj-qa-title-w nj-qa-desc-w" xmlns="http://www.w3.org/2000/svg">
|
||||||
<title id="nj-qa-title-w">Quality assurance pipeline</title>
|
<title id="nj-qa-title-w">Quality assurance pipeline</title>
|
||||||
<desc id="nj-qa-desc-w">A push or pull request starts these checks; the badges say when each runs: Build matrix (every push / PR, before merge): 50+ compilers: G​CC, Clang, M​SVC, AppleClang, Intel, NVIDIA, ... C++11, 14, 17, 20, 23, and 26 libc++ and libstdc++ warnings: Clang -Weverything, G​CC 300+ warnings a subset on every push and PR, all before merge; Intel, NVIDIA, C​UDA, and ARM64 nightly; Unit tests (doctest) (every push / PR): every public A​PI function every exception id and message all Unicode code points single header and split headers custom number, string, array, and object types; Coverage (nightly): unit tests cover all lines of the code base; Static analysis (every push / PR, scheduled): Clang-Tidy, Cppcheck, Infer Clang Static Analyzer (52 checkers), I​WYU, Codacy CodeQL, Semgrep, Flawfinder (also scheduled); Infer nightly; Dynamic analysis (every push / PR, nightly): runtime assertions Valgrind (Memcheck), nightly address and undefined behavior sanitizers, integer overflow, nullability violations; Fuzzing (every PR, continuous): CIFuzz on every pull request OSS-Fuzz: billions of inputs every crash fixed with a reproducing unit test; Style (every push / PR): Artistic Style (astyle), enforced in the C​I cpplint with 61 rules; Docs and examples (every push / PR, scheduled): every documentation example is tested; changed output fails site built with warnings as errors; links checked regularly (scheduled); Integration, supply chain (every push / PR, scheduled): CMake 3.5, 3.31.6, and 4.0.0 Meson, Swift Package Manager dependency review on every PR Dependabot daily, OpenSSF Scorecard, R​EUSE. When they pass, the change is merged into develop; a release is made only when no vulnerability alert is open.</desc>
|
<desc id="nj-qa-desc-w">Every push or pull request starts these checks in parallel: Build matrix (every push / PR): 50+ compilers: G​CC, Clang, M​SVC, AppleClang, Intel, NVIDIA, ... C++11, 14, 17, 20, 23, and 26 libc++ and libstdc++ warnings: Clang -Weverything, G​CC 300+ warnings; Unit tests (doctest) (every push / PR): every public A​PI function every exception id and message all Unicode code points single header and split headers custom number, string, array, and object types; Coverage (every push / PR): unit tests cover all lines of the code base; Static analysis (every push / PR, scheduled): Clang-Tidy, Cppcheck, Infer Clang Static Analyzer (52 checkers), I​WYU, Codacy CodeQL, Semgrep, Flawfinder (also scheduled); Dynamic analysis (every push / PR): runtime assertions Valgrind (Memcheck) address and undefined behavior sanitizers, integer overflow, nullability violations; Fuzzing (every PR, continuous): CIFuzz on every pull request OSS-Fuzz: billions of inputs every crash fixed with a reproducing unit test; Style (every push / PR): Artistic Style (astyle), enforced in the C​I cpplint with 61 rules; Docs and examples (every push / PR, scheduled): every documentation example is tested; changed output fails site built with warnings as errors; links checked regularly (scheduled); Integration, supply chain (every push / PR, scheduled): CMake 3.5, 3.31.6, and 4.0.0 Meson, Swift Package Manager dependency review on every PR Dependabot daily, OpenSSF Scorecard, R​EUSE. When they pass, the change is merged into develop; a release is made only when no vulnerability alert is open.</desc>
|
||||||
<defs><marker id="nj-qa-arrow-w" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto"><path class="fg-head" d="M0 1 L10 5 L0 9 z"/></marker></defs>
|
<defs><marker id="nj-qa-arrow-w" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto"><path class="fg-head" d="M0 1 L10 5 L0 9 z"/></marker></defs>
|
||||||
<rect class="nj-exceptions-groupbox" x="12" y="70" width="736" height="648" rx="12"/>
|
<rect class="nj-exceptions-groupbox" x="12" y="70" width="736" height="594" rx="12"/>
|
||||||
<rect class="nj-exceptions-stage" x="265" y="8" width="230" height="34" rx="17"/>
|
<rect class="nj-exceptions-stage" x="265" y="8" width="230" height="34" rx="17"/>
|
||||||
<text class="nj-exceptions-stagetext" x="380" y="30">push or pull request</text>
|
<text class="nj-exceptions-stagetext" x="380" y="30">push or pull request</text>
|
||||||
<line class="nj-exceptions-edge" x1="380" y1="42" x2="380" y2="68" marker-end="url(#nj-qa-arrow-w)"/>
|
<line class="nj-exceptions-edge" x1="380" y1="42" x2="380" y2="68" marker-end="url(#nj-qa-arrow-w)"/>
|
||||||
<text class="nj-exceptions-group" x="26" y="94">checks, run in parallel on GitHub Actions and AppVeyor</text>
|
<text class="nj-exceptions-group" x="26" y="94">checks, run in parallel on GitHub Actions and AppVeyor</text>
|
||||||
<g class="nj-exceptions-card">
|
<g class="nj-exceptions-card">
|
||||||
<rect class="nj-exceptions-box" x="26" y="110" width="228" height="238" rx="8"/>
|
<rect class="nj-exceptions-box" x="26" y="110" width="228" height="184" rx="8"/>
|
||||||
<text class="nj-exceptions-ctitle" x="40" y="135">Build matrix</text>
|
<text class="nj-exceptions-ctitle" x="40" y="135">Build matrix</text>
|
||||||
<rect class="nj-exceptions-badge nj-exceptions-badge--push" x="40" y="146" width="110.5" height="20" rx="10"/>
|
<rect class="nj-exceptions-badge nj-exceptions-badge--push" x="40" y="146" width="110.5" height="20" rx="10"/>
|
||||||
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--push" x="95.25" y="160">every push / PR</text>
|
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--push" x="95.25" y="160">every push / PR</text>
|
||||||
<rect class="nj-exceptions-badge nj-exceptions-badge--push" x="156.5" y="146" width="91.6" height="20" rx="10"/>
|
|
||||||
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--push" x="202.3" y="160">before merge</text>
|
|
||||||
<text class="nj-exceptions-line" x="40" y="188">50+ compilers: G​CC, Clang, M​SVC,</text>
|
<text class="nj-exceptions-line" x="40" y="188">50+ compilers: G​CC, Clang, M​SVC,</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="206">AppleClang, Intel, NVIDIA, ...</text>
|
<text class="nj-exceptions-line" x="40" y="206">AppleClang, Intel, NVIDIA, ...</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="224">C++11, 14, 17, 20, 23, and 26</text>
|
<text class="nj-exceptions-line" x="40" y="224">C++11, 14, 17, 20, 23, and 26</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="242">libc++ and libstdc++</text>
|
<text class="nj-exceptions-line" x="40" y="242">libc++ and libstdc++</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="260">warnings: Clang -Weverything,</text>
|
<text class="nj-exceptions-line" x="40" y="260">warnings: Clang -Weverything,</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="278">G​CC 300+ warnings</text>
|
<text class="nj-exceptions-line" x="40" y="278">G​CC 300+ warnings</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="296">a subset on every push and PR,</text>
|
|
||||||
<text class="nj-exceptions-line" x="40" y="314">all before merge; Intel, NVIDIA,</text>
|
|
||||||
<text class="nj-exceptions-line" x="40" y="332">C​UDA, and ARM64 nightly</text>
|
|
||||||
</g>
|
</g>
|
||||||
<g class="nj-exceptions-card">
|
<g class="nj-exceptions-card">
|
||||||
<rect class="nj-exceptions-box" x="266" y="110" width="228" height="238" rx="8"/>
|
<rect class="nj-exceptions-box" x="266" y="110" width="228" height="184" rx="8"/>
|
||||||
<text class="nj-exceptions-ctitle" x="280" y="135">Unit tests (doctest)</text>
|
<text class="nj-exceptions-ctitle" x="280" y="135">Unit tests (doctest)</text>
|
||||||
<rect class="nj-exceptions-badge nj-exceptions-badge--push" x="280" y="146" width="110.5" height="20" rx="10"/>
|
<rect class="nj-exceptions-badge nj-exceptions-badge--push" x="280" y="146" width="110.5" height="20" rx="10"/>
|
||||||
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--push" x="335.25" y="160">every push / PR</text>
|
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--push" x="335.25" y="160">every push / PR</text>
|
||||||
@@ -37,218 +32,209 @@
|
|||||||
<text class="nj-exceptions-line" x="280" y="278">and object types</text>
|
<text class="nj-exceptions-line" x="280" y="278">and object types</text>
|
||||||
</g>
|
</g>
|
||||||
<g class="nj-exceptions-card">
|
<g class="nj-exceptions-card">
|
||||||
<rect class="nj-exceptions-box" x="506" y="110" width="228" height="238" rx="8"/>
|
<rect class="nj-exceptions-box" x="506" y="110" width="228" height="184" rx="8"/>
|
||||||
<text class="nj-exceptions-ctitle" x="520" y="135">Coverage</text>
|
<text class="nj-exceptions-ctitle" x="520" y="135">Coverage</text>
|
||||||
<rect class="nj-exceptions-badge nj-exceptions-badge--sched" x="520" y="146" width="60.1" height="20" rx="10"/>
|
<rect class="nj-exceptions-badge nj-exceptions-badge--push" x="520" y="146" width="110.5" height="20" rx="10"/>
|
||||||
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--sched" x="550.05" y="160">nightly</text>
|
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--push" x="575.25" y="160">every push / PR</text>
|
||||||
<text class="nj-exceptions-line" x="520" y="188">unit tests cover all lines</text>
|
<text class="nj-exceptions-line" x="520" y="188">unit tests cover all lines</text>
|
||||||
<text class="nj-exceptions-line" x="520" y="206">of the code base</text>
|
<text class="nj-exceptions-line" x="520" y="206">of the code base</text>
|
||||||
</g>
|
</g>
|
||||||
<g class="nj-exceptions-card">
|
<g class="nj-exceptions-card">
|
||||||
<rect class="nj-exceptions-box" x="26" y="360" width="228" height="166" rx="8"/>
|
<rect class="nj-exceptions-box" x="26" y="306" width="228" height="166" rx="8"/>
|
||||||
<text class="nj-exceptions-ctitle" x="40" y="385">Static analysis</text>
|
<text class="nj-exceptions-ctitle" x="40" y="331">Static analysis</text>
|
||||||
<rect class="nj-exceptions-badge nj-exceptions-badge--push" x="40" y="396" width="110.5" height="20" rx="10"/>
|
<rect class="nj-exceptions-badge nj-exceptions-badge--push" x="40" y="342" width="110.5" height="20" rx="10"/>
|
||||||
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--push" x="95.25" y="410">every push / PR</text>
|
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--push" x="95.25" y="356">every push / PR</text>
|
||||||
<rect class="nj-exceptions-badge nj-exceptions-badge--sched" x="156.5" y="396" width="72.7" height="20" rx="10"/>
|
<rect class="nj-exceptions-badge nj-exceptions-badge--sched" x="156.5" y="342" width="72.7" height="20" rx="10"/>
|
||||||
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--sched" x="192.85" y="410">scheduled</text>
|
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--sched" x="192.85" y="356">scheduled</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="438">Clang-Tidy, Cppcheck, Infer</text>
|
<text class="nj-exceptions-line" x="40" y="384">Clang-Tidy, Cppcheck, Infer</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="456">Clang Static Analyzer (52</text>
|
<text class="nj-exceptions-line" x="40" y="402">Clang Static Analyzer (52</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="474">checkers), I​WYU, Codacy</text>
|
<text class="nj-exceptions-line" x="40" y="420">checkers), I​WYU, Codacy</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="492">CodeQL, Semgrep, Flawfinder</text>
|
<text class="nj-exceptions-line" x="40" y="438">CodeQL, Semgrep, Flawfinder</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="510">(also scheduled); Infer nightly</text>
|
<text class="nj-exceptions-line" x="40" y="456">(also scheduled)</text>
|
||||||
</g>
|
</g>
|
||||||
<g class="nj-exceptions-card">
|
<g class="nj-exceptions-card">
|
||||||
<rect class="nj-exceptions-box" x="266" y="360" width="228" height="166" rx="8"/>
|
<rect class="nj-exceptions-box" x="266" y="306" width="228" height="166" rx="8"/>
|
||||||
<text class="nj-exceptions-ctitle" x="280" y="385">Dynamic analysis</text>
|
<text class="nj-exceptions-ctitle" x="280" y="331">Dynamic analysis</text>
|
||||||
<rect class="nj-exceptions-badge nj-exceptions-badge--push" x="280" y="396" width="110.5" height="20" rx="10"/>
|
<rect class="nj-exceptions-badge nj-exceptions-badge--push" x="280" y="342" width="110.5" height="20" rx="10"/>
|
||||||
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--push" x="335.25" y="410">every push / PR</text>
|
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--push" x="335.25" y="356">every push / PR</text>
|
||||||
<rect class="nj-exceptions-badge nj-exceptions-badge--sched" x="396.5" y="396" width="60.1" height="20" rx="10"/>
|
<text class="nj-exceptions-line" x="280" y="384">runtime assertions</text>
|
||||||
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--sched" x="426.55" y="410">nightly</text>
|
<text class="nj-exceptions-line" x="280" y="402">Valgrind (Memcheck)</text>
|
||||||
<text class="nj-exceptions-line" x="280" y="438">runtime assertions</text>
|
<text class="nj-exceptions-line" x="280" y="420">address and undefined behavior</text>
|
||||||
<text class="nj-exceptions-line" x="280" y="456">Valgrind (Memcheck), nightly</text>
|
<text class="nj-exceptions-line" x="280" y="438">sanitizers, integer overflow,</text>
|
||||||
<text class="nj-exceptions-line" x="280" y="474">address and undefined behavior</text>
|
<text class="nj-exceptions-line" x="280" y="456">nullability violations</text>
|
||||||
<text class="nj-exceptions-line" x="280" y="492">sanitizers, integer overflow,</text>
|
|
||||||
<text class="nj-exceptions-line" x="280" y="510">nullability violations</text>
|
|
||||||
</g>
|
</g>
|
||||||
<g class="nj-exceptions-card">
|
<g class="nj-exceptions-card">
|
||||||
<rect class="nj-exceptions-box" x="506" y="360" width="228" height="166" rx="8"/>
|
<rect class="nj-exceptions-box" x="506" y="306" width="228" height="166" rx="8"/>
|
||||||
<text class="nj-exceptions-ctitle" x="520" y="385">Fuzzing</text>
|
<text class="nj-exceptions-ctitle" x="520" y="331">Fuzzing</text>
|
||||||
<rect class="nj-exceptions-badge nj-exceptions-badge--push" x="520" y="396" width="66.4" height="20" rx="10"/>
|
<rect class="nj-exceptions-badge nj-exceptions-badge--push" x="520" y="342" width="66.4" height="20" rx="10"/>
|
||||||
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--push" x="553.2" y="410">every PR</text>
|
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--push" x="553.2" y="356">every PR</text>
|
||||||
<rect class="nj-exceptions-badge nj-exceptions-badge--cont" x="592.4" y="396" width="79" height="20" rx="10"/>
|
<rect class="nj-exceptions-badge nj-exceptions-badge--cont" x="592.4" y="342" width="79" height="20" rx="10"/>
|
||||||
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--cont" x="631.9" y="410">continuous</text>
|
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--cont" x="631.9" y="356">continuous</text>
|
||||||
<text class="nj-exceptions-line" x="520" y="438">CIFuzz on every pull request</text>
|
<text class="nj-exceptions-line" x="520" y="384">CIFuzz on every pull request</text>
|
||||||
<text class="nj-exceptions-line" x="520" y="456">OSS-Fuzz: billions of inputs</text>
|
<text class="nj-exceptions-line" x="520" y="402">OSS-Fuzz: billions of inputs</text>
|
||||||
<text class="nj-exceptions-line" x="520" y="474">every crash fixed with a</text>
|
<text class="nj-exceptions-line" x="520" y="420">every crash fixed with a</text>
|
||||||
<text class="nj-exceptions-line" x="520" y="492">reproducing unit test</text>
|
<text class="nj-exceptions-line" x="520" y="438">reproducing unit test</text>
|
||||||
</g>
|
</g>
|
||||||
<g class="nj-exceptions-card">
|
<g class="nj-exceptions-card">
|
||||||
<rect class="nj-exceptions-box" x="26" y="538" width="228" height="166" rx="8"/>
|
<rect class="nj-exceptions-box" x="26" y="484" width="228" height="166" rx="8"/>
|
||||||
<text class="nj-exceptions-ctitle" x="40" y="563">Style</text>
|
<text class="nj-exceptions-ctitle" x="40" y="509">Style</text>
|
||||||
<rect class="nj-exceptions-badge nj-exceptions-badge--push" x="40" y="574" width="110.5" height="20" rx="10"/>
|
<rect class="nj-exceptions-badge nj-exceptions-badge--push" x="40" y="520" width="110.5" height="20" rx="10"/>
|
||||||
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--push" x="95.25" y="588">every push / PR</text>
|
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--push" x="95.25" y="534">every push / PR</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="616">Artistic Style (astyle),</text>
|
<text class="nj-exceptions-line" x="40" y="562">Artistic Style (astyle),</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="634">enforced in the C​I</text>
|
<text class="nj-exceptions-line" x="40" y="580">enforced in the C​I</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="652">cpplint with 61 rules</text>
|
<text class="nj-exceptions-line" x="40" y="598">cpplint with 61 rules</text>
|
||||||
</g>
|
</g>
|
||||||
<g class="nj-exceptions-card">
|
<g class="nj-exceptions-card">
|
||||||
<rect class="nj-exceptions-box" x="266" y="538" width="228" height="166" rx="8"/>
|
<rect class="nj-exceptions-box" x="266" y="484" width="228" height="166" rx="8"/>
|
||||||
<text class="nj-exceptions-ctitle" x="280" y="563">Docs and examples</text>
|
<text class="nj-exceptions-ctitle" x="280" y="509">Docs and examples</text>
|
||||||
<rect class="nj-exceptions-badge nj-exceptions-badge--push" x="280" y="574" width="110.5" height="20" rx="10"/>
|
<rect class="nj-exceptions-badge nj-exceptions-badge--push" x="280" y="520" width="110.5" height="20" rx="10"/>
|
||||||
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--push" x="335.25" y="588">every push / PR</text>
|
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--push" x="335.25" y="534">every push / PR</text>
|
||||||
<rect class="nj-exceptions-badge nj-exceptions-badge--sched" x="396.5" y="574" width="72.7" height="20" rx="10"/>
|
<rect class="nj-exceptions-badge nj-exceptions-badge--sched" x="396.5" y="520" width="72.7" height="20" rx="10"/>
|
||||||
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--sched" x="432.85" y="588">scheduled</text>
|
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--sched" x="432.85" y="534">scheduled</text>
|
||||||
<text class="nj-exceptions-line" x="280" y="616">every documentation example is</text>
|
<text class="nj-exceptions-line" x="280" y="562">every documentation example is</text>
|
||||||
<text class="nj-exceptions-line" x="280" y="634">tested; changed output fails</text>
|
<text class="nj-exceptions-line" x="280" y="580">tested; changed output fails</text>
|
||||||
<text class="nj-exceptions-line" x="280" y="652">site built with warnings</text>
|
<text class="nj-exceptions-line" x="280" y="598">site built with warnings</text>
|
||||||
<text class="nj-exceptions-line" x="280" y="670">as errors; links checked</text>
|
<text class="nj-exceptions-line" x="280" y="616">as errors; links checked</text>
|
||||||
<text class="nj-exceptions-line" x="280" y="688">regularly (scheduled)</text>
|
<text class="nj-exceptions-line" x="280" y="634">regularly (scheduled)</text>
|
||||||
</g>
|
</g>
|
||||||
<g class="nj-exceptions-card">
|
<g class="nj-exceptions-card">
|
||||||
<rect class="nj-exceptions-box" x="506" y="538" width="228" height="166" rx="8"/>
|
<rect class="nj-exceptions-box" x="506" y="484" width="228" height="166" rx="8"/>
|
||||||
<text class="nj-exceptions-ctitle" x="520" y="563">Integration, supply chain</text>
|
<text class="nj-exceptions-ctitle" x="520" y="509">Integration, supply chain</text>
|
||||||
<rect class="nj-exceptions-badge nj-exceptions-badge--push" x="520" y="574" width="110.5" height="20" rx="10"/>
|
<rect class="nj-exceptions-badge nj-exceptions-badge--push" x="520" y="520" width="110.5" height="20" rx="10"/>
|
||||||
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--push" x="575.25" y="588">every push / PR</text>
|
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--push" x="575.25" y="534">every push / PR</text>
|
||||||
<rect class="nj-exceptions-badge nj-exceptions-badge--sched" x="636.5" y="574" width="72.7" height="20" rx="10"/>
|
<rect class="nj-exceptions-badge nj-exceptions-badge--sched" x="636.5" y="520" width="72.7" height="20" rx="10"/>
|
||||||
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--sched" x="672.85" y="588">scheduled</text>
|
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--sched" x="672.85" y="534">scheduled</text>
|
||||||
<text class="nj-exceptions-line" x="520" y="616">CMake 3.5, 3.31.6, and 4.0.0</text>
|
<text class="nj-exceptions-line" x="520" y="562">CMake 3.5, 3.31.6, and 4.0.0</text>
|
||||||
<text class="nj-exceptions-line" x="520" y="634">Meson, Swift Package Manager</text>
|
<text class="nj-exceptions-line" x="520" y="580">Meson, Swift Package Manager</text>
|
||||||
<text class="nj-exceptions-line" x="520" y="652">dependency review on every PR</text>
|
<text class="nj-exceptions-line" x="520" y="598">dependency review on every PR</text>
|
||||||
<text class="nj-exceptions-line" x="520" y="670">Dependabot daily, OpenSSF</text>
|
<text class="nj-exceptions-line" x="520" y="616">Dependabot daily, OpenSSF</text>
|
||||||
<text class="nj-exceptions-line" x="520" y="688">Scorecard, R​EUSE</text>
|
<text class="nj-exceptions-line" x="520" y="634">Scorecard, R​EUSE</text>
|
||||||
</g>
|
</g>
|
||||||
<line class="nj-exceptions-edge" x1="380" y1="718" x2="380" y2="744" marker-end="url(#nj-qa-arrow-w)"/>
|
<line class="nj-exceptions-edge" x1="380" y1="664" x2="380" y2="690" marker-end="url(#nj-qa-arrow-w)"/>
|
||||||
<rect class="nj-exceptions-stage nj-exceptions-stage--merge" x="265" y="746" width="230" height="34" rx="17"/>
|
<rect class="nj-exceptions-stage nj-exceptions-stage--merge" x="265" y="692" width="230" height="34" rx="17"/>
|
||||||
<text class="nj-exceptions-stagetext" x="380" y="768">merge into develop</text>
|
<text class="nj-exceptions-stagetext" x="380" y="714">merge into develop</text>
|
||||||
<line class="nj-exceptions-edge" x1="380" y1="780" x2="380" y2="806" marker-end="url(#nj-qa-arrow-w)"/>
|
<line class="nj-exceptions-edge" x1="380" y1="726" x2="380" y2="752" marker-end="url(#nj-qa-arrow-w)"/>
|
||||||
<rect class="nj-exceptions-stage nj-exceptions-stage--merge" x="230" y="808" width="300" height="46" rx="23"/>
|
<rect class="nj-exceptions-stage nj-exceptions-stage--merge" x="230" y="754" width="300" height="46" rx="23"/>
|
||||||
<text class="nj-exceptions-stagetext" x="380" y="830">release</text>
|
<text class="nj-exceptions-stagetext" x="380" y="776">release</text>
|
||||||
<text class="nj-exceptions-stagesub" x="380" y="846">no open vulnerability alert</text>
|
<text class="nj-exceptions-stagesub" x="380" y="792">no open vulnerability alert</text>
|
||||||
</svg>
|
</svg>
|
||||||
<svg class="nj-fig nj-exceptions-qa nj-exceptions-qa--narrow" viewBox="0 0 360 1840" role="img" aria-labelledby="nj-qa-title-n nj-qa-desc-n" xmlns="http://www.w3.org/2000/svg">
|
<svg class="nj-fig nj-exceptions-qa nj-exceptions-qa--narrow" viewBox="0 0 360 1786" role="img" aria-labelledby="nj-qa-title-n nj-qa-desc-n" xmlns="http://www.w3.org/2000/svg">
|
||||||
<title id="nj-qa-title-n">Quality assurance pipeline</title>
|
<title id="nj-qa-title-n">Quality assurance pipeline</title>
|
||||||
<desc id="nj-qa-desc-n">A push or pull request starts these checks; the badges say when each runs: Build matrix (every push / PR, before merge): 50+ compilers: G​CC, Clang, M​SVC, AppleClang, Intel, NVIDIA, ... C++11, 14, 17, 20, 23, and 26 libc++ and libstdc++ warnings: Clang -Weverything, G​CC 300+ warnings a subset on every push and PR, all before merge; Intel, NVIDIA, C​UDA, and ARM64 nightly; Unit tests (doctest) (every push / PR): every public A​PI function every exception id and message all Unicode code points single header and split headers custom number, string, array, and object types; Coverage (nightly): unit tests cover all lines of the code base; Static analysis (every push / PR, scheduled): Clang-Tidy, Cppcheck, Infer Clang Static Analyzer (52 checkers), I​WYU, Codacy CodeQL, Semgrep, Flawfinder (also scheduled); Infer nightly; Dynamic analysis (every push / PR, nightly): runtime assertions Valgrind (Memcheck), nightly address and undefined behavior sanitizers, integer overflow, nullability violations; Fuzzing (every PR, continuous): CIFuzz on every pull request OSS-Fuzz: billions of inputs every crash fixed with a reproducing unit test; Style (every push / PR): Artistic Style (astyle), enforced in the C​I cpplint with 61 rules; Docs and examples (every push / PR, scheduled): every documentation example is tested; changed output fails site built with warnings as errors; links checked regularly (scheduled); Integration, supply chain (every push / PR, scheduled): CMake 3.5, 3.31.6, and 4.0.0 Meson, Swift Package Manager dependency review on every PR Dependabot daily, OpenSSF Scorecard, R​EUSE. When they pass, the change is merged into develop; a release is made only when no vulnerability alert is open.</desc>
|
<desc id="nj-qa-desc-n">Every push or pull request starts these checks in parallel: Build matrix (every push / PR): 50+ compilers: G​CC, Clang, M​SVC, AppleClang, Intel, NVIDIA, ... C++11, 14, 17, 20, 23, and 26 libc++ and libstdc++ warnings: Clang -Weverything, G​CC 300+ warnings; Unit tests (doctest) (every push / PR): every public A​PI function every exception id and message all Unicode code points single header and split headers custom number, string, array, and object types; Coverage (every push / PR): unit tests cover all lines of the code base; Static analysis (every push / PR, scheduled): Clang-Tidy, Cppcheck, Infer Clang Static Analyzer (52 checkers), I​WYU, Codacy CodeQL, Semgrep, Flawfinder (also scheduled); Dynamic analysis (every push / PR): runtime assertions Valgrind (Memcheck) address and undefined behavior sanitizers, integer overflow, nullability violations; Fuzzing (every PR, continuous): CIFuzz on every pull request OSS-Fuzz: billions of inputs every crash fixed with a reproducing unit test; Style (every push / PR): Artistic Style (astyle), enforced in the C​I cpplint with 61 rules; Docs and examples (every push / PR, scheduled): every documentation example is tested; changed output fails site built with warnings as errors; links checked regularly (scheduled); Integration, supply chain (every push / PR, scheduled): CMake 3.5, 3.31.6, and 4.0.0 Meson, Swift Package Manager dependency review on every PR Dependabot daily, OpenSSF Scorecard, R​EUSE. When they pass, the change is merged into develop; a release is made only when no vulnerability alert is open.</desc>
|
||||||
<defs><marker id="nj-qa-arrow-n" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto"><path class="fg-head" d="M0 1 L10 5 L0 9 z"/></marker></defs>
|
<defs><marker id="nj-qa-arrow-n" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto"><path class="fg-head" d="M0 1 L10 5 L0 9 z"/></marker></defs>
|
||||||
<rect class="nj-exceptions-groupbox" x="12" y="70" width="336" height="1626" rx="12"/>
|
<rect class="nj-exceptions-groupbox" x="12" y="70" width="336" height="1572" rx="12"/>
|
||||||
<rect class="nj-exceptions-stage" x="65" y="8" width="230" height="34" rx="17"/>
|
<rect class="nj-exceptions-stage" x="65" y="8" width="230" height="34" rx="17"/>
|
||||||
<text class="nj-exceptions-stagetext" x="180" y="30">push or pull request</text>
|
<text class="nj-exceptions-stagetext" x="180" y="30">push or pull request</text>
|
||||||
<line class="nj-exceptions-edge" x1="180" y1="42" x2="180" y2="68" marker-end="url(#nj-qa-arrow-n)"/>
|
<line class="nj-exceptions-edge" x1="180" y1="42" x2="180" y2="68" marker-end="url(#nj-qa-arrow-n)"/>
|
||||||
<text class="nj-exceptions-group" x="26" y="94">checks, run in parallel</text>
|
<text class="nj-exceptions-group" x="26" y="94">checks, run in parallel</text>
|
||||||
<g class="nj-exceptions-card">
|
<g class="nj-exceptions-card">
|
||||||
<rect class="nj-exceptions-box" x="26" y="110" width="308" height="238" rx="8"/>
|
<rect class="nj-exceptions-box" x="26" y="110" width="308" height="184" rx="8"/>
|
||||||
<text class="nj-exceptions-ctitle" x="40" y="135">Build matrix</text>
|
<text class="nj-exceptions-ctitle" x="40" y="135">Build matrix</text>
|
||||||
<rect class="nj-exceptions-badge nj-exceptions-badge--push" x="40" y="146" width="110.5" height="20" rx="10"/>
|
<rect class="nj-exceptions-badge nj-exceptions-badge--push" x="40" y="146" width="110.5" height="20" rx="10"/>
|
||||||
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--push" x="95.25" y="160">every push / PR</text>
|
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--push" x="95.25" y="160">every push / PR</text>
|
||||||
<rect class="nj-exceptions-badge nj-exceptions-badge--push" x="156.5" y="146" width="91.6" height="20" rx="10"/>
|
|
||||||
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--push" x="202.3" y="160">before merge</text>
|
|
||||||
<text class="nj-exceptions-line" x="40" y="188">50+ compilers: G​CC, Clang, M​SVC,</text>
|
<text class="nj-exceptions-line" x="40" y="188">50+ compilers: G​CC, Clang, M​SVC,</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="206">AppleClang, Intel, NVIDIA, ...</text>
|
<text class="nj-exceptions-line" x="40" y="206">AppleClang, Intel, NVIDIA, ...</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="224">C++11, 14, 17, 20, 23, and 26</text>
|
<text class="nj-exceptions-line" x="40" y="224">C++11, 14, 17, 20, 23, and 26</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="242">libc++ and libstdc++</text>
|
<text class="nj-exceptions-line" x="40" y="242">libc++ and libstdc++</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="260">warnings: Clang -Weverything,</text>
|
<text class="nj-exceptions-line" x="40" y="260">warnings: Clang -Weverything,</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="278">G​CC 300+ warnings</text>
|
<text class="nj-exceptions-line" x="40" y="278">G​CC 300+ warnings</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="296">a subset on every push and PR,</text>
|
|
||||||
<text class="nj-exceptions-line" x="40" y="314">all before merge; Intel, NVIDIA,</text>
|
|
||||||
<text class="nj-exceptions-line" x="40" y="332">C​UDA, and ARM64 nightly</text>
|
|
||||||
</g>
|
</g>
|
||||||
<g class="nj-exceptions-card">
|
<g class="nj-exceptions-card">
|
||||||
<rect class="nj-exceptions-box" x="26" y="360" width="308" height="184" rx="8"/>
|
<rect class="nj-exceptions-box" x="26" y="306" width="308" height="184" rx="8"/>
|
||||||
<text class="nj-exceptions-ctitle" x="40" y="385">Unit tests (doctest)</text>
|
<text class="nj-exceptions-ctitle" x="40" y="331">Unit tests (doctest)</text>
|
||||||
<rect class="nj-exceptions-badge nj-exceptions-badge--push" x="40" y="396" width="110.5" height="20" rx="10"/>
|
<rect class="nj-exceptions-badge nj-exceptions-badge--push" x="40" y="342" width="110.5" height="20" rx="10"/>
|
||||||
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--push" x="95.25" y="410">every push / PR</text>
|
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--push" x="95.25" y="356">every push / PR</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="438">every public A​PI function</text>
|
<text class="nj-exceptions-line" x="40" y="384">every public A​PI function</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="456">every exception id and message</text>
|
<text class="nj-exceptions-line" x="40" y="402">every exception id and message</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="474">all Unicode code points</text>
|
<text class="nj-exceptions-line" x="40" y="420">all Unicode code points</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="492">single header and split headers</text>
|
<text class="nj-exceptions-line" x="40" y="438">single header and split headers</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="510">custom number, string, array,</text>
|
<text class="nj-exceptions-line" x="40" y="456">custom number, string, array,</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="528">and object types</text>
|
<text class="nj-exceptions-line" x="40" y="474">and object types</text>
|
||||||
</g>
|
</g>
|
||||||
<g class="nj-exceptions-card">
|
<g class="nj-exceptions-card">
|
||||||
<rect class="nj-exceptions-box" x="26" y="556" width="308" height="112" rx="8"/>
|
<rect class="nj-exceptions-box" x="26" y="502" width="308" height="112" rx="8"/>
|
||||||
<text class="nj-exceptions-ctitle" x="40" y="581">Coverage</text>
|
<text class="nj-exceptions-ctitle" x="40" y="527">Coverage</text>
|
||||||
<rect class="nj-exceptions-badge nj-exceptions-badge--sched" x="40" y="592" width="60.1" height="20" rx="10"/>
|
<rect class="nj-exceptions-badge nj-exceptions-badge--push" x="40" y="538" width="110.5" height="20" rx="10"/>
|
||||||
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--sched" x="70.05" y="606">nightly</text>
|
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--push" x="95.25" y="552">every push / PR</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="634">unit tests cover all lines</text>
|
<text class="nj-exceptions-line" x="40" y="580">unit tests cover all lines</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="652">of the code base</text>
|
<text class="nj-exceptions-line" x="40" y="598">of the code base</text>
|
||||||
</g>
|
</g>
|
||||||
<g class="nj-exceptions-card">
|
<g class="nj-exceptions-card">
|
||||||
<rect class="nj-exceptions-box" x="26" y="680" width="308" height="166" rx="8"/>
|
<rect class="nj-exceptions-box" x="26" y="626" width="308" height="166" rx="8"/>
|
||||||
<text class="nj-exceptions-ctitle" x="40" y="705">Static analysis</text>
|
<text class="nj-exceptions-ctitle" x="40" y="651">Static analysis</text>
|
||||||
<rect class="nj-exceptions-badge nj-exceptions-badge--push" x="40" y="716" width="110.5" height="20" rx="10"/>
|
<rect class="nj-exceptions-badge nj-exceptions-badge--push" x="40" y="662" width="110.5" height="20" rx="10"/>
|
||||||
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--push" x="95.25" y="730">every push / PR</text>
|
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--push" x="95.25" y="676">every push / PR</text>
|
||||||
<rect class="nj-exceptions-badge nj-exceptions-badge--sched" x="156.5" y="716" width="72.7" height="20" rx="10"/>
|
<rect class="nj-exceptions-badge nj-exceptions-badge--sched" x="156.5" y="662" width="72.7" height="20" rx="10"/>
|
||||||
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--sched" x="192.85" y="730">scheduled</text>
|
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--sched" x="192.85" y="676">scheduled</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="758">Clang-Tidy, Cppcheck, Infer</text>
|
<text class="nj-exceptions-line" x="40" y="704">Clang-Tidy, Cppcheck, Infer</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="776">Clang Static Analyzer (52</text>
|
<text class="nj-exceptions-line" x="40" y="722">Clang Static Analyzer (52</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="794">checkers), I​WYU, Codacy</text>
|
<text class="nj-exceptions-line" x="40" y="740">checkers), I​WYU, Codacy</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="812">CodeQL, Semgrep, Flawfinder</text>
|
<text class="nj-exceptions-line" x="40" y="758">CodeQL, Semgrep, Flawfinder</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="830">(also scheduled); Infer nightly</text>
|
<text class="nj-exceptions-line" x="40" y="776">(also scheduled)</text>
|
||||||
</g>
|
</g>
|
||||||
<g class="nj-exceptions-card">
|
<g class="nj-exceptions-card">
|
||||||
<rect class="nj-exceptions-box" x="26" y="858" width="308" height="166" rx="8"/>
|
<rect class="nj-exceptions-box" x="26" y="804" width="308" height="166" rx="8"/>
|
||||||
<text class="nj-exceptions-ctitle" x="40" y="883">Dynamic analysis</text>
|
<text class="nj-exceptions-ctitle" x="40" y="829">Dynamic analysis</text>
|
||||||
<rect class="nj-exceptions-badge nj-exceptions-badge--push" x="40" y="894" width="110.5" height="20" rx="10"/>
|
<rect class="nj-exceptions-badge nj-exceptions-badge--push" x="40" y="840" width="110.5" height="20" rx="10"/>
|
||||||
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--push" x="95.25" y="908">every push / PR</text>
|
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--push" x="95.25" y="854">every push / PR</text>
|
||||||
<rect class="nj-exceptions-badge nj-exceptions-badge--sched" x="156.5" y="894" width="60.1" height="20" rx="10"/>
|
<text class="nj-exceptions-line" x="40" y="882">runtime assertions</text>
|
||||||
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--sched" x="186.55" y="908">nightly</text>
|
<text class="nj-exceptions-line" x="40" y="900">Valgrind (Memcheck)</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="936">runtime assertions</text>
|
<text class="nj-exceptions-line" x="40" y="918">address and undefined behavior</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="954">Valgrind (Memcheck), nightly</text>
|
<text class="nj-exceptions-line" x="40" y="936">sanitizers, integer overflow,</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="972">address and undefined behavior</text>
|
<text class="nj-exceptions-line" x="40" y="954">nullability violations</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="990">sanitizers, integer overflow,</text>
|
|
||||||
<text class="nj-exceptions-line" x="40" y="1008">nullability violations</text>
|
|
||||||
</g>
|
</g>
|
||||||
<g class="nj-exceptions-card">
|
<g class="nj-exceptions-card">
|
||||||
<rect class="nj-exceptions-box" x="26" y="1036" width="308" height="148" rx="8"/>
|
<rect class="nj-exceptions-box" x="26" y="982" width="308" height="148" rx="8"/>
|
||||||
<text class="nj-exceptions-ctitle" x="40" y="1061">Fuzzing</text>
|
<text class="nj-exceptions-ctitle" x="40" y="1007">Fuzzing</text>
|
||||||
<rect class="nj-exceptions-badge nj-exceptions-badge--push" x="40" y="1072" width="66.4" height="20" rx="10"/>
|
<rect class="nj-exceptions-badge nj-exceptions-badge--push" x="40" y="1018" width="66.4" height="20" rx="10"/>
|
||||||
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--push" x="73.2" y="1086">every PR</text>
|
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--push" x="73.2" y="1032">every PR</text>
|
||||||
<rect class="nj-exceptions-badge nj-exceptions-badge--cont" x="112.4" y="1072" width="79" height="20" rx="10"/>
|
<rect class="nj-exceptions-badge nj-exceptions-badge--cont" x="112.4" y="1018" width="79" height="20" rx="10"/>
|
||||||
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--cont" x="151.9" y="1086">continuous</text>
|
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--cont" x="151.9" y="1032">continuous</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="1114">CIFuzz on every pull request</text>
|
<text class="nj-exceptions-line" x="40" y="1060">CIFuzz on every pull request</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="1132">OSS-Fuzz: billions of inputs</text>
|
<text class="nj-exceptions-line" x="40" y="1078">OSS-Fuzz: billions of inputs</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="1150">every crash fixed with a</text>
|
<text class="nj-exceptions-line" x="40" y="1096">every crash fixed with a</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="1168">reproducing unit test</text>
|
<text class="nj-exceptions-line" x="40" y="1114">reproducing unit test</text>
|
||||||
</g>
|
</g>
|
||||||
<g class="nj-exceptions-card">
|
<g class="nj-exceptions-card">
|
||||||
<rect class="nj-exceptions-box" x="26" y="1196" width="308" height="130" rx="8"/>
|
<rect class="nj-exceptions-box" x="26" y="1142" width="308" height="130" rx="8"/>
|
||||||
<text class="nj-exceptions-ctitle" x="40" y="1221">Style</text>
|
<text class="nj-exceptions-ctitle" x="40" y="1167">Style</text>
|
||||||
<rect class="nj-exceptions-badge nj-exceptions-badge--push" x="40" y="1232" width="110.5" height="20" rx="10"/>
|
<rect class="nj-exceptions-badge nj-exceptions-badge--push" x="40" y="1178" width="110.5" height="20" rx="10"/>
|
||||||
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--push" x="95.25" y="1246">every push / PR</text>
|
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--push" x="95.25" y="1192">every push / PR</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="1274">Artistic Style (astyle),</text>
|
<text class="nj-exceptions-line" x="40" y="1220">Artistic Style (astyle),</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="1292">enforced in the C​I</text>
|
<text class="nj-exceptions-line" x="40" y="1238">enforced in the C​I</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="1310">cpplint with 61 rules</text>
|
<text class="nj-exceptions-line" x="40" y="1256">cpplint with 61 rules</text>
|
||||||
</g>
|
</g>
|
||||||
<g class="nj-exceptions-card">
|
<g class="nj-exceptions-card">
|
||||||
<rect class="nj-exceptions-box" x="26" y="1338" width="308" height="166" rx="8"/>
|
<rect class="nj-exceptions-box" x="26" y="1284" width="308" height="166" rx="8"/>
|
||||||
<text class="nj-exceptions-ctitle" x="40" y="1363">Docs and examples</text>
|
<text class="nj-exceptions-ctitle" x="40" y="1309">Docs and examples</text>
|
||||||
<rect class="nj-exceptions-badge nj-exceptions-badge--push" x="40" y="1374" width="110.5" height="20" rx="10"/>
|
<rect class="nj-exceptions-badge nj-exceptions-badge--push" x="40" y="1320" width="110.5" height="20" rx="10"/>
|
||||||
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--push" x="95.25" y="1388">every push / PR</text>
|
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--push" x="95.25" y="1334">every push / PR</text>
|
||||||
<rect class="nj-exceptions-badge nj-exceptions-badge--sched" x="156.5" y="1374" width="72.7" height="20" rx="10"/>
|
<rect class="nj-exceptions-badge nj-exceptions-badge--sched" x="156.5" y="1320" width="72.7" height="20" rx="10"/>
|
||||||
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--sched" x="192.85" y="1388">scheduled</text>
|
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--sched" x="192.85" y="1334">scheduled</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="1416">every documentation example is</text>
|
<text class="nj-exceptions-line" x="40" y="1362">every documentation example is</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="1434">tested; changed output fails</text>
|
<text class="nj-exceptions-line" x="40" y="1380">tested; changed output fails</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="1452">site built with warnings</text>
|
<text class="nj-exceptions-line" x="40" y="1398">site built with warnings</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="1470">as errors; links checked</text>
|
<text class="nj-exceptions-line" x="40" y="1416">as errors; links checked</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="1488">regularly (scheduled)</text>
|
<text class="nj-exceptions-line" x="40" y="1434">regularly (scheduled)</text>
|
||||||
</g>
|
</g>
|
||||||
<g class="nj-exceptions-card">
|
<g class="nj-exceptions-card">
|
||||||
<rect class="nj-exceptions-box" x="26" y="1516" width="308" height="166" rx="8"/>
|
<rect class="nj-exceptions-box" x="26" y="1462" width="308" height="166" rx="8"/>
|
||||||
<text class="nj-exceptions-ctitle" x="40" y="1541">Integration, supply chain</text>
|
<text class="nj-exceptions-ctitle" x="40" y="1487">Integration, supply chain</text>
|
||||||
<rect class="nj-exceptions-badge nj-exceptions-badge--push" x="40" y="1552" width="110.5" height="20" rx="10"/>
|
<rect class="nj-exceptions-badge nj-exceptions-badge--push" x="40" y="1498" width="110.5" height="20" rx="10"/>
|
||||||
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--push" x="95.25" y="1566">every push / PR</text>
|
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--push" x="95.25" y="1512">every push / PR</text>
|
||||||
<rect class="nj-exceptions-badge nj-exceptions-badge--sched" x="156.5" y="1552" width="72.7" height="20" rx="10"/>
|
<rect class="nj-exceptions-badge nj-exceptions-badge--sched" x="156.5" y="1498" width="72.7" height="20" rx="10"/>
|
||||||
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--sched" x="192.85" y="1566">scheduled</text>
|
<text class="nj-exceptions-badgetext nj-exceptions-badgetext--sched" x="192.85" y="1512">scheduled</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="1594">CMake 3.5, 3.31.6, and 4.0.0</text>
|
<text class="nj-exceptions-line" x="40" y="1540">CMake 3.5, 3.31.6, and 4.0.0</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="1612">Meson, Swift Package Manager</text>
|
<text class="nj-exceptions-line" x="40" y="1558">Meson, Swift Package Manager</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="1630">dependency review on every PR</text>
|
<text class="nj-exceptions-line" x="40" y="1576">dependency review on every PR</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="1648">Dependabot daily, OpenSSF</text>
|
<text class="nj-exceptions-line" x="40" y="1594">Dependabot daily, OpenSSF</text>
|
||||||
<text class="nj-exceptions-line" x="40" y="1666">Scorecard, R​EUSE</text>
|
<text class="nj-exceptions-line" x="40" y="1612">Scorecard, R​EUSE</text>
|
||||||
</g>
|
</g>
|
||||||
<line class="nj-exceptions-edge" x1="180" y1="1696" x2="180" y2="1722" marker-end="url(#nj-qa-arrow-n)"/>
|
<line class="nj-exceptions-edge" x1="180" y1="1642" x2="180" y2="1668" marker-end="url(#nj-qa-arrow-n)"/>
|
||||||
<rect class="nj-exceptions-stage nj-exceptions-stage--merge" x="65" y="1724" width="230" height="34" rx="17"/>
|
<rect class="nj-exceptions-stage nj-exceptions-stage--merge" x="65" y="1670" width="230" height="34" rx="17"/>
|
||||||
<text class="nj-exceptions-stagetext" x="180" y="1746">merge into develop</text>
|
<text class="nj-exceptions-stagetext" x="180" y="1692">merge into develop</text>
|
||||||
<line class="nj-exceptions-edge" x1="180" y1="1758" x2="180" y2="1784" marker-end="url(#nj-qa-arrow-n)"/>
|
<line class="nj-exceptions-edge" x1="180" y1="1704" x2="180" y2="1730" marker-end="url(#nj-qa-arrow-n)"/>
|
||||||
<rect class="nj-exceptions-stage nj-exceptions-stage--merge" x="30" y="1786" width="300" height="46" rx="23"/>
|
<rect class="nj-exceptions-stage nj-exceptions-stage--merge" x="30" y="1732" width="300" height="46" rx="23"/>
|
||||||
<text class="nj-exceptions-stagetext" x="180" y="1808">release</text>
|
<text class="nj-exceptions-stagetext" x="180" y="1754">release</text>
|
||||||
<text class="nj-exceptions-stagesub" x="180" y="1824">no open vulnerability alert</text>
|
<text class="nj-exceptions-stagesub" x="180" y="1770">no open vulnerability alert</text>
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 22 KiB |
@@ -22,24 +22,19 @@ P = "nj-exceptions-"
|
|||||||
TRIGGERS = {
|
TRIGGERS = {
|
||||||
"push": ("every push / PR", "push"),
|
"push": ("every push / PR", "push"),
|
||||||
"pr": ("every PR", "push"),
|
"pr": ("every PR", "push"),
|
||||||
"merge": ("before merge", "push"),
|
|
||||||
"nightly": ("nightly", "sched"),
|
|
||||||
"sched": ("scheduled", "sched"),
|
"sched": ("scheduled", "sched"),
|
||||||
"cont": ("continuous", "cont"),
|
"cont": ("continuous", "cont"),
|
||||||
}
|
}
|
||||||
|
|
||||||
# title, triggers, lines (at most 31 characters, so they fit a card of the three-column drawing)
|
# title, triggers, lines (at most 31 characters, so they fit a card of the three-column drawing)
|
||||||
LANES = [
|
LANES = [
|
||||||
("Build matrix", ["push", "merge"], [
|
("Build matrix", ["push"], [
|
||||||
"50+ compilers: GCC, Clang, MSVC,",
|
"50+ compilers: GCC, Clang, MSVC,",
|
||||||
"AppleClang, Intel, NVIDIA, ...",
|
"AppleClang, Intel, NVIDIA, ...",
|
||||||
"C++11, 14, 17, 20, 23, and 26",
|
"C++11, 14, 17, 20, 23, and 26",
|
||||||
"libc++ and libstdc++",
|
"libc++ and libstdc++",
|
||||||
"warnings: Clang -Weverything,",
|
"warnings: Clang -Weverything,",
|
||||||
"GCC 300+ warnings",
|
"GCC 300+ warnings",
|
||||||
"a subset on every push and PR,",
|
|
||||||
"all before merge; Intel, NVIDIA,",
|
|
||||||
"CUDA, and ARM64 nightly",
|
|
||||||
]),
|
]),
|
||||||
("Unit tests (doctest)", ["push"], [
|
("Unit tests (doctest)", ["push"], [
|
||||||
"every public API function",
|
"every public API function",
|
||||||
@@ -49,7 +44,7 @@ LANES = [
|
|||||||
"custom number, string, array,",
|
"custom number, string, array,",
|
||||||
"and object types",
|
"and object types",
|
||||||
]),
|
]),
|
||||||
("Coverage", ["nightly"], [
|
("Coverage", ["push"], [
|
||||||
"unit tests cover all lines",
|
"unit tests cover all lines",
|
||||||
"of the code base",
|
"of the code base",
|
||||||
]),
|
]),
|
||||||
@@ -58,11 +53,11 @@ LANES = [
|
|||||||
"Clang Static Analyzer (52",
|
"Clang Static Analyzer (52",
|
||||||
"checkers), IWYU, Codacy",
|
"checkers), IWYU, Codacy",
|
||||||
"CodeQL, Semgrep, Flawfinder",
|
"CodeQL, Semgrep, Flawfinder",
|
||||||
"(also scheduled); Infer nightly",
|
"(also scheduled)",
|
||||||
]),
|
]),
|
||||||
("Dynamic analysis", ["push", "nightly"], [
|
("Dynamic analysis", ["push"], [
|
||||||
"runtime assertions",
|
"runtime assertions",
|
||||||
"Valgrind (Memcheck), nightly",
|
"Valgrind (Memcheck)",
|
||||||
"address and undefined behavior",
|
"address and undefined behavior",
|
||||||
"sanitizers, integer overflow,",
|
"sanitizers, integer overflow,",
|
||||||
"nullability violations",
|
"nullability violations",
|
||||||
@@ -183,7 +178,7 @@ def drawing(W, cols, suffix):
|
|||||||
defs = ('<defs><marker id="%s" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto">'
|
defs = ('<defs><marker id="%s" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto">'
|
||||||
'<path class="fg-head" d="M0 1 L10 5 L0 9 z"/></marker></defs>' % mid)
|
'<path class="fg-head" d="M0 1 L10 5 L0 9 z"/></marker></defs>' % mid)
|
||||||
tid, did = "nj-qa-title-" + suffix, "nj-qa-desc-" + suffix
|
tid, did = "nj-qa-title-" + suffix, "nj-qa-desc-" + suffix
|
||||||
desc = ("A push or pull request starts these checks; the badges say when each runs: " +
|
desc = ("Every push or pull request starts these checks in parallel: " +
|
||||||
"; ".join("%s (%s): %s" % (t, ", ".join(TRIGGERS[k][0] for k in trig), " ".join(lines)) for t, trig, lines in LANES) +
|
"; ".join("%s (%s): %s" % (t, ", ".join(TRIGGERS[k][0] for k in trig), " ".join(lines)) for t, trig, lines in LANES) +
|
||||||
". When they pass, the change is merged into develop; a release is made only when no vulnerability alert is open.")
|
". When they pass, the change is merged into develop; a release is made only when no vulnerability alert is open.")
|
||||||
out = ['<svg class="nj-fig %sqa %sqa--%s" viewBox="0 0 %d %d" role="img" aria-labelledby="%s %s" xmlns="http://www.w3.org/2000/svg">'
|
out = ['<svg class="nj-fig %sqa %sqa--%s" viewBox="0 0 %d %d" role="img" aria-labelledby="%s %s" xmlns="http://www.w3.org/2000/svg">'
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
#include <bit> //byteswap
|
#include <bit> //byteswap
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <nlohmann/detail/bit_ops.hpp>
|
||||||
#include <nlohmann/detail/exceptions.hpp>
|
#include <nlohmann/detail/exceptions.hpp>
|
||||||
#include <nlohmann/detail/input/input_adapters.hpp>
|
#include <nlohmann/detail/input/input_adapters.hpp>
|
||||||
#include <nlohmann/detail/input/json_sax.hpp>
|
#include <nlohmann/detail/input/json_sax.hpp>
|
||||||
@@ -741,11 +742,13 @@ class binary_reader
|
|||||||
bool get_cbor_negative_integer()
|
bool get_cbor_negative_integer()
|
||||||
{
|
{
|
||||||
NumberType number{};
|
NumberType number{};
|
||||||
if (JSON_HEDLEY_UNLIKELY(!get_number(number)))
|
return get_number(number) && emit_cbor_negative_integer(number);
|
||||||
{
|
}
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/// pass the CBOR negative integer -1 - @a number to the SAX parser
|
||||||
|
template<typename NumberType>
|
||||||
|
bool emit_cbor_negative_integer(const NumberType number)
|
||||||
|
{
|
||||||
// the value is -1 - number, which fits into number_integer_t
|
// the value is -1 - number, which fits into number_integer_t
|
||||||
// whenever number does; the outer cast undoes the integral promotion
|
// whenever number does; the outer cast undoes the integral promotion
|
||||||
// for number_integer_t types narrower than int
|
// for number_integer_t types narrower than int
|
||||||
@@ -970,7 +973,7 @@ class binary_reader
|
|||||||
case 0x95:
|
case 0x95:
|
||||||
case 0x96:
|
case 0x96:
|
||||||
case 0x97:
|
case 0x97:
|
||||||
return enter_array(conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu));
|
return enter_cbor_array(conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu));
|
||||||
|
|
||||||
case 0x98: // array (one-byte uint8_t for n follows)
|
case 0x98: // array (one-byte uint8_t for n follows)
|
||||||
case 0x99: // array (two-byte uint16_t for n follow)
|
case 0x99: // array (two-byte uint16_t for n follow)
|
||||||
@@ -979,7 +982,7 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
std::uint64_t len{};
|
std::uint64_t len{};
|
||||||
std::size_t size{};
|
std::size_t size{};
|
||||||
return get_cbor_argument(len) && get_cbor_container_size(len, size, "array") && enter_array(size);
|
return get_cbor_argument(len) && get_cbor_container_size(len, size, "array") && enter_cbor_array(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x9F: // array (indefinite length)
|
case 0x9F: // array (indefinite length)
|
||||||
@@ -1133,16 +1136,10 @@ class binary_reader
|
|||||||
return get_half_float(false);
|
return get_half_float(false);
|
||||||
|
|
||||||
case 0xFA: // Single-Precision Float (four-byte IEEE 754)
|
case 0xFA: // Single-Precision Float (four-byte IEEE 754)
|
||||||
{
|
return get_cbor_float<float>();
|
||||||
float number{};
|
|
||||||
return get_number(number) && emit_float(number);
|
|
||||||
}
|
|
||||||
|
|
||||||
case 0xFB: // Double-Precision Float (eight-byte IEEE 754)
|
case 0xFB: // Double-Precision Float (eight-byte IEEE 754)
|
||||||
{
|
return get_cbor_float<double>();
|
||||||
double number{};
|
|
||||||
return get_number(number) && emit_float(number);
|
|
||||||
}
|
|
||||||
|
|
||||||
default: // anything else (0xFF is handled inside the other types)
|
default: // anything else (0xFF is handled inside the other types)
|
||||||
{
|
{
|
||||||
@@ -1151,6 +1148,16 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// read a single- or double-precision float (kept out of
|
||||||
|
/// parse_cbor_value, which is inlined into parse_cbor_internal)
|
||||||
|
template<typename NumberType>
|
||||||
|
JSON_HEDLEY_NEVER_INLINE
|
||||||
|
bool get_cbor_float()
|
||||||
|
{
|
||||||
|
NumberType number{};
|
||||||
|
return get_number(number) && emit_float(number);
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief reports a nested indefinite-length CBOR string or byte array
|
@brief reports a nested indefinite-length CBOR string or byte array
|
||||||
@param[in] type_name name of the rejected string type
|
@param[in] type_name name of the rejected string type
|
||||||
@@ -1676,6 +1683,157 @@ class binary_reader
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief read a big-endian number of type @a NumberType at @a data
|
||||||
|
*/
|
||||||
|
template<typename NumberType>
|
||||||
|
NumberType load_big_endian(const unsigned char* data) const noexcept
|
||||||
|
{
|
||||||
|
NumberType result{};
|
||||||
|
std::memcpy(&result, data, sizeof(NumberType));
|
||||||
|
if (is_little_endian)
|
||||||
|
{
|
||||||
|
byte_swap(result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief read the integers, floats, booleans, and nulls that follow in a
|
||||||
|
definite-length array, without the per-element bookkeeping of
|
||||||
|
@ref parse_cbor_internal
|
||||||
|
|
||||||
|
Stops at the end of the array, at any other kind of value, and when fewer
|
||||||
|
than 9 bytes (the longest of these values) are left in the input, leaving
|
||||||
|
those to @ref parse_cbor_value, which reads them the same way.
|
||||||
|
|
||||||
|
@param[in,out] remaining the number of elements of the array not yet read
|
||||||
|
@return whether the SAX parser accepted the values
|
||||||
|
*/
|
||||||
|
bool parse_cbor_scalar_run(std::size_t& remaining)
|
||||||
|
{
|
||||||
|
while (remaining != 0 && ia.bulk_remaining() >= 9)
|
||||||
|
{
|
||||||
|
const auto* const data = reinterpret_cast<const unsigned char*>(ia.bulk_data());
|
||||||
|
const unsigned char head = data[0];
|
||||||
|
std::size_t size = 0;
|
||||||
|
bool accepted = true;
|
||||||
|
switch (head)
|
||||||
|
{
|
||||||
|
case 0x18:
|
||||||
|
size = 2;
|
||||||
|
accepted = emit_unsigned(data[1]);
|
||||||
|
break;
|
||||||
|
case 0x19:
|
||||||
|
size = 3;
|
||||||
|
accepted = emit_unsigned(load_big_endian<std::uint16_t>(data + 1));
|
||||||
|
break;
|
||||||
|
case 0x1A:
|
||||||
|
size = 5;
|
||||||
|
accepted = emit_unsigned(load_big_endian<std::uint32_t>(data + 1));
|
||||||
|
break;
|
||||||
|
case 0x1B:
|
||||||
|
size = 9;
|
||||||
|
accepted = emit_unsigned(load_big_endian<std::uint64_t>(data + 1));
|
||||||
|
break;
|
||||||
|
case 0x38:
|
||||||
|
size = 2;
|
||||||
|
accepted = emit_cbor_negative_integer(data[1]);
|
||||||
|
break;
|
||||||
|
case 0x39:
|
||||||
|
size = 3;
|
||||||
|
accepted = emit_cbor_negative_integer(load_big_endian<std::uint16_t>(data + 1));
|
||||||
|
break;
|
||||||
|
case 0x3A:
|
||||||
|
size = 5;
|
||||||
|
accepted = emit_cbor_negative_integer(load_big_endian<std::uint32_t>(data + 1));
|
||||||
|
break;
|
||||||
|
case 0x3B:
|
||||||
|
size = 9;
|
||||||
|
accepted = emit_cbor_negative_integer(load_big_endian<std::uint64_t>(data + 1));
|
||||||
|
break;
|
||||||
|
case 0xF4:
|
||||||
|
size = 1;
|
||||||
|
accepted = sax->boolean(false);
|
||||||
|
break;
|
||||||
|
case 0xF5:
|
||||||
|
size = 1;
|
||||||
|
accepted = sax->boolean(true);
|
||||||
|
break;
|
||||||
|
case 0xF6:
|
||||||
|
size = 1;
|
||||||
|
accepted = sax->null();
|
||||||
|
break;
|
||||||
|
case 0xFA:
|
||||||
|
size = 5;
|
||||||
|
accepted = emit_float(load_big_endian<float>(data + 1));
|
||||||
|
break;
|
||||||
|
case 0xFB:
|
||||||
|
size = 9;
|
||||||
|
accepted = emit_float(load_big_endian<double>(data + 1));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (head <= 0x17)
|
||||||
|
{
|
||||||
|
size = 1;
|
||||||
|
accepted = sax->number_unsigned(static_cast<number_unsigned_t>(head));
|
||||||
|
}
|
||||||
|
else if (head >= 0x20 && head <= 0x37)
|
||||||
|
{
|
||||||
|
size = 1;
|
||||||
|
accepted = sax->number_integer(static_cast<std::int8_t>(0x20 - 1 - head));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// any other value is read by parse_cbor_value
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// as if read by get() and get_number(): current is the head byte
|
||||||
|
ia.bulk_skip(size);
|
||||||
|
chars_read += size;
|
||||||
|
current = head;
|
||||||
|
--remaining;
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(!accepted))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief open a definite-length array and read the scalars it begins with
|
||||||
|
(see @ref parse_cbor_scalar_run)
|
||||||
|
|
||||||
|
Only the start of an array is tried, which covers arrays of numbers
|
||||||
|
(e.g. coordinates) but costs arrays of other values only one test, not one
|
||||||
|
per element.
|
||||||
|
|
||||||
|
@param[in] len the number of elements
|
||||||
|
@return whether the SAX parser accepted the array and the values read
|
||||||
|
*/
|
||||||
|
bool enter_cbor_array(const std::size_t len)
|
||||||
|
{
|
||||||
|
return enter_array(len) && (len == 0 || parse_cbor_array_start(std::integral_constant<bool, bulk_scan> {}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// read the scalars the array just opened begins with; out of line, so
|
||||||
|
/// that @ref parse_cbor_value stays small enough to be inlined into
|
||||||
|
/// @ref parse_cbor_internal
|
||||||
|
JSON_HEDLEY_NEVER_INLINE
|
||||||
|
bool parse_cbor_array_start(std::true_type /*bulk*/)
|
||||||
|
{
|
||||||
|
return parse_cbor_scalar_run(container_stack.back().remaining);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// input that is not contiguous: every value is read by parse_cbor_value
|
||||||
|
bool parse_cbor_array_start(std::false_type /*bulk*/) const noexcept
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief read a CBOR value and everything nested inside it
|
@brief read a CBOR value and everything nested inside it
|
||||||
|
|
||||||
@@ -1705,6 +1863,7 @@ class binary_reader
|
|||||||
// pop_back() below, which destroys the container_stack element
|
// pop_back() below, which destroys the container_stack element
|
||||||
// it would otherwise alias
|
// it would otherwise alias
|
||||||
const container_frame top = container_stack.back();
|
const container_frame top = container_stack.back();
|
||||||
|
const bool is_object = top.is_object;
|
||||||
bool at_end = false;
|
bool at_end = false;
|
||||||
|
|
||||||
if (top.remaining != npos)
|
if (top.remaining != npos)
|
||||||
@@ -1716,7 +1875,7 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
// claim the element about to be read
|
// claim the element about to be read
|
||||||
--container_stack.back().remaining;
|
--container_stack.back().remaining;
|
||||||
if (top.is_object)
|
if (is_object)
|
||||||
{
|
{
|
||||||
get();
|
get();
|
||||||
}
|
}
|
||||||
@@ -1729,7 +1888,7 @@ class binary_reader
|
|||||||
// Testing for it consumes a byte, which is the first byte
|
// Testing for it consumes a byte, which is the first byte
|
||||||
// of the next element when it is not one.
|
// of the next element when it is not one.
|
||||||
at_end = (get() == 0xFF);
|
at_end = (get() == 0xFF);
|
||||||
fetch = top.is_object;
|
fetch = is_object;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (at_end)
|
if (at_end)
|
||||||
@@ -1746,7 +1905,7 @@ class binary_reader
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (top.is_object)
|
if (is_object)
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!skip_cbor_key_tags(tag_handler)))
|
if (JSON_HEDLEY_UNLIKELY(!skip_cbor_key_tags(tag_handler)))
|
||||||
{
|
{
|
||||||
@@ -3691,13 +3850,13 @@ class binary_reader
|
|||||||
return sax->null();
|
return sax->null();
|
||||||
|
|
||||||
case 0xFB:
|
case 0xFB:
|
||||||
return sax->number_float(static_cast<number_float_t>(-1.0), "");
|
return sax->number_float(static_cast<number_float_t>(-1.0), no_source);
|
||||||
|
|
||||||
case 0xFC:
|
case 0xFC:
|
||||||
return sax->number_float(static_cast<number_float_t>(0.0), "");
|
return sax->number_float(static_cast<number_float_t>(0.0), no_source);
|
||||||
|
|
||||||
case 0xFD:
|
case 0xFD:
|
||||||
return sax->number_float(static_cast<number_float_t>(1.0), "");
|
return sax->number_float(static_cast<number_float_t>(1.0), no_source);
|
||||||
|
|
||||||
case 0xFF: // empty string
|
case 0xFF: // empty string
|
||||||
{
|
{
|
||||||
@@ -4066,6 +4225,27 @@ class binary_reader
|
|||||||
return current;
|
return current;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// reverse the byte order of a double with a single byteswap(): GCC does
|
||||||
|
/// not merge the byte-wise swap of the generic version into one
|
||||||
|
static void byte_swap(double& number) noexcept
|
||||||
|
{
|
||||||
|
static_assert(sizeof(double) == sizeof(std::uint64_t), "double must have 64 bits");
|
||||||
|
std::uint64_t bits{};
|
||||||
|
std::memcpy(&bits, &number, sizeof(bits));
|
||||||
|
bits = byteswap(bits);
|
||||||
|
std::memcpy(&number, &bits, sizeof(bits));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// reverse the byte order of a float with a single byteswap()
|
||||||
|
static void byte_swap(float& number) noexcept
|
||||||
|
{
|
||||||
|
static_assert(sizeof(float) == sizeof(std::uint32_t), "float must have 32 bits");
|
||||||
|
std::uint32_t bits{};
|
||||||
|
std::memcpy(&bits, &number, sizeof(bits));
|
||||||
|
bits = byteswap(bits);
|
||||||
|
std::memcpy(&number, &bits, sizeof(bits));
|
||||||
|
}
|
||||||
|
|
||||||
template<class NumberType>
|
template<class NumberType>
|
||||||
static void byte_swap(NumberType& number)
|
static void byte_swap(NumberType& number)
|
||||||
{
|
{
|
||||||
@@ -4201,7 +4381,7 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
return out_of_range_error(exception_id::number_overflow, "number overflow", "value");
|
return out_of_range_error(exception_id::number_overflow, "number overflow", "value");
|
||||||
}
|
}
|
||||||
return sax->number_float(result, "");
|
return sax->number_float(result, no_source);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -4261,7 +4441,7 @@ class binary_reader
|
|||||||
}();
|
}();
|
||||||
return sax->number_float((half & 0x8000u) != 0
|
return sax->number_float((half & 0x8000u) != 0
|
||||||
? static_cast<number_float_t>(-val)
|
? static_cast<number_float_t>(-val)
|
||||||
: static_cast<number_float_t>(val), "");
|
: static_cast<number_float_t>(val), no_source);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -4586,6 +4766,10 @@ class binary_reader
|
|||||||
/// the SAX parser
|
/// the SAX parser
|
||||||
json_sax_t* sax = nullptr;
|
json_sax_t* sax = nullptr;
|
||||||
|
|
||||||
|
/// the (empty) source text of every number_float event: binary formats
|
||||||
|
/// have none, and a string_t built for each float would cost more
|
||||||
|
const string_t no_source{};
|
||||||
|
|
||||||
/// the containers that have been opened and not closed yet; see @ref container_frame
|
/// the containers that have been opened and not closed yet; see @ref container_frame
|
||||||
std::vector<container_frame> container_stack{};
|
std::vector<container_frame> container_stack{};
|
||||||
|
|
||||||
|
|||||||
@@ -766,9 +766,6 @@ class basic_json_view
|
|||||||
template<typename, typename> friend class detail::view::view_side;
|
template<typename, typename> friend class detail::view::view_side;
|
||||||
friend iterator;
|
friend iterator;
|
||||||
|
|
||||||
/// whether the view is one of an editable document
|
|
||||||
static constexpr bool is_editable = Editable;
|
|
||||||
|
|
||||||
basic_json_view(const document_data* d, const node* n) noexcept
|
basic_json_view(const document_data* d, const node* n) noexcept
|
||||||
: m_doc(d), m_node(n)
|
: m_doc(d), m_node(n)
|
||||||
{}
|
{}
|
||||||
|
|||||||
Generated
+432
-247
@@ -8852,6 +8852,233 @@ NLOHMANN_JSON_NAMESPACE_END
|
|||||||
#include <bit> //byteswap
|
#include <bit> //byteswap
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// #include <nlohmann/detail/bit_ops.hpp>
|
||||||
|
// __ _____ _____ _____
|
||||||
|
// __| | __| | | | JSON for Modern C++
|
||||||
|
// | | |__ | | | | | | version 3.12.0
|
||||||
|
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||||
|
//
|
||||||
|
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include <cstdint> // uint64_t
|
||||||
|
#include <cstring> // memcpy
|
||||||
|
#if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_ARM64)) && (!defined(__SIZEOF_INT128__) || (!defined(__GNUC__) && !defined(__clang__)))
|
||||||
|
#include <intrin0.h> // __umulh, _umul128, _BitScanForward64, _BitScanReverse64
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#include <cstdlib> // _byteswap_ushort, _byteswap_ulong, _byteswap_uint64
|
||||||
|
#endif
|
||||||
|
#if defined(_MSC_VER) && !defined(__clang__) && (defined(_M_IX86) || defined(_M_ARM))
|
||||||
|
#include <intrin.h> // _BitScanForward, _BitScanReverse
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// #include <nlohmann/detail/macro_scope.hpp>
|
||||||
|
// JSON_HEDLEY_ALWAYS_INLINE, NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
#if defined(__cpp_lib_bit_cast) || defined(__cpp_lib_bitops) || defined(__cpp_lib_byteswap)
|
||||||
|
#include <bit> // bit_cast, byteswap, countl_zero, countr_zero
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Portable bit-level helpers for the number and string scanners. They use
|
||||||
|
// compiler builtins or platform-specific intrinsics where available, the
|
||||||
|
// C++20 <bit> functions next, and plain C++ otherwise, so they work regardless
|
||||||
|
// of byte order. (The builtins and intrinsics come first: MSVC's
|
||||||
|
// std::countl_zero checks for LZCNT at run time unless AVX2 is enabled.)
|
||||||
|
|
||||||
|
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
/// number of leading zero bits of x (x != 0)
|
||||||
|
inline int count_leading_zeros(std::uint64_t x) noexcept
|
||||||
|
{
|
||||||
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
|
return __builtin_clzll(x);
|
||||||
|
#elif defined(_MSC_VER) && (defined(_M_X64) || defined(_M_ARM64))
|
||||||
|
unsigned long index = 0; // NOLINT(runtime/int): the type _BitScan*64 takes
|
||||||
|
_BitScanReverse64(&index, x);
|
||||||
|
return 63 - static_cast<int>(index);
|
||||||
|
#elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_ARM))
|
||||||
|
unsigned long index = 0; // NOLINT(runtime/int): the type _BitScan* takes
|
||||||
|
if (_BitScanReverse(&index, static_cast<unsigned long>(x >> 32u))) // NOLINT(runtime/int)
|
||||||
|
{
|
||||||
|
return 31 - static_cast<int>(index);
|
||||||
|
}
|
||||||
|
_BitScanReverse(&index, static_cast<unsigned long>(x)); // NOLINT(runtime/int)
|
||||||
|
return 63 - static_cast<int>(index);
|
||||||
|
#elif defined(__cpp_lib_bitops)
|
||||||
|
return std::countl_zero(x);
|
||||||
|
#else
|
||||||
|
int n = 0;
|
||||||
|
for (int shift = 32; shift != 0; shift >>= 1)
|
||||||
|
{
|
||||||
|
if ((x >> (64 - shift)) == 0)
|
||||||
|
{
|
||||||
|
n += shift;
|
||||||
|
x <<= shift;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// number of trailing zero bits of x (x != 0)
|
||||||
|
inline int count_trailing_zeros(std::uint64_t x) noexcept
|
||||||
|
{
|
||||||
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
|
return __builtin_ctzll(x);
|
||||||
|
#elif defined(_MSC_VER) && (defined(_M_X64) || defined(_M_ARM64))
|
||||||
|
unsigned long index = 0; // NOLINT(runtime/int): the type _BitScan*64 takes
|
||||||
|
_BitScanForward64(&index, x);
|
||||||
|
return static_cast<int>(index);
|
||||||
|
#elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_ARM))
|
||||||
|
unsigned long index = 0; // NOLINT(runtime/int): the type _BitScan* takes
|
||||||
|
if (_BitScanForward(&index, static_cast<unsigned long>(x))) // NOLINT(runtime/int)
|
||||||
|
{
|
||||||
|
return static_cast<int>(index);
|
||||||
|
}
|
||||||
|
_BitScanForward(&index, static_cast<unsigned long>(x >> 32u)); // NOLINT(runtime/int)
|
||||||
|
return 32 + static_cast<int>(index);
|
||||||
|
#elif defined(__cpp_lib_bitops)
|
||||||
|
return std::countr_zero(x);
|
||||||
|
#else
|
||||||
|
int n = 0;
|
||||||
|
for (int shift = 32; shift != 0; shift >>= 1)
|
||||||
|
{
|
||||||
|
if ((x << (64 - shift)) == 0)
|
||||||
|
{
|
||||||
|
n += shift;
|
||||||
|
x >>= shift;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// the object representation of @a from as a @a To (std::bit_cast in C++20)
|
||||||
|
template<typename To, typename From>
|
||||||
|
JSON_HEDLEY_ALWAYS_INLINE To bit_cast(const From& from) noexcept
|
||||||
|
{
|
||||||
|
static_assert(sizeof(To) == sizeof(From), "bit_cast needs types of the same size");
|
||||||
|
#if defined(__cpp_lib_bit_cast)
|
||||||
|
return std::bit_cast<To>(from);
|
||||||
|
#else
|
||||||
|
To to {};
|
||||||
|
std::memcpy(&to, &from, sizeof(To));
|
||||||
|
return to;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// x with its bytes in reverse order (std::byteswap in C++23); compilers lower
|
||||||
|
/// it to a single bswap/rev/movbe
|
||||||
|
inline std::uint16_t byteswap(std::uint16_t x) noexcept
|
||||||
|
{
|
||||||
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
|
return __builtin_bswap16(x);
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
return _byteswap_ushort(x);
|
||||||
|
#elif defined(__cpp_lib_byteswap)
|
||||||
|
return std::byteswap(x);
|
||||||
|
#else
|
||||||
|
return static_cast<std::uint16_t>((x >> 8u) | (x << 8u));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// x with its bytes in reverse order (std::byteswap in C++23)
|
||||||
|
inline std::uint32_t byteswap(std::uint32_t x) noexcept
|
||||||
|
{
|
||||||
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
|
return __builtin_bswap32(x);
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
return _byteswap_ulong(x);
|
||||||
|
#elif defined(__cpp_lib_byteswap)
|
||||||
|
return std::byteswap(x);
|
||||||
|
#else
|
||||||
|
return ((x & 0x000000FFu) << 24u) | ((x & 0x0000FF00u) << 8u)
|
||||||
|
| ((x & 0x00FF0000u) >> 8u) | ((x & 0xFF000000u) >> 24u);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// x with its bytes in reverse order (std::byteswap in C++23)
|
||||||
|
inline std::uint64_t byteswap(std::uint64_t x) noexcept
|
||||||
|
{
|
||||||
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
|
return __builtin_bswap64(x);
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
return _byteswap_uint64(x);
|
||||||
|
#elif defined(__cpp_lib_byteswap)
|
||||||
|
return std::byteswap(x);
|
||||||
|
#else
|
||||||
|
x = ((x & 0x00000000FFFFFFFFull) << 32u) | ((x & 0xFFFFFFFF00000000ull) >> 32u);
|
||||||
|
x = ((x & 0x0000FFFF0000FFFFull) << 16u) | ((x & 0xFFFF0000FFFF0000ull) >> 16u);
|
||||||
|
x = ((x & 0x00FF00FF00FF00FFull) << 8u) | ((x & 0xFF00FF00FF00FF00ull) >> 8u);
|
||||||
|
return x;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// the 128-bit product of two 64-bit numbers
|
||||||
|
struct uint128_parts
|
||||||
|
{
|
||||||
|
std::uint64_t low;
|
||||||
|
std::uint64_t high;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline uint128_parts full_multiplication(std::uint64_t a, std::uint64_t b) noexcept
|
||||||
|
{
|
||||||
|
#if defined(__SIZEOF_INT128__)
|
||||||
|
__extension__ using uint128 = unsigned __int128;
|
||||||
|
const uint128 r = static_cast<uint128>(a) * b;
|
||||||
|
return {static_cast<std::uint64_t>(r), static_cast<std::uint64_t>(r >> 64u)};
|
||||||
|
#elif defined(_MSC_VER) && defined(_M_X64)
|
||||||
|
std::uint64_t high = 0;
|
||||||
|
const std::uint64_t low = _umul128(a, b, &high);
|
||||||
|
return {low, high};
|
||||||
|
#elif defined(_MSC_VER) && defined(_M_ARM64)
|
||||||
|
return {a * b, __umulh(a, b)};
|
||||||
|
#else
|
||||||
|
const std::uint64_t a_lo = a & 0xFFFFFFFFu;
|
||||||
|
const std::uint64_t a_hi = a >> 32u;
|
||||||
|
const std::uint64_t b_lo = b & 0xFFFFFFFFu;
|
||||||
|
const std::uint64_t b_hi = b >> 32u;
|
||||||
|
const std::uint64_t lo_lo = a_lo * b_lo;
|
||||||
|
const std::uint64_t hi_lo = a_hi * b_lo;
|
||||||
|
const std::uint64_t lo_hi = a_lo * b_hi;
|
||||||
|
const std::uint64_t hi_hi = a_hi * b_hi;
|
||||||
|
const std::uint64_t cross = (lo_lo >> 32u) + (hi_lo & 0xFFFFFFFFu) + lo_hi;
|
||||||
|
return {(cross << 32u) | (lo_lo & 0xFFFFFFFFu), (hi_lo >> 32u) + (cross >> 32u) + hi_hi};
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// eight bytes as a little-endian word (a single load on little-endian
|
||||||
|
/// targets; always inlined, as GCC otherwise calls it in the number loops)
|
||||||
|
JSON_HEDLEY_ALWAYS_INLINE std::uint64_t read_eight_bytes(const unsigned char* b) noexcept
|
||||||
|
{
|
||||||
|
#if JSON_LITTLE_ENDIAN_TARGET
|
||||||
|
// the byte order already matches
|
||||||
|
std::uint64_t result = 0;
|
||||||
|
std::memcpy(&result, b, sizeof(result));
|
||||||
|
return result;
|
||||||
|
#else
|
||||||
|
return static_cast<std::uint64_t>(b[0]) | (static_cast<std::uint64_t>(b[1]) << 8u)
|
||||||
|
| (static_cast<std::uint64_t>(b[2]) << 16u) | (static_cast<std::uint64_t>(b[3]) << 24u)
|
||||||
|
| (static_cast<std::uint64_t>(b[4]) << 32u) | (static_cast<std::uint64_t>(b[5]) << 40u)
|
||||||
|
| (static_cast<std::uint64_t>(b[6]) << 48u) | (static_cast<std::uint64_t>(b[7]) << 56u);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// eight bytes as a little-endian word
|
||||||
|
JSON_HEDLEY_ALWAYS_INLINE std::uint64_t read_eight_bytes(const char* p) noexcept
|
||||||
|
{
|
||||||
|
return read_eight_bytes(reinterpret_cast<const unsigned char*>(p)); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
NLOHMANN_JSON_NAMESPACE_END
|
||||||
|
|
||||||
// #include <nlohmann/detail/exceptions.hpp>
|
// #include <nlohmann/detail/exceptions.hpp>
|
||||||
|
|
||||||
// #include <nlohmann/detail/input/input_adapters.hpp>
|
// #include <nlohmann/detail/input/input_adapters.hpp>
|
||||||
@@ -10316,231 +10543,6 @@ NLOHMANN_JSON_NAMESPACE_END
|
|||||||
#include <vector> // vector
|
#include <vector> // vector
|
||||||
|
|
||||||
// #include <nlohmann/detail/bit_ops.hpp>
|
// #include <nlohmann/detail/bit_ops.hpp>
|
||||||
// __ _____ _____ _____
|
|
||||||
// __| | __| | | | JSON for Modern C++
|
|
||||||
// | | |__ | | | | | | version 3.12.0
|
|
||||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
|
||||||
//
|
|
||||||
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include <cstdint> // uint64_t
|
|
||||||
#include <cstring> // memcpy
|
|
||||||
#if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_ARM64)) && (!defined(__SIZEOF_INT128__) || (!defined(__GNUC__) && !defined(__clang__)))
|
|
||||||
#include <intrin0.h> // __umulh, _umul128, _BitScanForward64, _BitScanReverse64
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
#include <cstdlib> // _byteswap_ushort, _byteswap_ulong, _byteswap_uint64
|
|
||||||
#endif
|
|
||||||
#if defined(_MSC_VER) && !defined(__clang__) && (defined(_M_IX86) || defined(_M_ARM))
|
|
||||||
#include <intrin.h> // _BitScanForward, _BitScanReverse
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// #include <nlohmann/detail/macro_scope.hpp>
|
|
||||||
// JSON_HEDLEY_ALWAYS_INLINE, NLOHMANN_JSON_NAMESPACE_BEGIN
|
|
||||||
|
|
||||||
#if defined(__cpp_lib_bit_cast) || defined(__cpp_lib_bitops) || defined(__cpp_lib_byteswap)
|
|
||||||
#include <bit> // bit_cast, byteswap, countl_zero, countr_zero
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Portable bit-level helpers for the number and string scanners. They use
|
|
||||||
// compiler builtins or platform-specific intrinsics where available, the
|
|
||||||
// C++20 <bit> functions next, and plain C++ otherwise, so they work regardless
|
|
||||||
// of byte order. (The builtins and intrinsics come first: MSVC's
|
|
||||||
// std::countl_zero checks for LZCNT at run time unless AVX2 is enabled.)
|
|
||||||
|
|
||||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
|
||||||
namespace detail
|
|
||||||
{
|
|
||||||
|
|
||||||
/// number of leading zero bits of x (x != 0)
|
|
||||||
inline int count_leading_zeros(std::uint64_t x) noexcept
|
|
||||||
{
|
|
||||||
#if defined(__GNUC__) || defined(__clang__)
|
|
||||||
return __builtin_clzll(x);
|
|
||||||
#elif defined(_MSC_VER) && (defined(_M_X64) || defined(_M_ARM64))
|
|
||||||
unsigned long index = 0; // NOLINT(runtime/int): the type _BitScan*64 takes
|
|
||||||
_BitScanReverse64(&index, x);
|
|
||||||
return 63 - static_cast<int>(index);
|
|
||||||
#elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_ARM))
|
|
||||||
unsigned long index = 0; // NOLINT(runtime/int): the type _BitScan* takes
|
|
||||||
if (_BitScanReverse(&index, static_cast<unsigned long>(x >> 32u))) // NOLINT(runtime/int)
|
|
||||||
{
|
|
||||||
return 31 - static_cast<int>(index);
|
|
||||||
}
|
|
||||||
_BitScanReverse(&index, static_cast<unsigned long>(x)); // NOLINT(runtime/int)
|
|
||||||
return 63 - static_cast<int>(index);
|
|
||||||
#elif defined(__cpp_lib_bitops)
|
|
||||||
return std::countl_zero(x);
|
|
||||||
#else
|
|
||||||
int n = 0;
|
|
||||||
for (int shift = 32; shift != 0; shift >>= 1)
|
|
||||||
{
|
|
||||||
if ((x >> (64 - shift)) == 0)
|
|
||||||
{
|
|
||||||
n += shift;
|
|
||||||
x <<= shift;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return n;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/// number of trailing zero bits of x (x != 0)
|
|
||||||
inline int count_trailing_zeros(std::uint64_t x) noexcept
|
|
||||||
{
|
|
||||||
#if defined(__GNUC__) || defined(__clang__)
|
|
||||||
return __builtin_ctzll(x);
|
|
||||||
#elif defined(_MSC_VER) && (defined(_M_X64) || defined(_M_ARM64))
|
|
||||||
unsigned long index = 0; // NOLINT(runtime/int): the type _BitScan*64 takes
|
|
||||||
_BitScanForward64(&index, x);
|
|
||||||
return static_cast<int>(index);
|
|
||||||
#elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_ARM))
|
|
||||||
unsigned long index = 0; // NOLINT(runtime/int): the type _BitScan* takes
|
|
||||||
if (_BitScanForward(&index, static_cast<unsigned long>(x))) // NOLINT(runtime/int)
|
|
||||||
{
|
|
||||||
return static_cast<int>(index);
|
|
||||||
}
|
|
||||||
_BitScanForward(&index, static_cast<unsigned long>(x >> 32u)); // NOLINT(runtime/int)
|
|
||||||
return 32 + static_cast<int>(index);
|
|
||||||
#elif defined(__cpp_lib_bitops)
|
|
||||||
return std::countr_zero(x);
|
|
||||||
#else
|
|
||||||
int n = 0;
|
|
||||||
for (int shift = 32; shift != 0; shift >>= 1)
|
|
||||||
{
|
|
||||||
if ((x << (64 - shift)) == 0)
|
|
||||||
{
|
|
||||||
n += shift;
|
|
||||||
x >>= shift;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return n;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/// the object representation of @a from as a @a To (std::bit_cast in C++20)
|
|
||||||
template<typename To, typename From>
|
|
||||||
JSON_HEDLEY_ALWAYS_INLINE To bit_cast(const From& from) noexcept
|
|
||||||
{
|
|
||||||
static_assert(sizeof(To) == sizeof(From), "bit_cast needs types of the same size");
|
|
||||||
#if defined(__cpp_lib_bit_cast)
|
|
||||||
return std::bit_cast<To>(from);
|
|
||||||
#else
|
|
||||||
To to {};
|
|
||||||
std::memcpy(&to, &from, sizeof(To));
|
|
||||||
return to;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/// x with its bytes in reverse order (std::byteswap in C++23); compilers lower
|
|
||||||
/// it to a single bswap/rev/movbe
|
|
||||||
inline std::uint16_t byteswap(std::uint16_t x) noexcept
|
|
||||||
{
|
|
||||||
#if defined(__GNUC__) || defined(__clang__)
|
|
||||||
return __builtin_bswap16(x);
|
|
||||||
#elif defined(_MSC_VER)
|
|
||||||
return _byteswap_ushort(x);
|
|
||||||
#elif defined(__cpp_lib_byteswap)
|
|
||||||
return std::byteswap(x);
|
|
||||||
#else
|
|
||||||
return static_cast<std::uint16_t>((x >> 8u) | (x << 8u));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/// x with its bytes in reverse order (std::byteswap in C++23)
|
|
||||||
inline std::uint32_t byteswap(std::uint32_t x) noexcept
|
|
||||||
{
|
|
||||||
#if defined(__GNUC__) || defined(__clang__)
|
|
||||||
return __builtin_bswap32(x);
|
|
||||||
#elif defined(_MSC_VER)
|
|
||||||
return _byteswap_ulong(x);
|
|
||||||
#elif defined(__cpp_lib_byteswap)
|
|
||||||
return std::byteswap(x);
|
|
||||||
#else
|
|
||||||
return ((x & 0x000000FFu) << 24u) | ((x & 0x0000FF00u) << 8u)
|
|
||||||
| ((x & 0x00FF0000u) >> 8u) | ((x & 0xFF000000u) >> 24u);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/// x with its bytes in reverse order (std::byteswap in C++23)
|
|
||||||
inline std::uint64_t byteswap(std::uint64_t x) noexcept
|
|
||||||
{
|
|
||||||
#if defined(__GNUC__) || defined(__clang__)
|
|
||||||
return __builtin_bswap64(x);
|
|
||||||
#elif defined(_MSC_VER)
|
|
||||||
return _byteswap_uint64(x);
|
|
||||||
#elif defined(__cpp_lib_byteswap)
|
|
||||||
return std::byteswap(x);
|
|
||||||
#else
|
|
||||||
x = ((x & 0x00000000FFFFFFFFull) << 32u) | ((x & 0xFFFFFFFF00000000ull) >> 32u);
|
|
||||||
x = ((x & 0x0000FFFF0000FFFFull) << 16u) | ((x & 0xFFFF0000FFFF0000ull) >> 16u);
|
|
||||||
x = ((x & 0x00FF00FF00FF00FFull) << 8u) | ((x & 0xFF00FF00FF00FF00ull) >> 8u);
|
|
||||||
return x;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/// the 128-bit product of two 64-bit numbers
|
|
||||||
struct uint128_parts
|
|
||||||
{
|
|
||||||
std::uint64_t low;
|
|
||||||
std::uint64_t high;
|
|
||||||
};
|
|
||||||
|
|
||||||
inline uint128_parts full_multiplication(std::uint64_t a, std::uint64_t b) noexcept
|
|
||||||
{
|
|
||||||
#if defined(__SIZEOF_INT128__)
|
|
||||||
__extension__ using uint128 = unsigned __int128;
|
|
||||||
const uint128 r = static_cast<uint128>(a) * b;
|
|
||||||
return {static_cast<std::uint64_t>(r), static_cast<std::uint64_t>(r >> 64u)};
|
|
||||||
#elif defined(_MSC_VER) && defined(_M_X64)
|
|
||||||
std::uint64_t high = 0;
|
|
||||||
const std::uint64_t low = _umul128(a, b, &high);
|
|
||||||
return {low, high};
|
|
||||||
#elif defined(_MSC_VER) && defined(_M_ARM64)
|
|
||||||
return {a * b, __umulh(a, b)};
|
|
||||||
#else
|
|
||||||
const std::uint64_t a_lo = a & 0xFFFFFFFFu;
|
|
||||||
const std::uint64_t a_hi = a >> 32u;
|
|
||||||
const std::uint64_t b_lo = b & 0xFFFFFFFFu;
|
|
||||||
const std::uint64_t b_hi = b >> 32u;
|
|
||||||
const std::uint64_t lo_lo = a_lo * b_lo;
|
|
||||||
const std::uint64_t hi_lo = a_hi * b_lo;
|
|
||||||
const std::uint64_t lo_hi = a_lo * b_hi;
|
|
||||||
const std::uint64_t hi_hi = a_hi * b_hi;
|
|
||||||
const std::uint64_t cross = (lo_lo >> 32u) + (hi_lo & 0xFFFFFFFFu) + lo_hi;
|
|
||||||
return {(cross << 32u) | (lo_lo & 0xFFFFFFFFu), (hi_lo >> 32u) + (cross >> 32u) + hi_hi};
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/// eight bytes as a little-endian word (a single load on little-endian
|
|
||||||
/// targets; always inlined, as GCC otherwise calls it in the number loops)
|
|
||||||
JSON_HEDLEY_ALWAYS_INLINE std::uint64_t read_eight_bytes(const unsigned char* b) noexcept
|
|
||||||
{
|
|
||||||
#if JSON_LITTLE_ENDIAN_TARGET
|
|
||||||
// the byte order already matches
|
|
||||||
std::uint64_t result = 0;
|
|
||||||
std::memcpy(&result, b, sizeof(result));
|
|
||||||
return result;
|
|
||||||
#else
|
|
||||||
return static_cast<std::uint64_t>(b[0]) | (static_cast<std::uint64_t>(b[1]) << 8u)
|
|
||||||
| (static_cast<std::uint64_t>(b[2]) << 16u) | (static_cast<std::uint64_t>(b[3]) << 24u)
|
|
||||||
| (static_cast<std::uint64_t>(b[4]) << 32u) | (static_cast<std::uint64_t>(b[5]) << 40u)
|
|
||||||
| (static_cast<std::uint64_t>(b[6]) << 48u) | (static_cast<std::uint64_t>(b[7]) << 56u);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/// eight bytes as a little-endian word
|
|
||||||
JSON_HEDLEY_ALWAYS_INLINE std::uint64_t read_eight_bytes(const char* p) noexcept
|
|
||||||
{
|
|
||||||
return read_eight_bytes(reinterpret_cast<const unsigned char*>(p)); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace detail
|
|
||||||
NLOHMANN_JSON_NAMESPACE_END
|
|
||||||
|
|
||||||
// #include <nlohmann/detail/input/input_adapters.hpp>
|
// #include <nlohmann/detail/input/input_adapters.hpp>
|
||||||
|
|
||||||
@@ -18517,11 +18519,13 @@ class binary_reader
|
|||||||
bool get_cbor_negative_integer()
|
bool get_cbor_negative_integer()
|
||||||
{
|
{
|
||||||
NumberType number{};
|
NumberType number{};
|
||||||
if (JSON_HEDLEY_UNLIKELY(!get_number(number)))
|
return get_number(number) && emit_cbor_negative_integer(number);
|
||||||
{
|
}
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/// pass the CBOR negative integer -1 - @a number to the SAX parser
|
||||||
|
template<typename NumberType>
|
||||||
|
bool emit_cbor_negative_integer(const NumberType number)
|
||||||
|
{
|
||||||
// the value is -1 - number, which fits into number_integer_t
|
// the value is -1 - number, which fits into number_integer_t
|
||||||
// whenever number does; the outer cast undoes the integral promotion
|
// whenever number does; the outer cast undoes the integral promotion
|
||||||
// for number_integer_t types narrower than int
|
// for number_integer_t types narrower than int
|
||||||
@@ -18746,7 +18750,7 @@ class binary_reader
|
|||||||
case 0x95:
|
case 0x95:
|
||||||
case 0x96:
|
case 0x96:
|
||||||
case 0x97:
|
case 0x97:
|
||||||
return enter_array(conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu));
|
return enter_cbor_array(conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu));
|
||||||
|
|
||||||
case 0x98: // array (one-byte uint8_t for n follows)
|
case 0x98: // array (one-byte uint8_t for n follows)
|
||||||
case 0x99: // array (two-byte uint16_t for n follow)
|
case 0x99: // array (two-byte uint16_t for n follow)
|
||||||
@@ -18755,7 +18759,7 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
std::uint64_t len{};
|
std::uint64_t len{};
|
||||||
std::size_t size{};
|
std::size_t size{};
|
||||||
return get_cbor_argument(len) && get_cbor_container_size(len, size, "array") && enter_array(size);
|
return get_cbor_argument(len) && get_cbor_container_size(len, size, "array") && enter_cbor_array(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x9F: // array (indefinite length)
|
case 0x9F: // array (indefinite length)
|
||||||
@@ -18909,16 +18913,10 @@ class binary_reader
|
|||||||
return get_half_float(false);
|
return get_half_float(false);
|
||||||
|
|
||||||
case 0xFA: // Single-Precision Float (four-byte IEEE 754)
|
case 0xFA: // Single-Precision Float (four-byte IEEE 754)
|
||||||
{
|
return get_cbor_float<float>();
|
||||||
float number{};
|
|
||||||
return get_number(number) && emit_float(number);
|
|
||||||
}
|
|
||||||
|
|
||||||
case 0xFB: // Double-Precision Float (eight-byte IEEE 754)
|
case 0xFB: // Double-Precision Float (eight-byte IEEE 754)
|
||||||
{
|
return get_cbor_float<double>();
|
||||||
double number{};
|
|
||||||
return get_number(number) && emit_float(number);
|
|
||||||
}
|
|
||||||
|
|
||||||
default: // anything else (0xFF is handled inside the other types)
|
default: // anything else (0xFF is handled inside the other types)
|
||||||
{
|
{
|
||||||
@@ -18927,6 +18925,16 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// read a single- or double-precision float (kept out of
|
||||||
|
/// parse_cbor_value, which is inlined into parse_cbor_internal)
|
||||||
|
template<typename NumberType>
|
||||||
|
JSON_HEDLEY_NEVER_INLINE
|
||||||
|
bool get_cbor_float()
|
||||||
|
{
|
||||||
|
NumberType number{};
|
||||||
|
return get_number(number) && emit_float(number);
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief reports a nested indefinite-length CBOR string or byte array
|
@brief reports a nested indefinite-length CBOR string or byte array
|
||||||
@param[in] type_name name of the rejected string type
|
@param[in] type_name name of the rejected string type
|
||||||
@@ -19452,6 +19460,157 @@ class binary_reader
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief read a big-endian number of type @a NumberType at @a data
|
||||||
|
*/
|
||||||
|
template<typename NumberType>
|
||||||
|
NumberType load_big_endian(const unsigned char* data) const noexcept
|
||||||
|
{
|
||||||
|
NumberType result{};
|
||||||
|
std::memcpy(&result, data, sizeof(NumberType));
|
||||||
|
if (is_little_endian)
|
||||||
|
{
|
||||||
|
byte_swap(result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief read the integers, floats, booleans, and nulls that follow in a
|
||||||
|
definite-length array, without the per-element bookkeeping of
|
||||||
|
@ref parse_cbor_internal
|
||||||
|
|
||||||
|
Stops at the end of the array, at any other kind of value, and when fewer
|
||||||
|
than 9 bytes (the longest of these values) are left in the input, leaving
|
||||||
|
those to @ref parse_cbor_value, which reads them the same way.
|
||||||
|
|
||||||
|
@param[in,out] remaining the number of elements of the array not yet read
|
||||||
|
@return whether the SAX parser accepted the values
|
||||||
|
*/
|
||||||
|
bool parse_cbor_scalar_run(std::size_t& remaining)
|
||||||
|
{
|
||||||
|
while (remaining != 0 && ia.bulk_remaining() >= 9)
|
||||||
|
{
|
||||||
|
const auto* const data = reinterpret_cast<const unsigned char*>(ia.bulk_data());
|
||||||
|
const unsigned char head = data[0];
|
||||||
|
std::size_t size = 0;
|
||||||
|
bool accepted = true;
|
||||||
|
switch (head)
|
||||||
|
{
|
||||||
|
case 0x18:
|
||||||
|
size = 2;
|
||||||
|
accepted = emit_unsigned(data[1]);
|
||||||
|
break;
|
||||||
|
case 0x19:
|
||||||
|
size = 3;
|
||||||
|
accepted = emit_unsigned(load_big_endian<std::uint16_t>(data + 1));
|
||||||
|
break;
|
||||||
|
case 0x1A:
|
||||||
|
size = 5;
|
||||||
|
accepted = emit_unsigned(load_big_endian<std::uint32_t>(data + 1));
|
||||||
|
break;
|
||||||
|
case 0x1B:
|
||||||
|
size = 9;
|
||||||
|
accepted = emit_unsigned(load_big_endian<std::uint64_t>(data + 1));
|
||||||
|
break;
|
||||||
|
case 0x38:
|
||||||
|
size = 2;
|
||||||
|
accepted = emit_cbor_negative_integer(data[1]);
|
||||||
|
break;
|
||||||
|
case 0x39:
|
||||||
|
size = 3;
|
||||||
|
accepted = emit_cbor_negative_integer(load_big_endian<std::uint16_t>(data + 1));
|
||||||
|
break;
|
||||||
|
case 0x3A:
|
||||||
|
size = 5;
|
||||||
|
accepted = emit_cbor_negative_integer(load_big_endian<std::uint32_t>(data + 1));
|
||||||
|
break;
|
||||||
|
case 0x3B:
|
||||||
|
size = 9;
|
||||||
|
accepted = emit_cbor_negative_integer(load_big_endian<std::uint64_t>(data + 1));
|
||||||
|
break;
|
||||||
|
case 0xF4:
|
||||||
|
size = 1;
|
||||||
|
accepted = sax->boolean(false);
|
||||||
|
break;
|
||||||
|
case 0xF5:
|
||||||
|
size = 1;
|
||||||
|
accepted = sax->boolean(true);
|
||||||
|
break;
|
||||||
|
case 0xF6:
|
||||||
|
size = 1;
|
||||||
|
accepted = sax->null();
|
||||||
|
break;
|
||||||
|
case 0xFA:
|
||||||
|
size = 5;
|
||||||
|
accepted = emit_float(load_big_endian<float>(data + 1));
|
||||||
|
break;
|
||||||
|
case 0xFB:
|
||||||
|
size = 9;
|
||||||
|
accepted = emit_float(load_big_endian<double>(data + 1));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (head <= 0x17)
|
||||||
|
{
|
||||||
|
size = 1;
|
||||||
|
accepted = sax->number_unsigned(static_cast<number_unsigned_t>(head));
|
||||||
|
}
|
||||||
|
else if (head >= 0x20 && head <= 0x37)
|
||||||
|
{
|
||||||
|
size = 1;
|
||||||
|
accepted = sax->number_integer(static_cast<std::int8_t>(0x20 - 1 - head));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// any other value is read by parse_cbor_value
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// as if read by get() and get_number(): current is the head byte
|
||||||
|
ia.bulk_skip(size);
|
||||||
|
chars_read += size;
|
||||||
|
current = head;
|
||||||
|
--remaining;
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(!accepted))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief open a definite-length array and read the scalars it begins with
|
||||||
|
(see @ref parse_cbor_scalar_run)
|
||||||
|
|
||||||
|
Only the start of an array is tried, which covers arrays of numbers
|
||||||
|
(e.g. coordinates) but costs arrays of other values only one test, not one
|
||||||
|
per element.
|
||||||
|
|
||||||
|
@param[in] len the number of elements
|
||||||
|
@return whether the SAX parser accepted the array and the values read
|
||||||
|
*/
|
||||||
|
bool enter_cbor_array(const std::size_t len)
|
||||||
|
{
|
||||||
|
return enter_array(len) && (len == 0 || parse_cbor_array_start(std::integral_constant<bool, bulk_scan> {}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// read the scalars the array just opened begins with; out of line, so
|
||||||
|
/// that @ref parse_cbor_value stays small enough to be inlined into
|
||||||
|
/// @ref parse_cbor_internal
|
||||||
|
JSON_HEDLEY_NEVER_INLINE
|
||||||
|
bool parse_cbor_array_start(std::true_type /*bulk*/)
|
||||||
|
{
|
||||||
|
return parse_cbor_scalar_run(container_stack.back().remaining);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// input that is not contiguous: every value is read by parse_cbor_value
|
||||||
|
bool parse_cbor_array_start(std::false_type /*bulk*/) const noexcept
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief read a CBOR value and everything nested inside it
|
@brief read a CBOR value and everything nested inside it
|
||||||
|
|
||||||
@@ -19481,6 +19640,7 @@ class binary_reader
|
|||||||
// pop_back() below, which destroys the container_stack element
|
// pop_back() below, which destroys the container_stack element
|
||||||
// it would otherwise alias
|
// it would otherwise alias
|
||||||
const container_frame top = container_stack.back();
|
const container_frame top = container_stack.back();
|
||||||
|
const bool is_object = top.is_object;
|
||||||
bool at_end = false;
|
bool at_end = false;
|
||||||
|
|
||||||
if (top.remaining != npos)
|
if (top.remaining != npos)
|
||||||
@@ -19492,7 +19652,7 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
// claim the element about to be read
|
// claim the element about to be read
|
||||||
--container_stack.back().remaining;
|
--container_stack.back().remaining;
|
||||||
if (top.is_object)
|
if (is_object)
|
||||||
{
|
{
|
||||||
get();
|
get();
|
||||||
}
|
}
|
||||||
@@ -19505,7 +19665,7 @@ class binary_reader
|
|||||||
// Testing for it consumes a byte, which is the first byte
|
// Testing for it consumes a byte, which is the first byte
|
||||||
// of the next element when it is not one.
|
// of the next element when it is not one.
|
||||||
at_end = (get() == 0xFF);
|
at_end = (get() == 0xFF);
|
||||||
fetch = top.is_object;
|
fetch = is_object;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (at_end)
|
if (at_end)
|
||||||
@@ -19522,7 +19682,7 @@ class binary_reader
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (top.is_object)
|
if (is_object)
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!skip_cbor_key_tags(tag_handler)))
|
if (JSON_HEDLEY_UNLIKELY(!skip_cbor_key_tags(tag_handler)))
|
||||||
{
|
{
|
||||||
@@ -21467,13 +21627,13 @@ class binary_reader
|
|||||||
return sax->null();
|
return sax->null();
|
||||||
|
|
||||||
case 0xFB:
|
case 0xFB:
|
||||||
return sax->number_float(static_cast<number_float_t>(-1.0), "");
|
return sax->number_float(static_cast<number_float_t>(-1.0), no_source);
|
||||||
|
|
||||||
case 0xFC:
|
case 0xFC:
|
||||||
return sax->number_float(static_cast<number_float_t>(0.0), "");
|
return sax->number_float(static_cast<number_float_t>(0.0), no_source);
|
||||||
|
|
||||||
case 0xFD:
|
case 0xFD:
|
||||||
return sax->number_float(static_cast<number_float_t>(1.0), "");
|
return sax->number_float(static_cast<number_float_t>(1.0), no_source);
|
||||||
|
|
||||||
case 0xFF: // empty string
|
case 0xFF: // empty string
|
||||||
{
|
{
|
||||||
@@ -21842,6 +22002,27 @@ class binary_reader
|
|||||||
return current;
|
return current;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// reverse the byte order of a double with a single byteswap(): GCC does
|
||||||
|
/// not merge the byte-wise swap of the generic version into one
|
||||||
|
static void byte_swap(double& number) noexcept
|
||||||
|
{
|
||||||
|
static_assert(sizeof(double) == sizeof(std::uint64_t), "double must have 64 bits");
|
||||||
|
std::uint64_t bits{};
|
||||||
|
std::memcpy(&bits, &number, sizeof(bits));
|
||||||
|
bits = byteswap(bits);
|
||||||
|
std::memcpy(&number, &bits, sizeof(bits));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// reverse the byte order of a float with a single byteswap()
|
||||||
|
static void byte_swap(float& number) noexcept
|
||||||
|
{
|
||||||
|
static_assert(sizeof(float) == sizeof(std::uint32_t), "float must have 32 bits");
|
||||||
|
std::uint32_t bits{};
|
||||||
|
std::memcpy(&bits, &number, sizeof(bits));
|
||||||
|
bits = byteswap(bits);
|
||||||
|
std::memcpy(&number, &bits, sizeof(bits));
|
||||||
|
}
|
||||||
|
|
||||||
template<class NumberType>
|
template<class NumberType>
|
||||||
static void byte_swap(NumberType& number)
|
static void byte_swap(NumberType& number)
|
||||||
{
|
{
|
||||||
@@ -21977,7 +22158,7 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
return out_of_range_error(exception_id::number_overflow, "number overflow", "value");
|
return out_of_range_error(exception_id::number_overflow, "number overflow", "value");
|
||||||
}
|
}
|
||||||
return sax->number_float(result, "");
|
return sax->number_float(result, no_source);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -22037,7 +22218,7 @@ class binary_reader
|
|||||||
}();
|
}();
|
||||||
return sax->number_float((half & 0x8000u) != 0
|
return sax->number_float((half & 0x8000u) != 0
|
||||||
? static_cast<number_float_t>(-val)
|
? static_cast<number_float_t>(-val)
|
||||||
: static_cast<number_float_t>(val), "");
|
: static_cast<number_float_t>(val), no_source);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -22362,6 +22543,10 @@ class binary_reader
|
|||||||
/// the SAX parser
|
/// the SAX parser
|
||||||
json_sax_t* sax = nullptr;
|
json_sax_t* sax = nullptr;
|
||||||
|
|
||||||
|
/// the (empty) source text of every number_float event: binary formats
|
||||||
|
/// have none, and a string_t built for each float would cost more
|
||||||
|
const string_t no_source{};
|
||||||
|
|
||||||
/// the containers that have been opened and not closed yet; see @ref container_frame
|
/// the containers that have been opened and not closed yet; see @ref container_frame
|
||||||
std::vector<container_frame> container_stack{};
|
std::vector<container_frame> container_stack{};
|
||||||
|
|
||||||
|
|||||||
Generated
-3
@@ -9807,9 +9807,6 @@ class basic_json_view
|
|||||||
template<typename, typename> friend class detail::view::view_side;
|
template<typename, typename> friend class detail::view::view_side;
|
||||||
friend iterator;
|
friend iterator;
|
||||||
|
|
||||||
/// whether the view is one of an editable document
|
|
||||||
static constexpr bool is_editable = Editable;
|
|
||||||
|
|
||||||
basic_json_view(const document_data* d, const node* n) noexcept
|
basic_json_view(const document_data* d, const node* n) noexcept
|
||||||
: m_doc(d), m_node(n)
|
: m_doc(d), m_node(n)
|
||||||
{}
|
{}
|
||||||
|
|||||||
@@ -29,7 +29,9 @@ using nlohmann::json;
|
|||||||
#include "round_trip_corpus.hpp"
|
#include "round_trip_corpus.hpp"
|
||||||
#include "test_utils.hpp"
|
#include "test_utils.hpp"
|
||||||
#include "sax_countdown.hpp"
|
#include "sax_countdown.hpp"
|
||||||
|
#include "sax_event_loggers.hpp"
|
||||||
using utils::SaxCountdown;
|
using utils::SaxCountdown;
|
||||||
|
using utils::SaxEventLogger;
|
||||||
|
|
||||||
|
|
||||||
TEST_CASE("CBOR")
|
TEST_CASE("CBOR")
|
||||||
@@ -2446,6 +2448,78 @@ TEST_CASE("CBOR SAX parsing stops at every event")
|
|||||||
CHECK(count_events(std::vector<std::uint8_t>({0xBF, 0x61, 'a', 0x9F, 0x01, 0xFF, 0xFF})) == 6);
|
CHECK(count_events(std::vector<std::uint8_t>({0xBF, 0x61, 'a', 0x9F, 0x01, 0xFF, 0xFF})) == 6);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("CBOR arrays of scalars from contiguous and non-contiguous input")
|
||||||
|
{
|
||||||
|
// contiguous input reads the integers, floats, booleans, and nulls an
|
||||||
|
// array begins with in one loop, other input value by value; both must
|
||||||
|
// give the same events, value, or error for the complete document and for
|
||||||
|
// every truncation of it, and stop at every event a SAX handler rejects
|
||||||
|
const std::vector<std::uint8_t> cbor =
|
||||||
|
{
|
||||||
|
0xA3, // map(3)
|
||||||
|
0x61, 'a', // "a"
|
||||||
|
0x98, 0x16, // array(22)
|
||||||
|
0x00, 0x17, 0x18, 0xFF, 0x19, 0x01, 0x00, // 0, 23, 255, 256
|
||||||
|
0x1A, 0x00, 0x01, 0x00, 0x00, // 65536
|
||||||
|
0x1B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 18446744073709551615
|
||||||
|
0x20, 0x37, 0x38, 0xFF, 0x39, 0x01, 0x00, // -1, -24, -256, -257
|
||||||
|
0x3A, 0x00, 0x01, 0x00, 0x00, // -65537
|
||||||
|
0x3B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // -18446744073709551616 (a float)
|
||||||
|
0x3B, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // -9223372036854775808
|
||||||
|
0xF4, 0xF5, 0xF6, // false, true, null
|
||||||
|
0xFA, 0x3F, 0xC0, 0x00, 0x00, // 1.5 (float32)
|
||||||
|
0xFB, 0xBF, 0xB9, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9A, // -0.1
|
||||||
|
0xF9, 0x3C, 0x00, // 1.0 (float16, ends the loop)
|
||||||
|
0x01, 0x61, 'a', // 1, "a"
|
||||||
|
0xFB, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 2.5
|
||||||
|
0x61, 'b', // "b"
|
||||||
|
0x83, // array(3)
|
||||||
|
0x82, 0xFB, 0x3F, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // [1.25,
|
||||||
|
0xFB, 0xC0, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // -3.75]
|
||||||
|
0x82, 0xFB, 0x3F, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // [1.25,
|
||||||
|
0xFB, 0xC0, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // -3.75]
|
||||||
|
0x80, // []
|
||||||
|
0x61, 'c', // "c"
|
||||||
|
0x84, 0xA0, 0x01, 0xC1, 0x02, 0x81, 0x03 // [{}, 1, tag(1) 2, [3]]
|
||||||
|
};
|
||||||
|
const auto tags = json::cbor_tag_handler_t::ignore;
|
||||||
|
|
||||||
|
const json j = json::from_cbor(cbor, true, true, tags);
|
||||||
|
CHECK(j["a"].size() == 22);
|
||||||
|
CHECK(j["a"][5] == 18446744073709551615u);
|
||||||
|
CHECK(j["a"][11].is_number_float());
|
||||||
|
CHECK(j["a"][11] == -18446744073709551616.0);
|
||||||
|
CHECK(j["a"][12] == (std::numeric_limits<std::int64_t>::min)());
|
||||||
|
CHECK(j["a"][17] == -0.1);
|
||||||
|
CHECK(j["b"] == json::parse("[[1.25, -3.75], [1.25, -3.75], []]"));
|
||||||
|
CHECK(j["c"] == json::parse("[{}, 1, 2, [3]]"));
|
||||||
|
|
||||||
|
for (std::size_t length = 0; length <= cbor.size(); ++length)
|
||||||
|
{
|
||||||
|
CAPTURE(length)
|
||||||
|
const std::vector<std::uint8_t> contiguous(cbor.begin(), cbor.begin() + static_cast<std::ptrdiff_t>(length));
|
||||||
|
const std::list<std::uint8_t> bytewise(contiguous.begin(), contiguous.end());
|
||||||
|
|
||||||
|
CHECK(json::from_cbor(contiguous, true, false, tags).dump() == json::from_cbor(bytewise.begin(), bytewise.end(), true, false, tags).dump());
|
||||||
|
|
||||||
|
SaxEventLogger from_contiguous;
|
||||||
|
SaxEventLogger from_bytewise;
|
||||||
|
CHECK(json::sax_parse(contiguous, &from_contiguous, json::input_format_t::cbor, true, false, false, tags)
|
||||||
|
== json::sax_parse(bytewise.begin(), bytewise.end(), &from_bytewise, json::input_format_t::cbor, true, false, false, tags));
|
||||||
|
CHECK(from_contiguous.events == from_bytewise.events);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int events = 0; events <= 60; ++events)
|
||||||
|
{
|
||||||
|
CAPTURE(events)
|
||||||
|
const std::list<std::uint8_t> bytewise(cbor.begin(), cbor.end());
|
||||||
|
SaxCountdown from_contiguous(events);
|
||||||
|
SaxCountdown from_bytewise(events);
|
||||||
|
CHECK(json::sax_parse(cbor, &from_contiguous, json::input_format_t::cbor, true, false, false, tags)
|
||||||
|
== json::sax_parse(bytewise.begin(), bytewise.end(), &from_bytewise, json::input_format_t::cbor, true, false, false, tags));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("CBOR indefinite-length strings do not recurse per chunk")
|
TEST_CASE("CBOR indefinite-length strings do not recurse per chunk")
|
||||||
{
|
{
|
||||||
// Reading an indefinite-length string or byte array used to call itself
|
// Reading an indefinite-length string or byte array used to call itself
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
# check_path_filters
|
|
||||||
|
|
||||||
The compiler workflows [`ubuntu.yml`](../../.github/workflows/ubuntu.yml),
|
|
||||||
[`macos.yml`](../../.github/workflows/macos.yml), and [`windows.yml`](../../.github/workflows/windows.yml) skip pull
|
|
||||||
requests that change only files no compiler job reads: the documentation, Markdown files, GitHub metadata, other
|
|
||||||
workflows, and the license files. Pushes to `develop`, `master`, and `release/*` always run them. The documentation,
|
|
||||||
license, and path-filter checks run in [`repository_checks.yml`](../../.github/workflows/repository_checks.yml), which
|
|
||||||
has no path filter.
|
|
||||||
|
|
||||||
The filter starts with `'**'` and excludes these files explicitly, so a new file runs the compiler workflows unless it
|
|
||||||
is excluded on purpose. [`check_path_filters.py`](check_path_filters.py) holds the filter (`PATHS`) and checks that
|
|
||||||
|
|
||||||
- each of the three workflows has exactly this filter, with its own workflow file included,
|
|
||||||
- the CI targets that read excluded files (`UNFILTERED_TARGETS`) do not run in these workflows, and
|
|
||||||
- no file the filter keeps (sources, tests, build files, tools, workflows) mentions an excluded file or directory
|
|
||||||
outside a comment line, unless the mention is listed in `KNOWN_REFERENCES` with a reason.
|
|
||||||
|
|
||||||
When the check reports a mention, decide whether a compiler job reads that file:
|
|
||||||
|
|
||||||
- If it does, include the file in `PATHS` after the exclusions (as `package_managers.md` and the Swift example are) and
|
|
||||||
copy the new filter into the three workflows.
|
|
||||||
- If it does not, add the mention to `KNOWN_REFERENCES` with the reason.
|
|
||||||
|
|
||||||
Run the check with
|
|
||||||
|
|
||||||
```shell
|
|
||||||
python3 tools/check_path_filters/check_path_filters.py
|
|
||||||
```
|
|
||||||
|
|
||||||
To see which workflows a pull request would run, pass the changed files:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
python3 tools/check_path_filters/check_path_filters.py --changed $(git diff --name-only origin/develop)
|
|
||||||
```
|
|
||||||
@@ -1,273 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""Check the path filters of the compiler workflows.
|
|
||||||
|
|
||||||
ubuntu.yml, macos.yml and windows.yml skip pull requests that change only files
|
|
||||||
no compiler job reads (documentation, Markdown files, GitHub metadata, license
|
|
||||||
files). The filter starts with '**' and excludes these files explicitly, so a
|
|
||||||
new file runs the workflows unless it is excluded on purpose.
|
|
||||||
|
|
||||||
The script checks that
|
|
||||||
|
|
||||||
- each of the three workflows has exactly the filter defined in PATHS below,
|
|
||||||
with its own workflow file included, and
|
|
||||||
- no tracked file that the filter keeps (sources, tests, build files, tools,
|
|
||||||
workflows) mentions a file the filter excludes, unless the mention is listed
|
|
||||||
in KNOWN_REFERENCES with the reason why it does not matter.
|
|
||||||
|
|
||||||
A mention found this way must either be added to PATHS as an included path
|
|
||||||
(the file is read by a compiler job) or to KNOWN_REFERENCES (it is not).
|
|
||||||
|
|
||||||
With --changed FILE..., the script prints instead which workflows a pull
|
|
||||||
request changing these files would run.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import subprocess # nosec B404
|
|
||||||
import sys
|
|
||||||
|
|
||||||
REPO_ROOT = os.path.normpath(os.path.join(sys.path[0], '..', '..'))
|
|
||||||
SELF = 'tools/check_path_filters/check_path_filters.py'
|
|
||||||
|
|
||||||
WORKFLOWS = ['.github/workflows/ubuntu.yml', '.github/workflows/macos.yml', '.github/workflows/windows.yml']
|
|
||||||
|
|
||||||
# The pull_request paths filter of the workflows above; '{workflow}' is replaced
|
|
||||||
# with the workflow's own file. GitHub applies the patterns in order: a file
|
|
||||||
# matching a later pattern is included ('pattern') or excluded ('!pattern').
|
|
||||||
PATHS = [
|
|
||||||
'**',
|
|
||||||
'!docs/**',
|
|
||||||
'!**.md',
|
|
||||||
'!.github/*',
|
|
||||||
'!.github/ISSUE_TEMPLATE/**',
|
|
||||||
'!.github/external_ci/**',
|
|
||||||
'!.github/workflows/**',
|
|
||||||
'!.reuse/**',
|
|
||||||
'!LICENSES/**',
|
|
||||||
'!CITATION.cff',
|
|
||||||
'!REUSE.toml',
|
|
||||||
# read by tools/check_build_options in the ci_meson_install job (ubuntu.yml)
|
|
||||||
'docs/mkdocs/docs/integration/package_managers.md',
|
|
||||||
# built by the swiftpm job (macos.yml)
|
|
||||||
'docs/mkdocs/docs/integration/swift/**',
|
|
||||||
'{workflow}',
|
|
||||||
]
|
|
||||||
|
|
||||||
# Mentions of excluded files that no job of the three workflows depends on:
|
|
||||||
# (file that mentions it, the mention, reason)
|
|
||||||
KNOWN_REFERENCES = [
|
|
||||||
('cmake/ci.cmake', 'docs/', 'ci_test_examples; runs in repository_checks.yml, which has no path filter'),
|
|
||||||
('cmake/ci.cmake', 'docs/mkdocs/', 'ci_test_build_documentation and ci_test_documentation_mermaid; they run in '
|
|
||||||
'repository_checks.yml, which has no path filter'),
|
|
||||||
('cmake/ci.cmake', 'docs/mkdocs/docs/examples/', 'indentation check of ci_test_amalgamation; the same check runs in '
|
|
||||||
'check_amalgamation.yml, which has no path filter'),
|
|
||||||
('Makefile', 'docs/mkdocs/docs/examples/', 'make pretty; runs in check_amalgamation.yml, which has no path filter'),
|
|
||||||
('Makefile', 'ChangeLog.md', 'make ChangeLog.md writes the file; no CI job runs it'),
|
|
||||||
('Makefile', 'tests/fuzzing.md', 'help text'),
|
|
||||||
('Makefile', 'tools/generate_natvis/README.md', 'help text'),
|
|
||||||
('tests/src/make_test_data_available.hpp', 'README.md', 'a file of the external test data repository'),
|
|
||||||
('tools/check_build_options/check_build_options.py', 'docs/', 'builds the path of package_managers.md, which '
|
|
||||||
'PATHS includes'),
|
|
||||||
('tools/ci_jobs/ci_jobs.py', 'docs/mkdocs/docs/community/quality_assurance.md',
|
|
||||||
'checks the job table; runs in repository_checks.yml, which has no path filter'),
|
|
||||||
('tools/ci_jobs/ci_jobs.py', '.github/workflows/repository_checks.yml', 'read by --compare, a manual check'),
|
|
||||||
('tools/ci_jobs/ci_jobs.py', '.github/workflows/check_amalgamation.yml', 'read by --compare, a manual check'),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
# CI targets that read excluded files; they must run in a workflow without a path filter
|
|
||||||
UNFILTERED_TARGETS = ['ci_test_examples', 'ci_test_build_documentation', 'ci_test_documentation_mermaid',
|
|
||||||
'ci_test_amalgamation', 'ci_reuse_compliance']
|
|
||||||
|
|
||||||
|
|
||||||
def git_files(root):
|
|
||||||
# fixed command, no user input; includes new files not yet added, so a local run
|
|
||||||
# sees what CI sees after the commit
|
|
||||||
out = subprocess.check_output(['git', 'ls-files', '-z', '--cached', '--others', '--exclude-standard'], cwd=root) # nosec B603, B607
|
|
||||||
return [f for f in out.decode('utf-8').split('\0') if f]
|
|
||||||
|
|
||||||
|
|
||||||
def glob_to_regex(pattern):
|
|
||||||
"""Translate a GitHub Actions path pattern to a regular expression."""
|
|
||||||
regex = ''
|
|
||||||
i = 0
|
|
||||||
while i < len(pattern):
|
|
||||||
if pattern.startswith('**', i):
|
|
||||||
regex += '.*'
|
|
||||||
i += 2
|
|
||||||
elif pattern[i] == '*':
|
|
||||||
regex += '[^/]*'
|
|
||||||
i += 1
|
|
||||||
elif pattern[i] == '?':
|
|
||||||
regex += '[^/]'
|
|
||||||
i += 1
|
|
||||||
else:
|
|
||||||
regex += re.escape(pattern[i])
|
|
||||||
i += 1
|
|
||||||
return re.compile(regex + r'\Z')
|
|
||||||
|
|
||||||
|
|
||||||
def compile_filter(paths, workflow):
|
|
||||||
return [(p.startswith('!'), glob_to_regex(p.lstrip('!').replace('{workflow}', workflow))) for p in paths]
|
|
||||||
|
|
||||||
|
|
||||||
def included(compiled, path):
|
|
||||||
result = False
|
|
||||||
for negated, regex in compiled:
|
|
||||||
if regex.match(path):
|
|
||||||
result = not negated
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def read_filter(root, workflow):
|
|
||||||
"""Return the pull_request paths list of a workflow file."""
|
|
||||||
with open(os.path.join(root, workflow), encoding='utf-8') as f:
|
|
||||||
lines = f.read().splitlines()
|
|
||||||
paths = None
|
|
||||||
in_pull_request = False
|
|
||||||
for line in lines:
|
|
||||||
if re.match(r'^ pull_request:', line):
|
|
||||||
in_pull_request = True
|
|
||||||
continue
|
|
||||||
if in_pull_request:
|
|
||||||
if re.match(r'^ \S', line) or re.match(r'^\S', line):
|
|
||||||
break
|
|
||||||
if re.match(r'^ paths:\s*$', line):
|
|
||||||
paths = []
|
|
||||||
continue
|
|
||||||
m = re.match(r"^ - '(.*)'\s*(#.*)?$", line)
|
|
||||||
if paths is not None and m:
|
|
||||||
paths.append(m.group(1))
|
|
||||||
elif paths is not None and line.strip() and not line.strip().startswith('#'):
|
|
||||||
break
|
|
||||||
return paths
|
|
||||||
|
|
||||||
|
|
||||||
TOKEN = re.compile(r'(["\'/]?)([\w.-]+(?:/[\w.-]+)*)')
|
|
||||||
|
|
||||||
|
|
||||||
COMMENT_LINE = re.compile(r'^[ \t]*(//|#|/?\*|--).*$', re.MULTILINE)
|
|
||||||
|
|
||||||
|
|
||||||
class Mentions:
|
|
||||||
"""Finds the words and paths in a text that name an excluded file or directory."""
|
|
||||||
|
|
||||||
def __init__(self, files, excluded, is_excluded):
|
|
||||||
self.files = set(files)
|
|
||||||
self.excluded = excluded
|
|
||||||
self.dirs = {'/'.join(f.split('/')[:n]) for f in files for n in range(1, f.count('/') + 1)}
|
|
||||||
# an excluded directory: a new file in it would be excluded
|
|
||||||
self.excluded_dirs = {d for d in self.dirs if is_excluded(d + '/new_file')}
|
|
||||||
# base names that only excluded files have, e.g. 'ChangeLog.md' but not 'CMakeLists.txt'
|
|
||||||
self.names = {os.path.basename(f) for f in excluded} - {os.path.basename(f) for f in files if f not in excluded}
|
|
||||||
|
|
||||||
def find(self, text):
|
|
||||||
"""Yield (offset, mention) for every word or path in text that names an excluded file or directory."""
|
|
||||||
for m in TOKEN.finditer(text):
|
|
||||||
parts = m.group(2).rstrip('/').split('/')
|
|
||||||
# a single word names a directory only in a path context, e.g. 'docs' in os.path.join('docs', ...)
|
|
||||||
path_context = len(parts) > 1 or m.group(1)
|
|
||||||
for i in range(len(parts)):
|
|
||||||
path = '/'.join(parts[i:])
|
|
||||||
if path in self.files:
|
|
||||||
if path in self.excluded:
|
|
||||||
yield m.start(2), path
|
|
||||||
break
|
|
||||||
if path in self.dirs and path_context:
|
|
||||||
if path in self.excluded_dirs:
|
|
||||||
yield m.start(2), path + '/'
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
if parts[-1] in self.names and '.' in parts[-1]:
|
|
||||||
yield m.start(2), parts[-1]
|
|
||||||
|
|
||||||
|
|
||||||
def known(path, mention):
|
|
||||||
return any(path == file and mention == known_mention for file, known_mention, _ in KNOWN_REFERENCES)
|
|
||||||
|
|
||||||
|
|
||||||
def check_workflows(root):
|
|
||||||
"""Return the errors in the filters of the workflows and in the targets they run."""
|
|
||||||
errors = []
|
|
||||||
for workflow in WORKFLOWS:
|
|
||||||
expected = [p.replace('{workflow}', workflow) for p in PATHS]
|
|
||||||
actual = read_filter(root, workflow)
|
|
||||||
if actual != expected:
|
|
||||||
errors.append('{}: the pull_request paths filter differs from tools/check_path_filters/check_path_filters.py\n'
|
|
||||||
' expected: {}\n actual: {}'.format(workflow, expected, actual))
|
|
||||||
with open(os.path.join(root, workflow), encoding='utf-8') as f:
|
|
||||||
text = f.read()
|
|
||||||
errors += ['{}: runs {}, which reads excluded files; run it in repository_checks.yml'.format(workflow, target)
|
|
||||||
for target in UNFILTERED_TARGETS if re.search(r'\b' + target + r'\b', text)]
|
|
||||||
return errors
|
|
||||||
|
|
||||||
|
|
||||||
def scanned_text(root, path):
|
|
||||||
"""Return the text of a kept file without comment lines and without the filter itself, or None to skip it."""
|
|
||||||
# third-party code and Markdown files read nothing; this script lists the excluded files
|
|
||||||
if path.startswith('tests/thirdparty/') or path.endswith('.md') or path == SELF:
|
|
||||||
return None
|
|
||||||
try:
|
|
||||||
with open(os.path.join(root, path), encoding='utf-8') as f:
|
|
||||||
# a mention in a comment line reads nothing; keep the line numbers
|
|
||||||
text = COMMENT_LINE.sub('', f.read())
|
|
||||||
except (UnicodeDecodeError, OSError):
|
|
||||||
return None
|
|
||||||
if path in WORKFLOWS:
|
|
||||||
# the filter itself lists the excluded files
|
|
||||||
for p in PATHS:
|
|
||||||
text = text.replace(" - '{}'".format(p.replace('{workflow}', path)), '')
|
|
||||||
return text
|
|
||||||
|
|
||||||
|
|
||||||
def check_mentions(root, files, excluded, finder):
|
|
||||||
"""Return an error for every mention of an excluded file in a kept file that KNOWN_REFERENCES does not list."""
|
|
||||||
errors = []
|
|
||||||
for path in sorted(set(files) - excluded):
|
|
||||||
text = scanned_text(root, path)
|
|
||||||
reported = set()
|
|
||||||
for offset, mention in finder.find(text or ''):
|
|
||||||
if mention not in reported and not known(path, mention):
|
|
||||||
reported.add(mention)
|
|
||||||
line = text.count('\n', 0, offset) + 1
|
|
||||||
errors.append('{}:{}: mentions excluded {}; include it in PATHS if a compiler job reads it, '
|
|
||||||
'or add it to KNOWN_REFERENCES'.format(path, line, mention))
|
|
||||||
return errors
|
|
||||||
|
|
||||||
|
|
||||||
def check(root):
|
|
||||||
files = git_files(root)
|
|
||||||
union = [compile_filter(PATHS, w) for w in WORKFLOWS]
|
|
||||||
|
|
||||||
def is_excluded(path):
|
|
||||||
return not any(included(c, path) for c in union)
|
|
||||||
|
|
||||||
excluded = {f for f in files if is_excluded(f)}
|
|
||||||
errors = check_workflows(root) + check_mentions(root, files, excluded, Mentions(files, excluded, is_excluded))
|
|
||||||
for e in errors:
|
|
||||||
print(e)
|
|
||||||
if not errors:
|
|
||||||
print('path filters OK: {} of {} tracked files skip the compiler workflows'.format(len(excluded), len(files)))
|
|
||||||
return 1 if errors else 0
|
|
||||||
|
|
||||||
|
|
||||||
def explain(changed):
|
|
||||||
for workflow in WORKFLOWS:
|
|
||||||
compiled = compile_filter(PATHS, workflow)
|
|
||||||
hits = [f for f in changed if included(compiled, f)]
|
|
||||||
print('{}: {}'.format(workflow, 'runs ({})'.format(', '.join(hits[:3]) + (' ...' if len(hits) > 3 else ''))
|
|
||||||
if hits else 'skipped'))
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
|
||||||
parser.add_argument('--root', default=REPO_ROOT, help='repository root')
|
|
||||||
parser.add_argument('--changed', nargs='+', metavar='FILE', help='show which workflows these changed files run')
|
|
||||||
args = parser.parse_args()
|
|
||||||
return explain(args.changed) if args.changed else check(args.root)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
sys.exit(main())
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
# ci_jobs
|
|
||||||
|
|
||||||
The compiler jobs of [`ubuntu.yml`](../../.github/workflows/ubuntu.yml) are split into three sets, chosen by each job's
|
|
||||||
`if:` condition (see [How CI runs](https://json.nlohmann.me/community/quality_assurance/#how-ci-runs)):
|
|
||||||
|
|
||||||
- **PR set**: every push to a pull request, and every push to `develop`, `master`, and `release/*`;
|
|
||||||
- **full set**: after the PR set passed, on pull requests with the label "🚀 ready to merge" and on every push;
|
|
||||||
- **nightly set**: every night on `develop`, on every push to `master` and `release/*`, and when started by hand.
|
|
||||||
|
|
||||||
A matrix whose entries belong to different sets is split into one job per set: the full-set job gets the suffix
|
|
||||||
`_full`, the nightly-set job `_nightly`, and both share their steps with the PR-set job through YAML anchors.
|
|
||||||
|
|
||||||
[`ci_jobs.py`](ci_jobs.py) checks that
|
|
||||||
|
|
||||||
- every job of `ubuntu.yml` has the `if:` condition of exactly one set (the conditions are in `SETS`),
|
|
||||||
- every full-set job waits for (`needs:`) exactly the PR set, and `nightly_report` for exactly the nightly set, and
|
|
||||||
- the job table in [`quality_assurance.md`](../../docs/mkdocs/docs/community/quality_assurance.md) is up to date.
|
|
||||||
|
|
||||||
```shell
|
|
||||||
python3 -m venv venv && venv/bin/pip install -r tools/ci_jobs/requirements.txt
|
|
||||||
venv/bin/python tools/ci_jobs/ci_jobs.py # check
|
|
||||||
venv/bin/python tools/ci_jobs/ci_jobs.py --markdown # print the job table for the documentation
|
|
||||||
venv/bin/python tools/ci_jobs/ci_jobs.py --compare origin/develop # list configurations added or removed since develop
|
|
||||||
```
|
|
||||||
|
|
||||||
Use `--compare` when changing the workflows: a configuration that disappears fails it, so no compiler, version, or
|
|
||||||
standard is dropped by accident when jobs are split, merged, or moved between sets.
|
|
||||||
@@ -1,230 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""List and check the jobs of the compiler workflows and the set each job belongs to.
|
|
||||||
|
|
||||||
The jobs of a tiered workflow (ubuntu.yml) belong to one of three sets, chosen
|
|
||||||
by the job's `if:` condition:
|
|
||||||
|
|
||||||
- PR set: every pull request push and every push,
|
|
||||||
- full set: after the PR set passed, on pushes and on pull requests with the
|
|
||||||
"🚀 ready to merge" label,
|
|
||||||
- nightly set: nightly on develop, on pushes to master and release/*, and when
|
|
||||||
started by hand.
|
|
||||||
|
|
||||||
The jobs of the other workflows run on every push and pull request.
|
|
||||||
|
|
||||||
The script checks that every job of a tiered workflow belongs to exactly one
|
|
||||||
set, that the full set waits for the whole PR set, that the nightly report
|
|
||||||
waits for the whole nightly set, and that the job table in
|
|
||||||
docs/mkdocs/docs/community/quality_assurance.md is up to date.
|
|
||||||
|
|
||||||
--markdown print the job table for the documentation
|
|
||||||
--compare REF list the configurations (job and matrix values) added or
|
|
||||||
removed since the git revision REF; fails if one was removed
|
|
||||||
"""
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import collections
|
|
||||||
import itertools
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import subprocess # nosec B404
|
|
||||||
import sys
|
|
||||||
|
|
||||||
import yaml
|
|
||||||
|
|
||||||
REPO_ROOT = os.path.normpath(os.path.join(sys.path[0], '..', '..'))
|
|
||||||
DOCS = 'docs/mkdocs/docs/community/quality_assurance.md'
|
|
||||||
BEGIN, END = '<!-- ci_jobs: begin -->', '<!-- ci_jobs: end -->'
|
|
||||||
|
|
||||||
WORKFLOWS = ['.github/workflows/ubuntu.yml', '.github/workflows/macos.yml', '.github/workflows/windows.yml']
|
|
||||||
TIERED = ['.github/workflows/ubuntu.yml']
|
|
||||||
# --compare also counts the jobs of these workflows, so a job moved between workflows is not a removed one
|
|
||||||
COMPARED = WORKFLOWS + ['.github/workflows/repository_checks.yml', '.github/workflows/check_amalgamation.yml']
|
|
||||||
|
|
||||||
LABEL = '🚀 ready to merge'
|
|
||||||
SETS = collections.OrderedDict([
|
|
||||||
('PR set', "${{ github.event_name != 'schedule' && (github.event.action != 'labeled' || "
|
|
||||||
"github.event.label.name == '" + LABEL + "') }}"),
|
|
||||||
('full set', "${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' || "
|
|
||||||
"contains(github.event.pull_request.labels.*.name, '" + LABEL + "') }}"),
|
|
||||||
('nightly set', "${{ github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || "
|
|
||||||
"(github.event_name == 'push' && github.ref != 'refs/heads/develop') }}"),
|
|
||||||
])
|
|
||||||
UNTIERED = 'every push and PR'
|
|
||||||
REPORT_JOB = 'nightly_report'
|
|
||||||
|
|
||||||
# a job split by set keeps its name and gets one of these suffixes
|
|
||||||
SUFFIXES = {'full set': '_full', 'nightly set': '_nightly'}
|
|
||||||
|
|
||||||
|
|
||||||
def read(root, path, ref=None):
|
|
||||||
if ref is None:
|
|
||||||
with open(os.path.join(root, path), encoding='utf-8') as f:
|
|
||||||
return f.read()
|
|
||||||
# fixed command; ref is a git revision given on the command line
|
|
||||||
return subprocess.check_output(['git', 'show', '{}:{}'.format(ref, path)], cwd=root, # nosec B603, B607
|
|
||||||
stderr=subprocess.DEVNULL).decode('utf-8')
|
|
||||||
|
|
||||||
|
|
||||||
def add_include(combos, keys, inc):
|
|
||||||
"""Apply one include entry: extend the matching configurations, or add it as a new one."""
|
|
||||||
matching = [c for c in combos if all(c[k] == v for k, v in inc.items() if k in keys)]
|
|
||||||
for c in matching:
|
|
||||||
c.update({k: v for k, v in inc.items() if k not in keys})
|
|
||||||
if not matching:
|
|
||||||
combos.append(dict(inc))
|
|
||||||
|
|
||||||
|
|
||||||
def expand(matrix):
|
|
||||||
"""Return the configurations of a job's strategy.matrix like GitHub Actions does."""
|
|
||||||
if not matrix:
|
|
||||||
return [{}]
|
|
||||||
keys = [k for k in matrix if k not in ('include', 'exclude')]
|
|
||||||
combos = [dict(zip(keys, values)) for values in itertools.product(*(matrix[k] for k in keys))] if keys else []
|
|
||||||
for ex in matrix.get('exclude') or []:
|
|
||||||
combos = [c for c in combos if any(c.get(k) != v for k, v in ex.items())]
|
|
||||||
for inc in matrix.get('include') or []:
|
|
||||||
add_include(combos, keys, inc)
|
|
||||||
return combos
|
|
||||||
|
|
||||||
|
|
||||||
def jobs_of(root, workflow, ref=None):
|
|
||||||
"""Yield (job id, set, list of configurations) for every job of a workflow."""
|
|
||||||
data = yaml.safe_load(read(root, workflow, ref))
|
|
||||||
for job_id, job in data['jobs'].items():
|
|
||||||
condition = job.get('if')
|
|
||||||
job_set = UNTIERED if condition is None else next((s for s, c in SETS.items() if c == condition), None)
|
|
||||||
matrix = (job.get('strategy') or {}).get('matrix')
|
|
||||||
yield job_id, job_set, job, expand(matrix)
|
|
||||||
|
|
||||||
|
|
||||||
def base_id(job_id, job_set):
|
|
||||||
suffix = SUFFIXES.get(job_set)
|
|
||||||
return job_id[:-len(suffix)] if suffix and job_id.endswith(suffix) else job_id
|
|
||||||
|
|
||||||
|
|
||||||
def label(config):
|
|
||||||
return ', '.join(str(v) for k, v in config.items() if k != 'os' or len(config) == 1)
|
|
||||||
|
|
||||||
|
|
||||||
def configurations(root, ref=None):
|
|
||||||
"""Return a Counter of (job name without set suffix, configuration) over the compared workflows."""
|
|
||||||
result = collections.Counter()
|
|
||||||
for workflow in COMPARED:
|
|
||||||
try:
|
|
||||||
jobs = list(jobs_of(root, workflow, ref))
|
|
||||||
except (OSError, subprocess.CalledProcessError):
|
|
||||||
continue # the workflow does not exist in this revision
|
|
||||||
for job_id, job_set, _, configs in jobs:
|
|
||||||
if job_id == REPORT_JOB:
|
|
||||||
continue
|
|
||||||
for c in configs:
|
|
||||||
result[(base_id(job_id, job_set), tuple(sorted((k, str(v)) for k, v in c.items())))] += 1
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def markdown(root):
|
|
||||||
rows = []
|
|
||||||
order = list(SETS) + [UNTIERED]
|
|
||||||
for workflow in WORKFLOWS:
|
|
||||||
for job_id, job_set, _, configs in jobs_of(root, workflow):
|
|
||||||
if job_id == REPORT_JOB:
|
|
||||||
continue
|
|
||||||
labels = [label(c) for c in configs if c]
|
|
||||||
rows.append((order.index(job_set) if job_set in order else len(order), os.path.basename(workflow), job_id,
|
|
||||||
job_set or 'no set', '; '.join(labels) or '-'))
|
|
||||||
rows.sort(key=lambda r: (r[0], r[1]))
|
|
||||||
lines = ['| Set | Workflow | Job | Configurations |', '|-----|----------|-----|----------------|']
|
|
||||||
lines += ['| {} | {} | `{}` | {} |'.format(s, w, j, c) for _, w, j, s, c in rows]
|
|
||||||
return lines
|
|
||||||
|
|
||||||
|
|
||||||
def check_sets(workflow, jobs):
|
|
||||||
"""Return the errors in the sets of a tiered workflow's jobs and the jobs of each set."""
|
|
||||||
errors = []
|
|
||||||
by_set = collections.defaultdict(set)
|
|
||||||
for job_id, job_set, _, _ in jobs:
|
|
||||||
if job_id == REPORT_JOB:
|
|
||||||
continue
|
|
||||||
if job_set in SETS:
|
|
||||||
by_set[job_set].add(job_id)
|
|
||||||
else:
|
|
||||||
errors.append('{}: job {} has an if: condition of no set; use one of the conditions in '
|
|
||||||
'tools/ci_jobs/ci_jobs.py'.format(workflow, job_id))
|
|
||||||
return errors, by_set
|
|
||||||
|
|
||||||
|
|
||||||
def check_needs(workflow, jobs, by_set):
|
|
||||||
"""Return the errors in the needs: of a tiered workflow's jobs."""
|
|
||||||
required = {'full set': by_set['PR set'], 'PR set': set(), 'nightly set': set()}
|
|
||||||
errors = []
|
|
||||||
for job_id, job_set, job, _ in jobs:
|
|
||||||
needs = job.get('needs') or []
|
|
||||||
needs = {needs} if isinstance(needs, str) else set(needs)
|
|
||||||
expected = by_set['nightly set'] if job_id == REPORT_JOB else required.get(job_set, needs)
|
|
||||||
if needs != expected:
|
|
||||||
errors.append('{}: job {} must need exactly: {}'.format(workflow, job_id, ', '.join(sorted(expected)) or 'no job'))
|
|
||||||
return errors
|
|
||||||
|
|
||||||
|
|
||||||
def check_docs(root):
|
|
||||||
"""Return an error if the job table in the documentation is not up to date."""
|
|
||||||
text = read(root, DOCS)
|
|
||||||
block = re.search(re.escape(BEGIN) + r'\n(.*?)\n[ \t]*' + re.escape(END), text, re.S)
|
|
||||||
if not block:
|
|
||||||
return ['{}: markers {} and {} not found'.format(DOCS, BEGIN, END)]
|
|
||||||
indent = re.match(r'[ \t]*', block.group(1)).group(0)
|
|
||||||
if block.group(1) != '\n'.join(indent + line for line in markdown(root)):
|
|
||||||
return ['{}: the job table is out of date; replace it with the output of '
|
|
||||||
'python3 tools/ci_jobs/ci_jobs.py --markdown'.format(DOCS)]
|
|
||||||
return []
|
|
||||||
|
|
||||||
|
|
||||||
def check(root):
|
|
||||||
errors = []
|
|
||||||
for workflow in TIERED:
|
|
||||||
jobs = list(jobs_of(root, workflow))
|
|
||||||
set_errors, by_set = check_sets(workflow, jobs)
|
|
||||||
errors += set_errors + check_needs(workflow, jobs, by_set)
|
|
||||||
errors += check_docs(root)
|
|
||||||
|
|
||||||
for e in errors:
|
|
||||||
print(e)
|
|
||||||
if not errors:
|
|
||||||
counts = collections.Counter()
|
|
||||||
for workflow in WORKFLOWS:
|
|
||||||
for job_id, job_set, _, configs in jobs_of(root, workflow):
|
|
||||||
if job_id != REPORT_JOB:
|
|
||||||
counts[job_set] += len(configs)
|
|
||||||
print('CI jobs OK: ' + ', '.join('{} {}'.format(n, s) for s, n in counts.items()))
|
|
||||||
return 1 if errors else 0
|
|
||||||
|
|
||||||
|
|
||||||
def compare(root, ref):
|
|
||||||
before, after = configurations(root, ref), configurations(root)
|
|
||||||
removed, added = before - after, after - before
|
|
||||||
for title, diff in (('removed', removed), ('added', added)):
|
|
||||||
for job, config in sorted(diff.elements()):
|
|
||||||
print('{}: {} ({})'.format(title, job, ', '.join('{}={}'.format(k, v) for k, v in config)))
|
|
||||||
print('{} configurations before, {} after; {} removed, {} added'.format(
|
|
||||||
sum(before.values()), sum(after.values()), sum(removed.values()), sum(added.values())))
|
|
||||||
return 1 if removed else 0
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
|
||||||
parser.add_argument('--root', default=REPO_ROOT, help='repository root')
|
|
||||||
group = parser.add_mutually_exclusive_group()
|
|
||||||
group.add_argument('--markdown', action='store_true', help='print the job table for the documentation')
|
|
||||||
group.add_argument('--compare', metavar='REF', help='list the configurations added or removed since REF')
|
|
||||||
args = parser.parse_args()
|
|
||||||
if args.markdown:
|
|
||||||
print('\n'.join(markdown(args.root)))
|
|
||||||
return 0
|
|
||||||
if args.compare:
|
|
||||||
return compare(args.root, args.compare)
|
|
||||||
return check(args.root)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
sys.exit(main())
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
PyYAML==6.0.3
|
|
||||||
Reference in new issue
Block a user