瀏覽代碼

CI: Add new Windows build scripts

PatTheMav 2 年之前
父節點
當前提交
5d746f650e

+ 2 - 0
.github/scripts/.Wingetfile

@@ -0,0 +1,2 @@
+package '7zip.7zip', path: '7-zip', bin: '7z'
+package 'cmake', path: 'Cmake\bin', bin: 'cmake'

+ 137 - 0
.github/scripts/Build-Windows.ps1

@@ -0,0 +1,137 @@
+[CmdletBinding()]
+param(
+    [ValidateSet('x64')]
+    [string] $Target = 'x64',
+    [ValidateSet('Debug', 'RelWithDebInfo', 'Release', 'MinSizeRel')]
+    [string] $Configuration = 'RelWithDebInfo',
+    [switch] $SkipAll,
+    [switch] $SkipBuild,
+    [switch] $SkipDeps
+)
+
+$ErrorActionPreference = 'Stop'
+
+if ( $DebugPreference -eq 'Continue' ) {
+    $VerbosePreference = 'Continue'
+    $InformationPreference = 'Continue'
+}
+
+if ( ! ( [System.Environment]::Is64BitOperatingSystem ) ) {
+    throw "obs-studio requires a 64-bit system to build and run."
+}
+
+if ( $PSVersionTable.PSVersion -lt '7.2.0' ) {
+    Write-Warning 'The obs-studio PowerShell build script requires PowerShell Core 7. Install or upgrade your PowerShell version: https://aka.ms/pscore6'
+    exit 2
+}
+
+function Build {
+    trap {
+        Pop-Location -Stack BuildTemp -ErrorAction 'SilentlyContinue'
+        Write-Error $_
+        Log-Group
+        exit 2
+    }
+
+    $ScriptHome = $PSScriptRoot
+    $ProjectRoot = Resolve-Path -Path "$PSScriptRoot/../.."
+    $BuildSpecFile = "${ProjectRoot}/buildspec.json"
+
+    $UtilityFunctions = Get-ChildItem -Path $PSScriptRoot/utils.pwsh/*.ps1 -Recurse
+
+    foreach($Utility in $UtilityFunctions) {
+        Write-Debug "Loading $($Utility.FullName)"
+        . $Utility.FullName
+    }
+
+    $BuildSpec = Get-Content -Path ${BuildSpecFile} -Raw | ConvertFrom-Json
+
+    if ( ! $SkipDeps ) {
+        Install-BuildDependencies -WingetFile "${ScriptHome}/.Wingetfile"
+    }
+
+    Push-Location -Stack BuildTemp
+    if ( ! ( ( $SkipAll ) -or ( $SkipBuild ) ) ) {
+        Ensure-Location $ProjectRoot
+
+        $Preset = "windows-$(if ( $env:CI -ne $null ) { 'ci-' })${Target}"
+        $CmakeArgs = @(
+            '--preset', $Preset
+        )
+
+        $CmakeBuildArgs = @('--build')
+        $CmakeInstallArgs = @()
+
+        if ( ( $env:TWITCH_CLIENTID -ne '' ) -and ( $env:TWITCH_HASH -ne '' ) ) {
+            $CmakeArgs += @(
+                "-DTWITCH_CLIENTID:STRING=${env:TWITCH_CLIENTID}"
+                "-DTWITCH_HASH:STRING=${env:TWITCH_HASH}"
+            )
+        }
+
+        if ( ( $env:RESTREAM_CLIENTID -ne '' ) -and ( $env:RESTREAM_HASH -ne '' ) ) {
+            $CmakeArgs += @(
+                "-DRESTREAM_CLIENTID:STRING=${env:RESTREAM_CLIENTID}"
+                "-DRESTREAM_HASH:STRING=${env:RESTREAM_HASH}"
+            )
+        }
+
+        if ( ( $env:YOUTUBE_CLIENTID -ne '' ) -and ( $env:YOUTUBE_CLIENTID_HASH -ne '' ) -and
+             ( $env:YOUTUBE_SECRET -ne '' ) -and ( $env:YOUTUBE_SECRET_HASH-ne '' ) ) {
+            $CmakeArgs += @(
+                "-DYOUTUBE_CLIENTID:STRING=${env:YOUTUBE_CLIENTID}"
+                "-DYOUTUBE_CLIENTID_HASH:STRING=${env:YOUTUBE_CLIENTID_HASH}"
+                "-DYOUTUBE_SECRET:STRING=${env:YOUTUBE_SECRET}"
+                "-DYOUTUBE_SECRET_HASH:STRING=${env:YOUTUBE_SECRET_HASH}"
+            )
+        }
+
+        if ( $env:GPU_PRIORITY_VAL -ne '' ) {
+            $CmakeArgs += @(
+                "-DGPU_PRIORITY_VAL:STRING=${env:GPU_PRIORITY_VAL}"
+            )
+        }
+
+        if ( ( $env:CI -ne $null ) -and ( $env:CCACHE_CONFIGPATH -ne $null ) ) {
+            $CmakeArgs += @(
+                "-DENABLE_CCACHE:BOOL=TRUE"
+            )
+        }
+
+        if ( $VerbosePreference -eq 'Continue' ) {
+            $CmakeBuildArgs += ('--verbose')
+            $CmakeInstallArgs += ('--verbose')
+        }
+
+        if ( $DebugPreference -eq 'Continue' ) {
+            $CmakeArgs += ('--debug-output')
+        }
+
+        $CmakeBuildArgs += @(
+            '--preset', "windows-${Target}"
+            '--config', $Configuration
+            '--parallel'
+            '--', '/consoleLoggerParameters:Summary', '/noLogo'
+        )
+
+        $CmakeInstallArgs += @(
+            '--install', "build_${Target}"
+            '--prefix', "${ProjectRoot}/build_${Target}/install"
+            '--config', $Configuration
+        )
+
+        Log-Group "Configuring obs-studio..."
+        Invoke-External cmake @CmakeArgs
+
+        Log-Group "Building obs-studio..."
+        Invoke-External cmake @CmakeBuildArgs
+    }
+
+    Log-Group "Installing obs-studio..."
+    Invoke-External cmake @CmakeInstallArgs
+
+    Pop-Location -Stack BuildTemp
+    Log-Group
+}
+
+Build

+ 79 - 0
.github/scripts/Package-Windows.ps1

@@ -0,0 +1,79 @@
+[CmdletBinding()]
+param(
+    [ValidateSet('x64')]
+    [string] $Target = 'x64',
+    [ValidateSet('Debug', 'RelWithDebInfo', 'Release', 'MinSizeRel')]
+    [string] $Configuration = 'RelWithDebInfo',
+    [switch] $SkipDeps
+)
+
+$ErrorActionPreference = 'Stop'
+
+if ( $DebugPreference -eq 'Continue' ) {
+    $VerbosePreference = 'Continue'
+    $InformationPreference = 'Continue'
+}
+
+if ( ! ( [System.Environment]::Is64BitOperatingSystem ) ) {
+    throw "obs-studio requires a 64-bit system to build and run."
+}
+
+if ( $PSVersionTable.PSVersion -lt '7.2.0' ) {
+    Write-Warning 'The obs-studio packaging script requires PowerShell Core 7. Install or upgrade your PowerShell version: https://aka.ms/pscore6'
+    exit 2
+}
+
+function Package {
+    trap {
+        Write-Error $_
+        exit 2
+    }
+
+    $ScriptHome = $PSScriptRoot
+    $ProjectRoot = Resolve-Path -Path "$PSScriptRoot/../.."
+    $BuildSpecFile = "${ProjectRoot}/buildspec.json"
+
+    $UtilityFunctions = Get-ChildItem -Path $PSScriptRoot/utils.pwsh/*.ps1 -Recurse
+
+    foreach( $Utility in $UtilityFunctions ) {
+        Write-Debug "Loading $($Utility.FullName)"
+        . $Utility.FullName
+    }
+
+    if ( ! $SkipDeps ) {
+        Install-BuildDependencies -WingetFile "${ScriptHome}/.Wingetfile"
+    }
+
+    $GitDescription = Invoke-External git describe --tags --long
+    $Tokens = ($GitDescription -split '-')
+    $CommitVersion = $Tokens[0..$($Tokens.Count - 3)] -join '-'
+    $CommitHash = $($Tokens[-1]).SubString(1)
+    $CommitDistance = $Tokens[-2]
+
+    if ( $CommitDistance -gt 0 ) {
+        $OutputName = "obs-studio-${CommitVersion}-${CommitHash}"
+    } else {
+        $OutputName = "obs-studio-${CommitVersion}"
+    }
+
+    $CpackArgs = @(
+        '-C', "${Configuration}"
+    )
+
+    if ( $VerbosePreference -eq 'Continue' ) {
+        $CpackArgs += ('--verbose')
+    }
+
+    Log-Group "Packaging obs-studio..."
+
+    Push-Location -Stack PackageTemp "build_${Target}"
+
+    cpack @CpackArgs
+
+    $Package = Get-ChildItem -filter "obs-studio-*-windows-x64.zip" -File
+    Move-Item -Path $Package -Destination "${OutputName}-windows-x64.zip"
+
+    Pop-Location -Stack PackageTemp
+}
+
+Package

+ 29 - 0
.github/scripts/utils.pwsh/Ensure-Location.ps1

@@ -0,0 +1,29 @@
+function Ensure-Location {
+    <#
+        .SYNOPSIS
+            Ensures current location to be set to specified directory.
+        .DESCRIPTION
+            If specified directory exists, switch to it. Otherwise create it,
+            then switch.
+        .EXAMPLE
+            Ensure-Location "My-Directory"
+            Ensure-Location -Path "Path-To-My-Directory"
+    #>
+
+    param(
+        [Parameter(Mandatory)]
+        [string] $Path
+    )
+
+    if ( ! ( Test-Path $Path ) ) {
+        $_Params = @{
+            ItemType = "Directory"
+            Path = ${Path}
+            ErrorAction = "SilentlyContinue"
+        }
+
+        New-Item @_Params | Set-Location
+    } else {
+        Set-Location -Path ${Path}
+    }
+}

+ 70 - 0
.github/scripts/utils.pwsh/Expand-ArchiveExt.ps1

@@ -0,0 +1,70 @@
+function Expand-ArchiveExt {
+    <#
+        .SYNOPSIS
+            Expands archive files.
+        .DESCRIPTION
+            Allows extraction of zip, 7z, gz, and xz archives.
+            Requires tar and 7-zip to be available on the system.
+            Archives ending with .zip but created using LZMA compression are
+            expanded using 7-zip as a fallback.
+        .EXAMPLE
+            Expand-ArchiveExt -Path <Path-To-Your-Archive>
+            Expand-ArchiveExt -Path <Path-To-Your-Archive> -DestinationPath <Expansion-Path>
+    #>
+
+    param(
+        [Parameter(Mandatory)]
+        [string] $Path,
+        [string] $DestinationPath = [System.IO.Path]::GetFileNameWithoutExtension($Path),
+        [switch] $Force
+    )
+
+    switch ( [System.IO.Path]::GetExtension($Path) ) {
+        .zip {
+            try {
+                Expand-Archive -Path $Path -DestinationPath $DestinationPath -Force:$Force
+            } catch {
+                if ( Get-Command 7z ) {
+                    Invoke-External 7z x -y $Path "-o${DestinationPath}"
+                } else {
+                    throw "Fallback utility 7-zip not found. Please install 7-zip first."
+                }
+            }
+            break
+        }
+        { ( $_ -eq ".7z" ) -or ( $_ -eq ".exe" ) } {
+            if ( Get-Command 7z ) {
+                Invoke-External 7z x -y $Path "-o${DestinationPath}"
+            } else {
+                throw "Extraction utility 7-zip not found. Please install 7-zip first."
+            }
+            break
+        }
+        .gz {
+            try {
+                Invoke-External tar -x -o $DestinationPath -f $Path
+            } catch {
+                if ( Get-Command 7z ) {
+                    Invoke-External 7z x -y $Path "-o${DestinationPath}"
+                } else {
+                    throw "Fallback utility 7-zip not found. Please install 7-zip first."
+                }
+            }
+            break
+        }
+        .xz {
+            try {
+                Invoke-External tar -x -o $DestinationPath -f $Path
+            } catch {
+                if ( Get-Command 7z ) {
+                    Invoke-External 7z x -y $Path "-o${DestinationPath}"
+                } else {
+                    throw "Fallback utility 7-zip not found. Please install 7-zip first."
+                }
+            }
+        }
+        default {
+            throw "Unsupported archive extension provided."
+        }
+    }
+}

+ 70 - 0
.github/scripts/utils.pwsh/Install-BuildDependencies.ps1

@@ -0,0 +1,70 @@
+function Install-BuildDependencies {
+    <#
+        .SYNOPSIS
+            Installs required build dependencies.
+        .DESCRIPTION
+            Additional packages might be needed for successful builds. This module contains additional
+            dependencies available for installation via winget and, if possible, adds their locations
+            to the environment path for future invocation.
+        .EXAMPLE
+            Install-BuildDependencies
+    #>
+
+    param(
+        [string] $WingetFile = "$PSScriptRoot/.Wingetfile"
+    )
+
+    if ( ! ( Test-Path function:Log-Warning ) ) {
+        . $PSScriptRoot/Logger.ps1
+    }
+
+    $Prefixes = @{
+        'x64' = ${env:ProgramFiles}
+        'x86' = ${env:ProgramFiles(x86)}
+        'arm64' = ${env:ProgramFiles(arm)}
+    }
+    
+    $Paths = $env:Path -split [System.IO.Path]::PathSeparator
+
+    $WingetOptions = @('install', '--accept-package-agreements', '--accept-source-agreements')
+
+    if ( $script:Quiet ) {
+        $WingetOptions += '--silent'
+    }
+
+    Log-Group 'Check Windows build requirements'
+    Get-Content $WingetFile | ForEach-Object {
+        $_, $Package, $_, $Path, $_, $Binary, $_, $Version = $_ -replace ',','' -split " +(?=(?:[^\']*\'[^\']*\')*[^\']*$)" -replace "'",''
+
+        $Prefixes.GetEnumerator() | ForEach-Object {
+            $Prefix = $_.value
+            $FullPath = "${Prefix}\${Path}"
+            if ( ( Test-Path $FullPath ) -and ! ( $Paths -contains $FullPath ) ) {
+                $Paths = @($FullPath) + $Paths
+                $env:Path = $Paths -join [System.IO.Path]::PathSeparator
+            }
+        }
+
+        Log-Debug "Checking for command ${Binary}"
+        $Found = Get-Command -ErrorAction SilentlyContinue $Binary
+
+        if ( $Found ) {
+            Log-Status "Found dependency ${Binary} as $($Found.Source)"
+        } else {
+            Log-Status "Installing package ${Package} $(if ( $Version -ne $null ) { "Version: ${Version}" } )"
+
+            if ( $Version -ne $null ) {
+                $WingGetOptions += @('--version', ${Version})
+            }
+
+            try {
+                $Params = $WingetOptions + $Package
+
+                winget @Params
+            } catch {
+                throw "Error while installing winget package ${Package}: $_"
+            }
+        }
+    }
+    Log-Group
+}

+ 40 - 0
.github/scripts/utils.pwsh/Invoke-External.ps1

@@ -0,0 +1,40 @@
+function Invoke-External {
+    <#
+        .SYNOPSIS
+            Invokes a non-PowerShell command.
+        .DESCRIPTION
+            Runs a non-PowerShell command, and captures its return code.
+            Throws an exception if the command returns non-zero.
+        .EXAMPLE
+            Invoke-External 7z x $MyArchive
+    #>
+
+    if ( $args.Count -eq 0 ) {
+        throw 'Invoke-External called without arguments.'
+    }
+
+    if ( ! ( Test-Path function:Log-Information ) ) {
+        . $PSScriptRoot/Logger.ps1
+    }
+
+    $Command = $args[0]
+    $CommandArgs = @()
+
+    if ( $args.Count -gt 1) {
+        $CommandArgs = $args[1..($args.Count - 1)]
+    }
+
+    $_EAP = $ErrorActionPreference
+    $ErrorActionPreference = "Continue"
+
+    Log-Debug "Invoke-External: ${Command} ${CommandArgs}"
+
+    & $command $commandArgs
+    $Result = $LASTEXITCODE
+
+    $ErrorActionPreference = $_EAP
+
+    if ( $Result -ne 0 ) {
+        throw "${Command} ${CommandArgs} exited with non-zero code ${Result}."
+    }
+}

+ 149 - 0
.github/scripts/utils.pwsh/Logger.ps1

@@ -0,0 +1,149 @@
+function Log-Debug {
+    [CmdletBinding()]
+    param(
+        [Parameter(Mandatory,ValueFromPipeline)]
+        [ValidateNotNullOrEmpty()]
+        [string[]] $Message
+    )
+
+    Process {
+        foreach($m in $Message) {
+            Write-Debug $m
+        }
+    }
+}
+
+function Log-Verbose {
+    [CmdletBinding()]
+    param(
+        [Parameter(Mandatory,ValueFromPipeline)]
+        [ValidateNotNullOrEmpty()]
+        [string[]] $Message
+    )
+
+    Process {
+        foreach($m in $Message) {
+            Write-Verbose $m
+        }
+    }
+}
+
+function Log-Warning {
+    [CmdletBinding()]
+    param(
+        [Parameter(Mandatory,ValueFromPipeline)]
+        [ValidateNotNullOrEmpty()]
+        [string[]] $Message
+    )
+
+    Process {
+        foreach($m in $Message) {
+            Write-Warning $m
+        }
+    }
+}
+
+function Log-Error {
+    [CmdletBinding()]
+    param(
+        [Parameter(Mandatory,ValueFromPipeline)]
+        [ValidateNotNullOrEmpty()]
+        [string[]] $Message
+    )
+
+    Process {
+        foreach($m in $Message) {
+            Write-Error $m
+        }
+    }
+}
+
+function Log-Information {
+    [CmdletBinding()]
+    param(
+        [Parameter(Mandatory,ValueFromPipeline)]
+        [ValidateNotNullOrEmpty()]
+        [string[]] $Message
+    )
+
+    Process {
+        if ( ! ( $script:Quiet ) ) {
+            $StageName = $( if ( $script:StageName -ne $null ) { $script:StageName } else { '' })
+            $Icon = ' =>'
+
+            foreach($m in $Message) {
+                Write-Host -NoNewLine -ForegroundColor Blue "  ${StageName} $($Icon.PadRight(5)) "
+                Write-Host "${m}"
+            }
+        }
+    }
+}
+
+function Log-Group {
+    [CmdletBinding()]
+    param(
+        [Parameter(ValueFromPipeline)]
+        [string[]] $Message
+    )
+
+    Process {
+        if ( $Env:CI -ne $null )  {
+            if ( $script:LogGroup ) {
+                Write-Output '::endgroup::'
+                $script:LogGroup = $false
+            }
+
+            if ( $Message.count -ge 1 ) {
+                Write-Output "::group::$($Message -join ' ')"
+                $script:LogGroup = $true
+            }
+        } else {
+            if ( $Message.count -ge 1 ) {
+                Log-Information $Message
+            }
+        }
+    }
+}
+
+function Log-Status {
+    [CmdletBinding()]
+    param(
+        [Parameter(Mandatory,ValueFromPipeline)]
+        [ValidateNotNullOrEmpty()]
+        [string[]] $Message
+    )
+
+    Process {
+        if ( ! ( $script:Quiet ) ) {
+            $StageName = $( if ( $StageName -ne $null ) { $StageName } else { '' })
+            $Icon = '  >'
+
+            foreach($m in $Message) {
+                Write-Host -NoNewLine -ForegroundColor Green "  ${StageName} $($Icon.PadRight(5)) "
+                Write-Host "${m}"
+            }
+        }
+    }
+}
+
+function Log-Output {
+    [CmdletBinding()]
+    param(
+        [Parameter(Mandatory,ValueFromPipeline)]
+        [ValidateNotNullOrEmpty()]
+        [string[]] $Message
+    )
+
+    Process {
+        if ( ! ( $script:Quiet ) ) {
+            $StageName = $( if ( $script:StageName -ne $null ) { $script:StageName } else { '' })
+            $Icon = ''
+
+            foreach($m in $Message) {
+                Write-Output "  ${StageName} $($Icon.PadRight(5)) ${m}"
+            }
+        }
+    }
+}
+
+$Columns = (Get-Host).UI.RawUI.WindowSize.Width - 5