Skip to content

Commit 9b2abb8

Browse files
authored
[TRTLLM-9208][infra] Document the process for C++ deps (#9016)
Signed-off-by: Josh Bialkowski <[email protected]> Co-authored-by: Josh Bialkowski <[email protected]>
1 parent 5df907b commit 9b2abb8

File tree

1 file changed

+337
-0
lines changed

1 file changed

+337
-0
lines changed

3rdparty/README.md

Lines changed: 337 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,337 @@
1+
# Adding new C++ Dependencies
2+
3+
## Step 1: Make the package available to the build
4+
5+
First, decide if you must install the package in the container or if you
6+
may defer fetching until the build phase. In general, *prefer to fetch
7+
packages during the build phase*. You may be required to install
8+
packages into the container, however, if there is a runtime component
9+
(e.g. shared objects) that cannot be reasonably distributed with the
10+
wheel.
11+
12+
### Install in the container
13+
14+
#### Debian Packages via os package manager (e.g. apt, dnf)
15+
16+
Add your package to one of the existing shell scripts used by the docker build
17+
under [docker/common/][1] Find the location where the package manager is
18+
invoked, and add the name of your package there.
19+
20+
NOTE: Internal compliance tooling will automatically detect the
21+
installation of this package and fetch sources using the source-fetching
22+
facilities of the OS package manager.
23+
24+
[1]: https://github.com/NVIDIA/TensorRT-LLM/tree/main/docker/common.
25+
26+
#### Python Packages via pip
27+
28+
If it makes sense, add your package to one of the existing shell scripts used by
29+
the docker build under [docker/common/][2]. Grep for "pip3 install" to see
30+
existing invocations. If none of the existing shell scripts make sense, add a
31+
new shell script to install your package and then invoke that script in
32+
Dockerfile.multi.
33+
34+
NOTE: If the new python package you are adding has a compiled component (e.g. a
35+
python extension module), you must coordinate with the [Security Team][20] to
36+
ensure that the source for this component is managed correctly.
37+
38+
[2]: https://github.com/NVIDIA/TensorRT-LLM/tree/main/docker/common
39+
40+
#### Tarball packages via HTTP/FTP
41+
42+
Invoke `wget` in a shell script which is called from the docker build file.
43+
When it makes sense, please prefer to extend an existing script in
44+
[docker/common/][3] rather than creating a new one. If you are downloading a
45+
binary package, you must also download the source package that produced that
46+
binary.
47+
48+
Ensure that the source package is copied to /third-party-source and retained
49+
after all cleanup within the docker image layer.
50+
51+
[3]: https://github.com/NVIDIA/TensorRT-LLM/tree/main/docker/common
52+
53+
### Fetch during the build
54+
55+
#### Python Packages via pip
56+
57+
Add an entry to [requirements-dev.txt][4].
58+
The package will be installed by build\_wheel.py during virtual
59+
environment initialization prior to configuring the build with cmake.
60+
Include a comment indicating the intended usage of the package.
61+
62+
[4]: https://github.com/NVIDIA/TensorRT-LLM/blob/main/requirements-dev.txt
63+
64+
**Example:**
65+
66+
`requirements-dev.txt`:
67+
68+
``` requirements.txt
69+
# my-package is needed by <feature> where it is used for <reason>
70+
my-package==1.2.24
71+
```
72+
73+
#### C/C++ Packages via conan
74+
75+
Add a new entry to [conandata.yml][6] indicating the package version for the
76+
dependency you are adding. Include a yaml comment indicating the intended usage
77+
of the package. Then add a new invocation of `self.require()` within the `def
78+
requirements(self)` method of [conanfile.py], referencing the version you added
79+
to conandata.
80+
81+
[6]: https://github.com/NVIDIA/TensorRT-LLM/blob/main/cpp/conandata.yml
82+
[7]: https://github.com/NVIDIA/TensorRT-LLM/blob/main/cpp/conanfile.py
83+
84+
**Example:**
85+
86+
`conandata.yml`:
87+
88+
```.yml
89+
# my_dependency is needed by <feature> where it is used for <reason>
90+
my_dependency: 1.2.24+1
91+
```
92+
93+
`conanfile.py`:
94+
95+
```.py
96+
def requirements(self):
97+
...
98+
my_dependency_version = self.conandata["my_dependency"]
99+
self.requires(f"my_dependency/{my_dependency_version}")
100+
```
101+
102+
#### Source integration via CMake
103+
104+
If you have a package you need to build from source then use CMake
105+
[FetchContent][8] of [ExternalProject][9] to fetch the package sources and
106+
integrate it with the build. See the details in the next section.
107+
108+
[8]: https://cmake.org/cmake/help/latest/module/FetchContent.html
109+
[9]: https://cmake.org/cmake/help/latest/module/ExternalProject.html#id1
110+
111+
#### git Submodule - Don't Use
112+
113+
Please *avoid use of git-submodule*. If, for some reason, the CMake integrations
114+
described below don't work and git-submodule is absolutely required, please add
115+
the submodule under the 3rdparty directory.
116+
117+
**Rationale:**
118+
119+
For a source-code dependency distributed via git,
120+
FetchContent/ExternalProject and git submodules both ultimately contain
121+
the same referential information (repository URL, commit sha) and, at
122+
the end of the day, do the same things. However
123+
FetchContent/ExternalProject have the following advantages:
124+
125+
1. The git operations happen during the build and are interleaved with the rest
126+
of the build processing, rather than requiring an additional step managed
127+
outside of CMake.
128+
129+
2. The fetch, patch, and build steps for the sub project are individually named
130+
in the build, so any failures are more clearly identified
131+
132+
3. The build state is better contained within the build tree where it is less
133+
prone to interference by development actions.
134+
135+
4. For source code that is modified, FetchContent/ExternalProject can manage
136+
application of the patches making it clear what modifications are present.
137+
138+
5. The build does not have to make assumptions about the version control
139+
configuration of the source tree, which may be incorrect due to the fact
140+
that it is bind-mounted in a container. For example, `git submodule --init`
141+
inside a container will corrupt the git configuration outside the container
142+
if the source tree is a git worktree.
143+
144+
6. External project references and their patches are collected under a more
145+
narrow surface, rather than being spread across different tools. This makes
146+
it easier to track third part dependencies as well as to recognize them
147+
during code review.
148+
149+
**Example:**
150+
151+
``` bash
152+
git submodule add https://github.com/some-organization/some-project.git 3rdparty/some-project
153+
```
154+
155+
156+
## Step 2: Integrate the package
157+
158+
There are many ways to integrate a package with the build through cmake.
159+
160+
### find\_package for binary packages
161+
162+
For binary packages (os-provided via apt-get or yum, or conan-provided), prefer
163+
the use of [find\_package][10] to integrate the package into the build. Conan
164+
will generate a find-script for packages that don't already come with a Cmake
165+
configuration file and the conan-specific logic is provided through the
166+
conan-generated toolchain already used in our build.
167+
168+
For any packages which do not have provided find modules (either built-in, or
169+
available from conan), please implement one in [cpp/cmake/modules][11]. Please
170+
do not add "direct" invocations of `find_library` / `add_library` / `find_file`
171+
/ `find_path` outside of a find module the package.
172+
173+
Please add invocations of `find_package` directly in the root Cmake file.
174+
175+
[10]: https://cmake.org/cmake/help/latest/command/find_package.html
176+
[11]: https://github.com/NVIDIA/TensorRT-LLM/tree/main//cpp/cmake/modules?ref_type=heads
177+
178+
**Example:**
179+
180+
cpp/CMakeLists.txt
181+
182+
```.cmake
183+
find_package(NIXL)
184+
```
185+
186+
cpp/cmake/modules/FindNIXL.cmake
187+
```.cmake
188+
...
189+
find_library(
190+
NIXL_LIBRARY nixl
191+
HINTS
192+
${NIXL_ROOT}/lib/${NIXL_TARGET_ARCH}
193+
${NIXL_ROOT}/lib64)
194+
...
195+
add_library(NIXL::nixl SHARED IMPORTED)
196+
set_target_properties(
197+
NIXL::nixl
198+
PROPERTIES
199+
INTERFACE_INCLUDE_DIRECTORIES ${NIXL_INCLUDE_DIR}
200+
IMPORTED_LOCATION ${NIXL_LIBRARY}
201+
${NIXL_BUILD_LIBRARY}
202+
${SERDES_LIBRARY}
203+
)
204+
```
205+
206+
### FetchContent for source packages with compatible cmake builds
207+
208+
For source packages that have a compatible cmake (e.g. where add\_subdirectory
209+
will work correctly), please use [FetchContent][12] to download the sources and
210+
integrate them into the build. Please add new invocations of
211+
FetchContent\_Declare in [3rdparty/CMakeLists.txt][13]. Add new invocations for
212+
FetchContent\_MakeAvailable wherever it makes sense in the build where you are
213+
integrating it, but prefer the root listfile for that build
214+
([cpp/CMakeLists.txt][14] for the primary build).
215+
216+
CODEOWNERS for this file will consist of PLC reviewers who verify that
217+
third-party license compliance strategies are being followed.
218+
219+
If the dependency you are adding has modified sources, please do the
220+
following:
221+
222+
1. Create a repository on gitlab to mirror the upstream source files. If the
223+
upstream is also in git, please use the gitlab "mirror" repository option.
224+
Otherwise, please use branches/tags to help identify the upstream source
225+
versions.
226+
227+
2. Track nvidia changes in a branch. Use a linear sequence (trunk-based)
228+
development strategy. Use meaningful, concise commit message subjects and
229+
comprehensive commit messages for the changes applied.
230+
231+
3. Use `git format-patch \<upstream-commit\>\...HEAD` to create a list of
232+
patches, one file per commit,
233+
234+
4. Add your patches under 3rdparty/patches/\<package-name\>
235+
236+
5. Use CMake's [PATCH\_COMMAND][15] option to apply the patches during the
237+
build process.
238+
239+
[12]: https://cmake.org/cmake/help/latest/module/FetchContent.html
240+
[13]: https://github.com/NVIDIA/TensorRT-LLM/tree/main//3rdparty/CMakeLists.txt?ref_type=heads
241+
[14]: https://github.com/NVIDIA/TensorRT-LLM/blob/main/cpp/CMakeLists.txt
242+
[15]: https://cmake.org/cmake/help/latest/module/ExternalProject.html#patch-step-options
243+
244+
**Example:**
245+
246+
3rdparty/CMakeLists.txt
247+
248+
```.cmake
249+
FetchContent_Declare(
250+
pybind11
251+
GIT_REPOSITORY https://github.com/pybind/pybind11.git
252+
GIT_TAG f99ffd7e03001810a3e722bf48ad1a9e08415d7d
253+
)
254+
```
255+
256+
cpp/CmakeLists.txt
257+
258+
```.cmake
259+
FetchContent_MakeAvailable(pybind11)
260+
```
261+
262+
### ExternalProject
263+
264+
If the package you are adding doesn't support FetchContent (e.g. if it's not
265+
built by CMake or if its CMake configuration doesn't nest well), then please use
266+
[ExternalProject][16]. In this case that project's build system will be invoked
267+
as a build step of the primary build system. Note that, unless both the primary
268+
and child build systems are GNU Make, they will not share a job server and will
269+
independently schedule parallelism (e.g. -j flags).
270+
271+
[16]: https://cmake.org/cmake/help/latest/module/ExternalProject.html#id1
272+
273+
**Example:**
274+
275+
```.cmake
276+
ExternalProject_Add(
277+
nvshmem_project
278+
URL https://developer.download.nvidia.com/compute/nvshmem/redist/libnvshmem/linux-x86_64/libnvshmem-linux-x86_64-3.2.5_cuda12-archive.tar.xz
279+
URL_HASH ${NVSHMEM_URL_HASH}
280+
PATCH_COMMAND patch -p1 --forward --batch -i
281+
${DEEP_EP_SOURCE_DIR}/third-party/nvshmem.patch
282+
...
283+
CMAKE_CACHE_ARGS
284+
-DCMAKE_C_COMPILER:STRING=${CMAKE_C_COMPILER}
285+
-DCMAKE_C_COMPILER_LAUNCHER:STRING=${CMAKE_C_COMPILER_LAUNCHER}
286+
...
287+
BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/nvshmem-build
288+
BUILD_BYPRODUCTS
289+
${CMAKE_CURRENT_BINARY_DIR}/nvshmem-build/src/lib/libnvshmem.a
290+
)
291+
add_library(nvshmem_project::nvshmem STATIC IMPORTED)
292+
add_dependencies(nvshmem_project::nvshmem nvshmem_project)
293+
...
294+
set_target_properties(
295+
nvshmem_project::nvshmem
296+
PROPERTIES IMPORTED_LOCATION
297+
${CMAKE_CURRENT_BINARY_DIR}/nvshmem-build/src/lib/libnvshmem.a
298+
INTERFACE_INCLUDE_DIRECTORIES
299+
${CMAKE_CURRENT_BINARY_DIR}/nvshmem-build/src/include)
300+
```
301+
302+
## Step 3: Update third-party attributions and license tracking
303+
304+
1. Clone the dependency source code to an NVIDIA-controlled repository. The
305+
consumed commit must be stored as-received (ensure the consumed commit-sha
306+
is present in the clone). For sources available via git (or git-adaptable)
307+
SCM, mirror the repository in the [oss-components][18] gitlab project.
308+
309+
2. Collect the license text of the consumed commit
310+
311+
3. If the license does not include a copyright notice, collect any copyright
312+
notices that were originally published with the dependency (these may be on
313+
individual file levels, in metadata files, or in packaging control files).
314+
315+
4. Add the license and copyright notices to the ATTRIBUTIONS-CPP-x86\_64.md and
316+
ATTRIBUTIONS-CPP-aarch64.md files
317+
318+
CODEOWNERS for ATTRIBUTIONS-CPP-\*.md are members of the PLC team and modifying
319+
this file will signal to reviewers that they are verifying that your change
320+
follows the process in this document.
321+
322+
[18]: https://gitlab.com/nvidia/tensorrt-llm/oss-components
323+
324+
## Step 4: File a JIRA ticket if you need help from the Security team
325+
326+
This step is optional, if you need assistance from the Security team.
327+
328+
File a Jira ticket using the issue template [TRTLLM-8383][19] to request
329+
inclusion of this new dependency and initiate license and/or security review.
330+
The Security Team will triage and assign the ticket.
331+
332+
If you don’t have access to the JIRA project, please email the [Security
333+
Team][20].
334+
335+
336+
[19]: https://jirasw.nvidia.com/browse/TRTLLM-8383
337+
[20]: mailto://[email protected]

0 commit comments

Comments
 (0)