diff --git a/Compile.ps1 b/Compile.ps1 index f86fbce62e..1a306daefe 100644 --- a/Compile.ps1 +++ b/Compile.ps1 @@ -3,29 +3,39 @@ param ( [switch]$Run, [switch]$SkipPreprocessing ) + $OFS = "`r`n" $scriptname = "winutil.ps1" $workingdir = $PSScriptRoot +$logFilePath = Join-Path $workingdir "compile.log" # Variable to sync between runspaces $sync = [Hashtable]::Synchronized(@{}) $sync.PSScriptRoot = $workingdir $sync.configs = @{} -function Update-Progress { - param ( - [Parameter(Mandatory, position=0)] - [string]$StatusMessage, +# Dot-source external functions +. "$PSScriptRoot\tools\Update-Progress.ps1" - [Parameter(Mandatory, position=1)] - [ValidateRange(0,100)] - [int]$Percent, +# Function to log messages +function Log-Message { + param([string]$Message) + $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" + Add-Content -Path $logFilePath -Value "$timestamp - $Message" +} - [Parameter(position=2)] - [string]$Activity = "Compiling" +# Function to encode special characters in JSON +function Encode-JsonSpecialChars { + param ( + [Parameter(Mandatory)] + [PSObject]$jsonObject ) - - Write-Progress -Activity $Activity -Status $StatusMessage -PercentComplete $Percent + foreach ($prop in $jsonObject.PSObject.Properties) { + if ($prop.Value -is [string]) { + $prop.Value = $prop.Value.Replace('&','&').Replace('“','“').Replace('”','”').Replace("'",''').Replace('<','<').Replace('>','>').Replace('—','—') + } + } + return $jsonObject } $header = @" @@ -36,16 +46,25 @@ $header = @" ################################################################################################################ "@ -if (-NOT $SkipPreprocessing) { - Update-Progress "Pre-req: Running Preprocessor..." 0 +if ($Debug) { + Write-Debug "Debug mode enabled" +} - # Dot source the 'Invoke-Preprocessing' Function from 'tools/Invoke-Preprocessing.ps1' Script - $preprocessingFilePath = ".\tools\Invoke-Preprocessing.ps1" - . "$(($workingdir -replace ('\\$', '')) + '\' + ($preprocessingFilePath -replace ('\.\\', '')))" +if (-NOT $SkipPreprocessing) { + try { + Update-Progress "Pre-req: Running Preprocessor..." 0 + $preprocessingFilePath = Join-Path $PSScriptRoot "tools\Invoke-Preprocessing.ps1" + . "$preprocessingFilePath" - $excludedFiles = @('.\.git\', '.\.gitignore', '.\.gitattributes', '.\.github\CODEOWNERS', '.\LICENSE', "$preprocessingFilePath", '*.png', '*.exe') - $msg = "Pre-req: Code Formatting" - Invoke-Preprocessing -WorkingDir "$workingdir" -ExcludedFiles $excludedFiles -ProgressStatusMessage $msg + $excludedFiles = @('.\.git\', '.\.gitignore', '.\.gitattributes', '.\.github\CODEOWNERS', '.\LICENSE', "$preprocessingFilePath", '*.png', '*.exe') + $msg = "Pre-req: Code Formatting" + Invoke-Preprocessing -WorkingDir "$workingdir" -ExcludedFiles $excludedFiles -ProgressStatusMessage $msg + } + catch { + Write-Error "Preprocessing failed: $($_.Exception.Message)" + Log-Message "Preprocessing failed: $($_.Exception.Message)" + exit 1 + } } # Create the script in memory. @@ -61,71 +80,43 @@ $script_content.Add($(Get-Content "$workingdir\scripts\start.ps1").replace('#{re Update-Progress "Adding: Functions" 20 Get-ChildItem "$workingdir\functions" -Recurse -File | ForEach-Object { $script_content.Add($(Get-Content $psitem.FullName)) - } +} + Update-Progress "Adding: Config *.json" 40 -Get-ChildItem "$workingdir\config" | Where-Object {$psitem.extension -eq ".json"} | ForEach-Object { - - $json = (Get-Content $psitem.FullName).replace("'","''") - - # Replace every XML Special Character so it'll render correctly in final build - # Only do so if json files has content to be displayed (for example the applications, tweaks, features json files) - # Make an Array List containing every name at first level of Json File - $jsonAsObject = $json | convertfrom-json - $firstLevelJsonList = [System.Collections.ArrayList]::new() - $jsonAsObject.PSObject.Properties.Name | ForEach-Object {$null = $firstLevelJsonList.Add($_)} - # Note: - # Avoid using HTML Entity Codes, for example '”' (stands for "Right Double Quotation Mark"), - # Use **HTML decimal/hex codes instead**, as using HTML Entity Codes will result in XML parse Error when running the compiled script. - for ($i = 0; $i -lt $firstLevelJsonList.Count; $i += 1) { - $firstLevelName = $firstLevelJsonList[$i] - if ($jsonAsObject.$firstLevelName.content -ne $null) { - $jsonAsObject.$firstLevelName.content = $jsonAsObject.$firstLevelName.content.replace('&','&').replace('“','“').replace('”','”').replace("'",''').replace('<','<').replace('>','>').replace('—','—') - $jsonAsObject.$firstLevelName.content = $jsonAsObject.$firstLevelName.content.replace('''',"'") # resolves the Double Apostrophe caused by the first replace function in the main loop - } - if ($jsonAsObject.$firstLevelName.description -ne $null) { - $jsonAsObject.$firstLevelName.description = $jsonAsObject.$firstLevelName.description.replace('&','&').replace('“','“').replace('”','”').replace("'",''').replace('<','<').replace('>','>').replace('—','—') - $jsonAsObject.$firstLevelName.description = $jsonAsObject.$firstLevelName.description.replace('''',"'") # resolves the Double Apostrophe caused by the first replace function in the main loop - } - } +Get-ChildItem "$workingdir\config" | Where-Object {$_.extension -eq ".json"} | ForEach-Object { + $json = Get-Content -Path $psitem.FullName -Raw + $jsonAsObject = $json | ConvertFrom-Json + $jsonAsObject = Encode-JsonSpecialChars -jsonObject $jsonAsObject - # Add 'WPFInstall' as a prefix to every entry-name in 'applications.json' file if ($psitem.Name -eq "applications.json") { - for ($i = 0; $i -lt $firstLevelJsonList.Count; $i += 1) { - $appEntryName = $firstLevelJsonList[$i] + foreach ($appEntryName in $jsonAsObject.PSObject.Properties.Name) { $appEntryContent = $jsonAsObject.$appEntryName - # Remove the entire app entry, so we could add it later with a different name $jsonAsObject.PSObject.Properties.Remove($appEntryName) - # Add the app entry, but with a different name (WPFInstall + The App Entry Name) $jsonAsObject | Add-Member -MemberType NoteProperty -Name "WPFInstall$appEntryName" -Value $appEntryContent } } - # The replace at the end is required, as without it the output of 'converto-json' will be somewhat weird for Multiline Strings - # Most Notably is the scripts in some json files, making it harder for users who want to review these scripts, which're found in the compiled script - $json = ($jsonAsObject | convertto-json -Depth 3).replace('\r\n',"`r`n") - - $sync.configs.$($psitem.BaseName) = $json | convertfrom-json - $script_content.Add($(Write-output "`$sync.configs.$($psitem.BaseName) = '$json' `| convertfrom-json" )) + $json = ($jsonAsObject | ConvertTo-Json -Depth 3).replace('\r\n', "`r`n") + $sync.configs.$($psitem.BaseName) = $json | ConvertFrom-Json + $script_content.Add("`$sync.configs.$($psitem.BaseName) = '$json' | ConvertFrom-Json") } -$xaml = (Get-Content "$workingdir\xaml\inputXML.xaml").replace("'","''") +$xaml = (Get-Content "$workingdir\xaml\inputXML.xaml" -Raw).Replace("'","''") # Dot-source the Get-TabXaml function . "$workingdir\functions\private\Get-TabXaml.ps1" -Update-Progress "Building: Xaml " 75 +Update-Progress "Building: Xaml" 75 $appXamlContent = Get-TabXaml "applications" 5 $tweaksXamlContent = Get-TabXaml "tweaks" $featuresXamlContent = Get-TabXaml "feature" - -Update-Progress "Adding: Xaml " 90 -# Replace the placeholder in $inputXML with the content of inputApp.xaml +Update-Progress "Adding: Xaml" 90 $xaml = $xaml -replace "{{InstallPanel_applications}}", $appXamlContent $xaml = $xaml -replace "{{InstallPanel_tweaks}}", $tweaksXamlContent $xaml = $xaml -replace "{{InstallPanel_features}}", $featuresXamlContent -$script_content.Add($(Write-output "`$inputXML = '$xaml'")) +$script_content.Add("`$inputXML = '$xaml'") $script_content.Add($(Get-Content "$workingdir\scripts\main.ps1")) @@ -146,19 +137,22 @@ Write-Progress -Activity "Compiling" -Completed Update-Progress -Activity "Validating" -StatusMessage "Checking winutil.ps1 Syntax" -Percent 0 try { - $null = Get-Command -Syntax .\winutil.ps1 + $null = Get-Command -Syntax ".\winutil.ps1" } catch { Write-Warning "Syntax Validation for 'winutil.ps1' has failed" Write-Host "$($Error[0])" -ForegroundColor Red + Log-Message "Syntax Validation failed: $($Error[0])" } Write-Progress -Activity "Validating" -Completed -if ($run) { +if ($Run) { try { Start-Process -FilePath "pwsh" -ArgumentList "$workingdir\$scriptname" - } catch { + } + catch { Start-Process -FilePath "powershell" -ArgumentList "$workingdir\$scriptname" } - } + +Log-Message "Compilation completed" diff --git a/tools/Update-Progress.ps1 b/tools/Update-Progress.ps1 new file mode 100644 index 0000000000..45ebb88b3f --- /dev/null +++ b/tools/Update-Progress.ps1 @@ -0,0 +1,48 @@ +# Update-Progress.ps1 + +function Update-Progress { + <# + .SYNOPSIS + A wrapper for PowerShell 'Write-Progress' Cmdlet. + + .PARAMETER StatusMessage + A mandatory parameter which’ll be used when displaying progress bar using 'Write-Progress' Cmdlet. + + .PARAMETER Percent + An integer value (0-100) representing the completion percentage. + + .PARAMETER Activity + The activity name to be displayed in the progress bar. Defaults to "Processing" if not provided. + + .PARAMETER LogProgress + A switch that indicates whether the progress should be logged to a file. + + .EXAMPLE + Update-Progress "Processing files..." 50 "File Processing" -LogProgress + #> + + param ( + [Parameter(Mandatory, Position = 0)] + [ValidateNotNullOrEmpty()] + [string]$StatusMessage, + + [Parameter(Position = 1)] + [ValidateRange(0, 100)] + [int]$Percent, + + [Parameter(Position = 2)] + [string]$Activity = "Processing", # Default activity to "Processing" if not provided + + [Parameter(Position = 3)] + [switch]$LogProgress + ) + + # Write the progress to the console + Write-Progress -Activity $Activity -Status $StatusMessage -PercentComplete $Percent + + # Optionally log the progress to a file + if ($LogProgress) { + $logMessage = "{0:yyyy-MM-dd HH:mm:ss} - {1} - {2}% - {3}" -f (Get-Date), $Activity, $Percent, $StatusMessage + $logMessage | Out-File -FilePath "$PSScriptRoot\progress.log" -Append -Encoding utf8 + } +}