Skip to content

tamp-build/tamp-tauri

Tamp.Tauri.V2

Wrapper for the Tauri 2.x CLI (@tauri-apps/cli) plus typed path helpers for the externalBin sidecar contract. Pairs with Tamp.Cargo for the canonical Rust + Tauri build chain.

Package Status
Tamp.Tauri.V2 0.1.0 (initial)

Why this exists

Tauri's externalBin contract requires sidecar binaries to land at a specific path: <src-tauri>/binaries/<name>-<target-triple>[.exe]. Many shipped Tauri apps have a class of bug where the sidecar is in the wrong directory, the wrong target-triple, or missing the .exe suffix — the user installs the app and gets a "service not running" dialog because Tauri's runtime sidecar resolver can't find the file.

The pattern is universal: the file-copy step that puts the Rust binary at the externalBin path lives in human memory (or a memo, or a bash script comment), not the build graph. Tamp.Tauri.V2.ExternalBinPath(...) makes that path a typed value flowing through the build script — so the step is part of the dependency graph, not a tribal-knowledge step that drifts from reality.

Install

dotnet add package Tamp.Tauri.V2

Multi-targets net8 / net9 / net10. Requires Tauri 2.x CLI on PATH or in node_modules/.bin/ (the typical install path).

Quick start — Rust + Tauri chain end-to-end

using Tamp;
using Tamp.Cargo;
using Tamp.Tauri.V2;

class Build : TampBuild
{
    public static int Main(string[] args) => Execute<Build>(args);

    [FromPath("cargo")] readonly Tool CargoBin = null!;
    [FromNodeModules("tauri")] readonly Tool TauriCli = null!;

    AbsolutePath ServiceCrate => RootDirectory / "dasbook-service";
    AbsolutePath SrcTauri => RootDirectory / "src-tauri";

    const string TargetTriple = "x86_64-pc-windows-msvc";

    Target BuildService => _ => _
        .Description("[Build] Compile dasbook-service for the Tauri sidecar slot")
        .Executes(() => Cargo.Build(CargoBin, s => s
            .SetWorkingDirectory(ServiceCrate)
            .SetRelease()
            .SetTarget(TargetTriple)
            .SetLocked()));

    Target StageSidecar => _ => _
        .DependsOn(nameof(BuildService))
        .Description("[Build] Stage the compiled service into Tauri's externalBin slot")
        .Executes(() =>
        {
            var built = ServiceCrate / "target" / TargetTriple / "release" / "dasbook-service.exe";
            var sidecar = Tauri.ExternalBinPath(SrcTauri, "dasbook-service", TargetTriple);
            Directory.CreateDirectory(sidecar.Parent!.Value);
            File.Copy(built.Value, sidecar.Value, overwrite: true);
            // The path that used to live in a memo is now a typed expression in the build graph.
        });

    Target BuildDesktop => _ => _
        .DependsOn(nameof(StageSidecar))
        .Description("[Build] Bundle the Tauri desktop app (msi + nsis)")
        .Executes(() => Tauri.Build(TauriCli, s => s
            .SetWorkingDirectory(RootDirectory)
            .AddBundles("msi", "nsis")
            .SetTarget(TargetTriple)));
}

The sidecar staging step that was previously documented in BUILD_NOTES.md and lived in human memory is now part of the dependency graph: BuildDesktop ← StageSidecar ← BuildService. Drift between docs and reality stops being possible because there are no docs to drift from.

Verb surface

Tamp method tauri command Notes
Tauri.Build(...) tauri build --bundles msi,nsis,..., --target, --no-bundle, --debug, --features, --bin, --runner.
Tauri.Info(...) tauri info Diagnostic snapshot of toolchain — useful in CI logs.
Tauri.Icon(...) tauri icon <png> [--output dir] Generate platform icon sets from a source PNG.
Tauri.Migrate(...) tauri migrate v1 → v2 project migration.
Tauri.Raw(...) tauri <anything> Escape hatch — covers plugin, init, signer, etc.

--ci defaults to ON for every verb (turn off via .SetCi(false) for interactive runs).

What's deferred

tauri signer generate and tauri signer sign are not yet typed in 0.1.0 — filed as TAM-190 for a 0.2.0 wave when an adopter actually needs to sign updater artifacts. (Originally this needed Tamp.Tauri.V2 on Tamp.Core's InternalsVisibleTo list to safely Reveal() the password into TAURI_SIGNING_PRIVATE_KEY_PASSWORD; as of Tamp.Core 1.6.0 Secret.Reveal() is public and TAMP004-gated, so the deferral now is just about the unwritten verb surface.) Until then, Tauri.Raw(tool, "signer", "generate", ...) with adopter-managed env covers the case.

ExternalBinPath — the load-bearing helper

public static AbsolutePath ExternalBinPath(
    AbsolutePath srcTauriDir,
    string name,
    string targetTriple,
    bool? isWindows = null);

Computes the absolute path Tauri expects for an external binary:

<srcTauriDir>/binaries/<name>-<target-triple>[.exe]

Examples:

  • ExternalBinPath(srcTauri, "dasbook-service", "x86_64-pc-windows-msvc")…/src-tauri/binaries/dasbook-service-x86_64-pc-windows-msvc.exe
  • ExternalBinPath(srcTauri, "dasbook-service", "x86_64-unknown-linux-gnu")…/src-tauri/binaries/dasbook-service-x86_64-unknown-linux-gnu

The .exe suffix is inferred from the target-triple (any triple containing windows). Override explicitly via the isWindows parameter for unusual cases.

Empty name or targetTriple is rejected at call time — the triple is required by Tauri's contract regardless of host platform; defaulting to host would produce silently-wrong sidecar names in cross-compile scenarios.

HostTargetTriple — for the "build for the current platform" case

var triple = Tauri.HostTargetTriple();
// e.g. "x86_64-pc-windows-msvc" on Windows x64
//      "x86_64-unknown-linux-gnu" on Linux x64
//      "aarch64-pc-windows-msvc" on Windows arm64

Useful when the build script targets the host architecture and doesn't want to hard-code the triple. For cross-compile, set the triple explicitly via Cargo.Build(...)'s SetTarget(...) and pass the same value to ExternalBinPath.

Tool resolution

Tauri's CLI ships as @tauri-apps/cli via npm. Adopters typically resolve via:

[FromNodeModules("tauri")] readonly Tool TauriCli = null!;

This finds node_modules/.bin/tauri(.cmd) for npm-installed projects. Alternative resolutions:

  • [FromPath("tauri")] — globally-installed via cargo install tauri-cli (less common)
  • [FromPath("npx")] + Tauri.Raw(tool, "tauri", "build", ...) — npx-mediated invocation (slower)

The wrapper doesn't opine on which path you choose; it just invokes the resolved Tool.

Bypassing Tauri.Build()CargoBuildSettings.AsTauriShell() (0.2.1+)

tauri build is a wrapper around cargo build against the Tauri shell crate, with its own bundling step appended. Some cargo flags don't surface to tauri-cli — most notably --profile <name> (used in production when default release triggers a compiler bug like the MSVC 14.50 fat-LTO crash that hit DasBook).

When you need cargo-only knobs, the adopter pattern is to bypass Tauri.Build() and configure cargo directly:

[FromPath("cargo")] readonly Tool CargoBin = null!;

Target BuildDesktop => _ => _
    .Executes(() => Cargo.Build(CargoBin, s => s
        .SetWorkingDirectory(SrcTauri)        // run cargo against src-tauri/
        .SetProfile("fast-release")            // custom profile for the LTO-crash workaround
        .AsTauriShell()                        // adds tauri/custom-protocol idempotently
        .SetLocked()));

AsTauriShell() is the cargo-side mirror of TauriBuildSettings.EnableCustomProtocol() (from 0.2.0). Without the tauri/custom-protocol feature, the release-built binary silently runs in dev mode at runtime — compile and sign succeed, but the distributed app fails on first launch. This helper makes the feature unforgettable for adopters going the cargo-direct route.

The extension lives in Tamp.Tauri.V2 (this package) but extends a type from Tamp.Cargo. Tamp.Tauri.V2 >= 0.2.1 declares Tamp.Cargo >= 0.2.0 as a transitive dep, so it's available without a separate dotnet add package step.

Sibling packages

  • Tamp.Cargo — Rust toolchain. The canonical paired wrapper for Tauri's Rust side.
  • Tamp.Npm.V10 — for the frontend stage of a Tauri app (vite dev / vite build orchestration via npm scripts).
  • Tamp.Msix — wraps makeappx / signtool for Microsoft Store packaging downstream of tauri build.

Releasing

Releases follow the Tamp dogfood pattern: bump <Version> in Directory.Build.props, tag v<X.Y.Z>, GitHub Actions runs dotnet tamp Ci then dotnet tamp Push.

License

MIT. See LICENSE.

About

Tamp wrapper for the Tauri 2.x CLI plus externalBin path helpers. Second non-.NET satellite after Tamp.Cargo.

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages