# Selectors in recipes

Recipe and variant configuration files can utilize selectors to conditionally
add, remove, or modify dependencies, configuration options, or even skip recipe
execution based on specific conditions.

Selectors are implemented using an `if / then / else` map, which is a
valid YAML dictionary. The condition is evaluated using [`minijinja`][minijinja] and
follows
the same syntax as a Python expression.

During rendering, several variables are set based on the platform and variant
being built. For example, the `unix` variable is true when the host platform is macOS or Linux, while
`win` is true when the host platform is Windows. Consider the following recipe executed on Linux:

```yaml
requirements:
  host:
    - if: unix
      then: unix-tool
    - if: win
      then: win-tool
```

This will be evaluated as:

```yaml
requirements:
  host:
    - unix-tool
```

The line containing the Windows-specific configuration is removed. Multiple
items can also be selected, such as:

```yaml
host:
  - if: linux
    then:
      - linux-tool-1
      - linux-tool-2
      - linux-tool-3
```

For Linux, this will result in:

```yaml
host:
  - linux-tool-1
  - linux-tool-2
  - linux-tool-3
```

Other examples often found in the wild:

```yaml
if: build_platform != target_platform ... # true if cross-platform build
if: osx and arm64 ... # true for apple silicon (osx-arm64)
if: linux and (aarch64 or ppc64le)) ... # true for linux ppc64le or linux-aarch64
```

### Available variables

The following variables are available during rendering of the recipe:

| Variable             | Description                                                            |
|----------------------|------------------------------------------------------------------------|
| `target_platform`    | the configured `target_platform` for the build                         |
| `build_platform`     | the configured `build_platform` for the build                          |
| `host_platform`      | the configured `host_platform` for the build                           |
| `linux`              | "true" if `host_platform` is Linux                                     |
| `osx`                | "true" if `host_platform` is OSX / macOS                               |
| `win`                | "true" if `host_platform` is Windows                                   |
| `ios`                | "true" if `host_platform` is iOS (device or simulator)                 |
| `iossimulator`       | "true" if `host_platform` is an iOS simulator                          |
| `android`            | "true" if `host_platform` is Android                                   |
| `unix`               | "true" if `host_platform` is a Unix (macOS, Linux, iOS or Android)     |
| `x86`, `x86_64`      | x86 32/64-bit Architecture (based on `host_platform`)                  |
| `aarch64`            | 64-bit Arm (if `host_platform` is `linux-aarch64` or `android-aarch64`)|
| `arm64`              | 64-bit Arm (if `host_platform` is `osx-arm64`, `win-arm64` or `ios-*`) |
| `armV6l`, `armV7l`   | 32-bit Arm                                                             |
| `armv7a`             | 32-bit Arm using Android's `armeabi-v7a` ABI                           |
| `ppc64`, `s390x`,    | Big endian                                                             |
| `ppc64le`            | Little endian                                                          |
| `riscv32`, `riscv64` | The [RISC-V](https://wikipedia.org/wiki/RISC-V) Architecture           |
| `wasm32`             | The [WebAssembly](https://wikipedia.org/wiki/WebAssembly) Architecture |

!!! note "iOS and Android are not `osx` / `linux`"
    Although iOS is Mach-O based like macOS, and Android is ELF based like Linux, they
    are *distinct* platform families: `osx` is false on iOS and `linux` is false on
    Android. Both are `unix`. Use `ios` / `android` to select them, and note that
    `ios` also covers the simulator targets (use `iossimulator` for the narrower case).

### Variant selectors

To select based on [variant configuration](variants.md) you can use the names in the selectors as well.
For example, if the build uses `python: 3.8` as a variant, we can use `if: python == "3.8"` to enable a dependency for
only
when the Python version is 3.8.

!!! note "String comparison"
The comparison is a string comparison done by [`minijinja`][minijinja], so it is important to use the correct string
representation of the variant.
Use the `match` function to compare versions.

```yaml title="variants.yaml"
python:
  - 3.8
  - 3.9
```

```yaml title="recipe.yaml"
requirements:
  host:
    - if: python == "3.8" # (1)!
      then: mydep
      else: otherdep
```

1. This will only add `mydep` when the Python version is 3.8. This comparison is a string comparison, so it is important
   to
   use the correct string representation of the variant.

### The `match` function

!!!  note "Rename from `cmp` to `match`"
    The `cmp` function has been renamed to `match` to better reflect its purpose.

Inside selectors, one can use a special `match` function to test if the selected variant version has a matching version.
For example, having the following variants file, we could use the these tests:

```yaml title="variants.yaml"
python:
  - 3.8
  - 3.9
```

```yaml title="recipe.yaml"
- if: match(python, "3.8")    # true, false
  then: mydep
- if: match(python, ">=3.8")  # true, true
  then: mydep
- if: match(python, "<3.8")   # false, false (1)
  then: mydep
```

1. `else: ` would also have worked here.

This function eliminates the need to implement any Python-specific `conda-build`
selectors (such as `py3k`, `py38`, etc.) or the `py` and `npy` integers.

Please note that during the _initial_ phase of rendering we do not know the
variant, and thus the `match` condition always evaluates to `true`.

### Selector evaluation

Except for the Rattler-Build specific selectors, the selectors are evaluated using the `minijinja` engine. This means
that the selectors are evaluated by [`minijinja`][minijinja] thus Python like expressions.
Some notable options are:

```yaml
- if: python == "3.8" # equal
- if: python != "3.8" # not equal
- if: python and linux # true if python variant is set and the host_platform is linux
- if: python and not linux # true if python variant is set and the host_platform is not linux
- if: python and (linux or osx) # true if python variant is set and the host_platform is linux or osx
```

[minijinja]: https://github.com/mitsuhiko/minijinja

### Alternatives for scalar fields

Some fields accept scalars rather than lists, and selectors cannot be used. Alternatives include:

```yaml
string: ${{ "foobar" if USE_OPENMP else "bla" }}
```

```yaml
string: |
  {% if USE_OPENMP %}
     blablabla
  {% else %}
     blabla
  {% endif %}
```
