Files
SingboxForPanel/building-windows.ps1
2026-04-16 10:29:41 +08:00

364 lines
11 KiB
PowerShell

[CmdletBinding()]
param(
[Parameter(Position = 0, ValueFromRemainingArguments = $true)]
[string[]]$Targets = @("all"),
[string]$GoBin = "",
[string]$DistDir = "",
[string]$GoCacheDir = "",
[string]$GoModCacheDir = "",
[string]$CgoEnabledValue = "0",
[string]$BuildJobs = "",
[string]$Version = "",
[string]$BuildTagsOthers = "",
[string]$BuildTagsWindows = "",
[switch]$Help
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$RootDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$MainPkg = "./cmd/sing-box"
$DefaultTargets = @(
"linux-amd64",
"linux-arm64",
"linux-armv7",
"windows-amd64",
"windows-arm64",
"darwin-amd64",
"darwin-arm64"
)
function Show-Usage {
@'
Usage:
.\building-windows.ps1
.\building-windows.ps1 all
.\building-windows.ps1 linux-amd64
.\building-windows.ps1 linux-amd64 windows-amd64
.\building-windows.ps1 current
Optional parameters:
-GoBin <path> Go binary path
-DistDir <path> Output directory, default: .\dist
-GoCacheDir <path> Go build cache directory, default: .\.cache\go-build
-GoModCacheDir <path> Go module cache directory, default: .\.cache\gomod
-CgoEnabledValue <0|1> CGO_ENABLED value, default: 0
-BuildJobs <int> Go build parallel jobs, default: GO_BUILD_JOBS or CPU core count
-Version <string> Embedded version, default: git describe --tags --always
-BuildTagsOthers <string> Override non-Windows build tags
-BuildTagsWindows <string> Override Windows build tags
'@ | Write-Host
}
function Require-File {
param([string]$Path)
if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) {
throw "Missing required file: $Path"
}
}
function Read-TrimmedFile {
param([string]$Path)
return ((Get-Content -LiteralPath $Path -Raw) -replace "`r", "").Trim()
}
function Resolve-GoBinary {
param([string]$RequestedGoBin)
if ($RequestedGoBin) {
if (-not (Test-Path -LiteralPath $RequestedGoBin -PathType Leaf)) {
throw "Go binary not found: $RequestedGoBin"
}
return (Resolve-Path -LiteralPath $RequestedGoBin).Path
}
$command = Get-Command go -ErrorAction SilentlyContinue
if ($command) {
return $command.Source
}
$defaultPath = "C:\Program Files\Go\bin\go.exe"
if (Test-Path -LiteralPath $defaultPath -PathType Leaf) {
return $defaultPath
}
throw "Go binary not found. Please install Go or pass -GoBin."
}
function Resolve-Version {
param(
[string]$RequestedVersion,
[string]$RepoRoot
)
if ($RequestedVersion) {
return $RequestedVersion
}
$gitCommand = Get-Command git -ErrorAction SilentlyContinue
if ($gitCommand) {
Push-Location $RepoRoot
try {
$described = git describe --tags --always 2>$null
if ($LASTEXITCODE -eq 0 -and $described) {
return $described.Trim()
}
$commit = git rev-parse --short HEAD 2>$null
if ($LASTEXITCODE -eq 0 -and $commit) {
return $commit.Trim()
}
}
finally {
Pop-Location
}
}
return "custom"
}
function Resolve-BuildJobs {
param([string]$RequestedBuildJobs)
if ($RequestedBuildJobs) {
return $RequestedBuildJobs
}
if ($env:GO_BUILD_JOBS) {
return $env:GO_BUILD_JOBS
}
return [string][Environment]::ProcessorCount
}
function Resolve-CachePath {
param(
[string]$RequestedPath,
[string]$DefaultRelativePath
)
if ($RequestedPath) {
return [System.IO.Path]::GetFullPath($RequestedPath)
}
return [System.IO.Path]::GetFullPath((Join-Path $RootDir $DefaultRelativePath))
}
function Get-TargetConfig {
param([string]$Target)
switch ($Target) {
"linux-amd64" {
return @{
GOOS = "linux"
GOARCH = "amd64"
Output = "sing-box-linux-amd64"
Tags = $script:ResolvedBuildTagsOthers
}
}
"linux-arm64" {
return @{
GOOS = "linux"
GOARCH = "arm64"
Output = "sing-box-linux-arm64"
Tags = $script:ResolvedBuildTagsOthers
}
}
"linux-armv7" {
return @{
GOOS = "linux"
GOARCH = "arm"
GOARM = "7"
Output = "sing-box-linux-armv7"
Tags = $script:ResolvedBuildTagsOthers
}
}
"windows-amd64" {
return @{
GOOS = "windows"
GOARCH = "amd64"
Output = "sing-box-windows-amd64.exe"
Tags = $script:ResolvedBuildTagsWindows
}
}
"windows-arm64" {
return @{
GOOS = "windows"
GOARCH = "arm64"
Output = "sing-box-windows-arm64.exe"
Tags = $script:ResolvedBuildTagsWindows
}
}
"darwin-amd64" {
return @{
GOOS = "darwin"
GOARCH = "amd64"
Output = "sing-box-darwin-amd64"
Tags = $script:ResolvedBuildTagsOthers
}
}
"darwin-arm64" {
return @{
GOOS = "darwin"
GOARCH = "arm64"
Output = "sing-box-darwin-arm64"
Tags = $script:ResolvedBuildTagsOthers
}
}
"current" {
$goos = (& $script:ResolvedGoBin env GOOS).Trim()
$goarch = (& $script:ResolvedGoBin env GOARCH).Trim()
$output = "sing-box-$goos-$goarch"
$tags = $script:ResolvedBuildTagsOthers
if ($goos -eq "windows") {
$output += ".exe"
$tags = $script:ResolvedBuildTagsWindows
}
return @{
GOOS = $goos
GOARCH = $goarch
Output = $output
Tags = $tags
}
}
default {
throw "Unsupported target: $Target"
}
}
}
function Invoke-BuildTarget {
param([string]$Target)
$config = Get-TargetConfig -Target $Target
$outputPath = Join-Path $script:ResolvedDistDir $config.Output
Write-Host "==> Building $Target (jobs: $script:ResolvedBuildJobs)" -ForegroundColor Cyan
Push-Location $RootDir
try {
$env:CGO_ENABLED = $CgoEnabledValue
$env:GOOS = $config.GOOS
$env:GOARCH = $config.GOARCH
$env:GOCACHE = $script:ResolvedGoCacheDir
$env:GOMODCACHE = $script:ResolvedGoModCacheDir
if ($config.ContainsKey("GOARM")) {
$env:GOARM = $config.GOARM
} else {
Remove-Item Env:GOARM -ErrorAction SilentlyContinue
}
& $script:ResolvedGoBin build -v -p $script:ResolvedBuildJobs -trimpath `
-ldflags "-X 'github.com/sagernet/sing-box/constant.Version=$script:ResolvedVersion' $script:ResolvedLdflagsShared -s -w -buildid=" `
-tags $config.Tags `
-o $outputPath `
$MainPkg
if ($LASTEXITCODE -ne 0) {
return [pscustomobject]@{
Target = $Target
Success = $false
OutputPath = $outputPath
Error = "go build exited with code $LASTEXITCODE"
}
}
return [pscustomobject]@{
Target = $Target
Success = $true
OutputPath = $outputPath
Error = ""
}
}
catch {
return [pscustomobject]@{
Target = $Target
Success = $false
OutputPath = $outputPath
Error = $_.Exception.Message
}
}
finally {
Pop-Location
}
}
if ($Help) {
Show-Usage
exit 0
}
if ($Targets.Count -eq 1 -and ($Targets[0] -eq "-h" -or $Targets[0] -eq "--help")) {
Show-Usage
exit 0
}
$releaseTagsOthersPath = Join-Path $RootDir "release\DEFAULT_BUILD_TAGS_OTHERS"
$releaseTagsWindowsPath = Join-Path $RootDir "release\DEFAULT_BUILD_TAGS_WINDOWS"
$releaseLdflagsPath = Join-Path $RootDir "release\LDFLAGS"
Require-File -Path $releaseTagsOthersPath
Require-File -Path $releaseTagsWindowsPath
Require-File -Path $releaseLdflagsPath
$script:ResolvedGoBin = Resolve-GoBinary -RequestedGoBin $GoBin
$script:ResolvedDistDir = if ($DistDir) { $DistDir } else { Join-Path $RootDir "dist" }
$script:ResolvedDistDir = [System.IO.Path]::GetFullPath($script:ResolvedDistDir)
$script:ResolvedGoCacheDir = Resolve-CachePath -RequestedPath $GoCacheDir -DefaultRelativePath ".cache\go-build"
$script:ResolvedGoModCacheDir = Resolve-CachePath -RequestedPath $GoModCacheDir -DefaultRelativePath ".cache\gomod"
$script:ResolvedBuildJobs = Resolve-BuildJobs -RequestedBuildJobs $BuildJobs
$script:ResolvedVersion = Resolve-Version -RequestedVersion $Version -RepoRoot $RootDir
$script:ResolvedBuildTagsOthers = if ($BuildTagsOthers) { $BuildTagsOthers } else { Read-TrimmedFile -Path $releaseTagsOthersPath }
$script:ResolvedBuildTagsWindows = if ($BuildTagsWindows) { $BuildTagsWindows } else { Read-TrimmedFile -Path $releaseTagsWindowsPath }
$script:ResolvedLdflagsShared = Read-TrimmedFile -Path $releaseLdflagsPath
New-Item -ItemType Directory -Force -Path $script:ResolvedDistDir | Out-Null
New-Item -ItemType Directory -Force -Path $script:ResolvedGoCacheDir | Out-Null
New-Item -ItemType Directory -Force -Path $script:ResolvedGoModCacheDir | Out-Null
$resolvedTargets = @()
if ($Targets.Count -eq 0 -or ($Targets.Count -eq 1 -and $Targets[0] -eq "all")) {
$resolvedTargets = $DefaultTargets
} else {
$resolvedTargets = $Targets
}
$results = @()
foreach ($target in $resolvedTargets) {
$results += Invoke-BuildTarget -Target $target
}
Write-Host ""
Write-Host "Build completed." -ForegroundColor Green
Write-Host "Output directory: $script:ResolvedDistDir"
Write-Host "Go build cache: $script:ResolvedGoCacheDir"
Write-Host "Go module cache: $script:ResolvedGoModCacheDir"
$successfulResults = @($results | Where-Object { $_.Success })
$failedResults = @($results | Where-Object { -not $_.Success })
Write-Host ""
Write-Host "Succeeded targets:" -ForegroundColor Green
if ($successfulResults.Count -eq 0) {
Write-Host " (none)"
} else {
foreach ($result in $successfulResults) {
Write-Host " $($result.Target) -> $($result.OutputPath)"
}
}
Write-Host ""
Write-Host "Failed targets:" -ForegroundColor Yellow
if ($failedResults.Count -eq 0) {
Write-Host " (none)"
} else {
foreach ($result in $failedResults) {
Write-Host " $($result.Target) -> $($result.Error)"
}
exit 1
}