Skip to content

Commit 0ae38c7

Browse files
authored
feat(build): experimental build presets (--build-presets-file) (#8886)
1 parent 194cae4 commit 0ae38c7

10 files changed

Lines changed: 1440 additions & 51 deletions

File tree

.pre-commit-config.yaml

Lines changed: 17 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,5 @@
1-
# Copyright 2023-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2-
#
3-
# Redistribution and use in source and binary forms, with or without
4-
# modification, are permitted provided that the following conditions
5-
# are met:
6-
# * Redistributions of source code must retain the above copyright
7-
# notice, this list of conditions and the following disclaimer.
8-
# * Redistributions in binary form must reproduce the above copyright
9-
# notice, this list of conditions and the following disclaimer in the
10-
# documentation and/or other materials provided with the distribution.
11-
# * Neither the name of NVIDIA CORPORATION nor the names of its
12-
# contributors may be used to endorse or promote products derived
13-
# from this software without specific prior written permission.
14-
#
15-
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
16-
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17-
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18-
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
19-
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20-
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21-
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22-
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23-
# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24-
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25-
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1+
# SPDX-FileCopyrightText: Copyright (c) 2023-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
# SPDX-License-Identifier: BSD-3-Clause
263

274
repos:
285
- repo: https://github.com/PyCQA/isort
@@ -82,3 +59,18 @@ repos:
8259
stages: [pre-commit]
8360
verbose: true
8461
require_serial: true
62+
# Incremental SPDX-header adoption: migrate each source file to the two-line
63+
# SPDX header the first time it is touched. pre-commit runs hooks only on the
64+
# files staged in a commit, so scoping by file type (below) -- not by directory
65+
# -- rolls SPDX out gradually as files change. The hook maintains an existing
66+
# SPDX header, replaces a legacy long-form NVIDIA BSD header in place, or
67+
# inserts one. It runs after add-license above, which only maintains the
68+
# copyright year on the string both headers share.
69+
- id: add-spdx-license
70+
name: Add SPDX License Header
71+
entry: python tools/add_spdx_header.py
72+
language: python
73+
files: \.(py|pyi|sh|bash|yaml|yml|cc|cpp|cxx|h|hpp|cu|cuh)$
74+
stages: [pre-commit]
75+
verbose: true
76+
require_serial: true

build.py

Lines changed: 113 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,6 @@
11
#!/usr/bin/env python3
2-
# Copyright 2020-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3-
#
4-
# Redistribution and use in source and binary forms, with or without
5-
# modification, are permitted provided that the following conditions
6-
# are met:
7-
# * Redistributions of source code must retain the above copyright
8-
# notice, this list of conditions and the following disclaimer.
9-
# * Redistributions in binary form must reproduce the above copyright
10-
# notice, this list of conditions and the following disclaimer in the
11-
# documentation and/or other materials provided with the distribution.
12-
# * Neither the name of NVIDIA CORPORATION nor the names of its
13-
# contributors may be used to endorse or promote products derived
14-
# from this software without specific prior written permission.
15-
#
16-
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
17-
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18-
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19-
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
20-
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21-
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22-
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23-
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
24-
# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25-
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26-
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2+
# SPDX-FileCopyrightText: Copyright (c) 2020-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3+
# SPDX-License-Identifier: BSD-3-Clause
274

285
import argparse
296
import importlib.util
@@ -2556,6 +2533,20 @@ def enable_all():
25562533
required=False,
25572534
help="Override specified backend CMake argument in the build as <backend>:<name>=<value>. The argument is passed to CMake as -D<name>=<value>. This flag only impacts CMake arguments that are used by build.py. To unconditionally add a CMake argument to the backend build use --extra-backend-cmake-arg.",
25582535
)
2536+
parser.add_argument(
2537+
"--build-presets-file",
2538+
type=str,
2539+
required=False,
2540+
default=None,
2541+
help="(EXPERIMENTAL; requires TRITON_BUILD_EXPERIMENTAL=1) Path to a build "
2542+
"presets JSON file that pins per-component cmake flags (tag, cmake_args, "
2543+
"extra_cmake_args, library_path) for core/backends/repoagents/caches. "
2544+
"Command-line flags take precedence over the file. With --dryrun, a "
2545+
"provenance-annotated snapshot of every resolved cmake flag (labeled "
2546+
"cli/preset/default) is written to build_presets.json in the build "
2547+
"directory; that file can be loaded back with this flag. See "
2548+
"tools/build/build_presets.py for the schema.",
2549+
)
25592550
parser.add_argument(
25602551
"--release-version",
25612552
required=False,
@@ -2870,6 +2861,45 @@ def enable_all():
28702861
OVERRIDE_BACKEND_CMAKE_FLAGS[be] = {}
28712862
OVERRIDE_BACKEND_CMAKE_FLAGS[be][parts[0]] = parts[1]
28722863

2864+
# Per-backend clone-organization overrides. Empty unless the experimental
2865+
# --build-presets-file supplies a TRITON_REPO_ORGANIZATION for a backend, in
2866+
# which case the backend build loop below clones from that organization.
2867+
backend_org_overrides = {}
2868+
2869+
# Snapshots captured BEFORE applying a preset, so the --dryrun dump can tell
2870+
# which values came from the CLI vs the preset file vs build.py defaults.
2871+
_bp_before_backends = dict(backends)
2872+
_bp_before_repoagents = dict(repoagents)
2873+
_bp_before_caches = dict(caches)
2874+
_bp_cli_extra_be = {be: set(m) for be, m in EXTRA_BACKEND_CMAKE_FLAGS.items()}
2875+
_bp_cli_override_be = {be: set(m) for be, m in OVERRIDE_BACKEND_CMAKE_FLAGS.items()}
2876+
_bp_cli_core = set(EXTRA_CORE_CMAKE_FLAGS) | set(OVERRIDE_CORE_CMAKE_FLAGS)
2877+
2878+
# Experimental: apply a build preset (all load/validate/precedence logic lives
2879+
# in tools/build/build_presets.py). Command-line flags win over the file.
2880+
if FLAGS.build_presets_file is not None:
2881+
from tools.build import build_presets
2882+
2883+
try:
2884+
for msg in build_presets.apply(
2885+
FLAGS.build_presets_file,
2886+
backends=backends,
2887+
repoagents=repoagents,
2888+
caches=caches,
2889+
cli_backend_specs=FLAGS.backend,
2890+
cli_repoagent_specs=FLAGS.repoagent,
2891+
cli_cache_specs=FLAGS.cache,
2892+
library_paths=library_paths,
2893+
extra_backend_flags=EXTRA_BACKEND_CMAKE_FLAGS,
2894+
override_backend_flags=OVERRIDE_BACKEND_CMAKE_FLAGS,
2895+
extra_core_flags=EXTRA_CORE_CMAKE_FLAGS,
2896+
override_core_flags=OVERRIDE_CORE_CMAKE_FLAGS,
2897+
backend_org_overrides=backend_org_overrides,
2898+
):
2899+
log(msg)
2900+
except build_presets.BuildPresetError as e:
2901+
fail(str(e))
2902+
28732903
# Initialize map of common components and repo-tag for each.
28742904
components = {
28752905
"common": default_repo_tag,
@@ -2907,6 +2937,61 @@ def enable_all():
29072937

29082938
# Write the build script that invokes cmake for the core, backends, repo-agents, and caches.
29092939
pathlib.Path(FLAGS.build_dir).mkdir(parents=True, exist_ok=True)
2940+
2941+
# Experimental: on a --dryrun, emit a provenance-annotated snapshot of the
2942+
# fully-resolved cmake configuration -- every -D each component receives, as
2943+
# in cmake_build -- to <build-dir>/build_presets.json, labeling each value's
2944+
# source (cli / preset / default). The file can be loaded back with
2945+
# --build-presets-file to pin every flag. Gated behind
2946+
# TRITON_BUILD_EXPERIMENTAL=1.
2947+
if FLAGS.dryrun and os.getenv("TRITON_BUILD_EXPERIMENTAL") == "1":
2948+
from tools.build import build_presets
2949+
2950+
_bp_be = [b for b in backends if b not in CORE_BACKENDS]
2951+
try:
2952+
for msg in build_presets.write_snapshot(
2953+
os.path.join(FLAGS.build_dir, "build_presets.json"),
2954+
parser=parser,
2955+
argv=sys.argv,
2956+
core_args=core_cmake_args(
2957+
components, backends, script_cmake_dir, script_install_dir
2958+
),
2959+
backend_args={
2960+
be: backend_cmake_args(
2961+
images, components, be, script_install_dir, library_paths
2962+
)
2963+
for be in _bp_be
2964+
},
2965+
repoagent_args={
2966+
ra: repoagent_cmake_args(images, components, ra, script_install_dir)
2967+
for ra in repoagents
2968+
},
2969+
cache_args={
2970+
ca: cache_cmake_args(images, components, ca, script_install_dir)
2971+
for ca in caches
2972+
},
2973+
backends=backends,
2974+
repoagents=repoagents,
2975+
caches=caches,
2976+
before_backends=_bp_before_backends,
2977+
before_repoagents=_bp_before_repoagents,
2978+
before_caches=_bp_before_caches,
2979+
cli_backend_specs=FLAGS.backend,
2980+
cli_repoagent_specs=FLAGS.repoagent,
2981+
cli_cache_specs=FLAGS.cache,
2982+
cli_extra_be=_bp_cli_extra_be,
2983+
cli_override_be=_bp_cli_override_be,
2984+
cli_core=_bp_cli_core,
2985+
extra_backend_flags=EXTRA_BACKEND_CMAKE_FLAGS,
2986+
override_backend_flags=OVERRIDE_BACKEND_CMAKE_FLAGS,
2987+
extra_core_flags=EXTRA_CORE_CMAKE_FLAGS,
2988+
override_core_flags=OVERRIDE_CORE_CMAKE_FLAGS,
2989+
library_paths=library_paths,
2990+
):
2991+
log(msg)
2992+
except build_presets.BuildPresetError as e:
2993+
fail(str(e))
2994+
29102995
with BuildScript(
29112996
os.path.join(FLAGS.build_dir, script_name),
29122997
verbose=FLAGS.verbose,
@@ -2940,7 +3025,9 @@ def enable_all():
29403025
if be == "armnn_tflite":
29413026
github_organization = "https://gitlab.com/arm-research/smarter/"
29423027
else:
2943-
github_organization = FLAGS.github_organization
3028+
github_organization = backend_org_overrides.get(
3029+
be, FLAGS.github_organization
3030+
)
29443031

29453032
if be == "vllm":
29463033
backend_clone(

docs/customization_guide/build.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,75 @@ you have a branch called "mybranch" in the
180180
repo that you want to use in the build, you would specify
181181
--backend=onnxruntime:mybranch.
182182

183+
#### Experimental: Build Presets
184+
185+
> **Experimental.** This feature is gated behind the
186+
> `TRITON_BUILD_EXPERIMENTAL=1` environment variable; without it the
187+
> `--build-presets-file` flag is rejected.
188+
189+
Build presets let you (a) inspect exactly which cmake flags each component
190+
receives, and (b) pin those flags via a single JSON file with
191+
`--build-presets-file <path>`.
192+
193+
**Snapshot (dump).** When run with `--dryrun`, build.py writes a
194+
provenance-annotated snapshot of the fully-resolved cmake configuration to
195+
`build_presets.json` in the build directory. It records, for `core`, each
196+
`backend`, `repoagent`, and `cache`, every `-D` flag that lands in `cmake_build`,
197+
each labeled with its `source`: `cli` (explicit command line), `preset` (from a
198+
loaded presets file), or `default` (build.py default/derived):
199+
200+
```json
201+
{
202+
"backends": {
203+
"onnxruntime": {
204+
"tag": { "value": "main", "source": "default" },
205+
"cmake_args": {
206+
"TRITON_ENABLE_GPU": { "value": "ON", "source": "cli" },
207+
"TRITON_BUILD_ONNXRUNTIME_VERSION": { "value": "1.27.0", "source": "default" }
208+
}
209+
}
210+
}
211+
}
212+
```
213+
214+
**Reload.** That same file can be fed back with `--build-presets-file` to pin its
215+
flags (the `source` field is informational on load). A hand-written preset may
216+
use bare scalars instead of `{value, source}` objects:
217+
218+
```json
219+
{
220+
"backends": {
221+
"onnxruntime": {
222+
"tag": "r25.08_fix",
223+
"cmake_args": { "TRITON_ENABLE_ONNXRUNTIME_OPENVINO": "OFF" }
224+
},
225+
"python": {
226+
"extra_cmake_args": { "TRITON_BOOST_URL": "https://.../boost_1_80_0.tar.gz" }
227+
}
228+
}
229+
}
230+
```
231+
232+
Notes:
233+
- A component named in the file must also be included in the build (via
234+
`--backend`/`--repoagent`/`--cache` or `--enable-all`).
235+
- Command-line flags always take precedence over the file.
236+
- `cmake_args` are flags build.py emits natively (applied via the override
237+
channel); `extra_cmake_args` are user-added flags build.py does not emit
238+
(applied via the append channel). Setting `TRITON_REPO_ORGANIZATION` in a
239+
backend's `cmake_args` sets that backend's clone organization *per backend*
240+
(both the `git clone` URL and the `-D`), which the global
241+
`--github-organization` cannot do.
242+
- `CMAKE_INSTALL_PREFIX` is omitted from snapshots (it is an absolute build-dir
243+
path). Repoagent/cache `cmake_args` are shown for visibility but only their
244+
`tag` is re-pinned on load. Reloading pins `-D` values but does not re-derive
245+
conditionally-emitted flags, so reload alongside the same top-level flags (or
246+
`--enable-all`) for exact reproduction.
247+
248+
A ready-to-copy example lives at
249+
[`tools/build/build_presets.example.json`](../../tools/build/build_presets.example.json);
250+
the full schema is documented in `tools/build/build_presets.py`.
251+
183252
#### CPU-Only Build
184253

185254
If you want to build without GPU support you must specify individual

qa/L2_build_presets/README.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<!--
2+
SPDX-FileCopyrightText: Copyright (c) 2018-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3+
SPDX-License-Identifier: BSD-3-Clause
4+
-->
5+
6+
# L2_build_presets
7+
8+
Validates the experimental **build presets** feature
9+
(`docs/customization_guide/build.md`) by running `build.py --dryrun` and checking
10+
the generated `build_presets.json`. No GPU, container, or real build needed.
11+
12+
## Run
13+
14+
```bash
15+
cd qa/L2_build_presets
16+
bash test.sh # installs deps, runs pytest, writes logs
17+
```
18+
19+
Or directly: `python3 -m pytest build_presets_test.py`.
20+
21+
## Finding build.py
22+
23+
`build.py` lives in the server repo. It is located in-tree (a checkout), or
24+
cloned when only this directory is present. Override via:
25+
26+
| Env var | Meaning | Default |
27+
|---|---|---|
28+
| `TRITON_BUILD_PY` | explicit path to `build.py` ||
29+
| `TRITON_SERVER_REPO` | repo to clone when not found | `.../triton-inference-server/server.git` |
30+
| `TRITON_SERVER_BRANCH_NAME` | branch/tag to clone — **must exist on the remote** | `main` |
31+
32+
Bare container, mounting only this directory:
33+
34+
```bash
35+
cd server/
36+
docker run --rm -v "$PWD":/workspace -w /workspace \
37+
-e TRITON_SERVER_BRANCH_NAME=<remote-branch> \
38+
-w /workspace/qa/L2_build_presets \
39+
nvcr.io/nvidia/tritonserver:26.06-py3 python3 build_presets_test.py
40+
```
41+
42+
## Output
43+
44+
`test.sh` writes `build_presets_test.log` (full console) and
45+
`build_presets_test.report.xml` (JUnit) — both git-ignored — and prints the
46+
`*** Test Passed ***` / `*** Test FAILED ***` markers.

0 commit comments

Comments
 (0)