Skip to content

Commit 0b09ecd

Browse files
committed
- Merges in latest from upstream/develop.
- Adapts batch downloading to the restuctured version in this branch.
2 parents 4195789 + 2102fb1 commit 0b09ecd

File tree

1 file changed

+197
-75
lines changed

1 file changed

+197
-75
lines changed

UnitySetup/UnitySetup.psm1

Lines changed: 197 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -554,107 +554,165 @@ function Request-UnitySetupInstaller {
554554
# name and this corrects the separators to current environment.
555555
$fullCachePath = (Resolve-Path -Path $Cache).Path
556556

557-
$downloads = @()
557+
$allInstallers = @()
558558
}
559559
process {
560+
# Append the full list of installers to enable batch downloading of installers.
560561
$Installers | ForEach-Object {
561-
$installerFileName = [io.Path]::GetFileName($_.DownloadUrl)
562-
$destination = [io.Path]::Combine($fullCachePath, "Installers", "Unity-$($_.Version)", "$installerFileName")
563-
564-
# Already downloaded?
565-
if ( Test-Path $destination ) {
566-
$destinationItem = Get-Item $destination
567-
if ( ($destinationItem.Length -eq $_.Length ) -and
568-
($destinationItem.LastWriteTime -eq $_.LastModified) ) {
569-
Write-Verbose "Skipping download because it's already in the cache: $($_.DownloadUrl)"
570-
571-
$resource = New-Object UnitySetupResource -Property @{
572-
'ComponentType' = $_.ComponentType
573-
'Path' = $destination
562+
$allInstallers += , $_
563+
}
564+
}
565+
end {
566+
$downloads = @()
567+
568+
try {
569+
$global:downloadData = [ordered]@{}
570+
$downloadIndex = 1
571+
572+
$allInstallers | ForEach-Object {
573+
$installerFileName = [io.Path]::GetFileName($_.DownloadUrl)
574+
$destination = [io.Path]::Combine($fullCachePath, "Installers", "Unity-$($_.Version)", "$installerFileName")
575+
576+
# Already downloaded?
577+
if ( Test-Path $destination ) {
578+
$destinationItem = Get-Item $destination
579+
if ( ($destinationItem.Length -eq $_.Length ) -and
580+
($destinationItem.LastWriteTime -eq $_.LastModified) ) {
581+
Write-Verbose "Skipping download because it's already in the cache: $($_.DownloadUrl)"
582+
583+
$resource = New-Object UnitySetupResource -Property @{
584+
'ComponentType' = $_.ComponentType
585+
'Path' = $destination
586+
}
587+
$downloads += , $resource
588+
return
574589
}
575-
$downloads += , $resource
576-
return
577590
}
578-
}
579591

580-
$destinationDirectory = [io.path]::GetDirectoryName($destination)
581-
if (!(Test-Path $destinationDirectory -PathType Container)) {
582-
New-Item "$destinationDirectory" -ItemType Directory | Out-Null
583-
}
592+
$destinationDirectory = [io.path]::GetDirectoryName($destination)
593+
if (!(Test-Path $destinationDirectory -PathType Container)) {
594+
New-Item "$destinationDirectory" -ItemType Directory | Out-Null
595+
}
584596

585-
$webClient = New-Object System.Net.WebClient
597+
$webClient = New-Object System.Net.WebClient
598+
599+
++$downloadIndex
600+
$global:downloadData[$installerFileName] = New-Object PSObject -Property @{
601+
installerFileName = $installerFileName
602+
startTime = Get-Date
603+
totalBytes = $_.Length
604+
receivedBytes = 0
605+
isDownloaded = $false
606+
destination = $destination
607+
lastModified = $_.LastModified
608+
componentType = $_.ComponentType
609+
webClient = $webClient
610+
downloadIndex = $downloadIndex
611+
}
586612

587-
# Register to events for showing progress of file download.
588-
Register-ObjectEvent -InputObject $webClient -EventName DownloadProgressChanged -SourceIdentifier DownloadProgressChanged -Action {
589-
$global:DownloadProgressEvent = $event
590-
} | Out-Null
591-
Register-ObjectEvent -InputObject $webClient -EventName DownloadFileCompleted -SourceIdentifier DownloadFileCompleted -Action {
592-
$global:isDownloaded = $true
593-
} | Out-Null
613+
# Register to events for showing progress of file download.
614+
Register-ObjectEvent -InputObject $webClient -EventName DownloadProgressChanged -SourceIdentifier "$installerFileName-Changed" -MessageData $installerFileName -Action {
615+
$global:downloadData[$event.MessageData].receivedBytes = $event.SourceArgs.BytesReceived
616+
} | Out-Null
617+
Register-ObjectEvent -InputObject $webClient -EventName DownloadFileCompleted -SourceIdentifier "$installerFileName-Completed" -MessageData $installerFileName -Action {
618+
$global:downloadData[$event.MessageData].isDownloaded = $true
619+
} | Out-Null
620+
621+
try
622+
{
623+
Write-Verbose "Downloading $($_.DownloadUrl) to $destination"
624+
$webClient.DownloadFileAsync($_.DownloadUrl, $destination)
625+
}
626+
catch [System.Net.WebException] {
627+
Write-Error "Failed downloading $installerFileName - $($_.Exception.Message)"
628+
$global:downloadData.Remove($installerFileName)
594629

595-
try
596-
{
597-
$startTime = Get-Date
630+
Unregister-Event -SourceIdentifier "$installerFileName-Completed" -Force
631+
Unregister-Event -SourceIdentifier "$installerFileName-Changed" -Force
598632

599-
$global:DownloadProgressEvent = $null
600-
$global:isDownloaded = $false
633+
$webClient.Dispose()
634+
}
635+
}
601636

602-
Write-Verbose "Downloading $($_.DownloadUrl) to $destination"
603-
$webClient.DownloadFileAsync($_.DownloadUrl, $destination)
637+
$totalDownloads = $global:downloadData.Count
604638

605-
# Showing progress of file download
606-
$totalBytes = $_.Length
607-
while (-not $global:isDownloaded) {
608-
if ($null -eq $global:DownloadProgressEvent) {
609-
continue
610-
}
639+
# Showing progress of all file downloads
640+
while ($global:downloadData.Count -gt 0) {
641+
$global:downloadData.Keys | ForEach-Object {
642+
$installerFileName = $_
643+
$data = $global:downloadData[$installerFileName]
611644

612-
$elapsedTime = (Get-Date) - $startTime
645+
# Finished downloading
646+
if ($null -eq $data.webClient) {
647+
return
648+
}
649+
if ($data.isDownloaded) {
650+
Write-Progress -Activity "Downloading $installerFileName" -Status "Done" -Completed `
651+
-Id $data.downloadIndex
613652

614-
$receivedBytes = $global:DownloadProgressEvent.SourceArgs.BytesReceived
615-
$progress = [int](($receivedBytes / [double]$totalBytes) * 100)
653+
Unregister-Event -SourceIdentifier "$installerFileName-Completed" -Force
654+
Unregister-Event -SourceIdentifier "$installerFileName-Changed" -Force
655+
656+
$data.webClient.Dispose()
657+
$data.webClient = $null
658+
659+
# Re-writes the last modified time for ensuring downloads are cached properly.
660+
$downloadedFile = Get-Item $data.destination
661+
$downloadedFile.LastWriteTime = $data.lastModified
662+
663+
$resource = New-Object UnitySetupResource -Property @{
664+
'ComponentType' = $data.componentType
665+
'Path' = $data.destination
666+
}
667+
$downloads += , $resource
668+
return
669+
}
616670

617-
$averageSpeed = $receivedBytes / $elapsedTime.TotalSeconds
618-
$secondsRemaining = ($totalBytes - $receivedBytes) / $averageSpeed
671+
$elapsedTime = (Get-Date) - $data.startTime
619672

673+
$progress = [int](($data.receivedBytes / [double]$data.totalBytes) * 100)
674+
675+
$averageSpeed = $data.receivedBytes / $elapsedTime.TotalSeconds
676+
$secondsRemaining = ($data.totalBytes - $data.receivedBytes) / $averageSpeed
677+
620678
if ([double]::IsInfinity($secondsRemaining)) {
621679
$averageSpeed = 0
622680
# -1 for Write-Progress prevents seconds remaining from showing.
623681
$secondsRemaining = -1
624682
}
625-
626-
$downloadSpeed = Format-BitsPerSecond -Bytes $receivedBytes -Seconds $elapsedTime.TotalSeconds
683+
684+
$downloadSpeed = Format-BitsPerSecond -Bytes $data.receivedBytes -Seconds $elapsedTime.TotalSeconds
627685

628686
Write-Progress -Activity "Downloading $installerFileName | $downloadSpeed" `
629-
-Status "$($receivedBytes | Format-Bytes) of $($totalBytes | Format-Bytes)" `
687+
-Status "$($data.receivedBytes | Format-Bytes) of $($data.totalBytes | Format-Bytes)" `
630688
-SecondsRemaining $secondsRemaining `
631-
-PercentComplete $progress
689+
-PercentComplete $progress `
690+
-Id $data.downloadIndex
632691
}
633692
}
634-
catch [System.Net.WebException] {
635-
Write-Error "Failed downloading $installerFileName - $($_.Exception.Message)"
636-
}
637-
finally {
638-
Unregister-Event -SourceIdentifier DownloadFileCompleted -Force
639-
Unregister-Event -SourceIdentifier DownloadProgressChanged -Force
693+
}
694+
finally {
695+
# If the script is stopped, e.g. Ctrl+C, we want to cancel any remaining downloads
696+
$global:downloadData.Keys | ForEach-Object {
697+
$installerFileName = $_
698+
$data = $global:downloadData[$installerFileName]
699+
700+
if ($null -ne $data.webClient) {
701+
if (-not $data.isDownloaded) {
702+
$data.webClient.CancelAsync()
703+
}
640704

641-
$webClient.Dispose()
705+
Unregister-Event -SourceIdentifier "$installerFileName-Completed" -Force
706+
Unregister-Event -SourceIdentifier "$installerFileName-Changed" -Force
642707

643-
Write-Progress -Activity "Downloading $($_.DownloadUrl)" -Status "Done" -Completed
708+
$data.webClient.Dispose()
709+
$data.webClient = $null
710+
}
644711
}
645712

646-
# Re-writes the last modified time for ensuring downloads are cached properly.
647-
$downloadedFile = Get-Item $destination
648-
$downloadedFile.LastWriteTime = $_.LastModified
649-
650-
$resource = New-Object UnitySetupResource -Property @{
651-
'ComponentType' = $_.ComponentType
652-
'Path' = $destination
653-
}
654-
$downloads += , $resource
713+
Remove-Variable -Name downloadData -Scope Global
655714
}
656-
}
657-
end {
715+
658716
return $downloads
659717
}
660718
}
@@ -1109,6 +1167,14 @@ function Get-UnityProjectInstance {
11091167
What serial should be used by Unity for activation? Implies BatchMode and Quit if they're not supplied by the User.
11101168
.PARAMETER ReturnLicense
11111169
Unity should return the current license it's been activated with. Implies Quit if not supplied by the User.
1170+
.PARAMETER EditorTestsCategories
1171+
Filter tests by category names.
1172+
.PARAMETER EditorTestsFilter
1173+
Filter tests by test names.
1174+
.PARAMETER EditorTestsResultFile
1175+
Where to put the results? Unity states, "If the path is a folder, the command line uses a default file name. If not specified, it places the results in the project’s root folder."
1176+
.PARAMETER RunEditorTests
1177+
Should Unity run the editor tests? Unity states, "[...]it’s good practice to run it with batchmode argument. quit is not required, because the Editor automatically closes down after the run is finished."
11121178
.PARAMETER BatchMode
11131179
Should the Unity Editor start in batch mode?
11141180
.PARAMETER Quit
@@ -1175,6 +1241,14 @@ function Start-UnityEditor {
11751241
[parameter(Mandatory = $false)]
11761242
[switch]$ForceFree,
11771243
[parameter(Mandatory = $false)]
1244+
[string[]]$EditorTestsCategory,
1245+
[parameter(Mandatory = $false)]
1246+
[string[]]$EditorTestsFilter,
1247+
[parameter(Mandatory = $false)]
1248+
[string]$EditorTestsResultFile,
1249+
[parameter(Mandatory = $false)]
1250+
[switch]$RunEditorTests,
1251+
[parameter(Mandatory = $false)]
11781252
[switch]$BatchMode,
11791253
[parameter(Mandatory = $false)]
11801254
[switch]$Quit,
@@ -1266,6 +1340,10 @@ function Start-UnityEditor {
12661340
if ( $ExportPackage ) { $sharedArgs += "-exportPackage", "$ExportPackage" }
12671341
if ( $ImportPackage ) { $sharedArgs += "-importPackage", "$ImportPackage" }
12681342
if ( $Credential ) { $sharedArgs += '-username', $Credential.UserName }
1343+
if ( $EditorTestsCategory ) { $sharedArgs += '-editorTestsCategories', ($EditorTestsCategory -join ',') }
1344+
if ( $EditorTestsFilter ) { $sharedArgs += '-editorTestsFilter', ($EditorTestsFilter -join ',') }
1345+
if ( $EditorTestsResultFile ) { $sharedArgs += '-editorTestsResultFile', $EditorTestsResultFile }
1346+
if ( $RunEditorTests ) { $sharedArgs += '-runEditorTests' }
12691347
if ( $ForceFree) { $sharedArgs += '-force-free' }
12701348

12711349
$instanceArgs = @()
@@ -1361,12 +1439,16 @@ function Start-UnityEditor {
13611439

13621440
$process = Start-Process @setProcessArgs
13631441
if ( $Wait ) {
1364-
if ( $process.ExitCode -ne 0 ) {
1365-
if ( $LogFile -and (Test-Path $LogFile -Type Leaf) ) {
1366-
Write-Verbose "Writing $LogFile to Information stream Tagged as 'Logs'"
1367-
Get-Content $LogFile | ForEach-Object { Write-Information -MessageData $_ -Tags 'Logs' }
1368-
}
1442+
if ( $LogFile -and (Test-Path $LogFile -Type Leaf) ) {
1443+
# Note that Unity sometimes returns a success ExitCode despite the presence of errors, but we want
1444+
# to make sure that we flag such errors.
1445+
Write-UnityErrors $LogFile
1446+
1447+
Write-Verbose "Writing $LogFile to Information stream Tagged as 'Logs'"
1448+
Get-Content $LogFile | ForEach-Object { Write-Information -MessageData $_ -Tags 'Logs' }
1449+
}
13691450

1451+
if ( $process.ExitCode -ne 0 ) {
13701452
Write-Error "Unity quit with non-zero exit code: $($process.ExitCode)"
13711453
}
13721454
}
@@ -1376,6 +1458,46 @@ function Start-UnityEditor {
13761458
}
13771459
}
13781460

1461+
# Open the specified Unity log file and write any errors found in the file to the error stream.
1462+
function Write-UnityErrors {
1463+
param([string] $LogFileName)
1464+
Write-Verbose "Checking $LogFileName for errors"
1465+
$errors = Get-Content $LogFileName | Where-Object { Get-IsUnityError $_ }
1466+
if ( $errors.Count -gt 0 ) {
1467+
$errors = $errors | select -uniq # Unity prints out errors as they occur and also in a summary list. We only want to see each unique error once.
1468+
$errorMessage = $errors -join "`r`n"
1469+
$errorMessage = "Errors were found in $LogFileName`:`r`n$errorMessage"
1470+
Write-Error $errorMessage
1471+
}
1472+
}
1473+
1474+
function Get-IsUnityError {
1475+
param([string] $LogLine)
1476+
1477+
# Detect Unity License error, for example:
1478+
# BatchMode: Unity has not been activated with a valid License. Could be a new activation or renewal...
1479+
if ( $LogLine -match 'Unity has not been activated with a valid License' ) {
1480+
return $true
1481+
}
1482+
1483+
# Detect that the method specified by -ExecuteMethod doesn't exist, for example:
1484+
# executeMethod method 'Invoke' in class 'Build' could not be found.
1485+
if ( $LogLine -match 'executeMethod method .* could not be found' ) {
1486+
return $true
1487+
}
1488+
1489+
# Detect compilation error, for example:
1490+
# Assets/Errors.cs(7,9): error CS0103: The name `NonexistentFunction' does not exist in the current context
1491+
if ( $LogLine -match '\.cs\(\d+,\d+\): error ' ) {
1492+
return $true
1493+
}
1494+
1495+
# In the future, additional kinds of errors that can be found in Unity logs could be added here:
1496+
# ...
1497+
1498+
return $false
1499+
}
1500+
13791501
function ConvertTo-DateTime {
13801502
param([string] $Text)
13811503

0 commit comments

Comments
 (0)