Skip to content

Commit 96cee32

Browse files
authored
doc: write developer docs for CMake tests. (#31)
1 parent f6ba278 commit 96cee32

File tree

1 file changed

+132
-0
lines changed

1 file changed

+132
-0
lines changed

doc/developer_guide.md

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,3 +225,135 @@ Anything `H5Easy` related goes in files with the appropriate name.
225225
What's left goes in `tests/unit/test_high_five_base.cpp`. This covers opening
226226
files, groups, dataset or attributes; checking certain pathological edge cases;
227227
etc.
228+
229+
## CMake Integration
230+
In `tests/cmake_integration` we test that HighFive can be used in downstream projects;
231+
and that those project can, in turn, be used.
232+
233+
### Overview
234+
#### Vendoring Strategies
235+
We'll refer to the process of embedding a copy of HighFive into a consuming
236+
library as *vendoring*. The *vendoring strategies* will include the strategy to
237+
not vendor.
238+
239+
There's two broad strategies for integrating HighFive into other code: *finding* or
240+
*vendoring*. When finding HighFive, the assumption by the consumer is that it's
241+
been installed somewhere and it can find it. When vendoring, the consumer
242+
brings their own copy of HighFive and uses it. The different vendoring
243+
strategies are:
244+
245+
- **find_package**: the standard way for finding dependencies in CMake. Usually
246+
the assumption is that HighFive was install properly, either systemwide or in
247+
a specific subdirectory. HighFive is then found with `find_package` (or
248+
`find_dependency` when called from `*Config.cmake`).
249+
250+
- **add_subdirectory**: the consuming code contains a submodule or subdirectory
251+
with the HighFive code; and `add_subdirectory` is used to bring HighFive and
252+
all it's targets into the consumer.
253+
254+
- **fetch_content**: the consuming code uses CMake's FetchContent to download
255+
and integrate HighFive.
256+
257+
- **external_project**: similar to FetchContent; we don't current test if this
258+
works.
259+
260+
#### Integration Strategies
261+
These refer to downstream projects picking different HighFive targerts to
262+
"link" with HighFive. There's four: two regular targets, a target that only
263+
adds `-I <dir>` and one that skips all HighFive CMake code.
264+
265+
#### Location Hints
266+
There are serveral ways of indicating where to find a package:
267+
268+
- **CMAKE_PREFIX_PATH**: which adds a list of directories to the list of
269+
directories that are used as prefixes when searching for `HighFiveConfig.`
270+
271+
- **HighFive_ROOT**: which specifies a guess for where to additionally look
272+
(but only when finding HighFive).
273+
274+
There's two types of directories where a dependency can be located:
275+
276+
- **install**: the place it ends up after `cmake --install build`.
277+
278+
- **build**: one can, if one wants to (and we have users that do), specify
279+
a build directory (not and install directory) as `HighFive_ROOT`.
280+
281+
282+
#### CMake's `export()`
283+
Furthermore, there's `export(...)`. Documentation describes it as being useful
284+
for cross-compilation, when one wants to have a set of host tool along with a
285+
library compiled for the device. It seems we don't need it in HighFive and can
286+
make it and easily write test consumers work perfectly without.
287+
288+
However, if one of our consumers adds `export(...)` to their `CMakeLists.txt`
289+
then their build breaks, complaining about missing HighFive targets (and it
290+
seems they can't "fix it up" on their end because then CMake complains that
291+
there's duplicate exported HighFive related targets).
292+
293+
The second way ommiting the missing `export()` can break downstream projects is
294+
if they attempt to use HighFive's build directory (not install directory) as
295+
`HighFive_ROOT` (or `CMAKE_PREFIX_PATH`).
296+
297+
#### HighFive Visibility
298+
This is the idea that libraries behave differently depending on whether they
299+
use HighFive in their distributed headers; or not. If they don't they could
300+
attempt to hide the fact that they use HighFive from their users.
301+
302+
Currently, we don't check that libraries can hide the use of HighFive.
303+
304+
#### Applications vs Libraries
305+
In what follows application will refer to code that compiles into an
306+
executable. While libraries refer to code that compiles into a binary that
307+
other developers will link to in their library or application.
308+
309+
Libraries and applications that have dependencies that use HighFive must be
310+
able to agree on a common version of HighFive they want to use. Otherwise,
311+
during the final linking phase, multiple definitions of the same HighFive
312+
symbols will exist, and they linker will pick one (arbitrarily); which is only
313+
safe if all definitions are identical.
314+
315+
### Test "Organization"
316+
The script to check everything is unwieldy. Here's a summary of what it attemps
317+
to do.
318+
319+
There's three downstream project in play: `dependent_library` is a library that
320+
uses HighFive in its public API, `application` is an executable that uses
321+
HighFive directly, `test_dependent_library` is an application that uses
322+
`dependent_library` its purpose is to check that our users can write CMake code
323+
that makes them integrate well with other projects.
324+
325+
The conceptually easy choices are:
326+
327+
- Application that don't have dependencies that use HighFive, can use any
328+
strategy to integrate HighFive; because they know they're the only ones using
329+
HighFive.
330+
331+
- Libraries and applications that have dependencies that use HighFive should
332+
use `find_package` since it's the easiest way of injecting a common version
333+
of HighFive everwhere.
334+
335+
Since we can't (and don't want to) force our consumers to use `find_package`
336+
and ban vendoring, we have to test what happens when libraries vendor
337+
HighFive. (Many of these are likely sources of headache if you try to figure
338+
out which code is when using which of the multiple copies of HighFive
339+
involved; and how they decide to use that version.)
340+
341+
We'll assume that there's some way that all involved projects agree on a single
342+
version of HighFive (i.e. the exact same source code or git commit).
343+
344+
The `dependent_library` will integrate HighFive using any of the following
345+
strategies: `external` in two variations one from HighFive's install dir and
346+
the other from HighFive's build dir; `submodule` in two variations which uses
347+
`add_subdirectory` once with and once without `EXCLUDE_FROM_ALL`,
348+
`fetch_content` in one variation.
349+
350+
The `test_dependent_library` itself always incorporates `dependent_library`
351+
using `find_package` (Config mode). Since that layer might choose any of the
352+
strategies of integrating HighFive, we again check several. Additionally,
353+
`test_dependent_library` isn't (and probably shouldn't be) required to
354+
integrate HighFive directly: option `none`.
355+
356+
Imagine a script that tries all combinations of the above; and attempts to only
357+
provide hints for the HighFive package location when needed, e.g. for `none`
358+
there's a `find_dependency` in `Hi5DependentConfig` that needs to be told where
359+
to look. This is what `test_cmake_integration.sh` attempts to do.

0 commit comments

Comments
 (0)