Phase 1: Repository & Infrastructure Setup
- Renamed directories: codex-rs -> llmx-rs, codex-cli -> llmx-cli
- Updated package.json files:
- Root: llmx-monorepo
- CLI: @llmx/llmx
- SDK: @llmx/llmx-sdk
- Updated pnpm workspace configuration
- Renamed binary: codex.js -> llmx.js
- Updated environment variables: CODEX_* -> LLMX_*
- Changed repository URLs to valknar/llmx
🤖 Generated with Claude Code
This commit is contained in:
375
llmx-rs/scripts/create_github_release
Executable file
375
llmx-rs/scripts/create_github_release
Executable file
@@ -0,0 +1,375 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import base64
|
||||
import json
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
REPO = "openai/codex"
|
||||
BRANCH_REF = "heads/main"
|
||||
CARGO_TOML_PATH = "codex-rs/Cargo.toml"
|
||||
|
||||
|
||||
def parse_args(argv: list[str]) -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(description="Publish a tagged Codex release.")
|
||||
parser.add_argument(
|
||||
"-n",
|
||||
"--dry-run",
|
||||
action="store_true",
|
||||
help="Print the version that would be used and exit before making changes.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--promote-alpha",
|
||||
metavar="VERSION",
|
||||
help="Promote an existing alpha tag (e.g., 0.56.0-alpha.5) by using its merge-base with main as the base commit.",
|
||||
)
|
||||
|
||||
group = parser.add_mutually_exclusive_group()
|
||||
group.add_argument(
|
||||
"--publish-alpha",
|
||||
action="store_true",
|
||||
help="Publish the next alpha release for the upcoming minor version.",
|
||||
)
|
||||
group.add_argument(
|
||||
"--publish-release",
|
||||
action="store_true",
|
||||
help="Publish the next stable release by bumping the minor version.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--emergency-version-override",
|
||||
help="Publish a specific version because tag was created for the previous release but it never succeeded. Value should be semver, e.g., `0.43.0-alpha.9`.",
|
||||
)
|
||||
|
||||
args = parser.parse_args(argv[1:])
|
||||
if not (
|
||||
args.publish_alpha
|
||||
or args.publish_release
|
||||
or args.emergency_version_override
|
||||
or args.promote_alpha
|
||||
):
|
||||
parser.error(
|
||||
"Must specify --publish-alpha, --publish-release, --promote-alpha, or --emergency-version-override."
|
||||
)
|
||||
return args
|
||||
|
||||
|
||||
def main(argv: list[str]) -> int:
|
||||
args = parse_args(argv)
|
||||
|
||||
# Strip the leading "v" if present.
|
||||
promote_alpha = args.promote_alpha
|
||||
if promote_alpha and promote_alpha.startswith("v"):
|
||||
promote_alpha = promote_alpha[1:]
|
||||
|
||||
try:
|
||||
if promote_alpha:
|
||||
version = derive_release_version_from_alpha(promote_alpha)
|
||||
elif args.emergency_version_override:
|
||||
version = args.emergency_version_override
|
||||
else:
|
||||
version = determine_version(args)
|
||||
print(f"Publishing version {version}")
|
||||
if promote_alpha:
|
||||
base_commit = get_promote_alpha_base_commit(promote_alpha)
|
||||
if args.dry_run:
|
||||
print(
|
||||
f"Would publish version {version} using base commit {base_commit} derived from rust-v{promote_alpha}."
|
||||
)
|
||||
return 0
|
||||
elif args.dry_run:
|
||||
return 0
|
||||
|
||||
if not promote_alpha:
|
||||
print("Fetching branch head...")
|
||||
base_commit = get_branch_head()
|
||||
print(f"Base commit: {base_commit}")
|
||||
print("Fetching commit tree...")
|
||||
base_tree = get_commit_tree(base_commit)
|
||||
print(f"Base tree: {base_tree}")
|
||||
print("Fetching Cargo.toml...")
|
||||
current_contents = fetch_file_contents(base_commit)
|
||||
print("Updating version...")
|
||||
updated_contents = replace_version(current_contents, version)
|
||||
print("Creating blob...")
|
||||
blob_sha = create_blob(updated_contents)
|
||||
print(f"Blob SHA: {blob_sha}")
|
||||
print("Creating tree...")
|
||||
tree_sha = create_tree(base_tree, blob_sha)
|
||||
print(f"Tree SHA: {tree_sha}")
|
||||
print("Creating commit...")
|
||||
commit_sha = create_commit(version, tree_sha, base_commit)
|
||||
print(f"Commit SHA: {commit_sha}")
|
||||
print("Creating tag...")
|
||||
tag_sha = create_tag(version, commit_sha)
|
||||
print(f"Tag SHA: {tag_sha}")
|
||||
print("Creating tag ref...")
|
||||
create_tag_ref(version, tag_sha)
|
||||
print("Done.")
|
||||
except ReleaseError as error:
|
||||
print(f"ERROR: {error}", file=sys.stderr)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
class ReleaseError(RuntimeError):
|
||||
pass
|
||||
|
||||
|
||||
def run_gh_api(endpoint: str, *, method: str = "GET", payload: dict | None = None) -> dict:
|
||||
print(f"Running gh api {method} {endpoint}")
|
||||
command = [
|
||||
"gh",
|
||||
"api",
|
||||
endpoint,
|
||||
"--method",
|
||||
method,
|
||||
"-H",
|
||||
"Accept: application/vnd.github+json",
|
||||
]
|
||||
json_payload = None
|
||||
if payload is not None:
|
||||
json_payload = json.dumps(payload)
|
||||
print(f"Payload: {json_payload}")
|
||||
command.extend(["-H", "Content-Type: application/json", "--input", "-"])
|
||||
result = subprocess.run(command, text=True, capture_output=True, input=json_payload)
|
||||
if result.returncode != 0:
|
||||
message = result.stderr.strip() or result.stdout.strip() or "gh api call failed"
|
||||
raise ReleaseError(message)
|
||||
try:
|
||||
return json.loads(result.stdout or "{}")
|
||||
except json.JSONDecodeError as error:
|
||||
raise ReleaseError("Failed to parse response from gh api.") from error
|
||||
|
||||
|
||||
def get_branch_head() -> str:
|
||||
response = run_gh_api(f"/repos/{REPO}/git/refs/{BRANCH_REF}")
|
||||
try:
|
||||
return response["object"]["sha"]
|
||||
except KeyError as error:
|
||||
raise ReleaseError("Unable to determine branch head.") from error
|
||||
|
||||
|
||||
def get_promote_alpha_base_commit(alpha_version: str) -> str:
|
||||
tag_name = f"rust-v{alpha_version}"
|
||||
tag_commit_sha = get_tag_commit_sha(tag_name)
|
||||
return get_merge_base_with_main(tag_commit_sha)
|
||||
|
||||
|
||||
def get_tag_commit_sha(tag_name: str) -> str:
|
||||
response = run_gh_api(f"/repos/{REPO}/git/refs/tags/{tag_name}")
|
||||
try:
|
||||
sha = response["object"]["sha"]
|
||||
obj_type = response["object"]["type"]
|
||||
except KeyError as error:
|
||||
raise ReleaseError(f"Unable to resolve tag {tag_name}.") from error
|
||||
while obj_type == "tag":
|
||||
tag_response = run_gh_api(f"/repos/{REPO}/git/tags/{sha}")
|
||||
try:
|
||||
sha = tag_response["object"]["sha"]
|
||||
obj_type = tag_response["object"]["type"]
|
||||
except KeyError as error:
|
||||
raise ReleaseError(f"Unable to resolve annotated tag {tag_name}.") from error
|
||||
if obj_type != "commit":
|
||||
raise ReleaseError(f"Tag {tag_name} does not reference a commit.")
|
||||
return sha
|
||||
|
||||
|
||||
def get_merge_base_with_main(commit_sha: str) -> str:
|
||||
response = run_gh_api(f"/repos/{REPO}/compare/main...{commit_sha}")
|
||||
try:
|
||||
return response["merge_base_commit"]["sha"]
|
||||
except KeyError as error:
|
||||
raise ReleaseError("Unable to determine merge base with main.") from error
|
||||
|
||||
|
||||
def get_commit_tree(commit_sha: str) -> str:
|
||||
response = run_gh_api(f"/repos/{REPO}/git/commits/{commit_sha}")
|
||||
try:
|
||||
return response["tree"]["sha"]
|
||||
except KeyError as error:
|
||||
raise ReleaseError("Commit response missing tree SHA.") from error
|
||||
|
||||
|
||||
def fetch_file_contents(ref_sha: str) -> str:
|
||||
response = run_gh_api(f"/repos/{REPO}/contents/{CARGO_TOML_PATH}?ref={ref_sha}")
|
||||
try:
|
||||
encoded_content = response["content"].replace("\n", "")
|
||||
encoding = response.get("encoding", "")
|
||||
except KeyError as error:
|
||||
raise ReleaseError("Failed to fetch Cargo.toml contents.") from error
|
||||
|
||||
if encoding != "base64":
|
||||
raise ReleaseError(f"Unexpected Cargo.toml encoding: {encoding}")
|
||||
|
||||
try:
|
||||
return base64.b64decode(encoded_content).decode("utf-8")
|
||||
except (ValueError, UnicodeDecodeError) as error:
|
||||
raise ReleaseError("Failed to decode Cargo.toml contents.") from error
|
||||
|
||||
|
||||
def replace_version(contents: str, version: str) -> str:
|
||||
updated, matches = re.subn(
|
||||
r'^version = "[^"]+"', f'version = "{version}"', contents, count=1, flags=re.MULTILINE
|
||||
)
|
||||
if matches != 1:
|
||||
raise ReleaseError("Unable to update version in Cargo.toml.")
|
||||
return updated
|
||||
|
||||
|
||||
def create_blob(content: str) -> str:
|
||||
response = run_gh_api(
|
||||
f"/repos/{REPO}/git/blobs",
|
||||
method="POST",
|
||||
payload={"content": content, "encoding": "utf-8"},
|
||||
)
|
||||
try:
|
||||
return response["sha"]
|
||||
except KeyError as error:
|
||||
raise ReleaseError("Blob creation response missing SHA.") from error
|
||||
|
||||
|
||||
def create_tree(base_tree_sha: str, blob_sha: str) -> str:
|
||||
response = run_gh_api(
|
||||
f"/repos/{REPO}/git/trees",
|
||||
method="POST",
|
||||
payload={
|
||||
"base_tree": base_tree_sha,
|
||||
"tree": [
|
||||
{
|
||||
"path": CARGO_TOML_PATH,
|
||||
"mode": "100644",
|
||||
"type": "blob",
|
||||
"sha": blob_sha,
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
try:
|
||||
return response["sha"]
|
||||
except KeyError as error:
|
||||
raise ReleaseError("Tree creation response missing SHA.") from error
|
||||
|
||||
|
||||
def create_commit(version: str, tree_sha: str, parent_sha: str) -> str:
|
||||
response = run_gh_api(
|
||||
f"/repos/{REPO}/git/commits",
|
||||
method="POST",
|
||||
payload={
|
||||
"message": f"Release {version}",
|
||||
"tree": tree_sha,
|
||||
"parents": [parent_sha],
|
||||
},
|
||||
)
|
||||
try:
|
||||
return response["sha"]
|
||||
except KeyError as error:
|
||||
raise ReleaseError("Commit creation response missing SHA.") from error
|
||||
|
||||
|
||||
def create_tag(version: str, commit_sha: str) -> str:
|
||||
tag_name = f"rust-v{version}"
|
||||
response = run_gh_api(
|
||||
f"/repos/{REPO}/git/tags",
|
||||
method="POST",
|
||||
payload={
|
||||
"tag": tag_name,
|
||||
"message": f"Release {version}",
|
||||
"object": commit_sha,
|
||||
"type": "commit",
|
||||
},
|
||||
)
|
||||
try:
|
||||
return response["sha"]
|
||||
except KeyError as error:
|
||||
raise ReleaseError("Tag creation response missing SHA.") from error
|
||||
|
||||
|
||||
def create_tag_ref(version: str, tag_sha: str) -> None:
|
||||
tag_ref = f"refs/tags/rust-v{version}"
|
||||
run_gh_api(
|
||||
f"/repos/{REPO}/git/refs",
|
||||
method="POST",
|
||||
payload={"ref": tag_ref, "sha": tag_sha},
|
||||
)
|
||||
|
||||
|
||||
def determine_version(args: argparse.Namespace) -> str:
|
||||
latest_version = get_latest_release_version()
|
||||
major, minor, patch = parse_semver(latest_version)
|
||||
next_minor_version = format_version(major, minor + 1, patch)
|
||||
|
||||
if args.publish_release:
|
||||
return next_minor_version
|
||||
|
||||
alpha_prefix = f"{next_minor_version}-alpha."
|
||||
releases = list_releases()
|
||||
highest_alpha = 0
|
||||
found_alpha = False
|
||||
for release in releases:
|
||||
tag = release.get("tag_name", "")
|
||||
candidate = strip_tag_prefix(tag)
|
||||
if candidate and candidate.startswith(alpha_prefix):
|
||||
suffix = candidate[len(alpha_prefix) :]
|
||||
try:
|
||||
alpha_number = int(suffix)
|
||||
except ValueError:
|
||||
continue
|
||||
highest_alpha = max(highest_alpha, alpha_number)
|
||||
found_alpha = True
|
||||
|
||||
if found_alpha:
|
||||
return f"{alpha_prefix}{highest_alpha + 1}"
|
||||
return f"{alpha_prefix}1"
|
||||
|
||||
|
||||
def get_latest_release_version() -> str:
|
||||
response = run_gh_api(f"/repos/{REPO}/releases/latest")
|
||||
tag = response.get("tag_name")
|
||||
version = strip_tag_prefix(tag)
|
||||
if not version:
|
||||
raise ReleaseError("Latest release tag has unexpected format.")
|
||||
return version
|
||||
|
||||
|
||||
def list_releases() -> list[dict]:
|
||||
response = run_gh_api(f"/repos/{REPO}/releases?per_page=100")
|
||||
if not isinstance(response, list):
|
||||
raise ReleaseError("Unexpected response when listing releases.")
|
||||
return response
|
||||
|
||||
|
||||
def strip_tag_prefix(tag: str | None) -> str | None:
|
||||
if not tag:
|
||||
return None
|
||||
prefix = "rust-v"
|
||||
if not tag.startswith(prefix):
|
||||
return None
|
||||
return tag[len(prefix) :]
|
||||
|
||||
|
||||
def parse_semver(version: str) -> tuple[int, int, int]:
|
||||
parts = version.split(".")
|
||||
if len(parts) != 3:
|
||||
raise ReleaseError(f"Unexpected version format: {version}")
|
||||
try:
|
||||
return int(parts[0]), int(parts[1]), int(parts[2])
|
||||
except ValueError as error:
|
||||
raise ReleaseError(f"Version components must be integers: {version}") from error
|
||||
|
||||
|
||||
def format_version(major: int, minor: int, patch: int) -> str:
|
||||
return f"{major}.{minor}.{patch}"
|
||||
|
||||
|
||||
def derive_release_version_from_alpha(alpha_version: str) -> str:
|
||||
match = re.match(r"^(\d+)\.(\d+)\.(\d+)-alpha\.(\d+)$", alpha_version)
|
||||
if match is None:
|
||||
raise ReleaseError(f"Unexpected alpha version format: {alpha_version}")
|
||||
return f"{match.group(1)}.{match.group(2)}.{match.group(3)}"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main(sys.argv))
|
||||
246
llmx-rs/scripts/setup-windows.ps1
Normal file
246
llmx-rs/scripts/setup-windows.ps1
Normal file
@@ -0,0 +1,246 @@
|
||||
<#
|
||||
Setup script for building codex-rs on Windows.
|
||||
|
||||
What it does:
|
||||
- Installs Rust toolchain (via winget rustup) and required components
|
||||
- Installs Visual Studio 2022 Build Tools (MSVC + Windows SDK)
|
||||
- Installs helpful CLIs used by the repo: git, ripgrep (rg), just, cmake
|
||||
- Installs cargo-insta (for snapshot tests) via cargo
|
||||
- Ensures PATH contains Cargo bin for the current session
|
||||
- Builds the workspace (cargo build)
|
||||
|
||||
Usage:
|
||||
- Right-click PowerShell and "Run as Administrator" (VS Build Tools require elevation)
|
||||
- From the repo root (codex-rs), run:
|
||||
powershell -ExecutionPolicy Bypass -File scripts/setup-windows.ps1
|
||||
|
||||
Notes:
|
||||
- Requires winget (Windows Package Manager). Most modern Windows 10/11 have it preinstalled.
|
||||
- The script is re-runnable; winget/cargo will skip/reinstall as appropriate.
|
||||
#>
|
||||
|
||||
param(
|
||||
[switch] $SkipBuild
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
function Ensure-Command($Name) {
|
||||
$exists = Get-Command $Name -ErrorAction SilentlyContinue
|
||||
return $null -ne $exists
|
||||
}
|
||||
|
||||
function Add-CargoBinToPath() {
|
||||
$cargoBin = Join-Path $env:USERPROFILE ".cargo\bin"
|
||||
if (Test-Path $cargoBin) {
|
||||
if (-not ($env:Path.Split(';') -contains $cargoBin)) {
|
||||
$env:Path = "$env:Path;$cargoBin"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Ensure-UserPathContains([string] $Segment) {
|
||||
try {
|
||||
$userPath = [Environment]::GetEnvironmentVariable('Path', 'User')
|
||||
if ($null -eq $userPath) { $userPath = '' }
|
||||
$parts = $userPath.Split(';') | Where-Object { $_ -ne '' }
|
||||
if (-not ($parts -contains $Segment)) {
|
||||
$newPath = if ($userPath) { "$userPath;$Segment" } else { $Segment }
|
||||
[Environment]::SetEnvironmentVariable('Path', $newPath, 'User')
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
function Ensure-UserEnvVar([string] $Name, [string] $Value) {
|
||||
try { [Environment]::SetEnvironmentVariable($Name, $Value, 'User') } catch {}
|
||||
}
|
||||
|
||||
function Ensure-VSComponents([string[]]$Components) {
|
||||
$vsInstaller = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vs_installer.exe"
|
||||
$vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
|
||||
if (-not (Test-Path $vsInstaller) -or -not (Test-Path $vswhere)) { return }
|
||||
|
||||
$instPath = & $vswhere -latest -products * -version "[17.0,18.0)" -requires Microsoft.VisualStudio.Workload.VCTools -property installationPath 2>$null
|
||||
if (-not $instPath) {
|
||||
# 2022 instance may be present without VC Tools; pick BuildTools 2022 and add components
|
||||
$instPath = & $vswhere -latest -products Microsoft.VisualStudio.Product.BuildTools -version "[17.0,18.0)" -property installationPath 2>$null
|
||||
}
|
||||
if (-not $instPath) {
|
||||
$instPath = & $vswhere -latest -products * -requires Microsoft.VisualStudio.Workload.VCTools -property installationPath 2>$null
|
||||
}
|
||||
if (-not $instPath) {
|
||||
$default2022 = 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2022\\BuildTools'
|
||||
if (Test-Path $default2022) { $instPath = $default2022 }
|
||||
}
|
||||
if (-not $instPath) { return }
|
||||
|
||||
$vsDevCmd = Join-Path $instPath 'Common7\Tools\VsDevCmd.bat'
|
||||
$verb = if (Test-Path $vsDevCmd) { 'modify' } else { 'install' }
|
||||
$args = @($verb, '--installPath', $instPath, '--quiet', '--norestart', '--nocache')
|
||||
if ($verb -eq 'install') { $args += @('--productId', 'Microsoft.VisualStudio.Product.BuildTools') }
|
||||
foreach ($c in $Components) { $args += @('--add', $c) }
|
||||
Write-Host "-- Ensuring VS components installed: $($Components -join ', ')" -ForegroundColor DarkCyan
|
||||
& $vsInstaller @args | Out-Host
|
||||
}
|
||||
|
||||
function Enter-VsDevShell() {
|
||||
$vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
|
||||
if (-not (Test-Path $vswhere)) { return }
|
||||
|
||||
$instPath = & $vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath 2>$null
|
||||
if (-not $instPath) {
|
||||
# Try ARM64 components
|
||||
$instPath = & $vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.ARM64 -property installationPath 2>$null
|
||||
}
|
||||
if (-not $instPath) { return }
|
||||
|
||||
$vsDevCmd = Join-Path $instPath 'Common7\Tools\VsDevCmd.bat'
|
||||
if (-not (Test-Path $vsDevCmd)) { return }
|
||||
|
||||
# Prefer ARM64 on ARM machines, otherwise x64
|
||||
$arch = if ($env:PROCESSOR_ARCHITEW6432 -eq 'ARM64' -or $env:PROCESSOR_ARCHITECTURE -eq 'ARM64') { 'arm64' } else { 'x64' }
|
||||
$devCmdStr = ('"{0}" -no_logo -arch={1} -host_arch={1} & set' -f $vsDevCmd, $arch)
|
||||
$envLines = & cmd.exe /c $devCmdStr
|
||||
foreach ($line in $envLines) {
|
||||
if ($line -match '^(.*?)=(.*)$') {
|
||||
$name = $matches[1]
|
||||
$value = $matches[2]
|
||||
try { [Environment]::SetEnvironmentVariable($name, $value, 'Process') } catch {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "==> Installing prerequisites via winget (may take a while)" -ForegroundColor Cyan
|
||||
|
||||
# Accept agreements up-front for non-interactive installs
|
||||
$WingetArgs = @('--accept-package-agreements', '--accept-source-agreements', '-e')
|
||||
|
||||
if (-not (Ensure-Command 'winget')) {
|
||||
throw "winget is required. Please update to the latest Windows 10/11 or install winget."
|
||||
}
|
||||
|
||||
# 1) Visual Studio 2022 Build Tools (MSVC toolchain + Windows SDK)
|
||||
# The VC Tools workload brings the required MSVC toolchains; include recommended components to pick up a Windows SDK.
|
||||
Write-Host "-- Installing Visual Studio Build Tools (VC Tools workload + ARM64 toolchains)" -ForegroundColor DarkCyan
|
||||
$vsOverride = @(
|
||||
'--quiet', '--wait', '--norestart', '--nocache',
|
||||
'--add', 'Microsoft.VisualStudio.Workload.VCTools',
|
||||
'--add', 'Microsoft.VisualStudio.Component.VC.Tools.ARM64',
|
||||
'--add', 'Microsoft.VisualStudio.Component.VC.Tools.ARM64EC',
|
||||
'--add', 'Microsoft.VisualStudio.Component.Windows11SDK.22000'
|
||||
) -join ' '
|
||||
winget install @WingetArgs --id Microsoft.VisualStudio.2022.BuildTools --override $vsOverride | Out-Host
|
||||
|
||||
# Ensure required VC components even if winget doesn't modify the instance
|
||||
$isArm64 = ($env:PROCESSOR_ARCHITEW6432 -eq 'ARM64' -or $env:PROCESSOR_ARCHITECTURE -eq 'ARM64')
|
||||
$components = @(
|
||||
'Microsoft.VisualStudio.Workload.VCTools',
|
||||
'Microsoft.VisualStudio.Component.VC.Tools.ARM64',
|
||||
'Microsoft.VisualStudio.Component.VC.Tools.ARM64EC',
|
||||
'Microsoft.VisualStudio.Component.Windows11SDK.22000'
|
||||
)
|
||||
Ensure-VSComponents -Components $components
|
||||
|
||||
# 2) Rustup
|
||||
Write-Host "-- Installing rustup" -ForegroundColor DarkCyan
|
||||
winget install @WingetArgs --id Rustlang.Rustup | Out-Host
|
||||
|
||||
# Make cargo available in this session
|
||||
Add-CargoBinToPath
|
||||
|
||||
# 3) Git (often present, but ensure installed)
|
||||
Write-Host "-- Installing Git" -ForegroundColor DarkCyan
|
||||
winget install @WingetArgs --id Git.Git | Out-Host
|
||||
|
||||
# 4) ripgrep (rg)
|
||||
Write-Host "-- Installing ripgrep (rg)" -ForegroundColor DarkCyan
|
||||
winget install @WingetArgs --id BurntSushi.ripgrep.MSVC | Out-Host
|
||||
|
||||
# 5) just
|
||||
Write-Host "-- Installing just" -ForegroundColor DarkCyan
|
||||
winget install @WingetArgs --id Casey.Just | Out-Host
|
||||
|
||||
# 6) cmake (commonly needed by native crates)
|
||||
Write-Host "-- Installing CMake" -ForegroundColor DarkCyan
|
||||
winget install @WingetArgs --id Kitware.CMake | Out-Host
|
||||
|
||||
# Ensure cargo is available after rustup install
|
||||
Add-CargoBinToPath
|
||||
if (-not (Ensure-Command 'cargo')) {
|
||||
# Some shells need a re-login; attempt to source cargo.env if present
|
||||
$cargoEnv = Join-Path $env:USERPROFILE ".cargo\env"
|
||||
if (Test-Path $cargoEnv) { . $cargoEnv }
|
||||
Add-CargoBinToPath
|
||||
}
|
||||
if (-not (Ensure-Command 'cargo')) {
|
||||
throw "cargo not found in PATH after rustup install. Please open a new terminal and re-run the script."
|
||||
}
|
||||
|
||||
Write-Host "==> Configuring Rust toolchain per rust-toolchain.toml" -ForegroundColor Cyan
|
||||
|
||||
# Pin to the workspace toolchain and install components
|
||||
$toolchain = '1.90.0'
|
||||
& rustup toolchain install $toolchain --profile minimal | Out-Host
|
||||
& rustup default $toolchain | Out-Host
|
||||
& rustup component add clippy rustfmt rust-src --toolchain $toolchain | Out-Host
|
||||
|
||||
# 6.5) LLVM/Clang (some crates/bindgen require clang/libclang)
|
||||
function Add-LLVMToPath() {
|
||||
$llvmBin = 'C:\\Program Files\\LLVM\\bin'
|
||||
if (Test-Path $llvmBin) {
|
||||
if (-not ($env:Path.Split(';') -contains $llvmBin)) {
|
||||
$env:Path = "$env:Path;$llvmBin"
|
||||
}
|
||||
if (-not $env:LIBCLANG_PATH) {
|
||||
$env:LIBCLANG_PATH = $llvmBin
|
||||
}
|
||||
Ensure-UserPathContains $llvmBin
|
||||
Ensure-UserEnvVar -Name 'LIBCLANG_PATH' -Value $llvmBin
|
||||
|
||||
$clang = Join-Path $llvmBin 'clang.exe'
|
||||
$clangxx = Join-Path $llvmBin 'clang++.exe'
|
||||
if (Test-Path $clang) {
|
||||
$env:CC = $clang
|
||||
Ensure-UserEnvVar -Name 'CC' -Value $clang
|
||||
}
|
||||
if (Test-Path $clangxx) {
|
||||
$env:CXX = $clangxx
|
||||
Ensure-UserEnvVar -Name 'CXX' -Value $clangxx
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "-- Installing LLVM/Clang" -ForegroundColor DarkCyan
|
||||
winget install @WingetArgs --id LLVM.LLVM | Out-Host
|
||||
Add-LLVMToPath
|
||||
|
||||
# 7) cargo-insta (used by snapshot tests)
|
||||
# Ensure MSVC linker is available before building/cargo-install by entering VS dev shell
|
||||
Enter-VsDevShell
|
||||
$hasLink = $false
|
||||
try { & where.exe link | Out-Null; $hasLink = $true } catch {}
|
||||
if ($hasLink) {
|
||||
Write-Host "-- Installing cargo-insta" -ForegroundColor DarkCyan
|
||||
& cargo install cargo-insta --locked | Out-Host
|
||||
} else {
|
||||
Write-Host "-- Skipping cargo-insta for now (MSVC linker not found yet)" -ForegroundColor Yellow
|
||||
}
|
||||
|
||||
if ($SkipBuild) {
|
||||
Write-Host "==> Skipping cargo build (SkipBuild specified)" -ForegroundColor Yellow
|
||||
exit 0
|
||||
}
|
||||
|
||||
Write-Host "==> Building workspace (cargo build)" -ForegroundColor Cyan
|
||||
pushd "$PSScriptRoot\.." | Out-Null
|
||||
try {
|
||||
# Clear RUSTFLAGS if coming from constrained environments
|
||||
$env:RUSTFLAGS = ''
|
||||
Enter-VsDevShell
|
||||
& cargo build
|
||||
}
|
||||
finally {
|
||||
popd | Out-Null
|
||||
}
|
||||
|
||||
Write-Host "==> Build complete" -ForegroundColor Green
|
||||
Reference in New Issue
Block a user