Public/Compile-SourceScript.ps1
function Compile-SourceScript { <# .SYNOPSIS A wrapper for compiling SourceMod (.sp) and AMX Mod X (.sma) plugin source files for Source / GoldSource games. .DESCRIPTION Specified plugins are compiled and subsequently copied into the mod's plugins directory if found to be new or have been changed. .PARAMETER File Path to the plugin's source file (.sp or .sma). .PARAMETER SkipWrapper To directly run the mod's compiler instead of using provided wrappers (such as 'compile.exe' and 'compile.sh') in the compilation process. .PARAMETER Force Copies the newly compiled plugin to the mod's plugins directory without user confirmation. .EXAMPLE Compile-SourceScript -File ~/servers/csgo/addons/sourcemod/scripting/plugin1.sp Compiles the SourceMod plugin source file 'plugin1.sp', and installs the compiled plugin with user confirmation for the game Counter-Strike: Global Offensive. .EXAMPLE Compile-SourceScript -File ~/servers/cstrike/addons/amxmodx/scripting/plugin2.sma -SkipWrapper -Force Compiles the AMX Mod X plugin source file 'plugin2.sma' without using the mod's compiler wrapper, and installs the compiled plugin without user confirmation for the game Counter-Strike 1.6. .LINK https://github.com/startersclan/Compile-SourceScript #> [CmdletBinding()] param( [Parameter(Mandatory=$False)] [ValidateScript({ Test-Path -Path $_ -PathType Leaf })] $File , [Parameter(Mandatory=$False)] [switch]$SkipWrapper , [Parameter(Mandatory=$False)] [switch]$Force ) begin { "Starting Compile-SourceScript" | Write-Host -ForegroundColor Cyan $ErrorActionPreference = 'Stop' $MOD = @{ sourcemod = @{ script_ext = '.sp' plugin_ext = '.smx' compiled_dir_name = 'compiled' plugins_dir_name = 'plugins' compiler = @{ windows = @{ wrapper = 'compile.exe' bin = 'spcomp.exe' } others = @{ wrapper = 'compile.sh' bin = 'spcomp' } } } amxmodx = @{ script_ext = '.sma' plugin_ext = '.amxx' compiled_dir_name = 'compiled' plugins_dir_name = 'plugins' compiler = @{ windows = @{ wrapper = 'compile.exe' bin = 'amxxpc.exe' } others = @{ wrapper = 'compile.sh' bin = 'amxxpc' } } } } $MOD_NAME = if ([System.IO.Path]::GetExtension($PSBoundParameters['File']) -eq '.sp') { 'sourcemod' } elseif ([System.IO.Path]::GetExtension($PSBoundParameters['File']) -eq '.sma') { 'amxmodx' } if (!$MOD_NAME) { throw "File is not a .sp or .sma source file." } $COMPILER_NAME = if ($env:OS) { if ($PSBoundParameters['SkipWrapper']) { $MOD[$MOD_NAME]['compiler']['windows']['bin'] } else { $MOD[$MOD_NAME]['compiler']['windows']['wrapper'] } }else { if ($PSBoundParameters['SkipWrapper']) { $MOD[$MOD_NAME]['compiler']['others']['bin'] } else { $MOD[$MOD_NAME]['compiler']['others']['wrapper'] } } try { $sourceFile = Get-Item -Path $PSBoundParameters['File'] # Normalize paths $SCRIPTING_DIR = $sourceFile.DirectoryName $COMPILED_DIR = Join-Path $SCRIPTING_DIR $MOD[$MOD_NAME]['compiled_dir_name'] $PLUGINS_DIR = Join-Path (Split-Path $SCRIPTING_DIR -Parent) $MOD[$MOD_NAME]['plugins_dir_name'] # Validate compiler binary $compiler = Get-Item -Path (Join-Path $SCRIPTING_DIR $COMPILER_NAME) }catch { throw } }process { try { "Compiler: '$($compiler.FullName)'" | Write-Host # Get all items in compiled directory before compilation by hash $compiledDirItemsPre = Get-ChildItem -Path $COMPILED_DIR -File -Recurse -Force | ? { $_.Extension -eq $MOD[$MOD_NAME]['plugin_ext'] } | Select-Object *, @{name='md5'; expression={(Get-FileHash -Path $_.Fullname -Algorithm MD5).Hash}} # Prepare for compilation "Compiling..." | Write-Host -ForegroundColor Cyan $epoch = [Math]::Floor([decimal](Get-Date(Get-Date).ToUniversalTime()-uformat "%s")) $stdInFile = New-Item -Path (Join-Path $SCRIPTING_DIR ".$epoch") -ItemType File -Force '1' | Out-File -FilePath $stdInFile.FullName -Force -Encoding utf8 $processArgs = @{ FilePath = $compiler.FullName WorkingDirectory = $SCRIPTING_DIR RedirectStandardInput = $stdInFile.FullName Wait = $true NoNewWindow = $true } if ($PSBoundParameters['SkipWrapper']) { $processArgs['ArgumentList'] = @( $sourceFile.Name "-o$($MOD[$MOD_NAME]['compiled_dir_name'])/$($sourceFile.Basename)$($MOD[$MOD_NAME]['plugin_ext'])" ) }else { $processArgs['ArgumentList'] = @( $sourceFile.Name ) } New-Item -Path $COMPILED_DIR -ItemType Directory -Force | Out-Null # Begin compilation if ($PSBoundParameters['SkipWrapper']) { "Compiling $($sourceFile.Name)..." | Write-Host -ForegroundColor Yellow } Start-Process @processArgs if ($PSBoundParameters['SkipWrapper']) { "End of compilation." | Write-Host -ForegroundColor Yellow } # Get all items in compiled directory after compilation by hash $compiledDirItemsPost = Get-ChildItem -Path $COMPILED_DIR -File -Recurse -Force | ? { $_.Extension -eq $MOD[$MOD_NAME]['plugin_ext'] } | Select-Object *, @{name='md5'; expression={(Get-FileHash -Path $_.FullName -Algorithm MD5).Hash}} # Get items with differing hashes $compiledDirItemsDiff = if ($compiledDirItemsPost) { if ($compiledDirItemsPre) { $hashesDiffObj = Compare-object -ReferenceObject $compiledDirItemsPre -DifferenceObject $compiledDirItemsPost -Property FullName, md5 | ? { $_.SideIndicator -eq '=>' } if ($hashesDiffObj) { $compiledDirItemsPost | ? { $_.md5 -in $hashesDiffObj.md5 } } }else { $compiledDirItemsPost } } if ($compiledDirItemsDiff) { # List successfully compiled plugins "`nNewly compiled plugins:" | Write-Host -ForegroundColor Cyan $compiledDirItemsDiff | % { $compiledPluginHash = (Get-FileHash -Path $_.FullName -Algorithm MD5).Hash " $($_.Name), $($_.LastWriteTime), $compiledPluginHash" | Write-Host -ForegroundColor White } New-Item -Path $PLUGINS_DIR -ItemType Directory -Force | Out-Null $installationFailure = $false $compiledDirItemsDiff | % { "`n$($_.Name):" | Write-Host -ForegroundColor Green if ($_.Basename -ne $sourceFile.Basename) { " The plugin's name does not match the specified script's name. The plugin will not copied to the plugins directory." | Write-Host -ForegroundColor Yellow return # continue in % } $existingPlugin = Get-Item -Path "$PLUGINS_DIR/$($_.Name)" -ErrorAction SilentlyContinue if (!$existingPlugin) { " Plugin does not currently exist in the plugins directory." | Write-Host -ForegroundColor Yellow }else { $existingPluginHash = (Get-FileHash -Path $existingPlugin -Algorithm MD5).Hash " Existing: $($existingPlugin.LastWriteTime), $existingPluginHash" | Write-Host -ForegroundColor Yellow } # Display the compiled and existing plugin's file info $compiledPluginHash = (Get-FileHash -Path $_.FullName -Algorithm MD5).Hash " Compiled: $($_.LastWriteTime), $compiledPluginHash" | Write-Host -ForegroundColor Green # Attempt to copy the compiled plugin to the plugins directory try { Copy-Item -Path $_.FullName -Destination $PLUGINS_DIR -Confirm:$(!$PSBoundParameters['Force']) }catch { " Plugin copy error." | Write-Host -ForegroundColor Magenta $installationFailure = $true return # continue in % } # Alert the user on the situation of the plugin $updatedPlugin = Get-Item -Path "$PLUGINS_DIR/$($_.Name)" $updatedPluginHash = (Get-FileHash -Path $updatedPlugin -Algorithm MD5).Hash if ($updatedPluginHash -eq $compiledPluginHash) { "`n Plugin successfully copied to '$($_.Fullname)'" | Write-Host -ForegroundColor Green } else { "`n Failed to copy to the plugins directory." | Write-Host -ForegroundColor Magenta; return } } if ($installationFailure) { throw "Failed to install one or more plugins." } }else { "`nNo changes to plugins were found. No operations were performed." | Write-Host -ForegroundColor Magenta } }catch { throw }finally { # Cleanup if ($stdInFile) { Remove-Item -Path $stdInFile -Force } "End of Compile-SourceScript." | Write-Host -ForegroundColor Cyan } } } |