Files
archived-tauri-docs/src/content/docs/develop/sidecar.mdx

245 lines
8.0 KiB
Plaintext

---
title: Embedding External Binaries
i18nReady: true
---
You may need to embed external binaries to add additional functionality to your application or prevent users from installing additional dependencies (e.g., Node.js or Python). We call this binary a `sidecar`.
Binaries are executables written in any programming language. Common use cases are Python CLI applications or API servers bundled using `pyinstaller`.
To bundle the binaries of your choice, you can add the `externalBin` property to the `tauri > bundle` object in your `tauri.conf.json`.
The `externalBin` configuration expects a list of strings targeting binaries either with absolute or relative paths.
Here is a Tauri configuration snippet to illustrate a sidecar configuration:
```json title="src-tauri/tauri.conf.json"
{
"bundle": {
"externalBin": [
"/absolute/path/to/sidecar",
"../relative/path/to/binary",
"binaries/my-sidecar"
]
}
}
```
:::note
The relative paths are relative to the `tauri.conf.json` file which is in the `src-tauri` directory.
So `binaries/my-sidecar` would represent `<PROJECT ROOT>/src-tauri/binaries/my-sidecar`.
:::
To make the external binary work on each supported architecture, a binary with the same name and a `-$TARGET_TRIPLE` suffix must exist on the specified path.
For instance, `"externalBin": ["binaries/my-sidecar"]` requires a `src-tauri/binaries/my-sidecar-x86_64-unknown-linux-gnu` executable on Linux or `src-tauri/binaries/my-sidecar-aarch64-apple-darwin` on Mac OS with Apple Silicon.
You can find your **current** platform's `-$TARGET_TRIPLE` suffix by looking at the `host:` property reported by the following command:
```sh
rustc -Vv
```
If the `grep` and `cut` commands are available, as they should on most Unix systems, you can extract the target triple directly with the following command:
```shell
rustc -Vv | grep host | cut -f2 -d' '
```
On Windows you can use PowerShell instead:
```powershell
rustc -Vv | Select-String "host:" | ForEach-Object {$_.Line.split(" ")[1]}
```
Here's a Node.js script to append the target triple to a binary:
```javascript
import { execSync } from 'child_process';
import fs from 'fs';
const extension = process.platform === 'win32' ? '.exe' : '';
const rustInfo = execSync('rustc -vV');
const targetTriple = /host: (\S+)/g.exec(rustInfo)[1];
if (!targetTriple) {
console.error('Failed to determine platform target triple');
}
fs.renameSync(
`src-tauri/binaries/sidecar${extension}`,
`src-tauri/binaries/sidecar-${targetTriple}${extension}`
);
```
Note that this script will not work if you compile for a different architecture than the one its running on,
so only use it as a starting point for your own build scripts.
## Running it from Rust
:::note
Please follow the [shell plugin guide](/plugin/shell/) first to set up and initialize the plugin correctly.
Without the plugin being initialized and configured the example won't work.
:::
On the Rust side, import the `tauri_plugin_shell::ShellExt` trait and call the `shell().sidecar()` function on the AppHandle:
```rust
use tauri_plugin_shell::ShellExt;
use tauri_plugin_shell::process::CommandEvent;
let sidecar_command = app.shell().sidecar("my-sidecar").unwrap();
let (mut rx, mut _child) = sidecar_command
.spawn()
.expect("Failed to spawn sidecar");
tauri::async_runtime::spawn(async move {
// read events such as stdout
while let Some(event) = rx.recv().await {
if let CommandEvent::Stdout(line_bytes) = event {
let line = String::from_utf8_lossy(&line_bytes);
window
.emit("message", Some(format!("'{}'", line)))
.expect("failed to emit event");
// write to stdin
child.write("message from Rust\n".as_bytes()).unwrap();
}
}
});
```
:::note
The `sidecar()` function expects just the filename, NOT the whole path configured in the `externalBin` array.
Given the following configuration:
```json title="src-tauri/tauri.conf.json"
{
"bundle": {
"externalBin": ["binaries/app", "my-sidecar", "../scripts/sidecar"]
}
}
```
The appropriate way to execute the sidecar is by calling `app.shell().sidecar(name)` where `name` is either `"app"`, `"my-sidecar"` or `"sidecar"`
instead of `"binaries/app"` for instance.
:::
You can place this code inside a Tauri command to easily pass the AppHandle or you can store a reference to the AppHandle in the builder script to access it elsewhere in your application.
## Running it from JavaScript
When running the sidecar, Tauri requires you to give the sidecar permission to run the `execute` or `spawn` method on the child process. To grant this permission, go to the file `<PROJECT ROOT>/src-tauri/capabilities/default.json` and add the section below to the permissions array. Don't forget to name your sidecar according to the relative path mentioned earlier.
```json title="src-tauri/capabilities/default.json" ins={4-13}
{
"permissions": [
"core:default",
{
"identifier": "shell:allow-execute",
"allow": [
{
"name": "binaries/app",
"sidecar": true
}
]
},
"shell:allow-open"
]
}
```
:::note
The `shell:allow-execute` identifier is used because the sidecar's child process will be started using the `command.execute()` method. To run it with `command.spawn()`, you need to change the identifier to `shell:allow-spawn` or add another entry to the array with the same structure as the one above, but with the identifier set to `shell:allow-spawn`.
:::
In the JavaScript code, import the `Command` class from the `@tauri-apps/plugin-shell` module and use the `sidecar` static method.
```javascript
import { Command } from '@tauri-apps/plugin-shell';
const command = Command.sidecar('binaries/my-sidecar');
const output = await command.execute();
```
:::note
The string provided to `Command.sidecar` must match one of the strings defined in the `externalBin` configuration array.
:::
## Passing arguments
You can pass arguments to Sidecar commands just like you would for running normal [Command][std::process::Command].
Arguments can be either **static** (e.g. `-o` or `serve`) or **dynamic** (e.g. `<file_path>` or `localhost:<PORT>`). You define the arguments in the exact order in which you'd call them. Static arguments are defined as-is, while dynamic arguments can be defined using a regular expression.
First, define the arguments that need to be passed to the sidecar command in `src-tauri/capabilities/default.json`:
```json title="src-tauri/capabilities/default.json" ins={8-25}
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "default",
"description": "Capability for the main window",
"windows": ["main"],
"permissions": [
"core:default",
{
"identifier": "shell:allow-execute",
"allow": [
{
"args": [
"arg1",
"-a",
"--arg2",
{
"validator": "\\S+"
}
],
"name": "binaries/my-sidecar",
"sidecar": true
}
]
},
"shell:allow-open"
]
}
```
:::note
If you are migrating from Tauri v1, the `migrate` command in Tauri v2 CLI should take care of this for you. Read [Automated Migration](/start/migrate/from-tauri-1/#automated-migration) for more.
:::
Then, to call the sidecar command, simply pass in **all** the arguments as an array.
In Rust:
```rust
use tauri_plugin_shell::ShellExt;
#[tauri::command]
async fn call_my_sidecar(app: tauri::AppHandle) {
let sidecar_command = app
.shell()
.sidecar("my-sidecar")
.unwrap()
.args(["arg1", "-a", "--arg2", "any-string-that-matches-the-validator"]);
let (mut _rx, mut _child) = sidecar_command.spawn().unwrap();
}
```
In JavaScript:
```javascript
import { Command } from '@tauri-apps/plugin-shell';
// notice that the args array matches EXACTLY what is specified in `capabilities/default.json`.
const command = Command.sidecar('binaries/my-sidecar', [
'arg1',
'-a',
'--arg2',
'any-string-that-matches-the-validator',
]);
const output = await command.execute();
```
[std::process::Command]: https://doc.rust-lang.org/std/process/struct.Command.html