# Build for your target architecture
Compile dotlottie-rs into a shared or static library for Android, Apple, Linux, or Windows architectures and generate the dotlottie_player.h C header.

A build produces two artifacts: a native library for one target architecture, and the `dotlottie_player.h` C header that declares the API. You need one library per architecture you ship, but the header is identical for all of them.

The repository provides a `make` target for every supported architecture. Use those unless you need a target the makefiles don't cover, in which case call `cargo rustc` yourself.

<Callout type="warning" title="cargo build does not produce a linkable library">
  The crate declares `crate-type = ["rlib"]`, so a plain `cargo build` produces only a Rust archive — no `.so`,
  `.dylib`, `.dll`, or `.a`, and no header. Every build must override the crate type on the command line with `cargo
    rustc --crate-type cdylib` (or `--crate-type staticlib`). The `make` targets already do this.
</Callout>

## Build for the machine you're on

To get a library for your own architecture with the software renderer, run:

```bash
make native
```

This compiles with the features `tvg,tvg-cpu,c_api` and writes:

```text
release/native/dotlottie-player/
├── include/dotlottie_player.h
└── lib/libdotlottie_player.dylib     # .so on Linux, .dll on Windows
```

Two variants swap the renderer for a GPU backend:

```bash
make native-opengl    # OpenGL / OpenGL ES backend
make native-webgpu    # WebGPU backend
```

<Callout type="warning" title="The terminal output prints the wrong path">
  `make native` reports its artifacts as being in `release/native/lib` and `release/native/include`. Those directories
  are never created — the real output is under `release/native/dotlottie-player/` as shown above.
</Callout>

## Build for a specific architecture

Each platform has one `make` target per architecture. Building a single architecture is much faster than building the whole platform, so target only what you need.

<Tabs
  defaultValue="android"
  items={[
  { label: 'Android', value: 'android' },
  { label: 'Apple', value: 'apple' },
  { label: 'Linux', value: 'linux' },
  { label: 'Windows', value: 'windows' },
]}
>
  <TabsContent value="android">
    Set `ANDROID_NDK_HOME` first, then build the architectures you need.

    | Target                 | Android ABI   | Rust target triple        |
    | ---------------------- | ------------- | ------------------------- |
    | `make android-aarch64` | `arm64-v8a`   | `aarch64-linux-android`   |
    | `make android-armv7`   | `armeabi-v7a` | `armv7-linux-androideabi` |
    | `make android-x86_64`  | `x86_64`      | `x86_64-linux-android`    |
    | `make android-x86`     | `x86`         | `i686-linux-android`      |

    ```bash
    export ANDROID_NDK_HOME=/path/to/android-ndk-r28b
    make android-aarch64
    ```

    Each target writes into a shared `jniLibs` tree, so you can build several architectures in sequence and they accumulate:

    ```text
    release/android/
    ├── include/dotlottie_player.h          # added by `make android`
    └── jniLibs/
        └── arm64-v8a/
            ├── libdotlottie_player.so
            └── libc++_shared.so
    ```

    Build all four architectures and copy the header in one step with `make android`.

    Android builds enable both the software and OpenGL ES renderers (`tvg-cpu,tvg-gl`) and target API level 21.

    :::warning\[Ship `libc++_shared.so` alongside the player]
    ThorVG is C++, so the library depends on the NDK's shared C++ runtime. Each `make android-*` target copies the matching `libc++_shared.so` next to the player. Package both files in your APK or your app will fail to load the library at runtime.
    :::
  </TabsContent>

  <TabsContent value="apple">
    Apple builds produce one framework slice per architecture, which `make apple` then combines into an XCFramework.

    | Target                          | Rust target triple           | Toolchain |
    | ------------------------------- | ---------------------------- | --------- |
    | `make apple-macos-arm64`        | `aarch64-apple-darwin`       | stable    |
    | `make apple-macos-x86_64`       | `x86_64-apple-darwin`        | stable    |
    | `make apple-ios-arm64`          | `aarch64-apple-ios`          | stable    |
    | `make apple-ios-sim-arm64`      | `aarch64-apple-ios-sim`      | stable    |
    | `make apple-ios-x86_64`         | `x86_64-apple-ios`           | stable    |
    | `make apple-maccatalyst-arm64`  | `aarch64-apple-ios-macabi`   | nightly   |
    | `make apple-maccatalyst-x86_64` | `x86_64-apple-ios-macabi`    | nightly   |
    | `make apple-visionos-arm64`     | `aarch64-apple-visionos`     | nightly   |
    | `make apple-visionos-sim-arm64` | `aarch64-apple-visionos-sim` | nightly   |
    | `make apple-tvos-arm64`         | `aarch64-apple-tvos`         | nightly   |
    | `make apple-tvos-sim-arm64`     | `aarch64-apple-tvos-sim`     | nightly   |
    | `make apple-watchos-arm64`      | `aarch64-apple-watchos`      | nightly   |
    | `make apple-watchos-arm64_32`   | `arm64_32-apple-watchos`     | nightly   |
    | `make apple-watchos-armv7k`     | `armv7k-apple-watchos`       | nightly   |
    | `make apple-watchos-sim-arm64`  | `aarch64-apple-watchos-sim`  | nightly   |

    ```bash
    make apple-ios-arm64
    ```

    Grouped targets build every slice for one OS and assemble its framework: `apple-macos`, `apple-ios`, `apple-maccatalyst`, `apple-visionos`, `apple-tvos`, `apple-watchos`.

    Minimum deployment targets are iOS 13.0, macOS 11.0, tvOS 13.0, watchOS 7.0, visionOS 1.0, and macCatalyst 13.1.

    The nightly targets have no prebuilt Rust standard library and are compiled with `-Z build-std`, which is why they need `rust-src` on the nightly toolchain.

    watchOS is the one platform built as a **static** library, because watchOS devices do not support dynamic linking.

    Running `make apple` builds all 15 slices and packages `release/apple/DotLottiePlayer.xcframework` together with `dotlottie_player.h`. On Apple platforms the header doubles as the framework's umbrella header, so Swift and Objective-C can import the C API directly through the generated module map.

    :::warning\[`make apple` finishes with a WebGPU packaging step]
    Its last step packages the `wgpu-native` framework, and the version it looks for is pinned separately from the version the build script downloads. If the two disagree, the build fails at that final copy even though every slice compiled. Override it on the command line to keep them aligned:

    ```bash
    make apple WGPU_NATIVE_VERSION=v29.0.1.1
    ```

    Build the per-OS targets individually — `make apple-ios`, `make apple-macos` — if you don't need the WebGPU framework at all.
    :::
  </TabsContent>

  <TabsContent value="linux">
    | Target              | Rust target triple          |
    | ------------------- | --------------------------- |
    | `make linux-x86_64` | `x86_64-unknown-linux-gnu`  |
    | `make linux-arm64`  | `aarch64-unknown-linux-gnu` |

    ```bash
    make linux-x86_64
    ```

    Linux builds produce **both** a static and a shared library:

    ```text
    release/linux/x86_64/dotlottie-player/
    ├── include/dotlottie_player.h
    └── lib/
        ├── libdotlottie_rs.a
        └── libdotlottie_rs.so
    ```

    Cross-compiling to ARM64 requires `gcc-aarch64-linux-gnu`. If it is missing, the target falls back to your host compiler and the build will almost certainly fail to link.
  </TabsContent>

  <TabsContent value="windows">
    | Target                | Rust target triple        |
    | --------------------- | ------------------------- |
    | `make windows-x86_64` | `x86_64-pc-windows-msvc`  |
    | `make windows-arm64`  | `aarch64-pc-windows-msvc` |

    ```bash
    make windows-x86_64
    ```

    Windows builds produce a static library, a DLL, and the DLL's import library:

    ```text
    release/windows/x86_64/dotlottie-player/
    ├── include/dotlottie_player.h
    └── lib/
        ├── dotlottie_player.lib          # static library
        ├── dotlottie_player.dll          # shared library
        └── dotlottie_player.dll.lib      # import library for the DLL
    ```

    Run `make` from Git Bash or MSYS2, and make sure `LIBCLANG_PATH` points at your LLVM `bin` directory so `bindgen` can run.
  </TabsContent>
</Tabs>

## Choose your features

The runtime is heavily feature-gated so you only compile the codecs and renderers you actually use. The crate's default features are `dotlottie`, `state-machines`, and `theming` — deliberately **no renderer and no C API**. Every platform target passes `--no-default-features` and lists what it wants explicitly.

| Feature                  | Enables                                                         |
| ------------------------ | --------------------------------------------------------------- |
| `c_api`                  | The C API and header generation. Required                       |
| `tvg`                    | The ThorVG renderer core. Required                              |
| `tvg-cpu`                | Software rendering into a pixel buffer                          |
| `tvg-gl`                 | OpenGL and OpenGL ES rendering                                  |
| `tvg-wg`                 | WebGPU rendering                                                |
| `dotlottie`              | `.lottie` archive support, manifests, and multi-animation files |
| `theming`                | Runtime theming. Implies `dotlottie`                            |
| `state-machines`         | Interactive state machines. Implies `dotlottie`                 |
| `tvg-png`                | PNG image assets                                                |
| `tvg-jpg`                | JPEG image assets                                               |
| `tvg-webp`               | WebP image assets                                               |
| `tvg-ttf`                | TrueType font rendering                                         |
| `tvg-otf`                | OpenType font rendering                                         |
| `tvg-lottie-expressions` | Lottie expressions                                              |
| `tvg-threads`            | Multithreaded rendering                                         |
| `audio`                  | Audio playback for animations with embedded audio               |

<Callout type="warning" title="The header needs both the c_api and tvg features">
  Header generation runs from the renderer branch of the build script, so `--features c_api` on its own produces no
  `dotlottie_player.h` at all — and no error explaining why. Always pass a renderer feature alongside it, for example
  `--features tvg,tvg-cpu,c_api`.
</Callout>

The Android, Apple, Linux, and Windows targets accept a `FEATURES` variable to add features on top of their defaults:

```bash
FEATURES=tvg-png,tvg-jpg,tvg-ttf,tvg-threads make android-aarch64
```

## Build with cargo directly

Use `cargo rustc` when you need a target the makefiles don't cover, or when you want full control over features and flags. The shape of the command is always the same:

```bash
cargo rustc \
  --manifest-path dotlottie-rs/Cargo.toml \
  --target aarch64-unknown-linux-gnu \
  --crate-type cdylib \
  --features tvg,tvg-cpu,c_api \
  --release
```

Swap `--crate-type cdylib` for `--crate-type staticlib` to get a static archive, or pass both to get each in one build. Omit `--target` to build for your host.

Cross-compiling this way means setting the toolchain environment yourself, because the repository's Cargo config contains no linker settings — the makefiles export them inline. At minimum you need `CC`, `CXX`, `AR`, `RANLIB`, and `CARGO_TARGET_<TRIPLE>_LINKER`, plus `BINDGEN_EXTRA_CLANG_ARGS` pointing at the target sysroot so `bindgen` can parse ThorVG's headers against the right platform.

Output from a direct `cargo rustc` build is **not** packaged into `release/`. Look for it here instead:

| Artifact | Path                                                                |
| -------- | ------------------------------------------------------------------- |
| Header   | `dotlottie-rs/build/dotlottie_player.h`                             |
| Library  | `dotlottie-rs/target/<triple>/release/libdotlottie_rs.{so,dylib,a}` |

When you build for your host without `--target`, the library lands in `dotlottie-rs/target/release/` instead.

<Callout type="info" title="The header is generated, not checked in">
  `dotlottie_player.h` is git-ignored and absent from a fresh clone. It appears only after a successful build with
  `c_api` and `tvg` enabled. Regenerate it whenever you change features, since the declarations it contains depend on
  which features are on.
</Callout>

## Verify the build

Confirm the header exists and the library exports the API:

```bash
ls dotlottie-rs/build/dotlottie_player.h
nm -gU release/native/dotlottie-player/lib/libdotlottie_player.dylib | grep dotlottie_new_player
```

On Linux use `nm -D --defined-only`, and on Windows use `dumpbin /exports`.

## Troubleshooting

<Accordion type="single">
  <AccordionItem title="No library was produced, only a .rlib" value="no-library">
    You ran `cargo build` instead of `cargo rustc --crate-type cdylib`. The crate declares only `rlib`, so the crate type has to be overridden on the command line. Use a `make` target, or add `--crate-type cdylib` to your `cargo rustc` invocation.
  </AccordionItem>

  <AccordionItem title="dotlottie_player.h was never created" value="no-header">
    The header is generated only when **both** `c_api` and `tvg` are enabled. Rebuild with `--features tvg,tvg-cpu,c_api`. Note that the file is git-ignored, so it is also missing in a fresh clone until you build.
  </AccordionItem>

  <AccordionItem title="The build fails compiling C++ sources, or bindgen cannot find headers" value="submodule">
    Usually a missing ThorVG submodule. Run `git submodule update --init --recursive`, then confirm with `git submodule status`. If the submodule is present, check that libclang is installed and that `LIBCLANG_PATH` is set on Windows.
  </AccordionItem>

  <AccordionItem title="A tvg-wg (WebGPU) build fails to find an artifact" value="wgpu">
    The WebGPU backend links against prebuilt `wgpu-native` binaries, which the build script downloads on demand. Artifacts exist only for macOS, iOS, Linux, and Android — there is none for any Windows target, and none for macCatalyst, tvOS, visionOS, or watchOS. Use `tvg-cpu` or `tvg-gl` on those platforms.

    Because the download happens at build time, a `tvg-wg` build needs network access. On an air-gapped machine, set `WGPU_NATIVE_INCLUDE` and `WGPU_NATIVE_LIB` to a local copy instead.
  </AccordionItem>

  <AccordionItem title="A nightly Apple target fails with a missing standard library" value="build-std">
    macCatalyst, visionOS, tvOS, and watchOS are compiled with `-Z build-std`, which needs the Rust source component on the nightly toolchain:

    ```bash
    rustup toolchain install nightly
    rustup component add rust-src --toolchain nightly
    ```
  </AccordionItem>
</Accordion>

## Next steps

- [Link the runtime into your project](/en/runtimes/distributions/native/v0.x/linking) — compiler and linker flags for your build system
- [Render your first animation](/en/runtimes/distributions/native/v0.x/first-animation) — a complete C program
