@git.zone/tsrust
A CLI build tool for Rust projects that follows the same conventions as @git.zone/tsbuild. It detects your rust/ source directory, parses Cargo.toml (including workspaces), runs cargo build --release with a managed target cache, and copies the resulting binaries into a clean dist_rust/ directory at the project root.
Issue Reporting and Security
For reporting bugs, issues, or security vulnerabilities, please visit community.foss.global/. This is the central community hub for all issue reporting. Developers who sign and comply with our contribution agreement and go through identification can also get a code.foss.global/ account to submit Pull Requests directly.
Install
Install globally with pnpm:
pnpm add -g @git.zone/tsrust
Or as a project-level dev dependency:
pnpm add --save-dev @git.zone/tsrust
⚡ No Rust required! If
cargoisn't found on your system,tsrustautomatically downloads and installs a minimal Rust toolchain to/tmp/tsrust_toolchain/. This gives a zero-setup experience. If you already have Rust installed,tsrustuses your system toolchain.
The Convention
tsrust mirrors the directory convention established by tsbuild:
| Tool | Source Directory | Output Directory |
|---|---|---|
tsbuild |
ts/ |
dist_ts/ |
tsrust |
rust/ |
dist_rust/ |
Your Rust code lives in rust/ (or ts_rust/ as fallback), and compiled binaries land in dist_rust/ — ready for packaging, deployment, or further tooling.
Usage
🔨 Build (Default Command)
Simply run tsrust from your project root:
tsrust
This will:
- Detect the Rust toolchain (system
cargo, or auto-install to/tmp/tsrust_toolchain/) - Locate your
rust/directory (containingCargo.toml) - Parse the workspace to discover all
[[bin]]targets - Run
cargo build --releasewith full streaming output - Store Cargo intermediates in
.nogit/tsrust-targetby default - Copy each binary to
dist_rust/with executable permissions (chmod 755) - Report file sizes and total build time
Example output:
Using cargo 1.90.0 (840b83a10 2025-07-30)
Found Rust project at: rust
Detected Cargo workspace
Binary targets: rustproxy
Running: CARGO_TARGET_DIR="/path/to/project/.nogit/tsrust-target" cargo build --release
Compiling rustproxy v0.1.0
Finished `release` profile [optimized] target(s) in 29.01s
Copied rustproxy (13.4 MB) -> dist_rust/rustproxy
Done in 29.2s
Automatic Rust Toolchain
tsrust provides a zero-setup experience through automatic toolchain management:
- System toolchain detected → uses it as-is (no download, no overhead)
- No system toolchain → checks
/tmp/tsrust_toolchain/for a previously installed bundled toolchain - No bundled toolchain → downloads
rustup-initand installs a minimal Rust toolchain (~70-90 MB download) to/tmp/tsrust_toolchain/
The bundled toolchain is stored in /tmp/, so it's cleaned up on reboot. Subsequent runs reuse the existing installation. This is ideal for CI environments and quick starts where you don't want to manage a system-wide Rust install.
Supported platforms for automatic install: Linux (x64, arm64) and macOS (x64, arm64).
🐛 Debug Build
Build with the debug profile instead of release:
tsrust --debug
Binaries are taken from .nogit/tsrust-target/debug/ instead of .nogit/tsrust-target/release/.
🧹 Clean Before Building
Run cargo clean before building to force a full rebuild:
tsrust --clean
Cross-Compilation
Cross-compile for different OS/architecture combinations using the --target flag:
# Cross-compile for a single target
tsrust --target linux_arm64
# Cross-compile for multiple targets
tsrust --target linux_arm64 --target linux_amd64
# Full Rust triples are also accepted
tsrust --target aarch64-unknown-linux-gnu
Supported friendly target names:
| Friendly name | Rust target triple |
|---|---|
linux_amd64 |
x86_64-unknown-linux-gnu |
linux_arm64 |
aarch64-unknown-linux-gnu |
linux_amd64_musl |
x86_64-unknown-linux-musl |
linux_arm64_musl |
aarch64-unknown-linux-musl |
macos_amd64 |
x86_64-apple-darwin |
macos_arm64 |
aarch64-apple-darwin |
When using --target, output binaries are named <binname>_<os>_<arch>:
dist_rust/
├── rustproxy_linux_arm64
└── rustproxy_linux_amd64
tsrust automatically installs missing rustup targets via rustup target add when needed.
Configuration via .smartconfig.json
You can set default cross-compilation targets in your project's .smartconfig.json file so you don't need to pass --target flags every time:
{
"@git.zone/tsrust": {
"targets": ["linux_arm64", "linux_amd64"],
"targetDir": ".nogit/tsrust-target",
"pruneAfterBuild": false
}
}
When targets are configured in .smartconfig.json, simply running tsrust will cross-compile for all listed targets. CLI --target flags take full precedence — if any --target is provided, the .smartconfig.json targets are ignored entirely.
targetDir is optional. It must point to a tsrust-owned path under .nogit/, such as .nogit/tsrust-target or .nogit/tsrust-custom-target. You can also set TSRUST_TARGET_DIR for one-off runs. pruneAfterBuild: true or TSRUST_PRUNE_AFTER_BUILD=true removes the marked managed target cache after binaries have been copied to dist_rust/.
Static Linking
tsrust can produce fully statically linked Linux binaries (static-pie) that run on both glibc distros (Debian/Ubuntu) and musl distros (Alpine). Enable it via .smartconfig.json:
{
"@git.zone/tsrust": {
"targets": ["linux_amd64", "linux_arm64"],
"static": true
}
}
Or per invocation with the --static flag:
tsrust --static
Behavior per target:
*-linux-gnu:tsrustinjectsRUSTFLAGS="-C target-feature=+crt-static"into its cargo invocation.*-linux-musl: already statically linked by default; no flags are injected.*-apple-darwin: full static linking is not applicable on macOS; the target builds with default linkage.
After the build, tsrust verifies every Linux binary in dist_rust/ is actually statically linked (no PT_INTERP ELF program header — the check is architecture-independent, so cross-compiled binaries are verified too) and fails the build otherwise.
Why tsrust injects the flag instead of the project setting rustflags in rust/.cargo/config.toml:
tsrustalways builds with an explicit--target, so the flag never applies to host artifacts. A repo-widerustflagsentry also applies to proc-macros and build scripts whenever cargo runs without--target(plaincargo test,cargo check, rust-analyzer) — and rustc cannot build proc-macros with+crt-staticon linux-gnu, breaking those commands.- Keep
rust/.cargo/config.tomlfree ofrustflagswhen usingstatic— the injectedRUSTFLAGSenvironment variable replaces any config-filerustflags(cargo does not merge them).linkerentries (e.g. for aarch64 cross-compilation) are unaffected and should stay.
Deterministic Path Remapping
Release binaries can embed absolute source paths in panic locations. Enable local path remapping to avoid publishing machine-specific paths:
{
"@git.zone/tsrust": {
"targets": ["linux_amd64", "linux_arm64"],
"static": true,
"remapLocalPaths": true
}
}
remapLocalPaths adds Rust --remap-path-prefix flags for the project root, Rust source directory, Cargo home, and rustup home. Additional flags can be supplied explicitly through rustflags; these flags are combined with automatic flags such as -C target-feature=+crt-static for Linux GNU static builds:
{
"@git.zone/tsrust": {
"rustflags": ["--cfg=my_feature"]
}
}
🗑️ Clean Only
Remove all build artifacts without rebuilding:
tsrust clean
This runs cargo clean in the Rust directory and deletes the dist_rust/ output directory.
🧹 Prune Rust Target Caches
Report Rust target caches without deleting anything:
tsrust prune
Apply cleanup to marked tsrust-managed target caches:
tsrust prune --apply --days 14 --max-size 5GiB
| Option | Description |
|---|---|
--apply |
Remove marked managed target caches that match the filters |
--days <n> |
Prune marked caches whose newest file is at least n days old; default 14 |
--max-size <size> |
Prune marked caches at or above a size such as 5GiB |
--workspace <path> |
Inspect another workspace path |
Conventional rust/target and ts_rust/target directories are report-only, even if they carry a marker. Only managed .nogit/tsrust* target directories are eligible for --apply; arbitrary app data is never marked or removed.
Project Structure
tsrust expects your project to follow this layout:
my-project/
├── rust/ # 🦀 Your Rust source code
│ ├── Cargo.toml # Root manifest (workspace or single crate)
│ ├── src/
│ │ └── main.rs # (for single-crate projects)
│ └── crates/ # (for workspace projects)
│ ├── my-binary/
│ │ ├── Cargo.toml # Contains [[bin]] targets
│ │ └── src/
│ └── my-lib/
│ ├── Cargo.toml
│ └── src/
├── dist_rust/ # 📦 Output: compiled binaries go here
│ └── my-binary
├── .nogit/
│ └── tsrust-target/ # 🧹 Managed Cargo target cache
├── ts/ # (your TypeScript code, built by tsbuild)
├── dist_ts/ # (TypeScript output)
└── package.json
Workspace Support
tsrust fully supports Cargo workspaces. It reads the [workspace] section from your root Cargo.toml, iterates through all members, and discovers binary targets from each member crate's Cargo.toml.
Binary target discovery follows Cargo's own rules:
- Explicit
[[bin]]entries → uses thenamefield from each entry - Implicit binary → if no
[[bin]]is declared butsrc/main.rsexists, uses the[package] name - Library-only crates → skipped (no binary output expected)
Fallback Directory
If no rust/ directory is found, tsrust checks for ts_rust/ as a fallback. This supports projects that use the ts_ prefix convention for all source directories.
Programmatic API
tsrust exports its internals for use in other Node.js/TypeScript tools:
import { CargoConfig, CargoRunner, FsHelpers, TsRustCli, resolveManagedTargetDir } from '@git.zone/tsrust';
// Parse a Cargo workspace
const config = new CargoConfig('/path/to/rust');
const info = await config.parse();
console.log(info.isWorkspace); // true
console.log(info.binTargets); // ['rustproxy']
// Run cargo build
const runner = new CargoRunner('/path/to/rust');
const targetDir = resolveManagedTargetDir('/path/to/project');
const result = await runner.build({ debug: false, clean: false, targetDir });
console.log(result.success); // true
console.log(result.exitCode); // 0
// File helpers
await FsHelpers.ensureEmptyDir('/path/to/dist_rust');
await FsHelpers.copyFile(src, dest);
await FsHelpers.makeExecutable(dest);
const size = await FsHelpers.getFileSize(dest);
console.log(FsHelpers.formatFileSize(size)); // "13.4 MB"
License and Legal Information
This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the license file.
Please note: The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
Trademarks
This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH or third parties, and are not included within the scope of the MIT license granted herein.
Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines or the guidelines of the respective third-party owners, and any usage must be approved in writing. Third-party trademarks used herein are the property of their respective owners and used only in a descriptive manner, e.g. for an implementation of an API or similar.
Company Information
Task Venture Capital GmbH Registered at District Court Bremen HRB 35230 HB, Germany
For any legal inquiries or further information, please contact us via email at hello@task.vc.
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.