mirror of
https://github.com/tailscale/tailscale.git
synced 2025-07-28 23:04:10 +00:00
gocross for Windows
Signed-off-by: Aaron Klotz <aaron@tailscale.com>
This commit is contained in:
parent
c87f44b687
commit
9d11a4f7f6
@ -1,2 +1,2 @@
|
||||
@echo off
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0go-win.ps1" %*
|
||||
pwsh -NoProfile -ExecutionPolicy Bypass "%~dp0..\tool\gocross\gocross-wrapper.ps1" %*
|
||||
|
@ -11,7 +11,7 @@ import (
|
||||
)
|
||||
|
||||
func doExec(cmd string, args []string, env []string) error {
|
||||
c := exec.Command(cmd, args...)
|
||||
c := exec.Command(cmd, args[1:]...)
|
||||
c.Env = env
|
||||
c.Stdin = os.Stdin
|
||||
c.Stdout = os.Stdout
|
||||
|
213
tool/gocross/gocross-wrapper.ps1
Normal file
213
tool/gocross/gocross-wrapper.ps1
Normal file
@ -0,0 +1,213 @@
|
||||
#Requires -Version 7.4
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
<#
|
||||
.DESCRIPTION
|
||||
Copies the global $args variable into an array, which is easier to work with
|
||||
when preparing to start child processes.
|
||||
#>
|
||||
function Copy-Args {
|
||||
$list = [System.Collections.Generic.List[string]]::new($Global:args.Count)
|
||||
foreach ($arg in $Global:args) {
|
||||
$list.Add($arg)
|
||||
}
|
||||
return $list.ToArray()
|
||||
}
|
||||
|
||||
<#
|
||||
.DESCRIPTION
|
||||
Copies the current environment into a hashtable, which is easier to work with
|
||||
when preparing to start child processes.
|
||||
#>
|
||||
function Copy-Environment {
|
||||
$result = @{}
|
||||
foreach ($pair in (Get-Item -Path Env:)) {
|
||||
$result[$pair.Key] = $pair.Value
|
||||
}
|
||||
return $result
|
||||
}
|
||||
|
||||
<#
|
||||
.DESCRIPTION
|
||||
Outputs the fully-qualified path to the repository's root directory. This
|
||||
function expects to be run from somewhere within a git repository.
|
||||
The directory containing the git executable must be somewhere in the PATH.
|
||||
#>
|
||||
function Get-RepoRoot {
|
||||
Get-Command -Name 'git' | Out-Null
|
||||
$repoRoot = & git rev-parse --show-toplevel
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "failed obtaining repo root: git failed with code $LASTEXITCODE"
|
||||
}
|
||||
|
||||
# Git outputs a path containing forward slashes. Canonicalize.
|
||||
return [System.IO.Path]::GetFullPath($repoRoot)
|
||||
}
|
||||
|
||||
<#
|
||||
.DESCRIPTION
|
||||
Runs the provided ScriptBlock in a child scope, restoring any changes to the
|
||||
current working directory once the script block completes.
|
||||
#>
|
||||
function Start-ChildScope {
|
||||
param (
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ScriptBlock]$ScriptBlock
|
||||
)
|
||||
|
||||
$initialLocation = Get-Location
|
||||
try {
|
||||
Invoke-Command -ScriptBlock $ScriptBlock
|
||||
}
|
||||
finally {
|
||||
Set-Location -Path $initialLocation
|
||||
}
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Write-Output with timestamps prepended to each line.
|
||||
#>
|
||||
function Write-Log {
|
||||
param ($message)
|
||||
$timestamp = (Get-Date).ToString('yyyy-MM-dd HH:mm:ss')
|
||||
Write-Output "$timestamp - $message"
|
||||
}
|
||||
|
||||
$bootstrapScriptBlock = {
|
||||
|
||||
$repoRoot = Get-RepoRoot
|
||||
|
||||
Set-Location -LiteralPath $repoRoot
|
||||
|
||||
switch -Wildcard -File .\go.toolchain.rev {
|
||||
"/*" { $toolchain = $_ }
|
||||
default {
|
||||
$rev = $_
|
||||
$tsgo = Join-Path $Env:USERPROFILE '.cache' 'tsgo'
|
||||
$toolchain = Join-Path $tsgo $rev
|
||||
if (-not (Test-Path -LiteralPath "$toolchain.extracted" -PathType Leaf -ErrorAction SilentlyContinue)) {
|
||||
New-Item -Force -Path $tsgo -ItemType Directory
|
||||
Remove-Item -Force -Recurse -LiteralPath $toolchain -ErrorAction Continue
|
||||
Remove-Item -Force -LiteralPath "$toolchain.extracted" -ErrorAction Continue
|
||||
Write-Log "Downloading Go toolchain $rev"
|
||||
|
||||
# Values from https://web.archive.org/web/20250227081443/https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.architecture?view=net-9.0
|
||||
$cpuArch = ([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture | Out-String -NoNewline)
|
||||
# Comparison in switch is case-insensitive by default.
|
||||
switch ($cpuArch) {
|
||||
'x86' { $goArch = '386' }
|
||||
'x64' { $goArch = 'amd64' }
|
||||
default { $goArch = $cpuArch }
|
||||
}
|
||||
|
||||
Invoke-WebRequest -Uri "https://github.com/tailscale/go/releases/download/build-$rev/windows-$goArch.tar.gz" -OutFile "$toolchain.tar.gz"
|
||||
try {
|
||||
New-Item -Force -Path $toolchain -ItemType Directory
|
||||
Start-ChildScope -ScriptBlock {
|
||||
Set-Location -LiteralPath $toolchain
|
||||
tar --strip-components=1 -xf "$toolchain.tar.gz"
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "tar failed with exit code $LASTEXITCODE"
|
||||
}
|
||||
}
|
||||
$rev | Out-File -FilePath "$toolchain.extracted"
|
||||
}
|
||||
finally {
|
||||
Remove-Item -Force "$toolchain.tar.gz" -ErrorAction Continue
|
||||
}
|
||||
|
||||
# Cleanup old toolchains.
|
||||
$maxDays = 90
|
||||
$oldFiles = Get-ChildItem -Path $tsgo -Filter '*.extracted' -File -Recurse -Depth 1 | Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-$maxDays) }
|
||||
foreach ($file in $oldFiles) {
|
||||
Write-Log "Cleaning up old Go toolchain $($file.Basename)"
|
||||
Remove-Item -LiteralPath $file.FullName -Force -ErrorAction Continue
|
||||
$dirName = Join-Path $file.DirectoryName $file.Basename -Resolve -ErrorAction Continue
|
||||
if ($dirName -and (Test-Path -LiteralPath $dirName -PathType Container -ErrorAction Continue)) {
|
||||
Remove-Item -LiteralPath $dirName -Recurse -Force -ErrorAction Continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($Env:TS_USE_GOCROSS -ne '1') {
|
||||
return
|
||||
}
|
||||
|
||||
if (Test-Path -LiteralPath $toolchain -PathType Container -ErrorAction SilentlyContinue) {
|
||||
$goMod = Join-Path $repoRoot 'go.mod' -Resolve
|
||||
$goLine = Get-Content -LiteralPath $goMod | Select-String -Pattern '^go (.*)$' -List
|
||||
$wantGoMinor = $goLine.Matches.Groups[1].Value.split('.')[1]
|
||||
$versionFile = Join-Path $toolchain 'VERSION'
|
||||
if (Test-Path -LiteralPath $versionFile -PathType Leaf -ErrorAction SilentlyContinue) {
|
||||
try {
|
||||
$haveGoMinor = ((Get-Content -LiteralPath $versionFile -TotalCount 1).split('.')[1]) -replace 'rc.*', ''
|
||||
}
|
||||
catch {
|
||||
}
|
||||
}
|
||||
|
||||
if ([string]::IsNullOrEmpty($haveGoMinor) -or ($haveGoMinor -lt $wantGoMinor)) {
|
||||
Remove-Item -Force -Recurse -LiteralPath $toolchain -ErrorAction Continue
|
||||
Remove-Item -Force -LiteralPath "$toolchain.extracted" -ErrorAction Continue
|
||||
}
|
||||
}
|
||||
|
||||
$wantVer = & git rev-parse HEAD
|
||||
$gocrossOk = $false
|
||||
$gocrossPath = '.\gocross.exe'
|
||||
if (Get-Command -Name $gocrossPath -CommandType Application -ErrorAction SilentlyContinue) {
|
||||
$gotVer = & $gocrossPath gocross-version 2> $null
|
||||
if ($gotVer -eq $wantVer) {
|
||||
$gocrossOk = $true
|
||||
}
|
||||
}
|
||||
|
||||
if (-not $gocrossOk) {
|
||||
$goBuildEnv = Copy-Environment
|
||||
$goBuildEnv['CGO_ENABLED'] = '0'
|
||||
$goBuildEnv.Remove('GOOS')
|
||||
$goBuildEnv.Remove('GOARCH')
|
||||
$goBuildEnv.Remove('GO111MODULE')
|
||||
$goBuildEnv.Remove('GOROOT')
|
||||
|
||||
$procExe = Join-Path $toolchain 'bin' 'go.exe' -Resolve
|
||||
$proc = Start-Process -FilePath $procExe -WorkingDirectory $repoRoot -Environment $goBuildEnv -ArgumentList 'build', '-o', $gocrossPath, "-ldflags=-X=tailscale.com/version.gitCommitStamp=$wantVer", 'tailscale.com/tool/gocross' -NoNewWindow -Wait -PassThru
|
||||
if ($proc.ExitCode -ne 0) {
|
||||
throw 'error building gocross'
|
||||
}
|
||||
}
|
||||
|
||||
} # bootstrapScriptBlock
|
||||
|
||||
Start-ChildScope -ScriptBlock $bootstrapScriptBlock
|
||||
|
||||
$repoRoot = Get-RepoRoot
|
||||
|
||||
$execEnv = Copy-Environment
|
||||
$execEnv.Remove('GOROOT')
|
||||
|
||||
$argList = Copy-Args
|
||||
|
||||
if ($Env:TS_USE_GOCROSS -ne '1') {
|
||||
$revFile = Join-Path $repoRoot 'go.toolchain.rev' -Resolve
|
||||
switch -Wildcard -File $revFile {
|
||||
"/*" { $toolchain = $_ }
|
||||
default {
|
||||
$rev = $_
|
||||
$tsgo = Join-Path $Env:USERPROFILE '.cache' 'tsgo'
|
||||
$toolchain = Join-Path $tsgo $rev -Resolve
|
||||
}
|
||||
}
|
||||
|
||||
$procExe = Join-Path $toolchain 'bin' 'go.exe' -Resolve
|
||||
$proc = Start-Process -FilePath $procExe -WorkingDirectory $repoRoot -Environment $execEnv -ArgumentList $argList -NoNewWindow -Wait -PassThru
|
||||
exit $proc.ExitCode
|
||||
}
|
||||
|
||||
$procExe = Join-Path $repoRoot 'gocross.exe' -Resolve
|
||||
$proc = Start-Process -FilePath $procExe -WorkingDirectory $repoRoot -Environment $execEnv -ArgumentList $argList -NoNewWindow -Wait -PassThru
|
||||
exit $proc.ExitCode
|
@ -15,6 +15,11 @@ if [[ "${CI:-}" == "true" && "${NOBASHDEBUG:-}" != "true" ]]; then
|
||||
set -x
|
||||
fi
|
||||
|
||||
if [[ "${OSTYPE:-}" == "cygwin" || "${OSTYPE:-}" == "msys" ]]; then
|
||||
echo "You're running on Windows: use go.cmd instead." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Locate a bootstrap toolchain and (re)build gocross if necessary. We run all of
|
||||
# this in a subshell because posix shell semantics make it very easy to
|
||||
# accidentally mutate the input environment that will get passed to gocross at
|
||||
|
@ -15,9 +15,9 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime/debug"
|
||||
|
||||
"tailscale.com/atomicfile"
|
||||
"tailscale.com/version"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@ -28,19 +28,13 @@ func main() {
|
||||
// any time.
|
||||
switch os.Args[1] {
|
||||
case "gocross-version":
|
||||
bi, ok := debug.ReadBuildInfo()
|
||||
if !ok {
|
||||
fmt.Fprintln(os.Stderr, "failed getting build info")
|
||||
commit := version.GetMeta().GitCommit
|
||||
if commit == "" {
|
||||
fmt.Fprintln(os.Stderr, "did not find VCS revision in build info")
|
||||
os.Exit(1)
|
||||
}
|
||||
for _, s := range bi.Settings {
|
||||
if s.Key == "vcs.revision" {
|
||||
fmt.Println(s.Value)
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
fmt.Fprintln(os.Stderr, "did not find vcs.revision in build info")
|
||||
os.Exit(1)
|
||||
fmt.Println(commit)
|
||||
os.Exit(0)
|
||||
case "is-gocross":
|
||||
// This subcommand exits with an error code when called on a
|
||||
// regular go binary, so it can be used to detect when `go` is
|
||||
@ -68,8 +62,13 @@ func main() {
|
||||
fmt.Fprintf(os.Stderr, "usage: gocross write-wrapper-script <path>\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := atomicfile.WriteFile(os.Args[2], wrapperScript, 0755); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "writing wrapper script: %v\n", err)
|
||||
if err := atomicfile.WriteFile(os.Args[2], wrapperScriptBash, 0755); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "writing bash wrapper script: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
psFileName := strings.TrimSuffix(os.Args[2], filepath.Ext(os.Args[2])) + ".ps1"
|
||||
if err := atomicfile.WriteFile(psFileName, wrapperScriptPowerShell, 0755); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "writing PowerShell wrapper script: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
os.Exit(0)
|
||||
@ -112,7 +111,10 @@ func main() {
|
||||
}
|
||||
|
||||
//go:embed gocross-wrapper.sh
|
||||
var wrapperScript []byte
|
||||
var wrapperScriptBash []byte
|
||||
|
||||
//go:embed gocross-wrapper.ps1
|
||||
var wrapperScriptPowerShell []byte
|
||||
|
||||
func debugf(format string, args ...any) {
|
||||
debug := os.Getenv("GOCROSS_DEBUG")
|
||||
|
@ -60,7 +60,12 @@ func getToolchain() (toolchainDir, gorootDir string, err error) {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
cache := filepath.Join(os.Getenv("HOME"), ".cache")
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
cache := filepath.Join(homeDir, ".cache")
|
||||
toolchainDir = filepath.Join(cache, "tsgo", rev)
|
||||
gorootDir = filepath.Join(cache, "tsgoroot", rev)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user