Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/actions/build-upstream/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -174,10 +174,12 @@ runs:
env:
INPUTS_TARGET: ${{ inputs.target }}

# The helper builds the excluded trampoline from its crate directory.
# It anchors CARGO_TARGET_DIR to the repository root.
- name: Build trampoline shim binary (Windows only)
if: steps.native.outputs.build == 'true' && contains(inputs.target, 'windows')
shell: bash
run: cargo build --release --target ${INPUTS_TARGET} -p vp_trampoline
run: node packages/tools/src/build-trampoline.ts --release --target "${INPUTS_TARGET}"
env:
INPUTS_TARGET: ${{ inputs.target }}

Expand Down
11 changes: 10 additions & 1 deletion .github/actions/build-windows-cli/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,20 @@ runs:
- name: Build Rust CLI binaries
if: steps.binaries-cache.outputs.cache-hit != 'true'
shell: bash
run: cargo xwin build --release --target x86_64-pc-windows-msvc -p vp_global_cli -p vp_trampoline -p vp_installer
run: cargo xwin build --release --target x86_64-pc-windows-msvc -p vp_global_cli -p vp_installer
env:
XWIN_ACCEPT_LICENSE: '1'
CXXFLAGS: -D_ALLOW_COMPILER_AND_STL_VERSION_MISMATCH

# The helper builds the excluded trampoline from its crate directory.
# It anchors CARGO_TARGET_DIR to the repository root.
- name: Build trampoline shim binary
if: steps.binaries-cache.outputs.cache-hit != 'true'
shell: bash
run: node packages/tools/src/build-trampoline.ts --xwin --release --target x86_64-pc-windows-msvc
env:
XWIN_ACCEPT_LICENSE: '1'

- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: ${{ inputs.artifact-name }}
Expand Down
6 changes: 5 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -206,11 +206,14 @@ jobs:
# Keep the package selection in sync with the `test` recipe in justfile.
# vp_cli_snapshots is excluded there too: its snapshot suite needs a
# built vp and node at runtime and joins the Windows archive later.
# vp_trampoline is not a workspace member.
# Run its portable parser and layout tests on Unix.
# The Windows CLI snapshot suite tests Windows shim behavior.
- name: Build test archive
run: |
eval "$(cargo xwin env --target x86_64-pc-windows-msvc | grep '^export ')"
unset RUSTFLAGS
cargo nextest archive $(for d in crates/*/; do n=$(basename $d); [ "$n" = "vp_cli_snapshots" ] || echo -n "-p $n "; done) -p vite-plus-cli \
cargo nextest archive $(for d in crates/*/; do n=$(basename $d); [ "$n" = "vp_cli_snapshots" ] || [ "$n" = "vp_trampoline" ] || echo -n "-p $n "; done) -p vite-plus-cli \
--target x86_64-pc-windows-msvc --archive-file windows-tests.tar.zst

- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
Expand Down Expand Up @@ -335,6 +338,7 @@ jobs:
- run: |
cargo shear
cargo fmt --check
cargo fmt --manifest-path crates/vp_trampoline/Cargo.toml --check
just lint
# RUSTDOCFLAGS='-D warnings' cargo doc --no-deps --document-private-items

Expand Down
53 changes: 48 additions & 5 deletions .github/workflows/test-standalone-install.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1269,6 +1269,10 @@ jobs:
- uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2
- uses: ./.github/actions/clone

- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 24

- name: Pin VP_HOME to USERPROFILE
# Namespace's Windows runners run jobs under a service account whose real
# profile (C:\Windows\system32\config\systemprofile) differs from
Expand All @@ -1292,7 +1296,50 @@ jobs:

- name: Build Windows installers
shell: bash
run: cargo build --release -p vp_global_cli -p vp_installer -p vp_trampoline
run: |
cargo build --release -p vp_global_cli -p vp_installer
node packages/tools/src/build-trampoline.ts --release

- name: Test trampoline with an extended-length payload path
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
$root = Join-Path $env:RUNNER_TEMP "vp-trampoline-long-payload"
$bin = Join-Path $root "bin"
$data = Join-Path $root "data"
$cache = Join-Path $root "cache"
$segment = "segment-" + ("x" * 60)
while ((Join-Path $data "current\bin\vp.exe").Length -le 300) {
$data = Join-Path $data $segment
}
$payloadBin = Join-Path $data "current\bin"
$payload = Join-Path $payloadBin "vp.exe"
if ($payload.Length -le 260) {
throw "The payload path must be longer than MAX_PATH: $payload"
}

Remove-Item -Recurse -Force $root -ErrorAction SilentlyContinue
[System.IO.Directory]::CreateDirectory($bin) | Out-Null
[System.IO.Directory]::CreateDirectory($payloadBin) | Out-Null
[System.IO.File]::Copy(
(Join-Path $env:DEV_DRIVE "target/release/vp-shim.exe"),
(Join-Path $bin "vp.exe"),
$true
)
[System.IO.File]::Copy(
(Join-Path $env:DEV_DRIVE "target/release/vp.exe"),
$payload,
$true
)
$pointer = "vite-plus-shim-v1`nlayout=split`ndata=$data`ncache=$cache`n"
[System.IO.File]::WriteAllText((Join-Path $bin "vp.shim"), $pointer)

$output = (& (Join-Path $bin "vp.exe") --version 2>&1) | Out-String
$exitCode = $LASTEXITCODE
Write-Host $output
if ($exitCode -ne 0) {
throw "The trampoline exited with $exitCode for payload path $payload"
}

- name: vp-setup.exe rejects invalid directory overrides
shell: pwsh
Expand Down Expand Up @@ -1704,10 +1751,6 @@ jobs:
}
& $vp --version

- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 24

- name: Start local preview registry for vp-setup.exe
shell: bash
run: |
Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,8 @@ vite

# PTY snapshot runner failure artifacts (reviewed via the diff, never committed)
crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/*/snapshots/*.md.new
# Cargo does not read the crate config when these commands run from the repo root:
# `cargo fmt --manifest-path crates/vp_trampoline/Cargo.toml`
# `cargo clippy --manifest-path crates/vp_trampoline/Cargo.toml`
# These commands create the nested target directory below.
/crates/vp_trampoline/target
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ vite-plus/
├── crates/vp_shared/ # Shared Rust env config, tracing, output, utilities
├── crates/vp_static_config/ # Static extraction of vite.config.* data
├── crates/vp_toolchain/ # toolchain.json manifest model, validation, and `why` hints
└── crates/vp_trampoline/ # Windows shim trampoline
└── crates/vp_trampoline/ # Standalone Windows shim trampoline outside the workspace
```

Vite+ resolves all on-disk paths through `vp_shared::VpDirs`.
Expand Down
4 changes: 0 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 5 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
[workspace]
resolver = "3"
members = ["bench", "crates/*", "packages/cli/binding"]
# vp_trampoline is a standalone package.
# It needs a separate release profile and a crate-local build-std config.
# Cargo ignores `panic` in per-package profile overrides.
# See crates/vp_trampoline/Cargo.toml.
exclude = ["crates/vp_trampoline"]

[workspace.metadata.cargo-shear]
ignored = [
Expand Down Expand Up @@ -431,11 +436,6 @@ strip = "symbols" # set to `false` for debug information
debug = false # set to `true` for debug information
panic = "abort" # Let it crash and force ourselves to write safe Rust.

# The trampoline binary is copied per shim tool (~5-10 copies), so optimize for
# size instead of speed. This reduces it from ~200KB to ~100KB on Windows.
[profile.release.package.vp_trampoline]
opt-level = "z"

# The installer binary is downloaded by users, so optimize for size.
[profile.release.package.vp_installer]
opt-level = "z"
2 changes: 1 addition & 1 deletion crates/vp_cli_snapshots/tests/cli_snapshots/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -592,7 +592,7 @@ impl CaseHome {
.join("vp-shim.exe");
if !shim.is_file() {
return Err(format!(
"global vp trampoline template not found at {}; run `cargo build -p vp_trampoline`",
"The global vp trampoline template does not exist at {}. Run `node packages/tools/src/build-trampoline.ts`.",
shim.display()
));
}
Expand Down
24 changes: 24 additions & 0 deletions crates/vp_trampoline/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Cargo reads this config only when it runs from this directory.
# Run `node packages/tools/src/build-trampoline.ts` from the repository root.
# The helper runs Cargo from this directory.

[unstable]
# Recompile std with this crate's release profile.
# The profile uses opt-level = "z" and panic = "immediate-abort".
# With the raw Win32 source, this reduces the x64 executable to 14 KiB.
# This operation needs the rust-src component.
build-std = ["std", "panic_abort"]
# Replace the default std features to remove panic-unwind and backtrace.
# The optimize_for_size feature enables smaller code paths.
# compiler-builtins-mem supplies memory functions without the CRT.
# The #![no_main] entry point needs these functions.
build-std-features = ["optimize_for_size", "compiler-builtins-mem"]
# Use the abort panic strategy for `cargo test`.
panic-abort-tests = true

[build]
# Store artifacts in the repository target/ directory.
# CI, the snapshot runner, and install-global-cli read artifacts there.
# Cargo resolves this path from the crate directory.
# CARGO_TARGET_DIR overrides this value.
target-dir = "../../target"
7 changes: 7 additions & 0 deletions crates/vp_trampoline/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

65 changes: 53 additions & 12 deletions crates/vp_trampoline/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,28 +1,69 @@
# The root Cargo.toml excludes this crate from the workspace.
# This crate needs a separate release profile with panic = "immediate-abort".
# Cargo ignores `panic` in per-package profile overrides.
# The crate-local .cargo/config.toml enables build-std.
# From the repository root, run:
#
# node packages/tools/src/build-trampoline.ts --release [--target <triple>]
#
# The crate config stores artifacts in the repository target/ directory.
# Workspace builds use the same directory.
# The build uses the pinned nightly toolchain and the rust-src component.
# The repository rust-toolchain.toml supplies both items.
#
# The x86_64-pc-windows-msvc executable is 14 KiB.
# The implementation with precompiled std was approximately 222 KiB.
# build-std recompiles std with this profile.
# panic = "immediate-abort" removes panic formatting, unwinding, and backtraces.
# src/win.rs uses #![no_main] and raw Win32 calls instead of std::process::Command.
# For more information, see rfcs/trampoline-exe-for-shims.md.
cargo-features = ["panic-immediate-abort"]

[package]
name = "vp_trampoline"
version = "0.0.0"
authors.workspace = true
edition.workspace = true
license.workspace = true
authors = ["Vite+ Authors"]
edition = "2024"
license = "MIT"
publish = false
rust-version.workspace = true
description = "Minimal Windows trampoline exe for vite-plus shims"

[[bin]]
name = "vp-shim"
path = "src/main.rs"

# No dependencies — the single Win32 FFI call (SetConsoleCtrlHandler) is
# declared inline to avoid pulling in the heavy `windows`/`windows-core` crates.
# This crate has no dependencies.
# It declares raw Win32 FFI calls to avoid the `windows` and `windows-core` crates.

# Override workspace lints: this is a standalone minimal binary that intentionally
# avoids dependencies on vp_shared, vt_path, vt_str, etc. to keep binary
# size small. It uses std types and macros directly.
# This crate does not inherit workspace lints.
# It uses std types and macros directly to keep the binary small.
# Thus, allow the .clippy.toml rules that require shared project abstractions.
[lints.clippy]
disallowed_macros = "allow"
disallowed_types = "allow"
disallowed_methods = "allow"

# Note: Release profile is defined at workspace root (Cargo.toml).
# The workspace already sets lto="fat", codegen-units=1, strip="symbols", panic="abort".
# For even smaller binaries, consider building this crate separately with opt-level="z".
[profile.release]
opt-level = "z"
lto = "fat"
codegen-units = 1
strip = "symbols"
# Convert panics to an immediate abort without message formatting.
# This prevents links to core::fmt and std::panicking.
panic = "immediate-abort"
debug = false

# Optimize debug builds at opt-level 1.
# At opt-level 0, the compiler can reference the MSVC helper __CxxFrameHandler3.
# This reference causes a link failure, even with panic = "immediate-abort".
# uv-trampoline has the same constraint.
[profile.dev]
opt-level = 1
lto = true
panic = "immediate-abort"
debug = true

[profile.test]
inherits = "dev"

[workspace]
Loading
Loading