sigmd 0.1.0

Windows API signature metadata
Documentation
#
# fetch-winsdk.ps1 — Download Windows SDK + CRT headers/libs via xwin.
#
# Uses an isolated CARGO_HOME/RUSTUP_HOME so nothing is left behind
# except the output directory containing crt/ and sdk/.
#

param(
    [string] $Output = ".\winsdk",
    [switch] $Force,
    [switch] $Isolated,
    [switch] $Help
)

Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"

# ---------- help ----------

if ($Help) {
    Write-Host @"
Usage: fetch-winsdk.ps1 [OPTIONS]

Download Windows SDK and CRT headers/libs via xwin.

Options:
  -Output DIR   Output directory (default: .\winsdk)
  -Force        Re-download even if output directory already exists
  -Isolated     Always download fresh Rust, ignore system Rust
  -Help         Show this help
"@
    exit 0
}

# ---------- helpers ----------

$Script:IsWin = ((Test-Path variable:IsWindows) -and $IsWindows) -or ($env:OS -eq "Windows_NT")
$Script:PathSep = if ($Script:IsWin) { ";" } else { ":" }

function Invoke-NativeCommand {
    param([string] $Description)

    if ($LASTEXITCODE -ne 0) {
        throw "$Description failed (exit code $LASTEXITCODE)"
    }
}

# ---------- skip if already downloaded ----------

if (-not $Force -and (Test-Path (Join-Path $Output "crt")) -and (Test-Path (Join-Path $Output "sdk"))) {
    Write-Host "Windows SDK already present in $Output (use -Force to re-download)"
    exit 0
}

# ---------- temp dir with cleanup ----------

$TmpDir = Join-Path ([System.IO.Path]::GetTempPath()) "fetch-winsdk.$([System.IO.Path]::GetRandomFileName())"
New-Item -ItemType Directory -Path $TmpDir -Force | Out-Null
Write-Host "Temp directory: $TmpDir"

try {
    # ---------- find or install xwin ----------

    $XwinPath = $null

    # 1) Check for existing xwin on PATH
    if (-not $Isolated) {
        $Found = Get-Command xwin -ErrorAction SilentlyContinue
        if ($Found) {
            $XwinPath = $Found.Source
            Write-Host "Using system xwin: $XwinPath"
        }
    }

    # 2) Fall back to cargo install
    if (-not $XwinPath) {
        $XwinRoot = Join-Path $TmpDir "xwin-install"
        New-Item -ItemType Directory -Path $XwinRoot -Force | Out-Null

        $HasCargo = $false
        if (-not $Isolated) {
            $Found = Get-Command cargo -ErrorAction SilentlyContinue
            if ($Found) {
                Write-Host "Using system cargo: $($Found.Source)"
                $HasCargo = $true
            }
        }

        if (-not $HasCargo) {
            Write-Host "Downloading Rust toolchain into temp directory..."
            $env:CARGO_HOME = Join-Path $TmpDir "cargo"
            $env:RUSTUP_HOME = Join-Path $TmpDir "rustup"
            New-Item -ItemType Directory -Path $env:CARGO_HOME -Force | Out-Null
            New-Item -ItemType Directory -Path $env:RUSTUP_HOME -Force | Out-Null

            if ($Script:IsWin) {
                $RustupInit = Join-Path $TmpDir "rustup-init.exe"
                Invoke-WebRequest -Uri "https://win.rustup.rs/x86_64" -OutFile $RustupInit
                & $RustupInit -y --no-modify-path --default-toolchain stable --profile minimal
                Invoke-NativeCommand "rustup-init"
            } else {
                $RustupScript = Join-Path $TmpDir "rustup.sh"
                Invoke-WebRequest -Uri "https://sh.rustup.rs" -OutFile $RustupScript
                & bash $RustupScript -y --no-modify-path --default-toolchain stable --profile minimal
                Invoke-NativeCommand "rustup install"
            }

            $env:PATH = "$(Join-Path $env:CARGO_HOME 'bin')$($Script:PathSep)$env:PATH"
        }

        Write-Host "Using cargo: $((Get-Command cargo).Source)"
        & cargo --version
        Invoke-NativeCommand "cargo"

        Write-Host "Installing xwin..."
        & cargo install xwin --root $XwinRoot
        Invoke-NativeCommand "cargo install xwin"

        $XwinBin = if ($Script:IsWin) { "xwin.exe" } else { "xwin" }
        $XwinPath = Join-Path (Join-Path $XwinRoot "bin") $XwinBin
    }

    # ---------- run xwin ----------

    if ($Force -and (Test-Path $Output)) {
        Write-Host "Removing existing $Output..."
        Remove-Item -Recurse -Force $Output
    }

    New-Item -ItemType Directory -Path $Output -Force | Out-Null
    $Output = (Resolve-Path $Output).Path

    Write-Host "Fetching Windows SDK + CRT into $Output..."
    $XwinCache = Join-Path $TmpDir "xwin-cache"
    & $XwinPath --accept-license --arch x86,x86_64 --include-atl --cache-dir $XwinCache splat --copy --output $Output
    Invoke-NativeCommand "xwin splat"

    Write-Host "Done. Windows SDK available at $Output"

} finally {
    Write-Host "Cleaning up temp directory..."
    Remove-Item -Recurse -Force $TmpDir -ErrorAction SilentlyContinue
}