Skip to content

Linux support #241

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 16 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions UnitySetup/UnitySetup.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@

# Modules that must be imported into the global environment prior to importing this module
RequiredModules = @(
@{ModuleName = "powershell-yaml"; ModuleVersion = "0.3"; Guid = "6a75a662-7f53-425a-9777-ee61284407da" }
@{ModuleName = "powershell-yaml"; ModuleVersion = "0.3"; Guid = "6a75a662-7f53-425a-9777-ee61284407da" },
@{ModuleName = "PsIni"; ModuleVersion = "3.1.3"; Guid = "98e1dc0f-2f03-4ca1-98bb-fd7b4b6ac652" }
)

# Assemblies that must be loaded prior to importing this module
Expand Down Expand Up @@ -155,4 +156,3 @@
# DefaultCommandPrefix = ''

}

217 changes: 191 additions & 26 deletions UnitySetup/UnitySetup.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ enum UnitySetupComponent {
Mac_IL2CPP = (1 -shl 14)
Lumin = (1 -shl 15)
Linux_IL2CPP = (1 -shl 16)
All = (1 -shl 17) - 1
Windows_Server = (1 -shl 17)
Linux_Server = (1 -shl 18)
Mac_Server = (1 -shl 19)
All = (1 -shl 20) - 1
}

[Flags()]
Expand Down Expand Up @@ -77,8 +80,16 @@ class UnitySetupInstance {
}
([OperatingSystem]::Linux) {
$this.Components = [UnitySetupComponent]::Linux

throw "UnitySetupInstance has not been implemented on the Linux platform. Contributions welcomed!";
$playbackEnginePath = [io.path]::Combine("$Path", "Editor/Data/PlaybackEngines");
@{
[UnitySetupComponent]::Documentation = , [io.path]::Combine("$Path", "Documentation");
[UnitySetupComponent]::StandardAssets = , [io.path]::Combine("$Path", "Standard Assets");
[UnitySetupComponent]::Mac = , [io.path]::Combine("$playbackEnginePath", "MacStandaloneSupport/Variations/macos_x64_player_development_mono");
[UnitySetupComponent]::Mac_IL2CPP = , [io.path]::Combine("$playbackEnginePath", "MacStandaloneSupport/Variations/macos_x64_player_development_il2cpp");
[UnitySetupComponent]::Windows = , [io.path]::Combine("$playbackEnginePath", "WindowsStandaloneSupport");
[UnitySetupComponent]::Linux_IL2CPP = , [io.path]::Combine("$playbackEnginePath", "LinuxStandaloneSupport/Variations/linux64_player_development_il2cpp");
[UnitySetupComponent]::Linux_Server = , [io.path]::Combine("$playbackEnginePath", "LinuxStandaloneSupport/Variations/linux64_server_development_mono");
}
}
([OperatingSystem]::Mac) {
$this.Components = [UnitySetupComponent]::Mac
Expand All @@ -103,6 +114,7 @@ class UnitySetupInstance {
$componentTests[[UnitySetupComponent]::Vuforia] = , [io.path]::Combine("$playbackEnginePath", "VuforiaSupport");
$componentTests[[UnitySetupComponent]::WebGL] = , [io.path]::Combine("$playbackEnginePath", "WebGLSupport");

Write-Verbose "path: $Path"
$componentTests.Keys | ForEach-Object {
foreach ( $test in $componentTests[$_] ) {
if ( Test-Path -PathType Container -Path $test ) {
Expand Down Expand Up @@ -286,7 +298,11 @@ function Get-UnityEditor {
}
}
([OperatingSystem]::Linux) {
throw "Get-UnityEditor has not been implemented on the Linux platform. Contributions welcomed!";
$editor = Join-Path "$p" 'Editor/Unity'

if (Test-Path $editor) {
Write-Output (Resolve-Path $editor).Path
}
}
([OperatingSystem]::Mac) {
$editor = Join-Path "$p" "Unity.app/Contents/MacOS/Unity"
Expand Down Expand Up @@ -352,6 +368,10 @@ function ConvertTo-UnitySetupComponent {
Manually specify the build hash, to select a private build.
.PARAMETER Components
What components would you like to search for? Defaults to All
.PARAMETER Cache
File path where installer and configuration for Linux will be downloaded to.
Preliminary installer and configuration download is necessary to retrieve actual paths of Unity components for Linux
and to allow subsequent components install. Defaults to ~/.unitysetup
.EXAMPLE
Find-UnitySetupInstaller -Version 2017.3.0f3
.EXAMPLE
Expand All @@ -367,9 +387,23 @@ function Find-UnitySetupInstaller {
[UnitySetupComponent] $Components = [UnitySetupComponent]::All,

[parameter(Mandatory = $false)]
[string] $Hash = ""
[string] $Hash = "",

[parameter(Mandatory = $false)]
[string]$Cache = [io.Path]::Combine("~", ".unitysetup")
)

# Note that this has to happen before calculating the full path since
# Resolve-Path throws an exception on missing paths.
if (!(Test-Path $Cache -PathType Container)) {
New-Item $Cache -ItemType Directory -ErrorAction Stop | Out-Null
}

# Expanding '~' to the absolute path on the system. `WebClient` on macOS asumes
# relative path. macOS also treats alt directory separators as part of the file
# name and this corrects the separators to current environment.
$fullCachePath = (Resolve-Path -Path $Cache).Path

$Components = ConvertTo-UnitySetupComponent -Component $Components -Version $Version

$currentOS = Get-OperatingSystem
Expand All @@ -379,22 +413,27 @@ function Find-UnitySetupInstaller {
$installerExtension = "exe"
}
([OperatingSystem]::Linux) {
throw "Find-UnitySetupInstaller has not been implemented on the Linux platform. Contributions welcomed!";
# This is default, but it seems some packages are used from Mac
$targetSupport = "LinuxEditorTargetInstaller"
$installerExtension = ""
}
([OperatingSystem]::Mac) {
$targetSupport = "MacEditorTargetInstaller"
$installerExtension = "pkg"
}
}

$unitySetupRegEx = "^(.+)\/([a-z0-9]+)\/(.+)\/(.+)-(\d+)\.(\d+)\.(\d+)([fpba])(\d+).$installerExtension$"
$hashRegEx = "([a-z0-9]+)"
$versionRegEx = "(\d+)\.(\d+)\.(\d+)([fpba])(\d+)"
$unitySetupRegEx = "^(.+)\/$hashRegEx\/(.+)\/(.+)-$versionRegEx.$installerExtension$"

$knownBaseUrls = @(
"https://download.unity3d.com/download_unity",
"https://netstorage.unity3d.com/unity",
"https://beta.unity3d.com/download"
)

# For Linux this hashtable will be rewritten later
$installerTemplates = @{
[UnitySetupComponent]::UWP = "$targetSupport/UnitySetup-UWP-.NET-Support-for-Editor-$Version.$installerExtension",
"$targetSupport/UnitySetup-Metro-Support-for-Editor-$Version.$installerExtension",
Expand Down Expand Up @@ -432,9 +471,10 @@ function Find-UnitySetupInstaller {
}
([OperatingSystem]::Linux) {
$setupComponent = [UnitySetupComponent]::Linux
# TODO: $installerTemplates[$setupComponent] = , "???/UnitySetup64-$Version.exe";

throw "Find-UnitySetupInstaller has not been implemented on the Linux platform. Contributions welcomed!";
# Note: This is the main installer small executable that is used to install all other components.
# It will be replaced later by Linux Editor component
$installerTemplates[$setupComponent] = , "UnitySetup-$Version";
}
([OperatingSystem]::Mac) {
$setupComponent = [UnitySetupComponent]::Mac
Expand Down Expand Up @@ -515,10 +555,75 @@ function Find-UnitySetupInstaller {
throw "Could not find archives for Unity version $Version"
}

$linkComponents = $prototypeLink -split $unitySetupRegEx -ne ""
if ($currentOS -ne [OperatingSystem]::Linux) {
$linkComponents = $prototypeLink -split $unitySetupRegEx -ne ""
}
else {
# For Linux we should get list of available components with their target
Import-Module PsIni -MinimumVersion '3.1.3' -ErrorAction Stop

$linkComponents = $prototypeLink -split "/$hashRegEx/" -ne ""

$unityPath = $linkComponents[0]
$unityHash = $linkComponents[1]
$unityVersion = $linkComponents[2] # simnilar to UnitySetup-2021.1.23f1

$baseUrl = "$unityPath/$unityHash"

Write-Verbose "Ready to download installer from $baseUrl"

# also download installer that will be used to install all other components
$installerName = $installerTemplates[[UnitySetupComponent]::Linux]
$installerUrl = "$baseUrl/$installerName"
$cachedInstallerBasePath = "$fullCachePath/Installers/Unity-$Version"
New-Item $cachedInstallerBasePath -ItemType Directory -Force | Out-Null

$localCachedInstaller = "$cachedInstallerBasePath/$installerName"

Write-Verbose "Saving installer to $localCachedInstaller"

Invoke-WebRequest $installerUrl -OutFile $localCachedInstaller

chmod +x $localCachedInstaller

# Configuration file for installer
$iniFileName = "unity-$Version-linux.ini"
$iniUrl = "$baseUrl/$iniFileName"

# PsIni can read only from files and from stdin
$localCachedIni = "$cachedInstallerBasePath/$iniFileName"

Invoke-WebRequest $iniUrl -UseBasicParsing -OutFile $localCachedIni

Write-Verbose $localCachedIni

$iniContent = Get-IniContent $localCachedIni

# fill from scratch accessible targets using retrieved .ini
$installerTemplates = @{}

foreach ($section in $iniContent.GetEnumerator()) {
# replace "-" with "_" to match enum entries
$sectionName = $section.Name.Replace('-', "_")
$url = $iniContent[$section.Name].url

Write-Verbose "Retrieved $url for $sectionName"

switch ($sectionName) {
"Unity" { $component = [UnitySetupComponent]::Linux }
"Windows_Mono" { $component = [UnitySetupComponent]::Windows }
"Mac_Mono" { $component = [UnitySetupComponent]::Mac }
Default { $component = $sectionName }
}

$installerTemplates[$component] = $url
}
}

Write-Verbose "components: $linkComponents"

if ($knownBaseUrls -notcontains $linkComponents[0]) {
$knownBaseUrls = $linkComponents[0], $knownBaseUrls
$knownBaseUrls = @($linkComponents[0]) + $knownBaseUrls
}
else {
$knownBaseUrls = $knownBaseUrls | Sort-Object -Property @{ Expression = { [math]::Abs(($_.CompareTo($linkComponents[0]))) }; Ascending = $true }
Expand Down Expand Up @@ -888,6 +993,19 @@ function Request-UnitySetupInstaller {
}
}

function StartProcessWithArgs($startProcessArgs)
{
$process = Start-Process @startProcessArgs
if ( $process ) {
if ( $process.ExitCode -ne 0) {
Write-Error "$(Get-Date): Failed with exit code: $($process.ExitCode)"
}
else {
Write-Verbose "$(Get-Date): Succeeded."
}
}
}

function Install-UnitySetupPackage {
[CmdletBinding()]
param(
Expand All @@ -909,7 +1027,63 @@ function Install-UnitySetupPackage {
}
}
([OperatingSystem]::Linux) {
throw "Install-UnitySetupPackage has not been implemented on the Linux platform. Contributions welcomed!";
# Assume that UnitySetup in the same folder as package
$basePackagePath = [System.IO.Path]::GetDirectoryName($Package.Path)

$versionRegEx = "(\d+)\.(\d+)\.(\d+)([fpba])(\d+)"
if (-not ($Package.Path -match $versionRegEx)) {
throw "Can't determine Unity version from package"
}

$version = $Matches[0]

# installer is expected to be already donwloaded to the same location during Find-UnitySetupInstaller invocation
$installerName = "UnitySetup-$version"
$installerPath = "$basePackagePath/$installerName"

if (-not (Test-Path $installerPath)) {
throw "Can't find Unity installer, expected at $installerPath"
}

Write-Verbose "Package path: $($Package.Path)"
Write-Verbose "Destination: $Destination"

Import-Module PsIni -MinimumVersion '3.1.3' -ErrorAction Stop

$iniFileName = "unity-$Version-linux.ini"
$iniPath = "$basePackagePath/$iniFileName"

$iniContent = Get-IniContent $iniPath

$componentName = ""
foreach ($section in $iniContent.GetEnumerator()) {
$sectionName = $section.Name
$url = $iniContent[$section.Name].url # e.g. "LinuxEditorInstaller/Unity.tar.xz"

# this should match package name
$urlFileName = [System.IO.Path]::GetFileName($url) # e.g. "Unity.tar.xz"

Write-Verbose "Retrieved $url for $sectionName"

if ($Package.Path -like "*$urlFileName") {
$componentName = $sectionName
break
}
}

if (-not $componentName) {
throw "Could not determine proper component name for $($Package.Path)"
}

# Note: this way user will be asked for license confirmation.
# To bypass it, one should use "yes | <command>"

$startProcessArgs = @{
'FilePath' = "$installerPath";
'ArgumentList' = @('-u', '-l', $Destination, '-d', $basePackagePath, '-c', $componentName);
'PassThru' = $true;
'Wait' = $true;
}
}
([OperatingSystem]::Mac) {
# Note that $Destination has to be a disk path.
Expand All @@ -924,15 +1098,7 @@ function Install-UnitySetupPackage {
}

Write-Verbose "$(Get-Date): Installing $($Package.ComponentType) to $Destination."
$process = Start-Process @startProcessArgs
if ( $process ) {
if ( $process.ExitCode -ne 0) {
Write-Error "$(Get-Date): Failed with exit code: $($process.ExitCode)"
}
else {
Write-Verbose "$(Get-Date): Succeeded."
}
}
StartProcessWithArgs($startProcessArgs)
}

<#
Expand Down Expand Up @@ -974,17 +1140,14 @@ function Install-UnitySetupInstance {
)
begin {
$currentOS = Get-OperatingSystem
if ($currentOS -eq [OperatingSystem]::Linux) {
throw "Install-UnitySetupInstance has not been implemented on the Linux platform. Contributions welcomed!";
}

if ( -not $PSBoundParameters.ContainsKey('BasePath') ) {
$defaultInstallPath = switch ($currentOS) {
([OperatingSystem]::Windows) {
"C:\Program Files\Unity\Hub\Editor\"
}
([OperatingSystem]::Linux) {
throw "Install-UnitySetupInstance has not been implemented on the Linux platform. Contributions welcomed!";
"~/Unity/Hub/Editor/"
}
([OperatingSystem]::Mac) {
"/Applications/Unity/Hub/Editor/"
Expand Down Expand Up @@ -1196,7 +1359,9 @@ function Get-UnitySetupInstance {
}
}
([OperatingSystem]::Linux) {
throw "Get-UnitySetupInstance has not been implemented on the Linux platform. Contributions welcomed!";
if (-not $BasePath) {
$BasePath = @('~/Unity/Hub/Editor/*')
}
}
([OperatingSystem]::Mac) {
if (-not $BasePath) {
Expand Down