# Installs Larva, the toolchain of the Antril language, on Windows (PowerShell 5 or later): # # irm https://antril.org/install.ps1 | iex # # It reads the latest release from https://antril.org/download/latest.json, downloads LarvaSetup--win-x64.exe # into a temporary folder, checks its SHA-256 and runs it with --silent: Larva for your user, no administrator rights, # into %LOCALAPPDATA%\Programs\Larva, with `larva` on your PATH. # # Settings (environment variables, set before running it): # $env:LARVA_INSTALL_DIR = "D:\Tools\Larva" install there (the setup's --dir) # $env:LARVA_INSTALL_ARGS = "--no-path --no-hive" more options for the setup: --no-path, --no-registry, --no-hive, --no-types # $env:LARVA_INSTALL_DRYRUN = "1" download and check, then stop before installing # # With options as arguments: # & ([scriptblock]::Create((irm https://antril.org/install.ps1))) --no-path function Install-Larva { param([string[]] $Options) $ErrorActionPreference = 'Stop' $base = 'https://antril.org' if ($env:LARVA_INSTALL_BASE) { $base = $env:LARVA_INSTALL_BASE.TrimEnd('/') } function Say([string] $text) { Write-Host $text } function Step([string] $text) { Write-Host "==> " -ForegroundColor Green -NoNewline; Write-Host $text } function Fail([string] $text) { Write-Host "error: " -ForegroundColor Red -NoNewline; Write-Host $text; return $false } if ($PSVersionTable.PSVersion.Major -lt 5) { return (Fail "PowerShell 5 or later is needed (this is $($PSVersionTable.PSVersion)).") } if (-not [Environment]::Is64BitOperatingSystem) { return (Fail "Larva needs 64-bit Windows.") } # Windows PowerShell 5 may still default to old TLS versions. try { [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 } catch { } # The setup's options: the folder, $env:LARVA_INSTALL_ARGS, then the arguments. $setup = New-Object System.Collections.Generic.List[string] $setup.Add('--silent') if ($env:LARVA_INSTALL_DIR) { $setup.Add('--dir') $setup.Add([IO.Path]::GetFullPath($env:LARVA_INSTALL_DIR)) } if ($env:LARVA_INSTALL_ARGS) { foreach ($word in ($env:LARVA_INSTALL_ARGS -split '\s+')) { if ($word) { $setup.Add($word) } } } $dryRun = $env:LARVA_INSTALL_DRYRUN -and $env:LARVA_INSTALL_DRYRUN -ne '0' foreach ($option in $Options) { if ($option -eq '--dry-run') { $dryRun = $true } elseif ($option) { $setup.Add($option) } } Step "Looking for the latest Larva at $base" try { $latest = Invoke-RestMethod -UseBasicParsing -Uri "$base/download/latest.json" -Headers @{ 'Cache-Control' = 'no-cache' } } catch { return (Fail "could not read $base/download/latest.json: $($_.Exception.Message)") } $version = [string] $latest.version if (-not $version) { return (Fail "$base/download/latest.json names no version.") } $arch = 'x64' if ($env:PROCESSOR_ARCHITECTURE -eq 'ARM64' -or $env:PROCESSOR_ARCHITEW6432 -eq 'ARM64') { $arch = 'arm64' } $asset = $latest.assets | Where-Object { $_.name -eq "LarvaSetup-$version-win-$arch.exe" } | Select-Object -First 1 if (-not $asset -and $arch -ne 'x64') { # Windows on Arm runs the x64 build. $asset = $latest.assets | Where-Object { $_.name -eq "LarvaSetup-$version-win-x64.exe" } | Select-Object -First 1 } if (-not $asset) { return (Fail "Larva $version has no Windows installer. See $base/download") } if (-not $asset.sha256) { return (Fail "$($asset.name) has no SHA-256 to check it against. See $base/download") } $folder = Join-Path ([IO.Path]::GetTempPath()) ("larva-install-" + [Guid]::NewGuid().ToString('N').Substring(0, 8)) New-Item -ItemType Directory -Force -Path $folder | Out-Null $file = Join-Path $folder $asset.name try { $megabytes = [Math]::Round($asset.size / 1MB, 1) Step "Downloading $($asset.name) ($megabytes MB)" $progress = $ProgressPreference $ProgressPreference = 'SilentlyContinue' # the progress bar of Windows PowerShell 5 makes downloads crawl try { Invoke-WebRequest -UseBasicParsing -Uri $asset.url -OutFile $file } catch { return (Fail "could not download $($asset.url): $($_.Exception.Message)") } finally { $ProgressPreference = $progress } Step "Checking its SHA-256" $hash = (Get-FileHash -Algorithm SHA256 -LiteralPath $file).Hash.ToLowerInvariant() if ($hash -ne ([string] $asset.sha256).ToLowerInvariant()) { return (Fail "the SHA-256 of $($asset.name) is $hash, not $($asset.sha256): the download is damaged or not Larva's. Nothing was installed.") } Say " $hash ok" # Start-Process joins its arguments with spaces: quote the ones that have some. $quoted = $setup | ForEach-Object { if ($_ -match '\s') { '"' + $_ + '"' } else { $_ } } if ($dryRun) { Step "Dry run: would run $($asset.name) $($quoted -join ' ')" Say "Nothing was installed (LARVA_INSTALL_DRYRUN)." return $true } Step "Installing Larva $version" $process = Start-Process -FilePath $file -ArgumentList $quoted -Wait -PassThru -NoNewWindow if ($process.ExitCode -ne 0) { return (Fail "the setup ended with exit code $($process.ExitCode). Run $($asset.name) from $base/download to see the wizard.") } $installed = if ($env:LARVA_INSTALL_DIR) { [IO.Path]::GetFullPath($env:LARVA_INSTALL_DIR) } else { Join-Path $env:LOCALAPPDATA 'Programs\Larva' } $larva = Join-Path $installed 'larva.exe' if (-not ($setup -contains '--no-path') -and (Test-Path $larva)) { # This window too (new ones read the PATH the setup changed). if (-not (($env:Path -split ';') -contains $installed)) { $env:Path = "$installed;$env:Path" } } if (Test-Path $larva) { $answer = & $larva --version 2>$null Step "Installed $answer into $installed" } else { Step "Installed Larva $version" } Say "" Say "Next: larva new Console-App Hello --lang antril; cd Hello; larva run" Say "Documentation: $base/docs" return $true } finally { Remove-Item -Recurse -Force -LiteralPath $folder -ErrorAction SilentlyContinue } } # Not `exit`: piped to iex, this runs in the caller's own session. $null = Install-Larva -Options $args