# Using Thrust with CMake

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

- Thrust >= 1.9.10
- CMake >= 3.15

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

## 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 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 cloning Thrust from github, this would be

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

#### 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` and `THRUST_DEVICE_SYSTEM` 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>]
  [HOST_OPTION_DOC <doc string>]
  [DEVICE_OPTION_DOC <doc string>]
  [HOST <default host system name>]
  [DEVICE <default device system name>]
  [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 |
| `HOST_OPTION_DOC`   | Thrust's host system.   | Docstring for host option       |
| `DEVICE_OPTION_DOC` | Thrust's device system. | Docstring for device option     |
| `HOST`              | `CPP`                   | Default host system             |
| `DEVICE`            | `CUDA`                  | Default device system           |
| `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 3.15.7 or newer:

```
$ 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.
