Skip to content

Commit a26d28d

Browse files
authored
Merge pull request #1408 from mickem/ci-build-speedups
ci: Speed up the Linux builds (parallel make, ccache, docs out of ALL)
2 parents f611392 + 06a7f9b commit a26d28d

5 files changed

Lines changed: 157 additions & 8 deletions

File tree

.github/workflows/build-debian.yml

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,46 @@ jobs:
5555
done
5656
}
5757
retry apt-get update
58-
retry env DEBIAN_FRONTEND=noninteractive apt-get install -y git curl unzip libboost-all-dev libgtest-dev protobuf-compiler libprotobuf-dev openssl libssl-dev libgmock-dev libcrypto++-dev ${{ matrix.lua_package }} cmake build-essential python3-pip libdbus-1-dev libzip-dev libtinyxml2-dev libmariadb-dev pkg-config
58+
# ccache: see the "Configure ccache" step below. zstd: actions/cache
59+
# compresses with it when present and falls back to a slower gzip when
60+
# it is not.
61+
retry env DEBIAN_FRONTEND=noninteractive apt-get install -y git curl unzip libboost-all-dev libgtest-dev protobuf-compiler libprotobuf-dev openssl libssl-dev libgmock-dev libcrypto++-dev ${{ matrix.lua_package }} cmake build-essential python3-pip libdbus-1-dev libzip-dev libtinyxml2-dev libmariadb-dev pkg-config ccache zstd
5962
6063
- uses: actions/checkout@v6
6164

65+
- name: Configure ccache
66+
shell: bash
67+
run: |
68+
# Every push recompiles all ~820 translation units from scratch, six
69+
# times over (four packages, the sanitizer job, three Windows builds),
70+
# so a compiler cache is the biggest lever left after -j.
71+
#
72+
# The cache lives inside the workspace on purpose: `path:` in the cache
73+
# step is resolved relative to it, and it is the one directory whose
74+
# location is spelled the same way by actions/cache and by the build
75+
# running inside this container. It has to be restored *after* checkout,
76+
# which prunes untracked files from the workspace.
77+
if command -v ccache > /dev/null; then
78+
echo "CCACHE_DIR=$GITHUB_WORKSPACE/.ccache" >> "$GITHUB_ENV"
79+
echo "CCACHE_MAXSIZE=500M" >> "$GITHUB_ENV"
80+
# Read by the CMake step; stays empty when ccache is unavailable so
81+
# the build still configures, just without a launcher.
82+
echo "CCACHE_LAUNCHER=ccache" >> "$GITHUB_ENV"
83+
else
84+
echo "::warning::ccache not available - building without a compiler cache"
85+
fi
86+
87+
- name: Restore ccache
88+
uses: actions/cache@v4
89+
with:
90+
path: .ccache
91+
# The sha in the key makes every run write a fresh entry; restore-keys
92+
# then picks the most recent one for this distro/arch. A branch also
93+
# sees main's caches, so a new branch starts warm.
94+
key: ccache-deb-${{ matrix.distro }}-${{ matrix.distro_version }}-${{ matrix.arch }}-${{ github.sha }}
95+
restore-keys: |
96+
ccache-deb-${{ matrix.distro }}-${{ matrix.distro_version }}-${{ matrix.arch }}-
97+
6298
- name: make dirs
6399
run: |
64100
mkdir -p tmp/nscp
@@ -108,6 +144,7 @@ jobs:
108144
# container; $GITHUB_WORKSPACE is set per-runtime to the
109145
# container-mounted workspace path (`/__w/nscp/nscp`).
110146
cmake ../.. \
147+
${CCACHE_LAUNCHER:+-DCMAKE_C_COMPILER_LAUNCHER=$CCACHE_LAUNCHER -DCMAKE_CXX_COMPILER_LAUNCHER=$CCACHE_LAUNCHER} \
111148
-DBUILD_VERSION=${{ inputs.version }} \
112149
-DCPACK_GENERATOR=DEB \
113150
-DCMAKE_BUILD_TYPE=Release \
@@ -121,7 +158,19 @@ jobs:
121158
- name: Build nsclient
122159
working-directory: tmp/nscp
123160
run: |
124-
make
161+
# -j matters more here than anywhere else in the build: this single
162+
# step is ~96% of the job, and the 70-odd modules are independent, so
163+
# the graph is wide enough to keep every core busy (a full build
164+
# measured 1433% CPU at -j16). A bare `make` left three of the
165+
# runner's four cores idle for 40+ minutes.
166+
if [ -n "${CCACHE_LAUNCHER:-}" ]; then ccache --zero-stats; fi
167+
make -j"$(nproc)"
168+
169+
- name: ccache statistics
170+
if: always()
171+
shell: bash
172+
run: |
173+
if [ -n "${CCACHE_LAUNCHER:-}" ]; then ccache --show-stats; fi
125174
126175
- name: CPack
127176
working-directory: tmp/nscp

.github/workflows/build-redhat.yml

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,33 @@ jobs:
8080
retry dnf update -y
8181
# Node.js / npm are no longer needed here: the web bundle ships
8282
# separately and is installed at runtime via `nscp web install-ui`.
83-
retry dnf install -y --allowerasing coreutils bash file findutils git boost-devel gtest-devel protobuf-compiler protobuf-devel openssl openssl-devel gmock-devel cryptopp-devel lua-devel cmake gcc-c++ python3-pip rpm-build python3-devel dbus-devel bash libzip-devel tinyxml2-devel mariadb-connector-c-devel pkgconf-pkg-config
83+
# ccache comes from EPEL, which is already enabled above. zstd lets
84+
# actions/cache compress with zstd instead of falling back to gzip.
85+
retry dnf install -y --allowerasing coreutils bash file findutils git boost-devel gtest-devel protobuf-compiler protobuf-devel openssl openssl-devel gmock-devel cryptopp-devel lua-devel cmake gcc-c++ python3-pip rpm-build python3-devel dbus-devel bash libzip-devel tinyxml2-devel mariadb-connector-c-devel pkgconf-pkg-config ccache zstd
8486
- uses: actions/checkout@v6
8587

88+
- name: Configure ccache
89+
shell: bash
90+
run: |
91+
# See build-debian.yml for the rationale and for why the cache lives
92+
# inside the workspace. ccache is an EPEL package here, so the guard
93+
# below is what keeps the build working if it ever goes missing.
94+
if command -v ccache > /dev/null; then
95+
echo "CCACHE_DIR=$GITHUB_WORKSPACE/.ccache" >> "$GITHUB_ENV"
96+
echo "CCACHE_MAXSIZE=500M" >> "$GITHUB_ENV"
97+
echo "CCACHE_LAUNCHER=ccache" >> "$GITHUB_ENV"
98+
else
99+
echo "::warning::ccache not available - building without a compiler cache"
100+
fi
101+
102+
- name: Restore ccache
103+
uses: actions/cache@v4
104+
with:
105+
path: .ccache
106+
key: ccache-rpm-${{ matrix.distro }}-${{ matrix.distro_version }}-${{ matrix.arch }}-${{ github.sha }}
107+
restore-keys: |
108+
ccache-rpm-${{ matrix.distro }}-${{ matrix.distro_version }}-${{ matrix.arch }}-
109+
86110
- name: make dirs
87111
run: |
88112
mkdir -p tmp/nscp
@@ -122,6 +146,7 @@ jobs:
122146
# resolve to the container-mounted workspace, not the runner
123147
# host's view. See build-debian.yml for the full rationale.
124148
cmake ../.. \
149+
${CCACHE_LAUNCHER:+-DCMAKE_C_COMPILER_LAUNCHER=$CCACHE_LAUNCHER -DCMAKE_CXX_COMPILER_LAUNCHER=$CCACHE_LAUNCHER} \
125150
-DBUILD_VERSION=${{ inputs.version }} \
126151
-DCPACK_GENERATOR=RPM \
127152
-DCMAKE_BUILD_TYPE=Release \
@@ -135,7 +160,16 @@ jobs:
135160
- name: Build nsclient
136161
working-directory: tmp/nscp
137162
run: |
138-
make
163+
# See build-debian.yml: this step is ~97% of the job and the module
164+
# graph is wide, so a serial `make` wasted three of the four cores.
165+
if [ -n "${CCACHE_LAUNCHER:-}" ]; then ccache --zero-stats; fi
166+
make -j"$(nproc)"
167+
168+
- name: ccache statistics
169+
if: always()
170+
shell: bash
171+
run: |
172+
if [ -n "${CCACHE_LAUNCHER:-}" ]; then ccache --show-stats; fi
139173
140174
- name: CPack
141175
working-directory: tmp/nscp

.github/workflows/tests-sanitizers.yml

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,32 @@ jobs:
4444
protobuf-compiler libprotobuf-dev \
4545
openssl libssl-dev libcrypto++-dev \
4646
liblua5.4-dev libdbus-1-dev \
47-
libzip-dev libtinyxml2-dev libmariadb-dev
47+
libzip-dev libtinyxml2-dev libmariadb-dev \
48+
ccache zstd
4849
4950
- uses: actions/checkout@v6
5051

52+
- name: Configure ccache
53+
shell: bash
54+
run: |
55+
# See build-debian.yml. This job is the slowest of the Linux three
56+
# (sanitizer instrumentation plus -g), so it has the most to gain.
57+
if command -v ccache > /dev/null; then
58+
echo "CCACHE_DIR=$GITHUB_WORKSPACE/.ccache" >> "$GITHUB_ENV"
59+
echo "CCACHE_MAXSIZE=500M" >> "$GITHUB_ENV"
60+
echo "CCACHE_LAUNCHER=ccache" >> "$GITHUB_ENV"
61+
else
62+
echo "::warning::ccache not available - building without a compiler cache"
63+
fi
64+
65+
- name: Restore ccache
66+
uses: actions/cache@v4
67+
with:
68+
path: .ccache
69+
key: ccache-asan-ubuntu-24.04-x64-${{ github.sha }}
70+
restore-keys: |
71+
ccache-asan-ubuntu-24.04-x64-
72+
5173
- uses: actions/setup-node@v6
5274
with:
5375
node-version: 20
@@ -74,6 +96,7 @@ jobs:
7496
# binary — sanitizer tests don't need it, and building the Rust
7597
# client just for this run is wasteful.
7698
cmake ../.. \
99+
${CCACHE_LAUNCHER:+-DCMAKE_C_COMPILER_LAUNCHER=$CCACHE_LAUNCHER -DCMAKE_CXX_COMPILER_LAUNCHER=$CCACHE_LAUNCHER} \
77100
-DBUILD_VERSION=${{ inputs.version }} \
78101
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
79102
-DNSCP_SANITIZE=address,undefined \
@@ -90,7 +113,15 @@ jobs:
90113
# its arena on exit. Disable leak-check during the build phase only;
91114
# we re-enable it for the test phase below.
92115
ASAN_OPTIONS: detect_leaks=0
93-
run: cmake --build . -j$(nproc)
116+
run: |
117+
if [ -n "${CCACHE_LAUNCHER:-}" ]; then ccache --zero-stats; fi
118+
cmake --build . -j$(nproc)
119+
120+
- name: ccache statistics
121+
if: always()
122+
shell: bash
123+
run: |
124+
if [ -n "${CCACHE_LAUNCHER:-}" ]; then ccache --show-stats; fi
94125
95126
- name: Run unit tests under sanitizers
96127
working-directory: tmp/nscp

build.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ with `-DNSCP_CMAKE_CONFIG=<file>`).
9595
| `NSCP_WEB_BACKEND` | `mongoose` | HTTP/REST backend for `WEBServer`: `mongoose` (needs the vendored Mongoose source) or `beast` (header-only, **requires OpenSSL**). The Linux package builds use `beast`. |
9696
| `NSCP_BOOST_PYTHON_VERSION` || Boost.Python component matching your Python, e.g. `python311`, `python312`, `python313`. Only relevant when building `PythonScript`. |
9797
| `NSCP_SANITIZE` | `off` | Comma-separated sanitizer list for gcc/clang on Linux: `address`, `undefined`, `address,undefined`, `thread`. See `tools/sanitizers/run.sh`. |
98+
| `NSCP_BUILD_DOCS_HTML` | `ON` on Windows, `OFF` elsewhere | Build the mkdocs HTML site as part of the default target. Only the Windows installer ships the site; elsewhere build it on demand with `cmake --build . --target build_docs_html`. |
9899
| `USE_STATIC_RUNTIME` | `OFF` | Link the C/C++ runtime statically (used by the Win32 static build). |
99100
| `USE_SYSTEMD` | `ON` (Linux) | Install systemd service files in the package. |
100101
| `USE_INITD` | `OFF` (Linux) | Install legacy init.d scripts in the package. |

docs/CMakeLists.txt

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,9 +129,40 @@ if(MKDOCS_FOUND)
129129
"docs"
130130
)
131131

132+
# Step 3: render the HTML site with mkdocs.
133+
#
134+
# Only the Windows installer ships it (the install() below, and the
135+
# installer target which lists build_docs_html among its dependencies but
136+
# is built with /p:BuildProjectReferences=false in CI - so on Windows this
137+
# has to be part of `ALL`). Everywhere else the site was built on every
138+
# single build and then thrown away, and because it sat in `ALL` a mkdocs
139+
# hiccup failed the entire build rather than just the docs.
140+
#
141+
# The target itself still exists on every platform: run
142+
# `cmake --build . --target build_docs_html` when you want the site.
143+
if(WIN32)
144+
set(NSCP_BUILD_DOCS_HTML_DEFAULT ON)
145+
else()
146+
set(NSCP_BUILD_DOCS_HTML_DEFAULT OFF)
147+
endif()
148+
option(
149+
NSCP_BUILD_DOCS_HTML
150+
"Build the mkdocs HTML site as part of the default build target"
151+
${NSCP_BUILD_DOCS_HTML_DEFAULT}
152+
)
153+
set(DOCS_HTML_ALL)
154+
if(NSCP_BUILD_DOCS_HTML)
155+
set(DOCS_HTML_ALL ALL)
156+
message(STATUS "HTML documentation will be built as part of ALL")
157+
else()
158+
message(
159+
STATUS
160+
"HTML documentation is available via the build_docs_html target (not part of ALL)"
161+
)
162+
endif()
132163
add_custom_target(
133164
build_docs_html
134-
ALL
165+
${DOCS_HTML_ALL}
135166
${MKDOCS_EXECUTABLE} build --site-dir "${CMAKE_CURRENT_BINARY_DIR}/html"
136167
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
137168
COMMENT "Building HTML documentation"
@@ -144,7 +175,10 @@ if(MKDOCS_FOUND)
144175
FOLDER
145176
"docs"
146177
)
147-
if(WIN32)
178+
# Guarded by the option as well: with the site not built there is no
179+
# directory to install, and install(DIRECTORY) on a missing source is a
180+
# hard error at package time.
181+
if(WIN32 AND NSCP_BUILD_DOCS_HTML)
148182
install(
149183
DIRECTORY
150184
"${CMAKE_CURRENT_BINARY_DIR}/html/"

0 commit comments

Comments
 (0)