Repository navigation
Flow pipeline #721
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Flow | |
| # Copyright 2023 Akamai Technologies, Inc. | |
| # | |
| # Licensed under the Apache License, Version 2.0 (the | |
| # "License"); you may not use this file except in | |
| # compliance with the License. You may obtain a copy | |
| # of the License at | |
| # | |
| # https://www.apache.org/licenses/LICENSE-2.0 | |
| # | |
| # Unless required by applicable law or agreed to in | |
| # writing, software distributed under the License is | |
| # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR | |
| # CONDITIONS OF ANY KIND, either express or implied. | |
| # See the License for the specific language governing | |
| # permissions and limitations under the License. | |
| name: Flow pipeline | |
| # TODO: Code reuse somehow versus Flow-IPC workflow counterpart file? Could apply to major parts of this file. | |
| # The copy/pasting and "see comment in other file" types of comments are a bit unseemly. | |
| # See comment in Flow-IPC workflow counterpart. We follow the same philosophy here. | |
| on: | |
| pull_request: | |
| branches: | |
| - main | |
| push: | |
| branches: | |
| - main | |
| tags: | |
| - v* | |
| workflow_dispatch: | |
| inputs: | |
| job-filter: | |
| description: >- | |
| If not blank: only jobs whose name contains this substring shall really run (e.g.: | |
| "clang-15-relwithdebinfo-asan-cxx17" = 1 cell; "asan" = the ASAN cells; "deps-newest" = the | |
| newest-dependency-versions cells; "doc" = the doc job). | |
| Others complete ~instantly with all steps skipped. | |
| required: false | |
| default: '' | |
| type: string | |
| jobs: | |
| # See comments in Flow-IPC workflow counterpart. We use same techniques. | |
| set-vars: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Set variables/constants for subsequent use | |
| run: | | |
| # Set variables/constants for subsequent use. | |
| outputs: | |
| doc-commit-message: (Commit by workflow script) Update generated documentation. | |
| # Impetus behind this is to gate whether the real jobs below actually need to run. Can of course also compute | |
| # other things as needed. | |
| # TODO: Already mentioned in a TODO above, but this stuff here is just copy/paste galore. Should really figure out | |
| # something to avoid it in this `setup` area. | |
| setup: | |
| needs: set-vars | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout VERSION file and tag history | |
| uses: actions/checkout@v6 | |
| with: | |
| sparse-checkout: VERSION | |
| fetch-depth: 0 # Get all history regarding tags. We'll need that for VERSION checking below. | |
| - name: Grab repository version from `VERSION` file | |
| id: repo_version | |
| run: | | |
| # Grab repository version from `VERSION` file. | |
| if [ ! -e VERSION ]; then | |
| echo '::error ::No VERSION file in repository root. One must exist.' | |
| exit 1 | |
| fi | |
| VERSION=`cat VERSION` | |
| echo "Determined version: [$VERSION]." | |
| echo "version=$VERSION" >> $GITHUB_OUTPUT | |
| - name: (On release creation only) Check repository version against release tag name | |
| if: success() && (!cancelled()) && startsWith(github.ref, 'refs/tags/v') | |
| run: | | |
| # Check repository version against release tag name. | |
| VERSION=${{ steps.repo_version.outputs.version }} | |
| TAG_VERSION=${GITHUB_REF#refs/tags/v} | |
| if [ "$VERSION" = "$TAG_VERSION" ]; then | |
| echo ":notice ::Tag name and repo version are an exact match. Looks good." | |
| elif [[ "$TAG_VERSION" == "$VERSION-"* ]]; then | |
| echo "::notice ::Tag name [v$TAG_VERSION] starts with repo version (pre-release). Allowed." | |
| else | |
| echo "::error ::VERSION file version v[$VERSION] must = release tag name [v$TAG_VERSION]." | |
| echo "::error ::Suggest deleting release and tag, fixing VERSION if needed, and re-creating release/tag." | |
| exit 1 | |
| fi | |
| # TODO: Would be nice to also grab release name and ensure it equals tag name v<...>. | |
| # Could be a separate step or just execute here if the above succeeded so far. Requires API fetch, so it's | |
| # a few lines. | |
| - name: (Except on release creation) Check repository version against already-existing release tag names | |
| if: success() && (!cancelled()) && (!startsWith(github.ref, 'refs/tags/v')) | |
| run: | | |
| # Check repo version against release tag name. | |
| # Do not fail on account of VERSION in this path however... just warn or gently inform. | |
| VERSION=${{ steps.repo_version.outputs.version }} | |
| if git rev-parse "v$VERSION" >/dev/null 2>&1; then | |
| echo "::warning ::Tag [v$VERSION] already exists. The repo/VERSION file will require "\ | |
| "a bump, before the next release. However this should be done *just* ahead of release creation and must "\ | |
| "follow semantic versioning conventions, especially regarding when to bump major/minor/patch components. "\ | |
| "When ready to do this consult [https://semver.org/] as needed." | |
| elif git tag | grep -q "^v$VERSION-"; then | |
| echo "::notice ::Since pre-release tag(s) [v$VERSION-*] already exist(s), but no tag "\ | |
| "[v$VERSION] exists, this repo state is likely intended for a later pre-release or official release. "\ | |
| "Cool! However, reminder: "\ | |
| "The repo/VERSION version should be decided/finalized *just* ahead of release creation and must "\ | |
| "follow semantic versioning conventions, especially regarding when to bump major/minor/patch components. "\ | |
| "When ready to do this consult [https://semver.org/] as needed." | |
| else | |
| echo "::notice ::Since no tag [v$VERSION] or [v$VERSION-*] exists, this repo state "\ | |
| "is likely intended for a future release. Cool! However, reminder: "\ | |
| "The repo/VERSION version should be decided/finalized *just* ahead of release creation and must "\ | |
| "follow semantic versioning conventions, especially regarding when to bump major/minor/patch components. "\ | |
| "When ready to do this consult [https://semver.org/] as needed." | |
| fi | |
| - id: compute_proceed_else_not | |
| name: Compute whether for main jobs to proceed or not | |
| # See comments in Flow-IPC workflow counterpart. We use same techniques. | |
| run: | | |
| # Compute whether for main jobs to proceed or not. | |
| TMP_MSG=/tmp/flow-pipeline-head-cmt-msg.txt | |
| cat <<'FLOW_PIPELINE_HEAD_CMD_MSG_EOF' > $TMP_MSG | |
| ${{ github.event.head_commit.message }} | |
| FLOW_PIPELINE_HEAD_CMD_MSG_EOF | |
| if [ '${{ github.ref }}' != 'refs/heads/main' ] || \ | |
| [ '${{ github.event_name }}' != 'push' ] || \ | |
| ! { head --lines=1 $TMP_MSG | fgrep -xq '${{ needs.set-vars.outputs.doc-commit-message }}'; }; then | |
| echo 'proceed-else-not=true' >> $GITHUB_OUTPUT | |
| else | |
| echo 'proceed-else-not=false' >> $GITHUB_OUTPUT | |
| echo 'The real jobs will not run: earlier `doc-and-release` job checked-in generated docs to `main`.' | |
| echo 'That is not a source change and requires no actual pipeline to execute.' | |
| fi | |
| outputs: | |
| proceed-else-not: ${{ steps.compute_proceed_else_not.outputs.proceed-else-not }} | |
| build-and-test: | |
| needs: [setup, set-vars] | |
| if: | | |
| needs.setup.outputs.proceed-else-not == 'true' | |
| strategy: | |
| fail-fast: false | |
| # Please read notes in the similar place in Flow-IPC workflow counterpart regarding the matrix's setup, | |
| # especially the bulk of it that discusses how the C++ standard setting (17, 20, etc.) is handled. | |
| # We omit that large comment here. (The shorter inline comments are simply repeated.) | |
| matrix: | |
| # First axis in basic 3D matrix is the 2 possible C++ standards. | |
| # For simplicity the Conan-profile-configuring script step simply keys off the `id` instead of our defining | |
| # other properties as for the other 2 more complex axes below (compiler, build config). | |
| cxx-std: | |
| - id: cxx17 | |
| - id: cxx20 | |
| # Second axis = the many compilers (gcc multiple versions, clang multiple versions). | |
| compiler: | |
| - id: gcc-9 | |
| name: gcc | |
| version: 9 | |
| c-path: /usr/bin/gcc-9 | |
| cpp-path: /usr/bin/g++-9 | |
| install: True | |
| - id: gcc-10 | |
| name: gcc | |
| version: 10 | |
| c-path: /usr/bin/gcc-10 | |
| cpp-path: /usr/bin/g++-10 | |
| - id: gcc-11 | |
| name: gcc | |
| version: 11 | |
| c-path: /usr/bin/gcc-11 | |
| cpp-path: /usr/bin/g++-11 | |
| - id: gcc-13 | |
| name: gcc | |
| version: 13 | |
| c-path: /usr/bin/gcc-13 | |
| cpp-path: /usr/bin/g++-13 | |
| install: True | |
| - id: clang-13 | |
| name: clang | |
| version: 13 | |
| c-path: /usr/bin/clang-13 | |
| cpp-path: /usr/bin/clang++-13 | |
| - id: clang-15 | |
| name: clang | |
| version: 15 | |
| c-path: /usr/bin/clang-15 | |
| cpp-path: /usr/bin/clang++-15 | |
| - id: clang-16 | |
| name: clang | |
| version: 16 | |
| c-path: /usr/bin/clang-16 | |
| cpp-path: /usr/bin/clang++-16 | |
| install: True | |
| - id: clang-17 | |
| name: clang | |
| version: 17 | |
| c-path: /usr/bin/clang-17 | |
| cpp-path: /usr/bin/clang++-17 | |
| install: True | |
| # Last axis are the many build/test configs. | |
| build-test-cfg: | |
| - id: debug | |
| conan-profile-build-type: Debug | |
| - id: release | |
| conan-profile-build-type: Release | |
| - id: relwithdebinfo | |
| conan-profile-build-type: RelWithDebInfo | |
| - id: minsizerel | |
| conan-profile-build-type: MinSizeRel | |
| # The sanitized (-*san) build/test configs follow. Here we follow Flow-IPC workflow counterpart. | |
| # This can be a non-trivial topic -- though by definition Flow's situation is simpler than Flow-IPC's, | |
| # as the latter includes the former entirely -- so if changing or trying to understand sanitizer-related | |
| # stuff, *please* check the Flow-IPC workflow counterpart file, particularly comments near each thing. | |
| # Keeping comments here light. | |
| # | |
| # Note that, as of this writing, MSAN support is present in Flow-IPC workflow counterpart but is disabled | |
| # for reasons explained therein. We therefore omit it here entirely; no need to have it taking up space here | |
| # too. | |
| - id: relwithdebinfo-asan | |
| conan-profile-build-type: RelWithDebInfo | |
| conan-profile-custom-conf: | | |
| tools.build:cflags = ["-fsanitize=address", "-fno-omit-frame-pointer"] | |
| tools.build:cxxflags = ["-fsanitize=address", "-fno-omit-frame-pointer"] | |
| tools.build:sharedlinkflags = ["-fsanitize=address"] | |
| tools.build:exelinkflags = ["-fsanitize=address"] | |
| conan-profile-custom-settings: | | |
| compiler.sanitizer = address | |
| conan-custom-settings-defs: | # Could we not copy/paste these 3x? | |
| data = { 'compiler': | |
| { 'gcc': { 'sanitizer': ['None', 'address', 'thread', 'memory', 'undefined'] }, | |
| 'clang': { 'sanitizer': ['None', 'address', 'thread', 'memory', 'undefined'] } } } | |
| sanitizer-name: asan | |
| no-lto: True | |
| - id: relwithdebinfo-ubsan | |
| conan-profile-build-type: RelWithDebInfo | |
| conan-profile-custom-conf: | | |
| tools.build:cflags = ["-fsanitize=undefined", "-fno-omit-frame-pointer"] | |
| tools.build:cxxflags = ["-fsanitize=undefined", "-fno-omit-frame-pointer"] | |
| tools.build:sharedlinkflags = ["-fsanitize=undefined"] | |
| tools.build:exelinkflags = ["-fsanitize=undefined"] | |
| conan-profile-custom-settings: | | |
| compiler.sanitizer = undefined | |
| conan-custom-settings-defs: | | |
| data = { 'compiler': | |
| { 'gcc': { 'sanitizer': ['None', 'address', 'thread', 'memory', 'undefined'] }, | |
| 'clang': { 'sanitizer': ['None', 'address', 'thread', 'memory', 'undefined'] } } } | |
| sanitizer-name: ubsan | |
| no-lto: True | |
| - id: relwithdebinfo-tsan | |
| conan-profile-build-type: RelWithDebInfo | |
| conan-profile-custom-conf: | | |
| tools.build:cflags = ["-fsanitize=thread"] | |
| tools.build:cxxflags = ["-fsanitize=thread"] | |
| tools.build:sharedlinkflags = ["-fsanitize=thread"] | |
| tools.build:exelinkflags = ["-fsanitize=thread"] | |
| conan-profile-custom-settings: | | |
| compiler.sanitizer = thread | |
| conan-custom-settings-defs: | | |
| data = { 'compiler': | |
| { 'gcc': { 'sanitizer': ['None', 'address', 'thread', 'memory', 'undefined'] }, | |
| 'clang': { 'sanitizer': ['None', 'address', 'thread', 'memory', 'undefined'] } } } | |
| sanitizer-name: tsan | |
| no-lto: True | |
| # 4th axis: dependency versions (`floor` = conanfile.py-contained verions = supported minimums; | |
| # `newest` = "early-warning" runs with newest conan-center versions of key dependencies). Explained in | |
| # Flow-IPC workflow counterpart. For Flow the only such dependency is Boost. Bump the `newest` version | |
| # when conan-center gets a newer one. (TODO: Some kind of reminder system to inform maintainer(s).) | |
| deps: | |
| - id: floor | |
| name-suffix: '' | |
| - id: newest | |
| name-suffix: -deps-newest | |
| conan-profile-replace-requires: | | |
| boost/*: boost/1.90.0 | |
| # Remove certain values from the 4D matrix so far. | |
| # The overall strategy is outlined in a large-ish comment above (or in Flow-IPC pipeline file; | |
| # referred to earlier above). | |
| exclude: | |
| # Firstly the exclusions from the C++17 sub-matrix (the other being the C++20 sub-matrix). | |
| # Again, these exclusions are explained in Flow-IPC workflow counterpart. | |
| - cxx-std: { id: cxx17 } | |
| compiler: { id: gcc-9 } | |
| build-test-cfg: { id: relwithdebinfo-asan } | |
| - cxx-std: { id: cxx17 } | |
| compiler: { id: gcc-10 } | |
| build-test-cfg: { id: relwithdebinfo-asan } | |
| - cxx-std: { id: cxx17 } | |
| compiler: { id: gcc-11 } | |
| build-test-cfg: { id: relwithdebinfo-asan } | |
| - cxx-std: { id: cxx17 } | |
| compiler: { id: gcc-13 } | |
| build-test-cfg: { id: relwithdebinfo-asan } | |
| - cxx-std: { id: cxx17 } | |
| compiler: { id: clang-13 } | |
| build-test-cfg: { id: relwithdebinfo-asan } | |
| - cxx-std: { id: cxx17 } | |
| compiler: { id: gcc-9 } | |
| build-test-cfg: { id: relwithdebinfo-ubsan } | |
| - cxx-std: { id: cxx17 } | |
| compiler: { id: gcc-10 } | |
| build-test-cfg: { id: relwithdebinfo-ubsan } | |
| - cxx-std: { id: cxx17 } | |
| compiler: { id: gcc-11 } | |
| build-test-cfg: { id: relwithdebinfo-ubsan } | |
| - cxx-std: { id: cxx17 } | |
| compiler: { id: gcc-13 } | |
| build-test-cfg: { id: relwithdebinfo-ubsan } | |
| - cxx-std: { id: cxx17 } | |
| compiler: { id: clang-13 } | |
| build-test-cfg: { id: relwithdebinfo-ubsan } | |
| - cxx-std: { id: cxx17 } | |
| compiler: { id: gcc-9 } | |
| build-test-cfg: { id: relwithdebinfo-tsan } | |
| - cxx-std: { id: cxx17 } | |
| compiler: { id: gcc-10 } | |
| build-test-cfg: { id: relwithdebinfo-tsan } | |
| - cxx-std: { id: cxx17 } | |
| compiler: { id: gcc-11 } | |
| build-test-cfg: { id: relwithdebinfo-tsan } | |
| - cxx-std: { id: cxx17 } | |
| compiler: { id: gcc-13 } | |
| build-test-cfg: { id: relwithdebinfo-tsan } | |
| - cxx-std: { id: cxx17 } | |
| compiler: { id: clang-13 } | |
| build-test-cfg: { id: relwithdebinfo-tsan } | |
| # Now the exclusions from the C++20 sub-matrix. | |
| # Again, these exclusions are explained in Flow-IPC workflow counterpart. | |
| # - First, simply exclude all gccs but gcc-13 and all clangs but clang-17. | |
| - cxx-std: { id: cxx20 } | |
| compiler: { id: gcc-9 } | |
| - cxx-std: { id: cxx20 } | |
| compiler: { id: gcc-10 } | |
| - cxx-std: { id: cxx20 } | |
| compiler: { id: gcc-11 } | |
| - cxx-std: { id: cxx20 } | |
| compiler: { id: clang-13 } | |
| - cxx-std: { id: cxx20 } | |
| compiler: { id: clang-15 } | |
| - cxx-std: { id: cxx20 } | |
| compiler: { id: clang-16 } | |
| # - Second, among the remaining C++20 configs eliminate all but debug and relwithdebinfo ones. | |
| - cxx-std: { id: cxx20 } | |
| build-test-cfg: { id: release } | |
| - cxx-std: { id: cxx20 } | |
| build-test-cfg: { id: minsizerel } | |
| - cxx-std: { id: cxx20 } | |
| build-test-cfg: { id: relwithdebinfo-asan } | |
| - cxx-std: { id: cxx20 } | |
| build-test-cfg: { id: relwithdebinfo-ubsan } | |
| - cxx-std: { id: cxx20 } | |
| build-test-cfg: { id: relwithdebinfo-tsan } | |
| # Now the exclusions from the `deps: newest` sub-matrix: keep only gcc-13 and clang-17, only debug and | |
| # relwithdebinfo, only C++17 (net: 4 cells). Explained in Flow-IPC workflow counterpart. | |
| - deps: { id: newest } | |
| compiler: { id: gcc-9 } | |
| - deps: { id: newest } | |
| compiler: { id: gcc-10 } | |
| - deps: { id: newest } | |
| compiler: { id: gcc-11 } | |
| - deps: { id: newest } | |
| compiler: { id: clang-13 } | |
| - deps: { id: newest } | |
| compiler: { id: clang-15 } | |
| - deps: { id: newest } | |
| compiler: { id: clang-16 } | |
| - deps: { id: newest } | |
| build-test-cfg: { id: release } | |
| - deps: { id: newest } | |
| build-test-cfg: { id: minsizerel } | |
| - deps: { id: newest } | |
| build-test-cfg: { id: relwithdebinfo-asan } | |
| - deps: { id: newest } | |
| build-test-cfg: { id: relwithdebinfo-ubsan } | |
| - deps: { id: newest } | |
| build-test-cfg: { id: relwithdebinfo-tsan } | |
| - deps: { id: newest } | |
| cxx-std: { id: cxx20 } | |
| # Not using ubuntu-latest, so as to avoid surprises with OS upgrades and such. | |
| runs-on: ubuntu-22.04 | |
| # The GitHub default job timeout is 6 hours -- extreme. Bound it to something reasonable, just in case | |
| # something hangs. | |
| timeout-minutes: 120 | |
| name: ${{ matrix.compiler.id }}-${{ matrix.build-test-cfg.id }}-${{ matrix.cxx-std.id }}${{ matrix.deps.name-suffix }} | |
| env: | |
| # Whether this matrix cell shall really run, per the workflow_dispatch job-filter input. See comments in | |
| # Flow-IPC workflow counterpart. We use same techniques. | |
| cell-selected: ${{ ((github.event.inputs.job-filter || '') == '') || contains(format('{0}-{1}-{2}{3}', matrix.compiler.id, matrix.build-test-cfg.id, matrix.cxx-std.id, matrix.deps.name-suffix), github.event.inputs.job-filter) }} | |
| build-dir: ${{ github.workspace }}/build/${{ matrix.build-test-cfg.conan-profile-build-type }} | |
| install-dir: ${{ github.workspace }}/install/${{ matrix.build-test-cfg.conan-profile-build-type }}/usr/local | |
| # For the remaining env entries please see comments in Flow-IPC workflow counterpart. We use same techniques. | |
| san-suppress-cfg-file: ${{ github.workspace }}/install/${{ matrix.build-test-cfg.conan-profile-build-type }}/usr/local/bin/san_suppressions.cfg | |
| san-suppress-cfg-in-file1: sanitize/${{ matrix.build-test-cfg.sanitizer-name }}/suppressions_${{ matrix.compiler.name }}.cfg | |
| san-suppress-cfg-in-file2: sanitize/${{ matrix.build-test-cfg.sanitizer-name }}/suppressions_${{ matrix.compiler.name }}_${{ matrix.compiler.version }}.cfg | |
| setup-tests-env: | | |
| if [ '${{ matrix.build-test-cfg.sanitizer-name }}' = asan ]; then | |
| export SAN_SUPP=1 | |
| export SAN_SUPP_CFG=${{ github.workspace }}/install/${{ matrix.build-test-cfg.conan-profile-build-type }}/usr/local/bin/san_suppressions.cfg | |
| export ASAN_OPTIONS='disable_coredump=0' | |
| export LSAN_OPTIONS="suppressions=$SAN_SUPP_CFG" | |
| echo "ASAN_OPTIONS = [$ASAN_OPTIONS]." | |
| echo "LSAN_OPTIONS = [$LSAN_OPTIONS]." | |
| elif [ '${{ matrix.build-test-cfg.sanitizer-name }}' = ubsan ]; then | |
| export SAN_SUPP=1 | |
| export SAN_SUPP_CFG=${{ github.workspace }}/install/${{ matrix.build-test-cfg.conan-profile-build-type }}/usr/local/bin/san_suppressions.cfg | |
| export UBSAN_OPTIONS="disable_coredump=0 print_stacktrace=1 suppressions=$SAN_SUPP_CFG" | |
| echo "UBSAN_OPTIONS = [$UBSAN_OPTIONS]." | |
| elif [ '${{ matrix.build-test-cfg.sanitizer-name }}' = tsan ]; then | |
| export SAN_SUPP=1 | |
| export SAN_SUPP_CFG=${{ github.workspace }}/install/${{ matrix.build-test-cfg.conan-profile-build-type }}/usr/local/bin/san_suppressions.cfg | |
| export TSAN_OPTIONS="disable_coredump=0 second_deadlock_stack=1 suppressions=$SAN_SUPP_CFG" | |
| echo "TSAN_OPTIONS = [$TSAN_OPTIONS]." | |
| fi | |
| # TODO: Add more *SAN here. | |
| if [ "$SAN_SUPP" != '' ]; then | |
| echo 'Sanitizer [${{ matrix.build-test-cfg.sanitizer-name }}] suppressions cfg contents:' | |
| echo '[[[ file--' | |
| cat $SAN_SUPP_CFG | |
| echo '--file ]]]' | |
| fi | |
| setup-run-env: | | |
| if [ '${{ matrix.build-test-cfg.sanitizer-name }}' = asan ]; then | |
| export BUILT_CMD_PREFIX='setarch -R' | |
| echo 'Shall use `setarch -R` to disable ASLR to avoid ASAN-built binary misbehavior.' | |
| elif [ '${{ matrix.build-test-cfg.sanitizer-name }}' = tsan ]; then | |
| export BUILT_CMD_PREFIX='setarch -R' | |
| echo 'Shall use `setarch -R` to disable ASLR to avoid TSAN-built binary misbehavior.' | |
| fi | |
| steps: | |
| - name: Update available software list for apt-get | |
| if: env.cell-selected == 'true' | |
| run: | | |
| # Update available software list for apt-get. | |
| lsb_release -a | |
| sudo apt-get update | |
| - name: Install clang compiler | |
| if: | | |
| (env.cell-selected == 'true') | |
| && matrix.compiler.install && (matrix.compiler.name == 'clang') | |
| run: | | |
| # Install clang compiler. | |
| wget https://apt.llvm.org/llvm.sh | |
| chmod u+x llvm.sh | |
| sudo ./llvm.sh ${{ matrix.compiler.version }} | |
| - name: Install gcc compiler | |
| if: | | |
| (env.cell-selected == 'true') | |
| && matrix.compiler.install && (matrix.compiler.name == 'gcc') | |
| run: | | |
| # Install gcc compiler. | |
| sudo apt-get install -y software-properties-common | |
| sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y | |
| sudo apt-get update | |
| sudo apt-get install -y gcc-${{ matrix.compiler.version }} g++-${{ matrix.compiler.version }} | |
| - name: Checkout `flow` repository | |
| if: env.cell-selected == 'true' | |
| uses: actions/checkout@v6 | |
| - name: Prepare Python virtual environment containing Conan | |
| if: env.cell-selected == 'true' | |
| run: | | |
| # Prepare Python virtual environment containing Conan. | |
| # (See comments in Flow-IPC workflow counterpart's analogous step. We use same techniques.) | |
| pip install virtualenv | |
| virtualenv ${{ github.workspace }}/conan_virtual_env | |
| source ${{ github.workspace }}/conan_virtual_env/bin/activate | |
| pip install pip-tools | |
| pip-compile ${{ github.workspace }}/.github/workflows/requirements.in | |
| pip-sync ${{ github.workspace }}/.github/workflows/requirements.txt | |
| - name: Add custom settings for Conan packages | |
| if: | | |
| (env.cell-selected == 'true') | |
| && matrix.build-test-cfg.conan-custom-settings-defs | |
| run: | | |
| # Add custom settings for Conan packages. | |
| # Conan keeps custom settings in settings_user.yml, an overlay merged onto the built-in settings.yml; | |
| # so simply write the overlay dict from our matrix higher-up. | |
| # (`conan config home` auto-creates the home dir if needed.) | |
| source ${{ github.workspace }}/conan_virtual_env/bin/activate | |
| CONAN_USER_SETTINGS_PATH=$(conan config home)/settings_user.yml | |
| python -c " | |
| import yaml | |
| ${{ matrix.build-test-cfg.conan-custom-settings-defs }} | |
| with open('$CONAN_USER_SETTINGS_PATH', 'w') as file: | |
| yaml.dump(data, file) | |
| " | |
| # Note: The C[XX]FLAGS and linker-flags supplied via ${{ matrix.build-test-cfg.conan-profile-custom-conf }} | |
| # affect not just our objects/libs/executables but also 3rd party dependency builds. For the interesting | |
| # implications please see notes atop the analogous step in Flow-IPC workflow counterpart. | |
| - name: Create Conan profile | |
| if: env.cell-selected == 'true' | |
| run: | | |
| # Create Conan profile. | |
| cat <<'EOF' > conan_profile | |
| [settings] | |
| compiler = ${{ matrix.compiler.name }} | |
| compiler.version = ${{ matrix.compiler.version }} | |
| compiler.cppstd = ${{ ((matrix.cxx-std.id == 'cxx20') && '20') || '17' }} | |
| # TODO: Consider testing with LLVM-libc++ also (with clang anyway). | |
| compiler.libcxx = libstdc++11 | |
| arch = x86_64 | |
| os = Linux | |
| build_type = ${{ matrix.build-test-cfg.conan-profile-build-type }} | |
| ${{ matrix.build-test-cfg.conan-profile-custom-settings }} | |
| [conf] | |
| tools.build:compiler_executables = {"c": "${{ matrix.compiler.c-path }}", "cpp": "${{ matrix.compiler.cpp-path }}"} | |
| ${{ matrix.build-test-cfg.conan-profile-custom-conf }} | |
| [buildenv] | |
| CC = ${{ matrix.compiler.c-path }} | |
| CXX = ${{ matrix.compiler.cpp-path }} | |
| [options] | |
| flow/*:build = True | |
| flow/*:doc = False | |
| # `0` here as of this writing would effectively mean `17` too. Specifying `17` explicitly has the same | |
| # effect in the end but goes down a slightly different code-path in FlowLikeCodeGenerate.cmake. It's not | |
| # a huge deal... but it's a little nice to cover more paths, so let's do the `0` path when applicable. | |
| flow/*:build_cxx_std = ${{ ((matrix.cxx-std.id == 'cxx20') && '20') || '0' }} | |
| EOF | |
| # `deps: newest` cells only: swap the conanfile.py-pinned dependency versions for the newest ones. | |
| # See Flow-IPC workflow counterpart. | |
| if [ '${{ matrix.deps.id }}' = newest ]; then | |
| cat <<'EOF' >> conan_profile | |
| [replace_requires] | |
| ${{ matrix.deps.conan-profile-replace-requires }} | |
| EOF | |
| fi | |
| # Cache the Conan-built dependencies (Boost et al): a fresh runner otherwise rebuilds them from source every | |
| # run. See explanatory comments atop the analogous steps in Flow-IPC workflow counterpart. We use same | |
| # techniques (including the restore/save split, with the save placed right after the dep-install step, so | |
| # even a run whose later steps fail still primes the cache; and the flag-snapshot guard in the dep-install | |
| # step, which makes the restore-keys fallback safe -- see the Caution comment there). | |
| - name: Restore Conan package cache | |
| if: env.cell-selected == 'true' | |
| id: conan-cache-restore | |
| uses: actions/cache/restore@v6 | |
| with: | |
| path: ~/.conan2/p | |
| key: conan2-data-${{ matrix.compiler.id }}-${{ matrix.build-test-cfg.id }}-${{ matrix.cxx-std.id }}${{ matrix.deps.name-suffix }}-${{ hashFiles('conan_profile', 'conanfile.py') }} | |
| restore-keys: | | |
| conan2-data-${{ matrix.compiler.id }}-${{ matrix.build-test-cfg.id }}-${{ matrix.cxx-std.id }}${{ matrix.deps.name-suffix }}- | |
| - name: Install Flow dependencies with Conan using the profile | |
| if: env.cell-selected == 'true' | |
| run: | | |
| # Install Flow dependencies with Conan using the profile. | |
| source ${{ github.workspace }}/conan_virtual_env/bin/activate | |
| ${{ env.setup-run-env }} | |
| # Flag-snapshot guard. For explanation see comments in Flow-IPC workflow counterpart -- both in its | |
| # analogous step and atop its conan-cache-restore step. We use same techniques. | |
| if [ '${{ steps.conan-cache-restore.outputs.cache-hit }}' != 'true' ]; then | |
| flag_sections() { awk '/^\[/{on=0} /^\[(conf|buildenv)\]/{on=1} on' "$1"; } | |
| SNAPSHOT=~/.conan2/p/.profile-snapshot | |
| if [ ! -f $SNAPSHOT ] || ! diff <(flag_sections conan_profile) <(flag_sections $SNAPSHOT); then | |
| echo 'Restored cache (if any) lacks a flag-match guarantee; evicting its packages (they will rebuild).' | |
| conan remove '*' -c 2> /dev/null || true | |
| fi | |
| fi | |
| # At least `configure` mini-programs are built with our build-settings, so $BUILT_CMD_PREFIX is required. | |
| $BUILT_CMD_PREFIX conan install . -pr:a ./conan_profile --build=missing || FAILED=yep | |
| # Dep build failure is easy to diagnose from stdout/err, unless it happens during a `configure`; | |
| # then it'll often print something insane like "checking size of void *... 0" and suggest looking at | |
| # config.log... which will usually reveal the true problem, like a seg-fault or TSAN-triggered problem | |
| # running the configure-generated test program. So let's get all the `config.log`s we can find | |
| # and put them where later the log artifact tarball will be sourced; recreate the same dir structure as | |
| # in the Conan dep build area, but include only the `config.log` files. Note: This is probably only truly | |
| # useful if indeed the above command fails; but can't hurt to include it regardless. | |
| SRC=~/.conan2/p | |
| DST=${{ env.install-dir }}/bin/logs/dep_build/conan-data | |
| find $SRC -name config.log | while read -r config_log; do | |
| # Remove the $SRC part of the path to get a relative path. | |
| REL_PATH="${config_log#$SRC/}" | |
| # Determine the directory path within $DST where the config.log should be copied. | |
| DST_DIR="$DST/${REL_PATH%/*}" | |
| mkdir -p "$DST_DIR" | |
| cp -v "$config_log" "$DST_DIR" | |
| done | |
| # Prune the source/build trees, which Conan retains after building from source; only the | |
| # built packages and recipes are needed by this (or later) runs. Keeps cache size down in next step; | |
| # cache is LRU-based but limited enough to matter in practice as of this writing. | |
| conan cache clean '*' --source --build | |
| if [ "$FAILED" != yep ]; then | |
| # Record the flags this cache's contents were built under, for the flag-snapshot guard (see above). | |
| cp conan_profile ~/.conan2/p/.profile-snapshot | |
| fi | |
| [ "$FAILED" != yep ] | |
| # (See comment atop the Restore counterpart above.) Skipped on an exact-key restore: the cache would be | |
| # bit-identical to what is already stored. | |
| - name: Save Conan package cache | |
| if: | | |
| (env.cell-selected == 'true') | |
| && (steps.conan-cache-restore.outputs.cache-hit != 'true') | |
| uses: actions/cache/save@v6 | |
| with: | |
| path: ~/.conan2/p | |
| key: ${{ steps.conan-cache-restore.outputs.cache-primary-key }} | |
| - name: Build library and demos/tests with Conan | |
| if: env.cell-selected == 'true' | |
| run: | | |
| # Build libraries and demos/tests with Conan. | |
| source ${{ github.workspace }}/conan_virtual_env/bin/activate | |
| ${{ env.setup-run-env }} | |
| # Prefixing with $BUILT_CMD_PREFIX here too, in case the build ends up running some binary built with our | |
| # build-settings. (In Flow-IPC workflow counterpart it definitely does -- the capnp compiler binary.) | |
| # (`conan build` recomputes the dep-graph and hence needs the profile, just like `conan install`.) | |
| $BUILT_CMD_PREFIX conan build . -pr:a ./conan_profile | |
| - name: Install built targets with CMake | |
| if: env.cell-selected == 'true' | |
| run: | | |
| # Install built targets with CMake. | |
| cmake --install ${{ env.build-dir }} --prefix ${{ env.install-dir }} | |
| # Save runner space: blow away build dir after install. | |
| rm -rf ${{ env.build-dir }} | |
| # From now on use !cancelled() to try to run any test/demo that exists regardless | |
| # of preceding failures if any. Same-ish (always()) with the log-upload at the end. | |
| # Worst-case they'll all fail in super-basic ways and immediately; no extra harm done. | |
| # From now on save logs in install-dir/bin/logs. They will be tarred up and uploaded | |
| # as artifacts at the end. Please see comments in Flow-IPC workflow counterpart. | |
| # We use the same techniques and reasoning. Those may seem overkill given the small number | |
| # of tests/demos below and their small amount of output (as of this writing); but this can | |
| # and will grow. The techniques will still apply. | |
| # Here, as in the other test below, we assemble a suppressions file in case this is a sanitized run. | |
| # See comments in Flow-IPC workflow counterpart (atop its first test step). We use same techniques. | |
| - name: Run test/demo [NetFlow echo] | |
| if: | | |
| (env.cell-selected == 'true') | |
| && (!cancelled()) | |
| run: | | |
| # Run test/demo [NetFlow echo]. | |
| cd ${{ env.install-dir }}/bin | |
| OUT_DIR=logs/net_flow_echo | |
| mkdir -p $OUT_DIR | |
| SUPP_DIR_A=${{ github.workspace }}/src | |
| { cat $SUPP_DIR_A/${{ env.san-suppress-cfg-in-file1 }} $SUPP_DIR_A/${{ env.san-suppress-cfg-in-file2 }} \ | |
| > ${{ env.san-suppress-cfg-file }} 2> /dev/null; } || true | |
| ${{ env.setup-tests-env }} | |
| ${{ env.setup-run-env }} | |
| $BUILT_CMD_PREFIX ./net_flow_echo_srv.exec 8888 localhost > $OUT_DIR/srv.console.log 2>&1 & | |
| sleep 1 | |
| if $BUILT_CMD_PREFIX ./net_flow_echo_cli.exec 1 127.0.01 8888 'Hello world!' > $OUT_DIR/cli.console.log 2>&1; \ | |
| then EC=0; else EC=$?; fi | |
| echo "Client finished with code [$EC]." | |
| echo 'Server may print exception due to SIGTERM interruption; that is fine.' | |
| kill `pidof $PWD/net_flow_echo_srv.exec` | |
| # The executables also produce Flow logs with these hard-coded names in CWD. | |
| touch flow_echo_srv.log # Create if needed just in case. | |
| touch flow_echo_cli.log | |
| mv flow_echo_srv.log $OUT_DIR/srv.log | |
| mv flow_echo_cli.log $OUT_DIR/cli.log | |
| [ $EC -eq 0 ] | |
| - name: Run unit tests | |
| if: | | |
| (env.cell-selected == 'true') | |
| && (!cancelled()) | |
| run: | | |
| # Run unit tests. | |
| cd ${{ env.install-dir }}/bin | |
| # Some newline issues with the possible additional args; so need to make a wrapper script | |
| # and then redirect, as desired, its output. | |
| cat <<'EOF' > ${{ env.install-dir }}/bin/run_unit_test.sh | |
| $BUILT_CMD_PREFIX ./libflow_unit_test.exec "$@" | |
| EOF | |
| RUN_IT='/usr/bin/bash -e ${{ env.install-dir }}/bin/run_unit_test.sh' | |
| OUT_DIR=logs/libflow_unit_test | |
| mkdir -p $OUT_DIR | |
| SUPP_DIR_A=${{ github.workspace }}/src | |
| # As of this writing there are TSAN suppressions for this test specifically. TODO: Revisit them; and then this. | |
| # Update: Now there are also ASAN (LSAN) suppressions. These are likely permanent as of this writing. | |
| # Reminder: the following construction handles suppression file(s) from *any* relevant sanitizer type (if any). | |
| SUPP_DIR_OWN=${{ github.workspace }}/test/suite/unit_test | |
| { cat $SUPP_DIR_A/${{ env.san-suppress-cfg-in-file1 }} $SUPP_DIR_A/${{ env.san-suppress-cfg-in-file2 }} \ | |
| $SUPP_DIR_OWN/${{ env.san-suppress-cfg-in-file1 }} $SUPP_DIR_OWN/${{ env.san-suppress-cfg-in-file2 }} \ | |
| > ${{ env.san-suppress-cfg-file }} 2> /dev/null; } || true | |
| ${{ env.setup-tests-env }} | |
| ${{ env.setup-run-env }} | |
| # Sensitive benchmarks in this setting should run and be warned about, if they "fail," but they should not | |
| # fail the test. | |
| $RUN_IT --do-not-fail-benchmarks > $OUT_DIR/console.log 2>&1 | |
| - name: Check test/demo logs for non-fatal sanitizer error(s) | |
| if: | | |
| (env.cell-selected == 'true') | |
| && (!cancelled()) && (matrix.build-test-cfg.sanitizer-name == 'ubsan') | |
| run: | | |
| # Check test/demo logs for non-fatal sanitizer error(s). | |
| cd ${{ env.install-dir }}/bin/logs | |
| # grep returns 0 if 1+ found, 1 if none found, 2+ on error. So check results explicitly instead of -e. | |
| # Namely, for us, the only OK result is an empty stdout and empty stderr. | |
| # Otherwise either grep failed (unlikely) or found 1+ problems; either way redirection target will | |
| # be not-empty, and we will force failure. | |
| { grep 'SUMMARY: UndefinedBehaviorSanitizer:' `find . -type f` > san_failure_summaries.txt 2>&1; } || true | |
| if [ -s san_failure_summaries.txt ]; then | |
| echo 'Error(s) found. Pipeline will fail. Failures summarized below.' | |
| echo 'Please peruse uploaded log artifacts, resolve the issues, and run pipeline again.' | |
| echo '[[[ file--' | |
| cat san_failure_summaries.txt | |
| echo '--file ]]]' | |
| false | |
| fi | |
| echo 'No errors found in logs.' | |
| - name: Package test/demo logs tarball | |
| if: | | |
| (env.cell-selected == 'true') | |
| && always() | |
| run: | | |
| # Package test/demo logs tarball. | |
| cd ${{ env.install-dir }}/bin | |
| tar cvzf logs.tgz logs | |
| rm -rf logs # Save runner space. | |
| - name: Upload test/demo logs (please inspect if failure(s) seen above) | |
| if: | | |
| (env.cell-selected == 'true') | |
| && always() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: flow-test-logs-${{ matrix.compiler.id }}-${{ matrix.build-test-cfg.id }}-${{ matrix.cxx-std.id }}${{ matrix.deps.name-suffix }} | |
| path: ${{ env.install-dir }}/bin/logs.tgz | |
| # TODO: See the crash-debuggability TODO at the analogous spot in Flow-IPC workflow counterpart; it applies | |
| # here equally. | |
| doc-and-release: | |
| needs: [setup, set-vars] | |
| # Re. job-filter: this job's matrix has exactly 1 cell, so we can just use its (constant) name directly -- | |
| # and at job level, unlike in build-and-test. (Keep the literal in sync with `name` below.) | |
| if: | | |
| (needs.setup.outputs.proceed-else-not == 'true') | |
| && (((github.event.inputs.job-filter || '') == '') | |
| || contains('doc-clang-15-release', github.event.inputs.job-filter)) | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| compiler: | |
| # Pick a reasonably modern but pre-installed compiler for building Doxygen/etc. | |
| - id: clang-15 | |
| name: clang | |
| version: 15 | |
| c-path: /usr/bin/clang-15 | |
| cpp-path: /usr/bin/clang++-15 | |
| build-cfg: | |
| - id: release | |
| conan-profile-build-type: Release | |
| conan-preset: release | |
| runs-on: ubuntu-22.04 | |
| name: doc-${{ matrix.compiler.id }}-${{ matrix.build-cfg.id }} | |
| steps: | |
| - name: Update available software list for apt-get | |
| run: sudo apt-get update | |
| - name: Install Flow dependencies (like Graphviz) with apt-get | |
| run: sudo apt-get install -y graphviz | |
| - name: Checkout `flow` repository | |
| uses: actions/checkout@v6 | |
| with: | |
| # Avoids the duplicate-authorization-error in (not-frequently-invoked) doc_check_in step later down. | |
| persist-credentials: false | |
| - name: Prepare Python virtual environment containing Conan | |
| run: | | |
| # Prepare Python virtual environment containing Conan. | |
| # (See comment in the build-and-test job's analogous step.) | |
| pip install virtualenv | |
| virtualenv ${{ github.workspace }}/conan_virtual_env | |
| source ${{ github.workspace }}/conan_virtual_env/bin/activate | |
| pip install pip-tools | |
| pip-compile ${{ github.workspace }}/.github/workflows/requirements.in | |
| pip-sync ${{ github.workspace }}/.github/workflows/requirements.txt | |
| # See comments in Flow-IPC workflow counterpart. We use same techniques. | |
| - name: (On release creation only) Signal Pages about release (web site should update) | |
| if: success() && (!cancelled()) && startsWith(github.ref, 'refs/tags/v') | |
| run: | | |
| # Signal Pages about release (web site should update). | |
| curl --fail-with-body -X POST \ | |
| -H 'Accept: application/vnd.github.v3+json' \ | |
| -H 'Authorization: token ${{ secrets.GIT_BOT_PAT }}' \ | |
| 'https://api.github.com/repos/Flow-IPC/flow-ipc.github.io/dispatches' \ | |
| -d '{"event_type": "flow-sync-doc-event", "client_payload": {"version": "${{ github.ref_name }}"}}' | |
| - name: Create Conan profile | |
| run: | | |
| # Create Conan profile. | |
| cat <<EOF > conan_profile | |
| [settings] | |
| compiler = ${{ matrix.compiler.name }} | |
| compiler.version = ${{ matrix.compiler.version }} | |
| compiler.cppstd = 17 | |
| compiler.libcxx = libstdc++11 | |
| arch = x86_64 | |
| os = Linux | |
| build_type = ${{ matrix.build-cfg.conan-profile-build-type }} | |
| [conf] | |
| tools.build:compiler_executables = { "c": "${{ matrix.compiler.c-path }}", "cpp": "${{ matrix.compiler.cpp-path }}" } | |
| [buildenv] | |
| CC = ${{ matrix.compiler.c-path }} | |
| CXX = ${{ matrix.compiler.cpp-path }} | |
| [options] | |
| flow/*:build = False | |
| flow/*:doc = True | |
| EOF | |
| # Cache Conan-built deps (here: mainly Doxygen). See longer explanation atop the analogous steps in the | |
| # build-and-test job. (Its flag-snapshot guard is deliberately not replicated here; see reasoning in | |
| # Flow-IPC workflow counterpart's analogous spot.) | |
| - name: Restore Conan package cache | |
| id: conan-cache-restore | |
| uses: actions/cache/restore@v6 | |
| with: | |
| path: ~/.conan2/p | |
| key: conan2-data-doc-${{ hashFiles('conan_profile', 'conanfile.py') }} | |
| restore-keys: | | |
| conan2-data-doc- | |
| - name: Install Flow dependencies (like Doxygen) with Conan using the profile | |
| run: | | |
| # Install Flow dependencies (like Doxygen) with Conan using the profile. | |
| source ${{ github.workspace }}/conan_virtual_env/bin/activate | |
| conan install . -pr:a ./conan_profile --build=missing | |
| # (See comment in the build-and-test job's analogous step.) | |
| conan cache clean '*' --source --build | |
| - name: Save Conan package cache | |
| if: steps.conan-cache-restore.outputs.cache-hit != 'true' | |
| uses: actions/cache/save@v6 | |
| with: | |
| path: ~/.conan2/p | |
| key: ${{ steps.conan-cache-restore.outputs.cache-primary-key }} | |
| - name: Generate code documentation using Conan and Doxygen | |
| run: | | |
| # Generate code documentation using Conan and Doxygen. | |
| source ${{ github.workspace }}/conan_virtual_env/bin/activate | |
| # (`conan build` recomputes the dep-graph and hence needs the profile, just like `conan install`.) | |
| conan build . -pr:a ./conan_profile | |
| - name: Create documentation tarball (full docs, API-only docs, landing page) | |
| run: | | |
| # Create documentation tarball (full docs, API-only docs, landing page). | |
| cd ${{ github.workspace }}/doc/flow_doc | |
| ${{ github.workspace }}/tools/doc/stage_generated_docs.sh \ | |
| ${{ github.workspace }}/build/${{ matrix.build-cfg.conan-profile-build-type }} | |
| - name: Upload documentation tarball | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: flow-doc | |
| path: ${{ github.workspace }}/doc/flow_doc.tgz | |
| # See comments in Flow-IPC workflow counterpart. We use same techniques. | |
| - name: (`main` branch only) Check-in generated documentation directly into source control | |
| id: doc_check_in | |
| if: success() && (!cancelled()) && (github.ref == 'refs/heads/main') | |
| run: | | |
| # Check-in generated documentation directly into source control. | |
| echo 'generated/ docs have been added or replaced locally; mirroring this into checked-in tree.' | |
| git config user.name github-actions | |
| git config user.email github-actions@github.com | |
| git config --local http.https://github.com/.extraheader \ | |
| "AUTHORIZATION: basic $(echo -n x-access-token:${{ secrets.GIT_BOT_PAT }} | base64 -w 0)" | |
| git config --local http.version HTTP/1.1 | |
| cd ${{ github.workspace }}/doc/flow_doc | |
| git rm -r --cached generated || echo 'No generated/ currently checked in; no problem.' | |
| git add generated | |
| git commit -m '${{ needs.set-vars.outputs.doc-commit-message }}' | |
| git push origin main | |
| # See comments in Flow-IPC workflow counterpart. We use same techniques. | |
| - name: (`main` branch only) Signal Pages that we have updated the generated docs (web site should update) | |
| if: success() && (!cancelled()) && (steps.doc_check_in.outcome != 'skipped') | |
| run: | | |
| # Signal Pages that we have updated the generated docs (web site should update). | |
| curl --fail-with-body -X POST \ | |
| -H 'Accept: application/vnd.github.v3+json' \ | |
| -H 'Authorization: token ${{ secrets.GIT_BOT_PAT }}' \ | |
| 'https://api.github.com/repos/Flow-IPC/flow-ipc.github.io/dispatches' \ | |
| -d '{"event_type": "flow-sync-doc-event"}' |