# Using Thrust with CMake

Thrust provides configuration files that simplify using Thrust
from other CMake projects. Requirements:

- Thrust >= 1.9.10
- CMake >= 3.18

See the [Fixing Legacy FindThrust.cmake](#fixing-legacy-findthrustcmake)
section for solutions that work on older Thrust versions.

## Quick Start: Use CCCL Example

The CCCL repository contains a [stand-alone example](https://github.com/NVIDIA/cccl/tree/main/examples/thrust_flexible_device_system) project the demonstrates how to use Thrust with any device system (CUDA, OpenMP, TBB, or CPP) from CMake using the CCCL package.
The choice of device system is flexible and determined at configure-time via the `CCCL_THRUST_DEVICE_SYSTEM` CMake variable, which initializes the default `CCCL::Thrust` target's configuration.

If the default `CCCL::Thrust` target is not desired and your project would like more control over the Thrust configuration, set the CMake variable `CCCL_ENABLE_DEFAULT_THRUST_TARGET=OFF` and use the `thrust_create_target` function manually, as described in the following document.

Note that `find_package(CCCL)` internally calls `find_package(Thrust)`. These may be used interchangeably if you're only interested in the Thrust component of CCCL.

## User Guide

#### Default Configuration (CUDA)

Thrust is configured using a `thrust_create_target` CMake function that
assembles a complete interface to the Thrust library:

```cmake
find_package(Thrust REQUIRED CONFIG)
thrust_create_target(Thrust)
target_link_libraries(MyProgram PRIVATE Thrust)
```

The first argument is the name of the interface target to create, and any
additional options will be used to configure the target. By default,
`thrust_create_target` will configure its result to use CUDA acceleration.

If desired, `thrust_create_target` may be called multiple times to build
several unique Thrust interface targets with different configurations, as
detailed below.

**Note:** If CMake is unable to locate Thrust, specify the path to Thrust's CMake
configuration directory (where this README file is located) as `Thrust_DIR`.
If using Thrust from the CCCL sources, this would be

```
$ cmake . -DThrust_DIR=<CCCL git repo root>/thrust/thrust/cmake/
```

#### Large Array (64-bit offseet) Handling: `DISPATCH`

The `DISPATCH` option allows users to select the tradeoff of compile-time / binary-size
vs. performance vs. scalability when given large inputs that require 64-bit offset types.
This currently only applies when DEVICE=CUDA.

- `Dynamic` May compile each kernel twice, once for 32-bit offsets and again for 64-bit
  offsets, and choose dynamically using the input size at runtime.
  This significantly increases compile-time and binary-size, but provides optimal performance
  for small input sizes while also supporting 64-bit indexed workloads.
- `Force32bit` forces Thrust to use a 32 bit offset type. This improves compile time and
  binary size but limits the input size.
- `Force64bit` forces Thrust to use a 64-bit offset type. This improves compile time and
  binary size and allows large input sizes. However, it may degrade runtime performance
  for 32-bit indexed workloads.

#### TBB / OpenMP

To explicitly specify host/device systems, `HOST` and `DEVICE` arguments can be
passed to `thrust_create_target`. If an explicit system is not specified, the
target will default to using CPP for host and/or CUDA for device.

```cmake
thrust_create_target(ThrustTBB DEVICE TBB)
thrust_create_target(ThrustOMP HOST CPP DEVICE OMP)
```

will create targets `ThrustTBB` and `ThrustOMP`. Both will use the serial `CPP`
host system, but will find and use TBB or OpenMP for the device system.

#### Configure Target from Cache Options

To allow a Thrust target to be configurable easily via `cmake-gui` or
`ccmake`, pass the `FROM_OPTIONS` flag to `thrust_create_target`. This will add
`THRUST_HOST_SYSTEM`, `THRUST_DEVICE_SYSTEM`, and `THRUST_DISPATCH_TYPE` options
to the CMake cache that allow selection from the systems supported by this version
of Thrust.

```cmake
thrust_create_target(Thrust FROM_OPTIONS
  [HOST_OPTION <option name>]
  [DEVICE_OPTION <option name>]
  [DISPATCH_OPTION <option name>]
  [HOST_OPTION_DOC <doc string>]
  [DEVICE_OPTION_DOC <doc string>]
  [DISPATCH_OPTION_DOC <doc string>]
  [HOST <default host system name>]
  [DEVICE <default device system name>]
  [DISPATCH <default dispatch type>]
  [ADVANCED]
)
```

The optional arguments have sensible defaults, but may be configured per
`thrust_create_target` call:

| Argument              | Default                 | Description                       |
|-----------------------|-------------------------|-----------------------------------|
| `HOST_OPTION`         | `THRUST_HOST_SYSTEM`    | Name of cache option for host     |
| `DEVICE_OPTION`       | `THRUST_DEVICE_SYSTEM`  | Name of cache option for device   |
| `DISPATCH_OPTION`     | `THRUST_DISPATCH_TYPE`  | Name of cache option for dispatch |
| `HOST_OPTION_DOC`     | Thrust's host system.   | Docstring for host option         |
| `DEVICE_OPTION_DOC`   | Thrust's device system. | Docstring for device option       |
| `DISPATCH_OPTION_DOC` | Thrust's dispatch type. | Docstring for dispatch option     |
| `HOST`                | `CPP`                   | Default host system               |
| `DEVICE`              | `CUDA`                  | Default device system             |
| `DISPATCH`            | `Dispatch`                  | Default dispatch type             |
| `ADVANCED`            | *N/A*                   | Mark cache options advanced       |

### Specifying Thrust Version Requirements

A specific version of Thrust may be required in the `find_package` call:

```cmake
find_package(Thrust 1.9.10)
```

will only consider Thrust installations with version `1.9.10.X`. An exact match
down to the patch version can be forced by using `EXACT` matching:

```cmake
find_package(Thrust 1.9.10.1 EXACT)
```

would only match the 1.9.10.1 release.

#### Using an Explicit TBB or OpenMP CMake Target

When `thrust_create_target` is called, it will lazily load the requested
systems on-demand through internal `find_package` calls. If a project already
uses TBB or OpenMP, it may specify a CMake target for Thrust to share instead:

```cmake
thrust_set_TBB_target(MyTBBTarget)
thrust_set_OMP_target(MyOMPTarget)
```

These functions must be called **before** the corresponding system is loaded
through `thrust_create_target` or `find_package(Thrust COMPONENT [OMP|TBB])`.

#### Using an Explicit libcu++ CMake Target

In contrast to the optional TBB/OMP dependencies, there is no
`thrust_set_libcudacxx_target` function that specifies an explicit libcu++
target. This is because libcu++ is always required and must be found during the
initial `find_target(Thrust)` call that defines these functions.

To force Thrust to use a specific libcu++ target, ensure that either the
`Thrust::libcudacxx` or `libcudacxx::libcudacxx` targets are defined prior to
the first invocation of `find_package(Thrust)`. Thrust will automatically use
these, giving preference to the `Thrust::libcudacxx` target.

#### Testing for Systems

The following functions check if a system has been found, either by lazy loading
through `thrust_create_target` or as a `find_package` `COMPONENT` /
`OPTIONAL_COMPONENT`:

```cmake
# Set var_name to TRUE or FALSE if an individual system has been found:
thrust_is_cuda_system_found(<var_name>)
thrust_is_cpp_system_found(<var_name>)
thrust_is_tbb_system_found(<var_name>)
thrust_is_omp_system_found(<var_name>)

# Generic version that takes a component name from CUDA, CPP, TBB, OMP:
thrust_is_system_found(<component_name> <var_name>)

# Defines `THRUST_*_FOUND` variables in the current scope that reflect the
# state of all known systems. Can be used to refresh these flags after
# lazy system loading.
thrust_update_system_found_flags()
```

#### Debugging

Thrust will produce a detailed log describing its targets, cache options, and
interfaces when `--log-level=VERBOSE` is passed to CMake:

```
$ cmake . --log-level=VERBOSE
```

This can be handy for inspecting interface and dependency information.

## Fixing Legacy FindThrust.cmake

A community-created `FindThrust.cmake` module exists and is necessary to find
Thrust installations prior to Thrust 1.9.10. Its usage is discouraged whenever
possible and the config files in this directory should be strongly preferred.
However, projects that need to support old versions of Thrust may still need to
use the legacy `FindThrust.cmake` with pre-1.9.10 installations.

One popular flavor of this find module has a version parsing bug. Projects that
rely on `FindThrust.cmake` should check for this and patch their copies as
follows.

Replace:

```cmake
string( REGEX MATCH "^[0-9]" major ${version} )
string( REGEX REPLACE "^${major}00" "" version "${version}" )
string( REGEX MATCH "^[0-9]" minor ${version} )
string( REGEX REPLACE "^${minor}0" "" version "${version}" )
```

with:

```cmake
math(EXPR major "${version} / 100000")
math(EXPR minor "(${version} / 100) % 1000")
math(EXPR version "${version} % 100")
```

# Thrust Developer Documentation

This portion of the file contains descriptions of Thrust's internal CMake target
structure for Thrust developers. It should not be necessary for users
who just want to use Thrust from their projects.

## Internal Targets

By default, `find_package(Thrust)` will only create a single `Thrust::Thrust`
target that describes where the actual Thrust headers are located. It does not
locate or create configurations for any dependencies; these are lazily loaded
on-demand by calls to `create_thrust_target`, or when explicitly requested via
`find_package`'s component mechanism.

As mentioned, the basic Thrust interface is described by the `Thrust::Thrust`
target.

Each backend system (`CPP`, `CUDA`, `TBB`, `OMP`) is described by multiple
targets:

- `Thrust::${system}`
  - Specifies an interface configured to build against all
    dependencies for this backend (including `Thrust::Thrust`).
  - For example, the `Thrust::CUDA` target is an interface
    target that combines the interfaces of both Thrust and CUB.
- `Thrust::${system}::Host`
  - Configures an interface for using a specific host system.
  - Multiple `::Host` targets cannot be combined in the same library/executable.
    Attempting to do so will produce a CMake configuration error.
  - Only defined for systems that support being used as the host.
- `Thrust::${system}::Device`
  - Configures an interface for using a specific device system.
  - Multiple `::Device` targets cannot be combined in the same library/executable.
    Attempting to do so will produce a CMake configuration error.
  - Only defined for systems that support being used as the device.
