Skip to content

Instantly share code, notes, and snippets.

@loopyd
Last active November 30, 2023 07:00
Show Gist options
  • Save loopyd/714df05343083a7f8cb6ed40a10b8484 to your computer and use it in GitHub Desktop.
Save loopyd/714df05343083a7f8cb6ed40a10b8484 to your computer and use it in GitHub Desktop.
[POLYGLOT] DeiGEAR - Bootstrapper for Windows Subsystem for Linux
# =================================================================================
# Title: Dei G. E. A. R. - Grossly Enormous Automated Runner
# WSL2 Polyglot Bootstrapping Script
#
# Description: This script configures WSL2 the easy way. It is useful from
# outside of a WSL container to configure the WSL2 environment. It is also
# useful from within a WSL container to configure the WSL2 environment. Thus it is
# an all-in-one single file solution to configure/manage WSL2.
#
# ---------------------------------------------------------------------------------
# Author: deitydragon <loopyd@github.com>
# Version: 0.1
# ---------------------------------------------------------------------------------
# License: Unilicense (https://unlicense.org)
#
# You are free to use, modify and distribute this software as per the license
# agreement in the gist stated here. However, I would appreciate it if you could
# give credit to the original author by linking to the gist.
#
# =================================================================================
# ---------------------------------------------------------------------------------
# wsl2-prep.ps1: PowerShell portion of the script
# ---------------------------------------------------------------------------------
<#
.SYNOPSIS
wslprep.ps1: The PowerShell portion of the wsl2-prep script.
.DESCRIPTION
This script registers a WSL distribution and configures it for use.
.PARAMETER Distro
The name of the WSL distribution to register. Defaults to 'ubuntu-22.04'.
.PARAMETER Username
The default username to create. Defaults to the current user.
.PARAMETER Password
The password to assign to the default user. Defaults to 'p@55w0rd1234!'.
.RETURNS
None
#>
param(
[Parameter(Mandatory=$false, HelpMessage="The name of the distrobution to work with.")]$Distro,
[Parameter(Mandatory=$false, HelpMessage="The root folder used by the -Import/-Export actions.")][string]$WSLRoot,
[Parameter(Mandatory=$false, HelpMessage="The -Register action registers the distrobution into the WSL2 registry.")][switch]$Register,
[Parameter(Mandatory=$false, HelpMessage="The -Unregister action unregisters the distrobution from the WSL2 registry, and deletes the root filesystem.")][switch]$Unregister,
[Parameter(Mandatory=$false, HelpMessage="The -Import action imports a saved distrobution tarball from the disk into the WSL2 registry.")][switch]$Import,
[Parameter(Mandatory=$false, HelpMessage="The -Export action commits a distrobution's disk to the host filesystem as a tarball.")][switch]$Export,
[Parameter(Mandatory=$false, HelpMessage="The -Check action displays status for the distrobution, and outputs a parseable exit code.")][switch]$Check,
[Parameter(Mandatory=$false, HelpMessage="The -Configure action configures the WSL2 instance.")][switch]$Configure,
[Parameter(Mandatory=$false, HelpMessage="The -Shutdown action shuts down the WSL2 instance.")][switch]$Shutdown,
[Parameter(Mandatory=$false, HelpMessage="The -Startup action starts up the WSL2 instance by sending a simple echo command. This forces the state of the WSL2 instance to 'Running'.")][switch]$Startup,
[Parameter(Mandatory=$false, HelpMessage="For -Configure: write proper /etc/wsl.conf and `$env:USERPROFILE\.wslconfig")][switch]$UpdateWSLConfig,
[Parameter(Mandatory=$false, HelpMessage="For -Configure: choose a default non-root username.")][string]$Username,
[Parameter(Mandatory=$false, HelpMessage="For -Configure: choose the password for the non-root username.")][string]$Password,
[Parameter(Mandatory=$false, HelpMessage="For -Configure: Update the base system and packages.")][switch]$UpdateSystem,
[Parameter(Mandatory=$false, HelpMessage="For -Configure: Install dependencies required to build software.")][switch]$InstallBuildDependencies,
[Parameter(Mandatory=$false, HelpMessage="For -Configure: Install the specified CUDA toolkit/runtime version.")][string]$InstallCudaVersion,
[Parameter(Mandatory=$false, HelpMessage="For -Configure: Install the specified CuDNN toolkit/runtime version.")][string]$InstallCudnnVersion,
[Parameter(Mandatory=$false, HelpMessage="For -Configure: Install the specified Anaconda3 version")][string]$InstallAnacondaVersion,
[Parameter(Mandatory=$false, HelpMessage="For -Configure: Install the specified Python version")][string]$InstallPythonVersion,
[Parameter(Mandatory=$false, HelpMessage="For -Configure: Build/Install OpenSSL at the specified version")][string]$InstallOpenSSLVersion,
[Parameter(Mandatory=$false, HelpMessage="For -Configure: Installs Rust and Cargo.")][switch]$InstallRust,
[Parameter(Mandatory=$false, HelpMessage="For -Configure: Installs Poetry.")][switch]$InstallPoetry,
[Parameter(Mandatory=$false, HelpMessage="For -Configure: Install Docker CLI/runtime/daemon.")][switch]$InstallDocker,
[Parameter(Mandatory=$false, HelpMessage="For -Configure: Install NVIDIA Container Toolkit for docker.")][switch]$InstallNVCT,
[Parameter(Mandatory=$false, HelpMessage="For -Configure: Shows the internal configure script's help screen. Normally used for testing that output.")][switch]$ShowHelp,
[Parameter(Mandatory=$false, HelpMessage="For -Configure: Keep a local copy of the internal configure script on disk, and reference it -- For testing and debug purposes.")][switch]$DebugMode
)
<#
.SYNOPSIS
Automate-StartProcess: Starts a process and redirects the output to the console.
.DESCRIPTION
This function starts a process and redirects the output to the console.
.PARAMETER Program
The program to start.
.PARAMETER Arguments
The arguments to pass to the program.
.RETURNS
The exit code of the process.
#>
Function Automate-StartProcess() {
Param (
[string]$Program,
[string[]]$Arguments
)
$oldEncoding = [Console]::OutputEncoding
[Console]::OutputEncoding = [System.Text.Encoding]::Unicode
$processData = [PSCustomObject]@{
OutputData = New-Object System.Collections.Concurrent.ConcurrentQueue[string]
ErrorData = New-Object System.Collections.Concurrent.ConcurrentQueue[string]
Distro = $Distro
}
$processInfo = New-Object -TypeName System.Diagnostics.ProcessStartInfo
$processInfo.FileName = "pwsh.exe"
$processInfo.UseShellExecute = $false
$processInfo.RedirectStandardOutput = $true
$processInfo.RedirectStandardError = $true
$processInfo.WorkingDirectory = $PWD
$processInfo.CreateNoWindow = $true
$ArgumentsCombined = $Arguments -join " "
$ArgumentsBuilt = "-NoProfile -ExecutionPolicy Bypass -Command `"$Program $ArgumentsCombined | Out-String -Stream; exit `$LASTEXITCODE`""
$processInfo.Arguments = $ArgumentsBuilt
$process = New-Object -TypeName System.Diagnostics.Process
$process.StartInfo = $processInfo
Write-Host -NoNewLine -ForegroundColor Black -BackgroundColor White "[$Distro]"
Write-Host -NoNewLine -ForegroundColor Magenta -BackgroundColor Black ": "
Write-Host -NoNewLine -ForegroundColor White -BackgroundColor Black "$Program $ArgumentsCombined`n`r"
$outputAction = {
$recievedData = $EventArgs.Data | Out-String -Stream
$recievedData = $recievedData `
-replace "\0", "" `
-replace "\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]", "" `
-replace "\x1B\[[0-9]{1,2}m", "" `
-replace "[\x00-\x1F]", "" `
-replace "[^\x20-\x7E]", ""
if (! [String]::IsNullOrEmpty($recievedData)) {
$Event.MessageData.OutputData.Enqueue($recievedData)
$Distro = $Event.MessageData.Distro
Write-Host -NoNewLine -ForegroundColor Black -BackgroundColor Green "[$Distro]"
Write-Host -NoNewLine -ForegroundColor Yellow -BackgroundColor Black ": "
Write-Host -ForegroundColor Green $recievedData
}
}
$errorAction = {
$recievedData = $EventArgs.Data | Out-String -Stream
$recievedData = $EventArgs.Data | Out-String -Stream
$recievedData = $recievedData `
-replace "\0", "" `
-replace "\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]", "" `
-replace "\x1B\[[0-9]{1,2}m", "" `
-replace "[\x00-\x1F]", "" `
-replace "[^\x20-\x7E]", ""
if (! [String]::IsNullOrEmpty($recievedData)) {
$Event.MessageData.ErrorData.Enqueue($recievedData)
$Distro = $Event.MessageData.Distro
Write-Host -NoNewLine -ForegroundColor Black -BackgroundColor Yellow "[$Distro]"
Write-Host -NoNewLine -ForegroundColor DarkYellow -BackgroundColor Black ": "
Write-Host -ForegroundColor Yellow $recievedData
}
}
$outputDataEvent = Register-ObjectEvent -InputObject $process -EventName "OutputDataReceived" -Action $outputAction -MessageData $processData
$errorDataEvent = Register-ObjectEvent -InputObject $process -EventName "ErrorDataReceived" -Action $errorAction -MessageData $processData
[Void]$process.Start()
$process.BeginOutputReadLine()
$process.BeginErrorReadLine()
# Main loop
while (!$process.HasExited) {
[System.Threading.Thread]::Sleep(5)
}
# Unregister the event handlers
Unregister-Event -SourceIdentifier $outputDataEvent.Name
Unregister-Event -SourceIdentifier $errorDataEvent.Name
[Console]::OutputEncoding = $oldEncoding
$exitCode = $process.ExitCode
if ($exitCode -ne 0) {
Write-Host -NoNewLine -ForegroundColor Black -BackgroundColor Red "[$Distro]"
Write-Host -NoNewLine -ForegroundColor Magenta -BackgroundColor Black ": "
Write-Host -NoNewLine -ForegroundColor Red -BackgroundColor Black "Command returned non-zero exit status code: $exitCode`n`r"
} else {
Write-Host -NoNewLine -ForegroundColor Black -BackgroundColor Cyan "[$Distro]"
Write-Host -NoNewLine -ForegroundColor DarkCyan -BackgroundColor Black ": "
Write-Host -NoNewLine -ForegroundColor Cyan -BackgroundColor Black "Command ran successfully`n`r"
}
return $exitCode
}
<#
.SYNOPSIS
Convert-PathToWsl: Converts a Windows path to a WSL path.
.DESCRIPTION
This function converts a Windows path to a WSL path.
.PARAMETER WindowsPath
The Windows path to convert.
.RETURNS
The converted WSL path.
#>
function Convert-PathToWsl {
param (
[string]$WindowsPath
)
$wslPath = $WindowsPath -replace ":", "" -replace "\\", "/"
$wslPath = $wslPath.Substring(0,1).ToLower() + $wslPath.Substring(1)
$wslPath = "/mnt/" + $wslPath
return $wslPath
}
<#
.SYNOPSIS
Check-WSL: Checks the status of a WSL distribution.
.DESCRIPTION
This function checks the status of a WSL distribution by parsing wsl tool outoput.
#>
function Check-WSL() {
param (
[Parameter(Mandatory=$true,Position=1)][string]$Distro,
[Parameter(Mandatory=$true,Position=2)][string]$WSLVersion,
[Parameter(Mandatory=$true,Position=3)][bool]$IsRunning
)
try {
$wsls = (wsl -l -v | Out-String -Stream | Where-Object { $_ -match '\*|\w+' } | Select-Object -Skip 1) | ForEach-Object {
$result = $_ -split '\s+' | Where-Object { $_ -match '\*|\w+' }
if ($result[0] -eq '*') {
[PSCustomObject]@{
Default = $true
Distro = $result[1].Trim()
State = $result[2].Trim()
WSLVersion = $result[3].Trim()
}
} else {
[PSCustomObject]@{
Default = $false
Distro = $result[0].Trim()
State = $result[1].Trim()
WSLVersion = $result[2].Trim()
}
}
} | Where-Object {
$_.Distro.ToLower() -eq $Distro.ToLower() -and
$_.WSLVersion -eq $WSLVersion -and
$_.State.ToLower() -eq ($IsRunning ? 'running' : 'stopped')
}
if ($wsls.Count -eq 0) {
return $false
}
return $true
} catch {
return $false
}
}
<#
.SYNOPSIS
Shutdown-WSL: Shuts down a WSL distribution.
.DESCRIPTION
This function shuts down a WSL distribution.
.PARAMETER Distro
The name of the WSL distribution to shut down.
.RETURNS
true if the WSL distribution was shut down successfully; otherwise, false.
#>
function Shutdown-WSL() {
param(
[Parameter(Mandatory=$true,Position=1)][string]$Distro
)
try {
if ($(Check-WSL -Distro $Distro -WSLVersion 2 -IsRunning $true) -eq $true) {
Write-Host "Shutting down WSL distribution '$Distro'..."
$result = Automate-StartProcess -Program "wsl" -Arguments @("-d", $Distro, "--shutdown")
if ($result -ne 0) {
throw "E: Failed to shutdown WSL distribution '$Distro'."
}
Start-Sleep -s 8
}
return $true
} catch {
Write-Error "$($_.Exception.Message)"
return $false
}
}
<#
.SYNOPSIS
Startup-WSL: Starts up a WSL distribution.
.DESCRIPTION
This function starts up a WSL distribution by issuing a simple echo command.
.PARAMETER Distro
The name of the WSL distribution to start up.
.RETURNS
true if the WSL distribution was started up successfully; otherwise, false.
#>
function Startup-WSL() {
param(
[Parameter(Mandatory=$true,Position=1)][string]$Distro
)
try {
if ($(Check-WSL -Distro $Distro -WSLVersion 2 -IsRunning $false) -eq $true) {
$lc = Automate-StartProcess -Program "wsl" -Arguments @("-d", $Distro, "--", "echo", "'OK'")
if ($lc -ne 0) {
throw "E: Failed to startup WSL distribution '$Distro'."
}
Start-Sleep -s 8
if (-not $(Check-WSL -Distro $Distro -WSLVersion 2 -IsRunning $true) -eq $true) {
throw "E: Failed to startup WSL distribution '$Distro'."
}
}
return $true
} catch {
Write-Error "$($_.Exception.Message)"
return $false
}
}
<#
.SYNOPSIS
Register-WSL: Registers a WSL distribution.
.DESCRIPTION
This function registers a WSL distribution.
.PARAMETER Distro
The name of the WSL distribution to register.
.RETURNS
None
#>
function Register-WSL() {
param (
[Parameter(Mandatory=$true,Position=1)][string]$Distro
)
try {
$lc = Automate-StartProcess -Program "wsl" -Arguments @("--install", $Distro, "--no-launch")
if ($lc -ne 0) {
throw "E: Failed to install WSL distribution '$Distro'."
}
$DistroShort = $Distro -replace "-.*", ""
$lc = Automate-StartProcess -Program "$DistroShort" -Arguments @("install", "--root")
if ($lc -ne 0) {
throw "E: Failed to initialize WSL distribution '$Distro'."
}
Start-Sleep -s 8
return $true
} catch {
Write-Error "$($_.Exception.Message)"
return $false
}
}
<#
.SYNOPSIS
Unregister-WSL: Unregisters a WSL distribution.
.DESCRIPTION
This function unregisters a WSL distribution.
.PARAMETER Distro
The name of the WSL distribution to unregister.
.RETURNS
None
#>
function Unregister-WSL() {
param (
[Parameter(Mandatory=$true,Position=1)][string]$Distro
)
try {
if ($(Check-WSL -Distro $Distro -WSLVersion 2 -IsRunning $true) -eq $true) {
if (-not $(Shutdown-WSL -Distro $Distro)) {
throw "E: Failed to shutdown WSL distribution '$Distro'."
}
}
Write-Host "Unregistering WSL distribution '$Distro'..."
$DistroShort = $Distro -replace "-.*", ""
$lc = Automate-StartProcess -Program "wsl" -Arguments @("--unregister", $DistroShort)
if ($lc -ne 0) {
throw "E: Failed to unregister WSL distribution '$Distro'."
}
return $true
} catch {
Write-Error "$($_.Exception.Message)"
return $false
}
}
<#
.SYNOPSIS
Ensure-WSLPaths: Ensures the WSL paths exist.
.DESCRIPTION
This function ensures the WSL paths exist.
.PARAMETER Distro
The name of the WSL distribution to register.
.PARAMETER WSLRoot
The root path for WSL installations.
.RETURNS
true if the WSL paths exist / where created successfully; otherwise, false.
#>
function Ensure-WSLPaths() {
param(
[Parameter(Mandatory=$true,Position=1)][string]$Distro,
[Parameter(Mandatory=$false)][string]$WSLRoot
)
try {
# Ensure apprioriate paths exist
if (-not $(Test-Path -Path $WSLRoot)) {
New-Item -Path $WSLRoot -ItemType Directory -Force | Out-Null
if (-not $(Test-Path -Path $WSLRoot)) {
throw "E: Failed to create WSL root directory: '$WSLRoot'."
}
}
$InstallPath = $WSLRoot + "\" + $Distro
if (-not $(Test-Path -Path $InstallPath)) {
New-Item -Path $InstallPath -ItemType Directory -Force | Out-Null
if (-not $(Test-Path -Path $InstallPath)) {
throw "E: Failed to create WSL installation directory: '$InstallPath'."
}
}
return $true
} catch {
Write-Error "$($_.Exception.Message)"
return $false
}
}
<#
.SYNOPSIS
Import-WSL: Imports a WSL distribution.
.DESCRIPTION
This function imports a WSL distribution.
.PARAMETER Distro
The name of the WSL distribution to import.
.PARAMETER WSLRoot
The root path for WSL installations.
.RETURNS
None
#>
Function Import-WSL() {
param(
[Parameter(Mandatory=$true,Position=1)][string]$Distro,
[Parameter(Mandatory=$false)][string]$WSLRoot
)
try {
if (-not $(Ensure-WSLPaths -Distro $Distro -WSLRoot $WSLRoot)) {
throw "E: Failed to ensure WSL paths exist."
}
$InstallPath = $WSLRoot + "\" + $Distro
$ExportPath = $InstallPath + "\" + $Distro + ".tar"
if ($(Check-WSL -Distro $Distro -WSLVersion 2 -IsRunning $true) -eq $true) {
throw "E: WSL distribution '$Distro' is already imported and running."
}
$lc = Automate-StartProcess -Program "wsl" -Arguments @("--import", $Distro, $InstallPath, "`"$ExportPath`"")
if ($lc -ne 0) {
throw "E: Failed to import WSL distribution '$Distro'."
}
return $true
} catch {
Write-Error "$($_.Exception.Message)"
return $false
}
}
<#
.SYNOPSIS
Export-WSL: Exports a WSL distribution.
.DESCRIPTION
This function exports a WSL distribution.
.PARAMETER Distro
The name of the WSL distribution to export.
.PARAMETER WSLRoot
The root path for WSL installations.
.RETURNS
None
#>
Function Export-WSL() {
param(
[Parameter(Mandatory=$true,Position=1)][string]$Distro,
[Parameter(Mandatory=$false)][string]$WSLRoot
)
try {
$InstallPath = $WSLRoot + "\" + $Distro
$ExportPath = $InstallPath + "\" + $Distro + ".tar"
if ($(Test-Path -Path $ExportPath -PathType File) -and $(Test-Path -Path $InstallPath --PathType Directory)) {
Remove-Item -Path $ExportPath -Force | Out-Null
if ($(Test-Path -Path $ExportPath)) {
throw "E: Failed to remove old WSL disk image: '$ExportPath'."
}
}
if (-not $(Ensure-WSLPaths -Distro $Distro -WSLRoot $WSLRoot)) {
throw "E: Failed to ensure WSL export paths exist."
}
$lc = Automate-StartProcess -Program "wsl" -Arguments @("--export", $Distro, "`"$ExportPath`"")
if ($lc -ne 0) {
throw "E: Failed to export WSL distribution '$Distro'."
}
return $true
} catch {
Write-Error "$($_.Exception.Message)"
return $false
}
}
<#
.SYNOPSIS
Configure-WSL: Configures a WSL distribution
.DESCRIPTION
This function configures a WSL distribution. It is a wrapper for the wsl2-prep.sh script.
.PARAMETER Distro
The name of the WSL distribution to configure.
.PARAMETER WSLRoot
The root path for WSL installations.
.PARAMETER Username
The default username to create.
.PARAMETER Password
The password to assign to the default user.
.PARAMETER OS
The OS to configure.
.PARAMETER UpdateSystem
Boolean to determine if system packages should be updated.
.PARAMETER CudaVersion
The version of CUDA to install.
.PARAMETER CudnnVersion
The version of cuDNN to install.
.PARAMETER PythonVersion
The version of Python to install.
.PARAMETER InstallRust
Boolean to determine if Rust should be installed.
.PARAMETER InstallPoetry
Boolean to determine if Poetry should be installed.
.PARAMETER InstallDocker
Boolean to determine if Docker should be installed.
.PARAMETER InstallNVCT
Boolean to determine if NVIDIA Container Toolkit should be installed.
.PARAMETER Help
Boolean to determine if the help message should be displayed.
.PARAMETER Debug
Boolean to determine if the WSL2 internal configure script should be saved to local disk / referenced from local disk for debugging purposes.
.RETURNS
None
#>
function Configure-WSL() {
param(
[Parameter(Mandatory=$true,Position=1)][string]$Distro,
[Parameter(Mandatory=$false)][switch]$UpdateWSLConfig,
[Parameter(Mandatory=$false)][string]$Username,
[Parameter(Mandatory=$false)][string]$Password,
[Parameter(Mandatory=$false)][switch]$UpdateSystem,
[Parameter(Mandatory=$false)][switch]$InstallBuildDependencies,
[Parameter(Mandatory=$false)][string]$InstallCudaVersion,
[Parameter(Mandatory=$false)][string]$InstallCudnnVersion,
[Parameter(Mandatory=$false)][string]$InstallPythonVersion,
[Parameter(Mandatory=$false)][string]$InstallAnacondaVersion,
[Parameter(Mandatory=$false)][string]$InstallOpenSSLVersion,
[Parameter(Mandatory=$false)][switch]$InstallRust,
[Parameter(Mandatory=$false)][switch]$InstallPoetry,
[Parameter(Mandatory=$false)][switch]$InstallDocker,
[Parameter(Mandatory=$false)][switch]$InstallNVCT,
[Parameter(Mandatory=$false)][switch]$ShowHelp,
[Parameter(Mandatory=$false)][switch]$DebugMode
)
try {
$ScriptPath = (Split-Path -Parent $PSCommandPath) + "\wsl2-prep.sh"
if ($(Test-Path -Path $ScriptPath) -eq $true -and $DebugMode -eq $false) {
Remove-Item -Path $ScriptPath
}
if ($(Test-Path -Path $ScriptPath) -eq $false) {
($global:BASH_SCRIPT -replace "`r`n", "`n" -replace "`n`r", "`n") | Set-Content -Path $ScriptPath -NoNewline -Encoding UTF8
}
if ($(Test-Path -Path $ScriptPath) -eq $false) {
throw "E: Failed to create configure script: '$ScriptPath'."
}
$DistroShort = $Distro -replace "-.*", ""
$lc = Automate-StartProcess -Program "wsl" -Arguments @(
"-d", $DistroShort, "-u", "root", "--", "cp",
$(Convert-PathToWsl -WindowsPath "$ScriptPath"),
"/usr/local/bin/wsl2-prep.sh")
if ($(Test-Path -Path $ScriptPath) -eq $true -and $DebugMode -eq $false) {
Remove-Item -Path $ScriptPath
}
if ($lc -ne 0) {
throw "E: Failed to copy configure script to WSL distribution '$Distro'."
}
$lc = Automate-StartProcess -Program "wsl" -Arguments @("-d", $DistroShort, "-u", "root", "--", "chmod", "+x", "/usr/local/bin/wsl2-prep.sh")
if ($lc -ne 0) {
throw "E: Failed to set permissions for configure script in WSL distribution '$Distro'."
}
$argsList = @(
"-d", $DistroShort, "-u", "root", "--", "/usr/local/bin/wsl2-prep.sh"
)
$argsList += "-o", $Distro
if ($ShowHelp -eq $true) { $argsList += "-h" }
if ($UpdateWSLConfig -eq $true) { $argsList += "-w" }
if ($UpdateSystem -eq $true) { $argsList += "-x" }
if ($InstallBuildDependencies -eq $true) { $argsList += "-b" }
if ($InstallNVCT -eq $true) { $argsList += "-n" }
if ($InstallRust -eq $true) { $argsList += "-r" }
if ($InstallPoetry -eq $true) { $argsList += "-y" }
if ($InstallDocker -eq $true) { $argsList += "-k" }
if ([String]::IsNullOrEmpty($InstallOpenSSLVersion) -eq $false) { $argsList += "-g", "`"$InstallOpenSSLVersion`"" }
if ([String]::IsNullOrEmpty($Username) -eq $false -and [String]::IsNullOrEmpty($Password) -eq $false) { $argsList += "-i", "-a", "`"$Username`"", "-t", "`"$Password`"" }
if ([String]::IsNullOrEmpty($InstallCudaVersion) -eq $false) { $argsList += "-c", "`"$InstallCudaVersion`"" }
if ([String]::IsNullOrEmpty($InstallCudnnVersion) -eq $false) { $argsList += "-e", "`"$InstallCudnnVersion`"" }
if ([String]::IsNullOrEmpty($InstallPythonVersion) -eq $false) { $argsList += "-p", "`"$InstallPythonVersion`"" }
if ([String]::IsNullOrEmpty($InstallAnacondaVersion) -eq $false) { $argsList += "-m", "`"$InstallAnacondaVersion`"" }
$lc = Automate-StartProcess -Program "wsl" -Arguments $argsList
if ($lc -ne 0) {
throw "E: Failed to execute wsl2-prep.sh script in WSL distrobution '$Distro'."
}
return $true
} catch {
Write-Error "$($_.Exception.Message)"
return $false
}
}
$global:BASH_SCRIPT = @'
#!/bin/bash
# =================================================================================
# wsl2-prep.sh: bash portion of the script
# ---------------------------------------------------------------------------------
#
# Supported linux distributions:
#
# - Debian - Ubuntu - Linux Mint
# - Fedora - Arch Linux - CentOS
# - RHEL - Alpine - Gentoo
# - Funtoo - Exherbo - Sabayon
# - Solus - Void Linux - Artix Linux
# - Calculate - Clear Linux
#
# Please report any bugs with supported distributions to the author.
#
# =================================================================================
if [[ $EUID -ne 0 ]]; then
echo "This script must be run as root"
exit 1
fi
# ---------------------------------------------------------------------------------
# usage: Displays the usage of the script
# ---------------------------------------------------------------------------------
# Arguments:
# None
#
# Returns:
# None
usage() {
printf "%s\n" "---------------------------------------------------------------------------------" >&2
printf "%-50s%s\n" "WSL2 Preparation Script" "Version 0.1" >&2
printf "%-50s%s\n" "Author: loopyd <loopyd@github.com>" "License: Unilicense" >&2
printf "%s\n" "---------------------------------------------------------------------------------" >&2
printf "\n%s\n" "This sysprep script configures WSL2 the easy way." >&2
printf "\nUsage: %s: [Arguments]" $(basename $0) >&2
printf "\n%-25s%-40s%s\n" "Arguments:" "Description:" "Environment Variable:" >&2
printf "%-25s%-40s%s\n" "-h | --help" "Display this help message and exit" "" >&2
printf "\n%s\n\n" "Auto WSL Options:" >&2
printf "%-25s%-40s%s\n" "-o | --os OS" "Specify the OS to configure" "OS=ubuntu-22.04" >&2
printf "%-25s%-40s%s\n" "-w | --wslconf" "Configure wslconfig and other options" "WSL=true" >&2
printf "%-25s%-40s%s\n" "-i | --init" "Perform initialization" "INITSYS=true" >&2
printf "%-25s%-40s%s\n" "-a | --username USERNAME" "Specify the username for the default user account" "INIT_USERNAME" >&2
printf "%-25s%-40s%s\n" "-t | --password PASSWORD" "Specify the password for the default user account" "INIT_PASSWORD" >&2
printf "%-25s%-40s%s\n" "-x | --system-update" "Update system packages" "SYSTEM_UPDATE=true" >&2
printf "\n%s\n\n" "NVIDIA Options:" >&2
printf "%-25s%-40s%s\n" "-c | --cuda VERSION" "Install CUDA runtime / SDK" "CUDA_VERSION=11.3" >&2
printf "%-25s%-40s%s\n" "-e | --cudnn VERSION" "Install cuDNN runtime / libraries" "CUDNN_VERSION=8" >&2
printf "\n%s\n\n" "Developer Toolkit Options:" >&2
printf "%-25s%-40s%s\n" "-b | --build" "Install additional build dependencies" "BUILD_DEPS=true" >&2
printf "%-25s%-40s%s\n" "-r | --rust" "Install Rust and Cargo" "RUST=true" >&2
printf "%-25s%-40s%s\n" "-y | --poetry" "Install Poetry" "POETRY=true" >&2
printf "%-25s%-40s%s\n" "-g | --openssl-version VERSION" "Install OpenSSL" "OPENSSL_VERSION=3.1.3" >&2
printf "%-25s%-40s%s\n" "-p | --python VERSION" "Install Python" "PYTHON_VERSION=3.11.6" >&2
printf "\n%s\n\n" "Docker Options:" >&2
printf "%-25s%-40s%s\n" "-k | --docker" "Install Docker" "DOCKER=true" >&2
printf "%-25s%-40s%s\n" "-n | --nvck" "Install NVIDIA Container Toolkit" "NVCK=true" >&2
return
}
# ---------------------------------------------------------------------------------
# validate_version: Validates a version string
# ---------------------------------------------------------------------------------
# Arguments:
# version: The version string to validate
#
# Returns:
# 0 if the version string is invalid, 1 otherwise
#
validate_version() {
local version=$1
local regex='^([0-9]+\.){1,3}[0-9]+(-[A-Za-z0-9]+)?$'
if ! [[ $version =~ $regex ]]; then
return 0
fi
return 1
}
# ---------------------------------------------------------------------------------
# validate_os: Validates an OS string
# ---------------------------------------------------------------------------------
# Arguments:
# os: The OS string to validate
#
# Returns:
# 0 if the OS string is invalid, 1 otherwise
#
validate_os() {
local os=$1
local regex='^([a-zA-Z]+)(\-[0-9]+\.?[0-9]?|latest|lts)?$'
if ! [[ $os =~ $regex ]]; then
return 0
fi
return 1
}
# ---------------------------------------------------------------------------------
# validate_username: Validates a username string
# ---------------------------------------------------------------------------------
# Arguments:
# username: The username string to validate
#
# Returns:
# 0 if the username string is invalid, 1 otherwise
#
validate_username() {
local username=$1
local regex='^[a-z_][a-z0-9_-]*[$]?$'
if ! [[ $username =~ $regex ]]; then
return 0
fi
return 1
}
# ---------------------------------------------------------------------------------
# validate_password: Validates a password string
# ---------------------------------------------------------------------------------
# Arguments:
# password: The password string to validate
#
# Returns:
# 0 if the password string is invalid, 1 otherwise
#
# Notes:
#
# Strict policy:
#
# - The password must be at least 8 characters long
# - The password must contain at least one lowercase letter
# - The password must contain at least one uppercase letter
# - The password must contain at least one number
# - The password must contain at least one special character of the following:
# ! @ # $ % ^ & * ( ) _ - +
#
validate_password() {
local password=$1
local regex='^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[\!\@\#\$\%\^\&\*\(\)\_\-\+]).{8,}$'
if ! [[ $password =~ $regex ]]; then
return 0
fi
return 1
}
# ---------------------------------------------------------------------------------
# generate_password: Generates a password string
# ---------------------------------------------------------------------------------
# Arguments:
# length: The length of the password to generate
#
# Returns:
# The generated password string
#
# Notes:
#
# Strict policy:
#
# - The generated password is at least 8 characters long
# - The generated password contains at least one lowercase letter
# - The generated password contains at least one uppercase letter
# - The generated password contains at least one number
# - The generated password contains at least one special character of the following:
# ! @ # $ % ^ & * ( ) _ - +
generate_password() {
local length=${1:-8}
local password
if (( length < 8 )); then
echo "error"
return 0
fi
while true; do
password=$(LC_ALL=C tr -dc 'A-Za-z0-9!@#$%^&*()_-+' < /dev/urandom | fold -w $length | head -n 1)
if validate_password "$password"; then
echo "$password"
return 1
fi
done
}
# ---------------------------------------------------------------------------------
# convert_version_format: Converts a version string to a specific format
# ---------------------------------------------------------------------------------
# Arguments:
# version_string: The version string to convert
# format_type: The format to convert the version string to
#
# Returns:
# The converted version string
#
convert_version_format() {
local version_string=$1
local format_type=$2
case $format_type in
"concat")
echo "$version_string" | sed -E 's/^([0-9]+)\.([0-9]+).*$/\1\2/'
;;
"two-part")
echo "$version_string" | sed -E 's/^([0-9]+)\.([0-9]+).*$/\1-\2/'
;;
"major")
echo "$version_string" | sed -E 's/^([0-9]+).*/\1/'
;;
"minor")
echo "$version_string" | sed -E 's/^[0-9]+\.([0-9]+).*/\1/'
;;
"patch")
echo "$version_string" | sed -E 's/^[0-9]+\.[0-9]+\.([0-9]+).*/\1/'
;;
"build")
echo "$version_string" | sed -E 's/^[0-9]+\.[0-9]+\.[0-9]+-([A-Za-z0-9]+).*/\1/'
;;
esac
}
# ---------------------------------------------------------------------------------
# parse_args: Parses the command line arguments
# ---------------------------------------------------------------------------------
# Arguments:
# $@: The command line arguments
#
# Returns:
# None
parse_args() {
# Define the options
OPTIONS=ho:wnc:e:p:ryxbkia:t:g:m:
LONGOPTIONS=help,os:,wslconf,nvck,cuda:,cudnn:,python-version:,rust,poetry,system-update,build,docker,init,username:,password:,openssl-version:,anaconda-version:
# Parse the options
PARSED=$(getopt --options=$OPTIONS --longoptions=$LONGOPTIONS --name "$0" -- "$@")
if [[ $? -ne 0 ]]; then
exit 2
fi
eval set -- "$PARSED"
unset -v sub
while true; do
case "$1" in
-h | --help)
usage
exit 0
;;
-o | --os)
if [ -z "$2" ]; then
echo "E: OS not specified"
exit 3
fi
if [ "$(validate_os "$2")" == "0" ]; then
echo "E: Invalid OS string format: $2"
exit 3
fi
os_name=$(echo "$2" | tr '[:upper:]' '[:lower:]' | cut -d'-' -f1)
if [[ $os_name != "debian" && $os_name != "ubuntu" ]]; then
echo "E: Unsupported OS: $2"
exit 3
fi
OS="$os_name"
shift 2
;;
-w | --wsl)
WSL="true"
shift
;;
-n | --nvck)
NVCK="true"
shift
;;
-c | --cuda)
if [ -z "$2" ]; then
echo "E: CUDA version not specified"
exit 3
fi
if [ "$(validate_version "$2")" == "0" ]; then
echo "E: Invalid CUDA version: $2"
exit 3
fi
CUDA_VERSION="$2"
shift 2
;;
-e | --cudnn)
if [ -z "$2" ]; then
echo "E: cuDNN version not specified"
exit 3
fi
if [ "$(validate_version "$2")" == "0" ]; then
echo "E: Invalid cuDNN version: $2"
exit 3
fi
CUDNN_VERSION="$2"
shift 2
;;
-p | --python)
if [ -z "$2" ]; then
echo "E: Python version not specified"
exit 3
fi
if [ "$(validate_version "$2")" == "0" ]; then
echo "E: Invalid Python version: $2"
exit 3
fi
PYTHON_VERSION="$2"
shift 2
;;
-g | --openssl-version)
if [ -z "$2" ]; then
echo "E: OpenSSL version not specified"
exit 3
fi
if [ "$(validate_version "$2")" == "0" ]; then
echo "E: Invalid OpenSSL version: $2"
exit 3
fi
OPENSSL_VERSION="$2"
shift 2
;;
-r | --rust)
RUST="true"
shift
;;
-y | --poetry)
POETRY="true"
shift
;;
-x | --system-update)
SYSTEM_UPDATE="true"
shift
;;
-b | --build)
BUILD_DEPS="true"
shift
;;
-k | --docker)
DOCKER="true"
shift
;;
-i | --init)
INITSYS="true"
shift
;;
-a | --username)
if [ -z "$2" ]; then
echo "E: Username not specified"
exit 3
fi
if [ "$(validate_username "$2")" == "0" ]; then
echo "E: Invalid username: $2"
exit 3
fi
INIT_USERNAME="$2"
shift 2
;;
-t | --password)
if [ -z "$2" ]; then
echo "E: Password not specified"
exit 3
fi
if [ "$(validate_password "$2")" == "0" ]; then
echo "E: Invalid password: $2"
exit 3
fi
INIT_PASSWORD="$2"
shift 2
;;
-m | --anaconda-version)
if [ -z "$2" ]; then
echo "E: Anaconda3 version not specified"
exit 3
fi
ANACONDA_VERSION="$2"
shift 2
;;
--)
shift
break
;;
*)
echo "E: Unrecognized option: $1"
exit 3
;;
esac
done
if [ -z "$OS" ]; then
echo "E: Please specify the OS using the -o or --os option"
exit 4
fi
if [ -z "$WSL" ]; then
WSL="false"
fi
if [ -z "$NVCK" ]; then
NVCK="false"
fi
if [ -z "$RUST" ]; then
RUST="false"
fi
if [ -z "$POETRY" ]; then
POETRY="false"
fi
if [ -z "$SYSTEM_UPDATE" ]; then
SYSTEM_UPDATE="false"
fi
if [ -z "$BUILD_DEPS" ]; then
BUILD_DEPS="false"
fi
if [ -z "$DOCKER" ]; then
DOCKER="false"
fi
if [ -z "$INITSYS" ]; then
if [ -n "$INIT_USERNAME" ] || [ -n "$INIT_PASSWORD" ]; then
echo "E: Please specify the -i | --init option to create the default user account"
exit 4
fi
INITSYS="false"
else
if [ -z "$INIT_USERNAME" ] && [ -z "$INIT_PASSWORD" ]; then
echo "E: Please specify both the username and password using the -a | --username and -t | --password options"
exit 4
fi
fi
if [ -n "$PYTHON_VERSION" ]; then
if [ -z "$PIP_VERSION" ]; then
PIP_VERSION="23.2.1"
fi
if [ -z "$SETUPTOOLS_VERSION" ]; then
SETUPTOOLS_VERSION="65.5.1"
fi
fi
if [[ $NVCK == "false" && $WSL == "false" && $RUST == "false" && $POETRY == "false" && $SYSTEM_UPDATE == "false" && $BUILD_DEPS == "false" && $DOCKER == "false" && $INITSYS == "false" && -z $CUDA_VERSION && -z $CUDNN_VERSION && -z $PYTHON_VERSION && -z $OPENSSL_VERSION && -z $ANACONDA_VERSION ]]; then
echo "E: No action specified. The script will do nothing."
exit 5
fi
}
# ---------------------------------------------------------------------------------
# create_user: Creates a user account
# ---------------------------------------------------------------------------------
#
# Arguments:
# username: The username of the user to create
#
# Returns:
# None
init_system() {
local username="$1"
local password="$2"
local passHash
passHash=$(perl -e 'print crypt($ARGV[0], "password")' "$password")
echo "Creating user account: $username"
useradd -m -s /bin/bash -G sudo -p $passHash -u 1000 $username
chown -R $username:$username /home/$username
echo "$username ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
}
# ---------------------------------------------------------------------------------
# manage_packages: Manages packages on the system
# ---------------------------------------------------------------------------------
# Arguments:
# command: The command to execute
# packages_ref: The array of packages to manage
#
# Returns:
# None
#
manage_packages() {
local command="$1"
shift
local packages_ref=("$@")
case "$command" in
"update")
case "$OS" in
"debian" | "ubuntu" | "mint")
apt-get update || { echo "Updating package list failed"; exit 1; }
;;
"fedora")
dnf update || { echo "Updating package list failed"; exit 1; }
;;
"arch")
pacman -Syu || { echo "Updating package list failed"; exit 1; }
;;
"centos" | "rhel")
yum update || { echo "Updating package list failed"; exit 1; }
;;
"alpine")
apk update || { echo "Updating package list failed"; exit 1; }
;;
"gentoo" | "funtoo" | "exherbo" | "sabayon" | "calculate" | "clear" | "solus" | "void" | "artix" )
emerge --sync || { echo "Updating package list failed"; exit 1; }
;;
*)
echo "Unsupported Linux distribution: $DISTRO"
exit 1
;;
esac
;;
"upgrade")
case "$OS" in
"debian" | "ubuntu" | "mint")
apt-get upgrade -y || { echo "Upgrading packages failed"; exit 1; }
;;
"fedora")
dnf upgrade -y || { echo "Upgrading packages failed"; exit 1; }
;;
"arch")
pacman -Syu --noconfirm || { echo "Upgrading packages failed"; exit 1; }
;;
"centos" | "rhel")
yum upgrade -y || { echo "Upgrading packages failed"; exit 1; }
;;
"alpine")
apk upgrade || { echo "Upgrading packages failed"; exit 1; }
;;
"gentoo" | "funtoo" | "exherbo" | "sabayon" | "calculate" | "clear" | "solus" | "void" | "artix" )
emerge --update --deep --with-bdeps=y @world || { echo "Upgrading packages failed"; exit 1; }
;;
*)
echo "Unsupported Linux distribution: $DISTRO"
exit 1
;;
esac
;;
"install")
for package in "${packages_ref[@]}"; do
if manage_packages query_notinstalled "$package"; then
case "$OS" in
"debian" | "ubuntu" | "mint")
apt-get install -y --no-install-recommends "$package" 2>/dev/null || { echo "Installation of $package failed"; exit 1; }
;;
"fedora")
dnf install -y "$package" 2>/dev/null || { echo "Installation of $package failed"; exit 1; }
;;
"arch")
pacman -S --noconfirm "$package" 2>/dev/null || { echo "Installation of $package failed"; exit 1; }
;;
"centos" | "rhel")
yum install -y "$package" 2>/dev/null || { echo "Installation of $package failed"; exit 1; }
;;
"alpine")
apk add "$package" 2>/dev/null || { echo "Installation of $package failed"; exit 1; }
;;
"gentoo" | "funtoo" | "exherbo" | "sabayon" | "calculate" | "clear" | "solus" | "void" | "artix" )
emerge "$package" 2>/dev/null || { echo "Installation of $package failed"; exit 1; }
;;
*)
echo "Unsupported Linux distribution: $DISTRO"
exit 1
;;
esac
else
echo "$package is already installed"
fi
done
;;
"autoremove" | "remove" | "purge")
for package in "${packages_ref[@]}"; do
if manage_packages query_installed "$package"; then
case "$OS" in
"debian" | "ubuntu" | "mint")
apt-get "$command" -y "$package" || { echo "Removal of $package failed"; exit 1; }
;;
"fedora")
dnf "$command" -y "$package" || { echo "Removal of $package failed"; exit 1; }
;;
"arch")
pacman -R --noconfirm "$package" || { echo "Removal of $package failed"; exit 1; }
;;
"centos" | "rhel")
yum "$command" -y "$package" || { echo "Removal of $package failed"; exit 1; }
;;
"alpine")
apk del "$package" || { echo "Removal of $package failed"; exit 1; }
;;
"gentoo" | "funtoo" | "exherbo" | "sabayon" | "calculate" | "clear" | "solus" | "void" | "artix" )
emerge --unmerge "$package" || { echo "Removal of $package failed"; exit 1; }
;;
*)
echo "Unsupported Linux distribution: $DISTRO"
exit 1
;;
esac
else
echo "$package is not installed"
fi
done
;;
"clean")
case "$OS" in
"debian" | "ubuntu" | "mint")
apt-get autoremove -y && apt-get clean || { echo "Cleaning up failed"; exit 1; }
;;
"fedora")
dnf autoremove -y && dnf clean all || { echo "Cleaning up failed"; exit 1; }
;;
"arch")
pacman -Rns $(pacman -Qtdq) || { echo "Cleaning up failed"; exit 1; }
;;
"centos" | "rhel")
yum autoremove -y && yum clean all || { echo "Cleaning up failed"; exit 1; }
;;
"alpine")
apk autoremove && apk clean || { echo "Cleaning up failed"; exit 1; }
;;
"gentoo" | "funtoo" | "exherbo" | "sabayon" | "calculate" | "clear" | "solus" | "void" | "artix" )
emerge --depclean || { echo "Cleaning up failed"; exit 1; }
;;
*)
echo "Unsupported Linux distribution: $DISTRO"
exit 1
;;
esac
;;
"query_installed")
packages_len=${#packages_ref[@]}
for package in "${packages_ref[@]}"; do
case "$OS" in
"debian" | "ubuntu" | "mint")
if dpkg-query -W -f='${Status}' "$package" 2>/dev/null | grep -q "ok installed"; then
((packages_len--))
fi
;;
"fedora")
if rpm -q "$package" &>/dev/null ; then
((packages_len--))
fi
;;
"arch")
if pacman -Q "$package" &>/dev/null ; then
((packages_len--))
fi
;;
"centos" | "rhel")
if yum list installed "$package" &>/dev/null ; then
((packages_len--))
fi
;;
"alpine")
if apk info "$package" &>/dev/null ; then
((packages_len--))
fi
;;
"gentoo" | "funtoo" | "exherbo" | "sabayon" | "calculate" | "clear" | "solus" | "void" | "artix" )
if eix -I "$package" &>/dev/null ; then
((packages_len--))
fi
;;
*)
echo "Unsupported Linux distribution: $DISTRO"
exit 1
;;
esac
done
if (( packages_len == 0 )); then
return 0
else
return 1
fi
;;
"query_notinstalled")
packages_len=${#packages_ref[@]}
for package in "${packages_ref[@]}"; do
case "$OS" in
"debian" | "ubuntu" | "mint")
if ! dpkg-query -W -f='${Status}' "$package" 2>/dev/null | grep -q "ok installed"; then
((packages_len--))
fi
;;
"fedora")
if ! rpm -q "$package" &>/dev/null ; then
((packages_len--))
fi
;;
"arch")
if ! pacman -Q "$package" &>/dev/null ; then
((packages_len--))
fi
;;
"centos" | "rhel")
if ! yum list installed "$package" &>/dev/null ; then
((packages_len--))
fi
;;
"alpine")
if ! apk info "$package" &>/dev/null ; then
((packages_len--))
fi
;;
"gentoo" | "funtoo" | "exherbo" | "sabayon" | "calculate" | "clear" | "solus" | "void" | "artix" )
if ! eix -I "$package" &>/dev/null ; then
((packages_len--))
fi
;;
*)
echo "Unsupported Linux distribution: $DISTRO"
exit 1
;;
esac
done
if (( packages_len == 0 )); then
return 0
else
return 1
fi
;;
"query_available")
case "$OS" in
"debian" | "ubuntu" | "mint")
apt-cache search "${packages_ref[@]}" | grep -F -x -v -f <(printf '%s\n' "${packages_ref[@]}") && return 1 || return 0
;;
"fedora")
dnf search "${packages_ref[@]}" | grep -F -x -v -f <(printf '%s\n' "${packages_ref[@]}") && return 1 || return 0
;;
"arch")
pacman -Ss "${packages_ref[@]}" | grep -F -x -v -f <(printf '%s\n' "${packages_ref[@]}") && return 1 || return 0
;;
"centos" | "rhel")
yum search "${packages_ref[@]}" | grep -F -x -v -f <(printf '%s\n' "${packages_ref[@]}") && return 1 || return 0
;;
"alpine")
apk search "${packages_ref[@]}" | grep -F -x -v -f <(printf '%s\n' "${packages_ref[@]}") && return 1 || return 0
;;
"gentoo" | "funtoo" | "exherbo" | "sabayon" | "calculate" | "clear" | "solus" | "void" | "artix" )
eix "${packages_ref[@]}" | grep -F -x -v -f <(printf '%s\n' "${packages_ref[@]}") && return 1 || return 0
;;
*)
echo "Unsupported Linux distribution: $DISTRO"
exit 1
;;
esac
;;
"blacklist")
case "$OS" in
"debian" | "ubuntu" | "mint")
for package in "${packages_ref[@]}"; do
echo "Package: $package"
echo "Pin: release *"
echo "Pin-Priority: -1"
done >> /etc/apt/preferences.d/01autoremove-blacklist
;;
"fedora")
for package in "${packages_ref[@]}"; do
echo "exclude=$package" >> /etc/dnf/dnf.conf
done
;;
"arch")
for package in "${packages_ref[@]}"; do
echo "IgnorePkg=$package" >> /etc/pacman.conf
done
;;
"centos" | "rhel")
for package in "${packages_ref[@]}"; do
echo "exclude=$package" >> /etc/yum.conf
done
;;
"alpine")
for package in "${packages_ref[@]}"; do
echo "$package" >> /etc/apk/world
done
;;
"gentoo" | "funtoo" | "exherbo" | "sabayon" | "calculate" | "clear" | "solus" | "void" | "artix" )
for package in "${packages_ref[@]}"; do
echo "=$package" >> /etc/portage/package.mask
done
;;
*)
echo "Unsupported Linux distribution: $DISTRO"
exit 1
;;
esac
;;
"whitelist")
case "$OS" in
"debian" | "ubuntu" | "mint")
for package in "${packages_ref[@]}"; do
sed -i "/Package: $package/,+2d" /etc/apt/preferences.d/01autoremove-blacklist
done
;;
"fedora")
for package in "${packages_ref[@]}"; do
sed -i "/$package/d" /etc/dnf/dnf.conf
done
;;
"arch")
for package in "${packages_ref[@]}"; do
sed -i "/$package/d" /etc/pacman.conf
done
;;
"centos" | "rhel")
for package in "${packages_ref[@]}"; do
sed -i "/$package/d" /etc/yum.conf
done
;;
"alpine")
for package in "${packages_ref[@]}"; do
sed -i "/$package/d" /etc/apk/world
done
;;
"gentoo" | "funtoo" | "exherbo" | "sabayon" | "calculate" | "clear" | "solus" | "void" | "artix" )
for package in "${packages_ref[@]}"; do
sed -i "/$package/d" /etc/portage/package.mask
done
;;
*)
echo "Unsupported Linux distribution: $DISTRO"
exit 1
;;
esac
;;
*)
echo "Unknown command: $command"
exit 1
;;
esac
}
# ---------------------------------------------------------------------------------
# update_system: Updates the system
# ---------------------------------------------------------------------------------
# Arguments:
# None
#
# Returns:
# None
update_system() {
manage_packages update
manage_packages upgrade
manage_packages clean
}
# ---------------------------------------------------------------------------------
# install_build_deps: Installs additional build dependencies
# ---------------------------------------------------------------------------------
# Arguments:
# None
#
# Returns:
# None
#
install_build_deps() {
local distro_packages=()
case $OS in
"debian" | "ubuntu" | "mint")
distro_packages=(
build-essential wslu bc wget curl ca-certificates apt-utils apt-transport-https git gnupg2
make pkg-config libssl-dev g++ llvm clang lld zlib1g-dev libbz2-dev libreadline-dev
zip unzip cmake ninja-build gawk valgrind libgmp-dev libmpfr-dev libmpc-dev
ffmpeg imagemagick libopencv-dev libopencv-core-dev libopencv-highgui-dev
libboost-dev libboost-filesystem-dev libboost-regex-dev
coreutils jq openssl rsync diffutils zip binutils patchutils opus-tools sox
patch libtool m4 automake lcov htop tmux vim nano emacs-nox neovim
clang autoconf bison flex gperf texinfo help2man
bzip2 xz-utils gzip tar p7zip-full cpio
)
;;
"fedora")
distro_packages=(
@development-tools wslu bc wget curl ca-certificates git gnupg2
make pkg-config openssl-devel gcc-c++ llvm clang lld zlib-devel bzip2-devel readline-devel
zip unzip cmake ninja-build gawk valgrind gmp-devel mpfr-devel libmpc-devel
ffmpeg ImageMagick opencv-devel boost-devel
coreutils jq openssl rsync diffutils zip binutils patchutils opus-tools sox
patch libtool m4 automake lcov htop tmux vim nano emacs-nox neovim
clang autoconf bison flex gperf texinfo help2man
bzip2 xz gzip tar p7zip cpio
)
;;
"arch")
distro_packages=(
base-devel wslu bc wget curl ca-certificates git gnupg
make pkg-config openssl gcc llvm clang lld zlib bzip2 readline
zip unzip cmake ninja gawk valgrind gmp mpfr libmpc
ffmpeg imagemagick opencv boost
coreutils jq openssl rsync diffutils zip binutils patchutils opus-tools sox
patch libtool m4 automake lcov htop tmux vim nano emacs-nox neovim
clang autoconf bison flex gperf texinfo help2man
bzip2 xz gzip tar p7zip cpio
)
;;
"centos" | "rhel")
distro_packages=(
@development-tools wslu bc wget curl ca-certificates git gnupg2
make pkg-config openssl-devel gcc-c++ llvm clang lld zlib-devel bzip2-devel readline-devel
zip unzip cmake ninja-build gawk valgrind gmp-devel mpfr-devel libmpc-devel
ffmpeg ImageMagick opencv-devel boost-devel
coreutils jq openssl rsync diffutils zip binutils patchutils opus-tools sox
patch libtool m4 automake lcov htop tmux vim nano emacs-nox neovim
clang autoconf bison flex gperf texinfo help2man
bzip2 xz gzip tar p7zip cpio
)
;;
"alpine")
distro_packages=(
build-base wslu bc wget curl ca-certificates git gnupg
make pkgconf openssl-dev gcc llvm clang lld zlib bzip2 readline
zip unzip cmake ninja gawk valgrind gmp mpfr libmpc
ffmpeg imagemagick opencv boost
coreutils jq openssl rsync diffutils zip binutils patchutils opus-tools sox
patch libtool m4 automake lcov htop tmux vim nano emacs-nox neovim
clang autoconf bison flex gperf texinfo help2man
bzip2 xz gzip tar p7zip cpio
)
;;
"gentoo" | "funtoo" | "exherbo" | "sabayon" | "calculate" | "clear" | "solus" | "void" | "artix" )
distro_packages=(
wslu bc wget curl ca-certificates git gnupg
make pkgconf openssl gcc llvm clang lld zlib bzip2 readline
zip unzip cmake ninja gawk valgrind gmp mpfr libmpc
ffmpeg imagemagick opencv boost
coreutils jq openssl rsync diffutils zip binutils patchutils opus-tools sox
patch libtool m4 automake lcov htop tmux vim nano emacs-nox neovim
clang autoconf bison flex gperf texinfo help2man
bzip2 xz gzip tar p7zip cpio
)
;;
*)
echo "Unsupported Linux distribution: $DISTRO"
exit 1
;;
esac
manage_packages install "${distro_packages[@]}"
}
# ---------------------------------------------------------------------------------
# install_wslconf: Installs WSL utilities
install_anaconda() {
local ANACONDA_VERSION=${1:-2023.09-0}
local ANACONDA_DIR="/usr/local/anaconda3"
local ANACONDA_BUILD_DIR="$(mktemp -d)"
if [ -d "$ANACONDA_DIR" ]; then
echo "Anaconda3 is already installed"
return 0
fi
if [ -f /var/log/anaconda-build.log ]; then
rm -f /var/log/anaconda-build.log
fi
anaconda_packages=()
case "$OS" in
"debian" | "ubuntu" | "mint")
anaconda_packages=(
libgl1-mesa-glx libegl1-mesa libxrandr2 libxrandr2 libxss1 libxcursor1 libxcomposite1 libasound2 libxi6 libxtst6
)
;;
"fedora")
anaconda_packages=(
mesa-libGL mesa-libEGL libXrandr libXrandr libXScrnSaver libXcursor libXcomposite alsa-lib libXi libXtst
)
;;
"arch")
anaconda_packages=(
libglvnd libxrandr libxss libxcursor libxcomposite libxtst alsa-lib libxi
)
;;
"centos" | "rhel")
anaconda_packages=(
libXcomposite libXcursor libXi libXtst libXrandr alsa-lib mesa-libEGL libXdamage mesa-libGL libXScrnSaver
)
;;
"alpine")
anaconda_packages=(
mesa-gl mesa-egl libxrandr libxss libxcursor libxcomposite libxtst alsa-lib libxi
)
;;
"gentoo" | "funtoo" | "exherbo" | "sabayon" | "calculate" | "clear" | "solus" | "void" | "artix" )
anaconda_packages=(
mesa libxrandr libxss libxcursor libxcomposite libxtst alsa-lib libxi
)
;;
*)
echo "Unsupported Linux distribution: $DISTRO"
exit 1
;;
esac
manage_packages install "${anaconda_packages[@]}"
echo "Downloading Anaconda3 build script..."
{
mkdir -p "$ANACONDA_BUILD_DIR"
curl -S -L "https://repo.anaconda.com/archive/Anaconda3-${ANACONDA_VERSION}-Linux-x86_64.sh" -o "${ANACONDA_BUILD_DIR}/anaconda3-install.sh"
chmod +x "${ANACONDA_BUILD_DIR}/anaconda3-install.sh"
} 2>&1 | tee -a /var/log/anaconda-build.log
echo "Installing Anaconda3..."
{
pushd "${ANACONDA_BUILD_DIR}" || { echo "Failed to change directory to $ANACONDA_BUILD_DIR"; exit 1; }
bash ./anaconda3-install.sh -b -p "$ANACONDA_DIR" -f
popd || { echo "Failed to change directory back"; exit 1; }
rm -rfv "$ANACONDA_BUILD_DIR" || { echo "Failed to remove $ANACONDA_BUILD_DIR"; exit 1; }
} 2>&1 | tee -a /var/log/anaconda-build.log
if [ ! -d "$ANACONDA_DIR" ]; then
echo "E: Anaconda3 installation failed"
exit 1
fi
echo "Configuring anaconda3..." | tee -a /var/log/anaconda-build.log
{
if ! getent group python >/dev/null; then
groupadd python
fi
chown -R root:root "$ANACONDA_DIR"
chgrp -R python "$ANACONDA_DIR"
chmod -R 750 "$ANACONDA_DIR"
if [ ! -f /etc/profile.d/anaconda3.sh ]; then
echo "Creating /etc/profile.d/anaconda3.sh..."
cat <<EOF | tee /etc/profile.d/anaconda3.sh
export ANACONDA_DIR="$ANACONDA_DIR"
if id -nG "\$USER" | grep -qw "python"; then
export PATH="$ANACONDA_DIR/bin:\$PATH"
source $ANACONDA_DIR/bin/activate
fi
EOF
export PATH="$ANACONDA_DIR/bin:\$PATH"
source $ANACONDA_DIR/bin/activate
conda init
coda config --set auto_activate_base false
conda deactivate
fi
} 2>&1 | tee -a /var/log/anaconda-build.log
# Variables to unset
variables_to_unset=(
ANACONDA_DIR
ANACONDA_BUILD_DIR
)
for var in "${variables_to_unset[@]}"; do
unset "$var"
done
}
# ---------------------------------------------------------------------------------
# install_rust: Installs Rust and Cargo
# ---------------------------------------------------------------------------------
# Arguments:
# None
#
# Returns:
# None
#
install_rust() {
echo "Installing Rust..."
export TMP_RUST_DIR="$(mktemp -d)"
pushd "${TMP_RUST_DIR}"
curl -sLf "https://static.rust-lang.org/dist/rust-${RUST_VERSION}-x86_64-unknown-linux-gnu.tar.gz" | \
tar xvzf - -C "${TMP_RUST_DIR}" --strip-components=1 --exclude=rust-docs
./install.sh --verbose
popd
rm -rf "${TMP_RUST_DIR}"
}
# ---------------------------------------------------------------------------------
# install_poetry: Installs Poetry
# ---------------------------------------------------------------------------------
# Arguments:
# None
#
# Returns:
# None
#
install_poetry() {
echo "Installing Poetry..."
curl -sSL https://install.python-poetry.org | python3 - >&1
local ntusername=$(basename "$(wslpath $(wslvar USERPROFILE))")
poetry completions bash >> /home/$ntusername/.bash_completion
chown $ntusername:$ntusername /home/$ntusername/.bash_completion
EOF
}
# ---------------------------------------------------------------------------------
# install_openssl: Installs OpenSSL
# ---------------------------------------------------------------------------------
# Arguments:
# None
#
# Returns:
# None
#
install_openssl() {
local OPENSSL_VERSION=${1:-3.1.3}
export OPENSSL_BUILD_DIR="$(mktemp -d)"
export NPROC=$(nproc)
# check installed version doesn't match requested version
if [ "$(openssl version | cut -d' ' -f2)" == "$OPENSSL_VERSION" ]; then
echo "OpenSSL $OPENSSL_VERSION is already installed"
return 0
fi
echo "Installing OpenSSL..."
echo "Download OpenSSL" | tee -a /var/log/openssl-build.log
{
openssl_tar=$(mktemp)
wget -O "$openssl_tar" --no-check-certificate https://www.openssl.org/source/openssl-$OPENSSL_VERSION.tar.gz
tar --extract --gzip --directory "$OPENSSL_BUILD_DIR" --strip-components=1 --file "$openssl_tar"
rm -f "$openssl_tar"
} 2>&1 | tee -a /var/log/openssl-build.log
echo "Building OpenSSL" | tee -a /var/log/openssl-build.log
{
pushd "$OPENSSL_BUILD_DIR" || { echo "Failed to change directory to $OPENSSL_BUILD_DIR"; exit 1; }
./config --prefix=/usr/local/ssl --openssldir=/usr/local/ssl --libdir=lib -shared
make -j "$NPROC"
make uninstall
make install
popd || { echo "Failed to change directory back"; exit 1; }
rm -rf "$OPENSSL_BUILD_DIR"
} 2>&1 | tee -a /var/log/openssl-build.log
variables_to_unset=(
OPENSSL_BUILD_DIR
NPROC
)
for var in "${variables_to_unset[@]}"; do
unset "$var"
done
}
# ---------------------------------------------------------------------------------
# install_python: Installs Python and PIP
# ---------------------------------------------------------------------------------
# Arguments:
# None
#
# Returns:
# None
#
install_pyenv() {
local PYTHON_VERSION=${1:-3.11.6}
local PYENV_DIR="/usr/local/pyenv"
export LANG=C.UTF-8
export DEBIAN_FRONTEND=noninteractive
export NPROC=$(nproc)
if [ -f /usr/local/pyenv/bin/pyenv ]; then
echo "Pyenv is already installed"
return 0
fi
if [ -f /var/log/python-build.log ]; then
rm -f /var/log/python-build.log
touch /var/log/python-build.log
fi
echo "Installing python build dependencies..." | tee -a /var/log/python-build.log
{
case "$OS" in
"debian" | "ubuntu" | "mint")
python_deps=(
dpkg-dev dpkg-sig gpgv2 equivs
libbz2-dev libexpat1-dev libffi-dev liblzma-dev libncursesw5-dev
zlib1g-dev libbluetooth-dev libncurses-dev libz-dev tk-dev libsqlite3-dev
libreadline-dev libgdm-dev libgdbm-compat-dev llvm libssl-dev
)
;;
"fedora")
python_deps=(
rpm-build rpm-sign gnupg2
bzip2-devel expat-devel libffi-devel lzma-devel ncurses-devel
zlib-devel bluez-libs-devel ncurses-devel zlib-devel tk-devel sqlite-devel
readline-devel gdbm-devel llvm libssl-devel
)
;;
"arch")
python_deps=(
base-devel gnupg
bzip2 expat libffi xz ncurses zlib tk sqlite readline gdbm llvm libssl
)
;;
"centos" | "rhel")
python_deps=(
rpm-build rpm-sign gnupg2
bzip2-devel expat-devel libffi-devel lzma-devel ncurses-devel
zlib-devel bluez-libs-devel ncurses-devel zlib-devel tk-devel sqlite-devel
readline-devel gdbm-devel llvm libssl-devel
)
;;
"alpine")
python_deps=(
build-base gnupg
bzip2-dev expat-dev libffi-dev xz-dev ncurses-dev zlib-dev tk-dev sqlite-dev
readline-dev gdbm-dev llvm libssl-dev
)
;;
"gentoo" | "funtoo" | "exherbo" | "sabayon" | "calculate" | "clear" | "solus" | "void" | "artix" )
python_deps=(
gnupg
bzip2 expat libffi xz ncurses zlib tk sqlite readline gdbm llvm libssl
)
;;
*)
echo "Unsupported Linux distribution: $DISTRO"
exit 1
;;
esac
manage_packages update
manage_packages install "${python_deps[@]}"
manage_packages clean
} 2>&1 | tee -a /var/log/python-build.log
# echo "Downloading and verifying Python source..." | tee -a /var/log/python-build.log
# {
# python_tar=$(mktemp)
# python_asc=$(mktemp)
# wget -O "$python_tar" "https://www.python.org/ftp/python/${PYTHON_VERSION%%[a-z]*}/Python-$PYTHON_VERSION.tar.xz"
# wget -O "$python_asc" "https://www.python.org/ftp/python/${PYTHON_VERSION%%[a-z]*}/Python-$PYTHON_VERSION.tar.xz.asc"
# GNUPGHOME="$(mktemp -d)"
# export GNUPGHOME
# gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys "$GPG_KEY"
# gpg --batch --verify "$python_asc" "$python_tar"
# gpgconf --kill all
# rm -rf $GNUPGHOME
# tar --extract --directory "$PYTHON_BUILD_DIR" --strip-components=1 --file "$python_tar"
# rm -f "$python_tar" "$python_asc"
# for src in idle2 pydoc2 python2 python2-config idle3 pydoc3 python3 python3-config; do
# for ver in 2 3; do
# dst="$(echo "$src" | tr -d $ver)"
# [ -s "/usr/local/bin/$src" ] && [ -e "/usr/local/bin/$dst" ] && \
# rm -v "/usr/local/bin/$dst"
# done
# done
# } 2>&1 | tee -a /var/log/python-build.log
# echo "Building Python..." | tee -a /var/log/python-build.log
# {
# pushd "$PYTHON_BUILD_DIR" || { echo "Failed to change directory to $PYTHON_BUILD_DIR"; exit 1; }
# gnuArch="$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)"; \
# ./configure \
# --build="$gnuArch" \
# --enable-loadable-sqlite-extensions \
# --enable-optimizations \
# --enable-option-checking=fatal \
# --enable-shared \
# --with-lto \
# --with-system-expat \
# --without-ensurepip \
# --prefix=/usr/local \
# --with-openssl=/usr/local/ssl \
# --with-openssl-rpath=/usr/local/lib \
# --with-ssl-default-suites=openssl \
# ; \
# nproc="$(nproc)"; \
# EXTRA_CFLAGS="$(dpkg-buildflags --get CFLAGS)"; \
# LDFLAGS="$(dpkg-buildflags --get LDFLAGS)"; \
# make -j "$nproc" \
# "EXTRA_CFLAGS=${EXTRA_CFLAGS:-}" \
# "LDFLAGS=${LDFLAGS:-}" ; \
# rm python; \
# make -j "$nproc" \
# "EXTRA_CFLAGS=${EXTRA_CFLAGS:-}" \
# "LDFLAGS=${LDFLAGS:--Wl},-rpath='\$\$ORIGIN/../lib'" \
# python \
# ; \
# make install
# popd || { echo "Failed to change directory back"; exit 1; }
# } 2>&1 | tee -a /var/log/python-build.log
# echo "Configuring installation..." | tee -a /var/log/python-build.log
# {
# pushd "$PYTHON_BUILD_DIR" || { echo "Failed to change directory to $PYTHON_BUILD_DIR"; exit 1; }
# bin="$(readlink -ve /usr/local/bin/python$PYTHON_MAJOR_VERSION)"
# dir="$(dirname "$bin")"
# mkdir -p "/usr/share/gdb/auto-load/$dir"
# cp -vL Tools/gdb/libpython.py "/usr/share/gdb/auto-load/$bin-gdb.py"
# popd || { echo "Failed to change directory back"; exit 1; }
# rm -rf "$PYTHON_BUILD_DIR"
# find /usr/local/bin -depth \
# \( \
# \( -type d -a \( -name test -o -name tests -o -name idle_test \) \) \
# -o \( -type f -a \( -name '*.pyc' -o -name '*.pyo' -o -name 'libpython*.a' \) \) \
# \) -exec rm -rf '{}' +
# ldconfig
# for src in idle$PYTHON_MAJOR_VERSION pydoc$PYTHON_MAJOR_VERSION python$PYTHON_MAJOR_VERSION python$PYTHON_MAJOR_VERSION-config; do
# dst="$(echo "$src" | tr -d $PYTHON_MAJOR_VERSION)"
# if [ -s "/usr/local/bin/$src" ]; then
# if [ -e "/usr/local/bin/$dst" ]; then
# echo "Removing existing symlink: /usr/local/bin/$dst"
# rm "/usr/local/bin/$dst"
# fi
# echo "Creating new symlink: /usr/local/bin/$dst -> $src"
# ln -svT "$src" "/usr/local/bin/$dst"
# fi
# done
# } 2>&1 | tee -a /var/log/python-build.log
# echo "Installing and configuring pip..." | tee -a /var/log/python-build.log
# {
# pushd "$PIP_BUILD_DIR" || { echo "Failed to change directory to $PIP_BUILD_DIR"; exit 1; }
# wget -O get-pip.py "$PYTHON_GET_PIP_URL"
# echo "$PYTHON_GET_PIP_SHA256 *get-pip.py" | sha256sum -c -
# export PYTHONDONTWRITEBYTECODE=1
# python get-pip.py --disable-pip-version-check --no-cache-dir --no-compile "pip==$PYTHON_PIP_VERSION" "setuptools==$PYTHON_SETUPTOOLS_VERSION"
# rm -f get-pip.py
# python -m pip install --disable-pip-version-check --no-cache-dir --no-compile --upgrade pip setuptools wheel
# popd || { echo "Failed to change directory back"; exit 1; }
# rm -rf "$PIP_BUILD_DIR"
# } 2>&1 | tee -a /var/log/python-build.log
if [[ ! -d "$PYENV_DIR" ]]; then
git clone https://github.com/pyenv/pyenv.git "$PYENV_DIR"
if ! getent group python >/dev/null; then
groupadd python
fi
chgrp -R python "$PYENV_DIR"
chmod -R 750 "$PYENV_DIR"
fi
if [[ ! -f "/etc/profile.d/pyenv.sh" ]]; then
echo "Creating /etc/profile.d/pyenv.sh..."
cat <<EOF | tee /etc/profile.d/pyenv.sh
if id -nG "\$USER" | grep -qw "python"; then
export PYENV_ROOT="$PYENV_DIR"
export PATH="\$PYENV_ROOT/bin:\$PATH"
eval "\$(\$PYENV_ROOT/bin/pyenv init --path)"
eval "\$(\$PYENV_ROOT/bin/pyenv init -)"
fi
EOF
fi
{
gnuArch="$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)"
CONFIGURE_OPTS="--build=$gnuArch --enable-loadable-sqlite-extensions --enable-optimizations --enable-option-checking=fatal --enable-shared --with-lto --with-system-expat --with-ensurepip --with-openssl=/usr/local/ssl --with-openssl-rpath=/usr/local/lib --with-ssl-default-suites=openssl"
EXTRA_CFLAGS="$(dpkg-buildflags --get CFLAGS)"
LDFLAGS="$(dpkg-buildflags --get LDFLAGS)"
export PYENV_ROOT="$PYENV_DIR"
export PATH="$PYENV_ROOT/bin:$PATH"
/usr/local/pyenv/bin/pyenv install -v $PYTHON_VERSION
} 2>&1 | tee -a /var/log/python-build.log
variables_to_unset=(
PYTHON_MAJOR_VERSION
GPG_KEY
PYTHON_GET_PIP_URL
PYTHON_GET_PIP_SHA256
PYTHON_PIP_VERSION
PYTHON_SETUPTOOLS_VERSION
LANG
DEBIAN_FRONTEND
PYTHON_BUILD_DIR
PIP_BUILD_DIR
NPROC
)
for var in "${variables_to_unset[@]}"; do
unset "$var"
done
}
# ---------------------------------------------------------------------------------
# install_cuda_cudnn: Installs CuDUNN runtime / libraries and / or CUDA SDK
# ---------------------------------------------------------------------------------
# Arguments:
# None
#
# Returns:
# None
#
#
# Notes:
#
# This is not gaurenteed to work in Gentoo or other compiler based distros
# as the CUDA SDK is not yet supported on those distros. Providing support
# for those distros is on the TODO list, if users are interested, comment
# working instructions for those distros, and they will be added by the
# script maintainer.
#
install_cuda_cudnn() {
local CUDA_VERSION=${1:-11.7}
local CUDNN_VERSION=${2:-8.0}
case "$OS" in
"ubuntu")
. /etc/os-release
UBUNTU_VERSION=$VERSION_ID
UBUNTU_VERSION=${UBUNTU_VERSION//./}
nvidia_deps=(
gnupg2 software-properties-common wget curl ca-certificates git
)
manage_packages install "${nvidia_deps[@]}"
if [ ! -f /etc/apt/preferences.d/cuda-repository-pin-600 ]; then
wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu${UBUNTU_VERSION}/x86_64/cuda-ubuntu${UBUNTU_VERSION}.pin
mv cuda-ubuntu${UBUNTU_VERSION}.pin /etc/apt/preferences.d/cuda-repository-pin-600
wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu${UBUNTU_VERSION}/x86_64/cuda-keyring_1.1-1_all.deb
dpkg -i cuda-keyring_1.1-1_all.deb
rm -f cuda-keyring_1.1-1_all.deb
add-apt-repository -y "deb http://developer.download.nvidia.com/compute/cuda/repos/ubuntu${UBUNTU_VERSION}/x86_64/ /"
# add-apt-repository -y "deb http://developer.download.nvidia.com/compute/machine-learning/repos/ubuntu${UBUNTU_VERSION}/x86_64/ /"
manage_packages update
fi
;;
"debian")
. /etc/os-release
DEBIAN_VERSION=$VERSION_ID
DEBIAN_VERSION=${DEBIAN_VERSION//./}
nvidia_deps=(
gnupg2 software-properties-common wget curl ca-certificates git
)
manage_packages install "${nvidia_deps[@]}"
if [ ! -f /etc/apt/preferences.d/cuda-repository-pin-600 ]; then
wget https://developer.download.nvidia.com/compute/cuda/repos/debian${DEBIAN_VERSION}/x86_64/cuda-debian${DEBIAN_VERSION}.pin
mv cuda-debian${DEBIAN_VERSION}.pin /etc/apt/preferences.d/cuda-repository-pin-600
wget https://developer.download.nvidia.com/compute/cuda/repos/debian${DEBIAN_VERSION}/x86_64/cuda-keyring_1.1-1_all.deb
dpkg -i cuda-keyring_1.1-1_all.deb
rm -f cuda-keyring_1.1-1_all.deb
add-apt-repository -y "deb http://developer.download.nvidia.com/compute/cuda/repos/debian${DEBIAN_VERSION}/x86_64/ /"
# add-apt-repository -y "deb http://developer.download.nvidia.com/compute/machine-learning/repos/debian${DEBIAN_VERSION}/x86_64/ /"
apt-get update
fi
;;
"fedora")
nvidia_deps=(
gnupg2 wget curl ca-certificates git
)
manage_packages install "${nvidia_deps[@]}"
if [ ! -f /etc/yum.repos.d/cuda.repo ]; then
wget https://developer.download.nvidia.com/compute/cuda/repos/fedora32/x86_64/cuda-fedora32.repo
mv cuda-fedora32.repo /etc/yum.repos.d/cuda.repo
rpm --import https://developer.download.nvidia.com/compute/cuda/repos/fedora32/x86_64/7fa2af80.pub
wget https://developer.download.nvidia.com/compute/machine-learning/repos/fedora32/x86_64/nvidia-machine-learning-repo-fedora32-1.0.0-1.x86_64.rpm
# rpm -i nvidia-machine-learning-repo-fedora32-1.0.0-1.x86_64.rpm
# rm -f nvidia-machine-learning-repo-fedora32-1.0.0-1.x86_64.rpm
dnf update
fi
;;
"arch")
nvidia_deps=(
gnupg2 wget curl ca-certificates git
)
manage_packages install "${nvidia_deps[@]}"
if [ ! -f /etc/pacman.d/cuda-repo.conf ]; then
wget https://developer.download.nvidia.com/compute/cuda/repos/archlinux/x86_64/cuda-archlinux.pin
mv cuda-archlinux.pin /etc/pacman.d/cuda-repo.conf
pacman -Syu
fi
;;
"centos" | "rhel")
nvidia_deps=(
gnupg2 wget curl ca-certificates git
)
manage_packages install "${nvidia_deps[@]}"
if [ ! -f /etc/yum.repos.d/cuda.repo ]; then
wget https://developer.download.nvidia.com/compute/cuda/repos/rhel8/x86_64/cuda-rhel8.repo
mv cuda-rhel8.repo /etc/yum.repos.d/cuda.repo
rpm --import https://developer.download.nvidia.com/compute/cuda/repos/rhel8/x86_64/7fa2af80.pub
# wget https://developer.download.nvidia.com/compute/machine-learning/repos/rhel8/x86_64/nvidia-machine-learning-repo-rhel8-1.0.0-1.x86_64.rpm
# rpm -i nvidia-machine-learning-repo-rhel8-1.0.0-1.x86_64.rpm
# rm -f nvidia-machine-learning-repo-rhel8-1.0.0-1.x86_64.rpm
yum update
fi
;;
"alpine")
nvidia_deps=(
gnupg2 wget curl ca-certificates git
)
manage_packages install "${nvidia_deps[@]}"
if [ ! -f /etc/apk/repositories ]; then
echo "http://dl-cdn.alpinelinux.org/alpine/edge/main" >> /etc/apk/repositories
echo "http://dl-cdn.alpinelinux.org/alpine/edge/community" >> /etc/apk/repositories
echo "http://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories
apk update
fi
;;
"gentoo" | "funtoo" | "exherbo" | "sabayon" | "calculate" | "clear" | "solus" | "void" | "artix" )
nvidia_deps=(
gnupg2 wget curl ca-certificates git
)
manage_packages install "${nvidia_deps[@]}"
# TODO: Add Gentoo support (https://wiki.gentoo.org/wiki/NVidia/nvidia-drivers)
;;
*)
echo "Unsupported Linux distribution: $DISTRO" >&2
exit 1
;;
esac
nvidia_deps=()
if [ -n "$CUDA_VERSION" ]; then
case "$OS" in
"ubuntu" | "debian" | "fedora" | "centos" | "rhel" | "alpine")
nvidia_deps+=(
cuda-$(convert_version_format "$CUDA_VERSION" "two-part")
)
;;
"arch")
nvidia_deps+=(
cuda-$(convert_version_format "$CUDA_VERSION" "two-part")
)
;;
"gentoo" | "funtoo" | "exherbo" | "sabayon" | "calculate" | "clear" | "solus" | "void" | "artix" )
nvidia_deps+=(
nvidia-cuda-toolkit
)
# TODO: Add Gentoo support
;;
*)
echo "Unsupported Linux distribution: $DISTRO" >&2
exit 1
;;
esac
fi
if [ -n "$CUDNN_VERSION" ]; then
case "$OS" in
"ubuntu" | "debian" | "fedora" | "centos" | "rhel" | "alpine")
nvidia_deps+=(
libcudnn$(convert_version_format "$CUDNN_VERSION" "major") libcudnn$(convert_version_format "$CUDNN_VERSION" "major")-dev
)
;;
"arch")
nvidia_deps+=(
libcudnn$(convert_version_format "$CUDNN_VERSION" "major") libcudnn$(convert_version_format "$CUDNN_VERSION" "major")-dev
)
;;
"gentoo" | "funtoo" | "exherbo" | "sabayon" | "calculate" | "clear" | "solus" | "void" | "artix" )
nvidia_deps+=(
nvidia-cuda-toolkit
)
# TODO: Add Gentoo support
;;
*)
echo "Unsupported Linux distribution: $DISTRO" >&2
exit 1
;;
esac
fi
if [[ "${#nvidia_deps[@]}" -gt 0 ]]; then
manage_packages install "${nvidia_deps[@]}"
fi
}
# ---------------------------------------------------------------------------------
# install_docker: Installs Docker
# ---------------------------------------------------------------------------------
# Arguments:
# None
#
# Returns:
# None
#
# Notes:
#
# Full Gento / compiler based distro support is not yet implemented.
# If you are using a Gentoo based distro, you will need to install Docker
# manually. Providing working instructions can be helpful to the script author
# in adding support for your compiler-based distro.
#
install_docker() {
case "$OS" in
"ubuntu" | "debian" | "mint")
export DEBIAN_FRONTEND=noninteractive
if [ ! -f /etc/apt/keyrings/docker.gpg ]; then
curl -fsSL https://download.docker.com/linux/$OS/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
chmod a+r /etc/apt/keyrings/docker.gpg
fi
if [ ! -f /etc/apt/sources.list.d/docker.list ]; then
echo "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
"$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
tee /etc/apt/sources.list.d/docker.list > /dev/null
fi
export DEBIAN_FRONTEND=
;;
"fedora")
if [ ! -f /etc/yum.repos.d/docker-ce.repo ]; then
curl -fsSL https://download.docker.com/linux/$OS/gpg | gpg --dearmor -o /etc/yum.repos.d/docker-ce.repo
chmod a+r /etc/yum.repos.d/docker-ce.repo
yum-config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repo
fi
;;
"arch")
if [ ! -f /etc/pacman.d/docker-ce.gpg ]; then
curl -fsSL https://download.docker.com/linux/$OS/gpg | gpg --dearmor -o /etc/pacman.d/docker-ce.gpg
chmod a+r /etc/pacman.d/docker-ce.gpg
{
echo "[docker]";
echo "SigLevel = PackageRequired";
echo "Server = https://download.docker.com/linux/arch/\$arch";
echo "";
} >> /etc/pacman.conf
fi
;;
"centos" | "rhel")
if [ ! -f /etc/yum.repos.d/docker-ce.repo ]; then
curl -fsSL https://download.docker.com/linux/$OS/gpg | gpg --dearmor -o /etc/yum.repos.d/docker-ce.repo
chmod a+r /etc/yum.repos.d/docker-ce.repo
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
fi
;;
"alpine")
# TODO: Add alipine support
;;
"gentoo" | "funtoo" | "exherbo" | "sabayon" | "calculate" | "clear" | "solus" | "void" | "artix" )
# TODO: Add Gentoo support
;;
*)
echo "Unsupported Linux distribution: $DISTRO"
exit 1
;;
esac
if manage_packages query_notinstalled docker-ce; then
manage_packages update
docker_packages=( docker-ce docker-ce-cli containerd docker-compose )
manage_packages install "${docker_packages[@]}"
usermod -aG docker $USER
fi
}
# ---------------------------------------------------------------------------------
# install_nvidia_container_toolkit: Installs NVIDIA Container Toolkit
# ---------------------------------------------------------------------------------
# Arguments:
# None
#
# Returns:
# None
#
# Notes:
#
# Not implemented for Gentoo or other compiler based distros yet.
# Not implemented for Alpine yet.
#
# If you want to see this change, please provide working instructions
# for your distro, and they will be added by the script maintainer.
#
install_nvidia_container_toolkit() {
case "$OS" in
"ubuntu" | "debian" | "mint")
if [ ! -f /etc/apt/keyrings/nvidia-container-toolkit.gpg ]; then
curl -fsSL https://nvidia.github.io/nvidia-docker/gpgkey | gpg --dearmor -o /etc/apt/keyrings/nvidia-container-toolkit.gpg
chmod a+r /etc/apt/keyrings/nvidia-container-toolkit.gpg
fi
if [ ! -f /etc/apt/sources.list.d/nvidia-container-toolkit.list ]; then
curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list |
sed 's#deb https://#deb [signed-by=/etc/apt/keyrings/nvidia-container-toolkit.gpg] https://#g' |
tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
manage_packages update
fi
;;
"fedora")
if [ ! -f /etc/yum.repos.d/nvidia-container-toolkit.repo ]; then
curl -fsSL https://nvidia.github.io/nvidia-docker/$(. /etc/os-release; echo "$ID")/nvidia-docker.repo | \
tee /etc/yum.repos.d/nvidia-container-toolkit.repo
manage_packages update
fi
;;
"arch")
if [ ! -f /etc/pacman.d/nvidia-container-toolkit.conf ]; then
curl -fsSL https://nvidia.github.io/nvidia-docker/arch/$ARCH/nvidia-container-toolkit.conf | \
tee /etc/pacman.d/nvidia-container-toolkit.conf
manage_packages update
fi
;;
"centos" | "rhel")
if [ ! -f /etc/yum.repos.d/nvidia-container-toolkit.repo ]; then
curl -fsSL https://nvidia.github.io/nvidia-docker/$(. /etc/os-release; echo "$ID")/nvidia-docker.repo | \
tee /etc/yum.repos.d/nvidia-container-toolkit.repo
manage_packages update
fi
;;
"alpine")
echo "Nothing to do for Apline (not implemented)"
;;
"gentoo" | "funtoo" | "exherbo" | "sabayon" | "calculate" | "clear" | "solus" | "void" | "artix" )
echo "Nothing to do for Gentoo (not implemented)"
;;
*)
echo "Unsupported Linux distribution: $DISTRO"
exit 1
;;
esac
if manage_packages query_notinstalled nvidia-container-toolkit; then
manage_packages install nvidia-container-toolkit
nvidia-ctk runtime configure --runtime=docker
systemctl restart docker
fi
}
# ---------------------------------------------------------------------------------
# configure_wsl: Configures WSL2
# ---------------------------------------------------------------------------------
# Arguments:
# None
#
# Returns:
# None
#
configure_wsl() {
local wsl_packages=( wslu dos2unix )
manage_packages install "${wsl_packages[@]}"
local userprofile=$(wslpath -au "$(wslvar USERPROFILE)")
local default_user=$(wslvar USERNAME)
local hostname="${default_user^^}-PC"
local swap_path=$(wslpath -au "$(wslvar LOCALAPPDATA)")/WSL2
[ ! -d "$swap_path" ] && mkdir -p "$swap_path"
local ram_size=$(free -m | awk '/^Mem:/{print $2}')
local swap_size=0
if [ "$ram_size" -lt 2048 ]; then
swap_size=$(($ram_size * 2))
else
swap_size=$ram_size
fi
swap_path=$(echo $(wslpath -aw "$swap_path")\\swap.vhdx | sed 's/\\/\\\\/g')
cat >/etc/wsl.conf <<EOF
[boot]
systemd=true
[automount]
enabled = true
root = /mnt/
options = "metadata,uid=1000,gid=1000,umask=077,fmask=11,case=off"
mountFsTab = true
[network]
hostname = $hostname
generateHosts = true
generateResolvConf = true
[user]
default = $default_user
[interop]
enabled = true
appendWindowsPath = false
EOF
cat >"$userprofile/.wslconfig" <<EOF
[wsl2]
memory = ${ram_size}MB
localhostForwarding = true
swap = ${swap_size}MB
swapFile = $swap_path
guiApplications = true
nestedVirtualization = true
vmIdleTimeout = 120000
EOF
unix2dos "$userprofile/.wslconfig" 2>/dev/null
echo "WSL2 .wslconfig updated."
}
# Main execution
parse_args "$@"
if [[ $INITSYS == "true" ]]; then
echo "Initializing system..."
init_system "$INIT_USERNAME" "$INIT_PASSWORD"
fi
if [[ $SYSTEM_UPDATE == "true" ]]; then
echo "Updating system..."
update_system
fi
if [[ $BUILD_DEPS == "true" ]]; then
echo "Installing build dependencies..."
install_build_deps
fi
if [[ $WSL == "true" ]]; then
echo "Configuring WSL2..."
configure_wsl
fi
if [[ -n $OPENSSL_VERSION ]]; then
echo "Installing OpenSSL..."
install_openssl "$OPENSSL_VERSION"
fi
if [[ -n $PYTHON_VERSION ]]; then
echo "Installing Python and PIP..."
install_pyenv "$PYTHON_VERSION"
fi
if [[ -n $ANACONDA_VERSION ]]; then
echo "Installing Anaconda..."
install_anaconda "$ANACONDA_VERSION"
fi
if [[ -n $CUDA_VERSION || -n $CUDNN_VERSION ]]; then
echo "Installing CUDA and cuDNN..."
install_cuda_cudnn "$CUDA_VERSION" "$CUDNN_VERSION"
fi
if [[ $DOCKER == "true" ]]; then
echo "Installing Docker..."
install_docker
fi
if [[ $NVCK == "true" ]]; then
echo "Installing NVIDIA Container Toolkit..."
install_nvidia_container_toolkit
fi
if [[ $RUST == "true" ]]; then
echo "Installing Rust..."
install_rust
fi
if [[ $POETRY == "true" ]]; then
echo "Installing Poetry..."
install_poetry
fi
exit 0
'@
# Load defaults
if (-not $Distro) {
$Distro = "ubuntu-22.04"
}
if (-not $WSLRoot) {
$WSLRoot = $env:LOCALAPPDATA + '\WSL2'
}
# Ensure only one of actions
if (($Register, $Unregister, $Import, $Export, $Check, $Configure, $Shutdown, $Startup) -contains $true) {
$actionCount = 0
if ($Register) { $actionCount++ }
if ($Unregister) { $actionCount++ }
if ($Import) { $actionCount++ }
if ($Export) { $actionCount++ }
if ($Check) { $actionCount++ }
if ($Configure) { $actionCount++ }
if ($Shutdown) { $actionCount++ }
if ($Startup) { $actionCount++ }
if ($actionCount -gt 1) {
Write-Error "E: Only one action can be specified."
exit 1
}
}
if ($Register) {
$result = Register-WSL -Distro $Distro
exit
}
if ($Unregister) {
$result = Unregister-WSL -Distro $Distro
exit
}
if ($Import) {
$result = Import-WSL -Distro $Distro -WSLRoot $WSLRoot
exit
}
if ($Export) {
$result = Export-WSL -Distro $Distro -WSLRoot $WSLRoot
exit
}
if ($Configure) {
$result = Configure-WSL -Distro $Distro `
-UpdateWSLConfig:$UpdateWSLConfig -Username:$Username `
-Password:$Password -UpdateSystem:$UpdateSystem `
-InstallBuildDependencies:$InstallBuildDependencies `
-InstallCudaVersion:$InstallCudaVersion `
-InstallCudnnVersion:$InstallCudnnVersion `
-InstallPythonVersion:$InstallPythonVersion `
-InstallOpenSSLVersion:$InstallOpenSSLVersion `
-InstallRust:$InstallRust -InstallPoetry:$InstallPoetry `
-InstallDocker:$InstallDocker -InstallNVCT:$InstallNVCT `
-InstallAnacondaVersion:$InstallAnacondaVersion `
-ShowHelp:$ShowHelp -DebugMode:$DebugMode
if ($result -ne $true) {
exit 1
}
exit
}
if ($Startup) {
$result = Startup-WSL -Distro $Distro
exit
}
if ($Shutdown) {
$result = Shutdown-WSL -Distro $Distro
exit
}
if ($Check) {
if ($(Check-WSL -Distro $Distro -WSLVersion 2 -IsRunning $true) -eq $true) {
Write-Host "WSL distribution '$Distro' is running."
exit 0
} else {
if ($(Check-WSL -Distro $Distro -WSLVersion 2 -IsRunning $false) -eq $true) {
Write-Host "WSL distribution '$Distro' is not running."
exit 1
} else {
Write-Host "WSL distribution '$Distro' is not installed."
exit 2
}
}
exit
}
exit
@loopyd
Copy link
Author

loopyd commented Nov 18, 2023

Dei G. E. A. R. - Grossly Enormous Automated Runner

Welcome

To the automated WSL bootstrapper. I doubt you've ever seen a single file solution to running python code everywhere. This is it

Installing

  1. Download the script as RAW and save it as a file with a .ps1 extension, in LF Linux line-ending format.

  2. Install PowerShell 7 (this will be automated in the future and auto-elevation will happen, for now that's manual)

  3. Give yourself execution rights from PowerShell 7 command prompt and change to the directory where you downloaded the tool.

  4. Reference the example below to use the tool. It makes managing your Windows Subsystem for Linux installation a lot easier. It provides an unattended solution to normally lengthy processes required to set up functioning development environments in WSL.

Example:

Create an ubuntu-22.04 distribution, configure it, export it to a tarball, then re-import it to commit the filesystem to disk.

The -Configure action in this example performs the following instructions:

  • Update the system
  • Install build dependencies
  • Write .wslconfig and /etc/wsl.conf taylored to available resources on system.
  • Install CUDA runtime/development kit version 11.7
  • Install CuDUNN runtime/development kit version 8.0
  • Install Docker
  • Install NVIDIA Container Toolkit
  • Compile Python version 3.11.6 with all frozen modules.
  • Update the OpenSSL binary on the system, to 3.1.3
  • Install the updated binaries as the system versions.
  • Make a GECOS username that matches the username on the NT host and configure a password for the user account: password12345!. Add this user to the sudoers group and the sudoers file, and assign it the UUID 1000
.\wsl2-prep.ps1 -Distro 'ubuntu-22.04' -Register
.\wsl2-prep.ps1 -Distro "ubuntu-22.04" -Configure -UpdateSystem -UpdateWSLConfig -InstallBuildDependencies -InstallCudaVersion "11.7" -InstallPythonVersion "3.11.6" -InstallOpenSSLVersion "3.1.3" -InstallCudnnVersion "8.0" -InstallDocker -InstallNVCT -Username "$env:USERNAME" -Password "password12345!" 
.\wsl2-prep.ps1 -Distro "ubuntu-22.04" -Shutdown
.\wsl2-prep.ps1 -Distro "ubuntu-22.04" -Export "$env:LOCALAPPDATA\WSL2"
.\wsl2-prep.ps1 -Distro "ubuntu-22.04" -Import "$env:LOCALAPPDATA\WSL2"
.\wsl2-prep.ps1 -Distro "ubuntu-22.04" -Startup

Changelog

1.0.0 - Initial release

  • This is the initial release of the tool.

1.0.1 - Quality-of-life fixes/polish

  • More robust argument type checking in Configure-WSL.
  • Change -Help flag to -ShowHelp to avoid conflict with PS Builtin -Help switch
  • Add -DebugMode flag that allows tool to work with locally saved copy of internal WSL configuration bash script (for testing and debug purposes)
  • Add HelpMessage text for each of the script arguments to make them easier to understand for users.

1.0.2 - The distrofork

  • Addition of multidistrobution execution forking (planned distro support in code comments)
  • Redaction of original python build script to use pyenv instead, where when user is added to the python group, they get access.
  • Removal of pip and setuptools version specification as pyenv installs these automatically for the user.
  • Logic tweaks to avoid rebuilding/reinstalling some things and messing up the system while doing so (sanity checking)
  • Package manager querying functions in manage_packages: query_installed, query_notinstalled, query_available

Beta Options

These features are still in beta.

🪳Please use these options with caution as they have been known to have some issues currently. I do not include these (yet), in the example because they are not coded as robustly as those provided in the example quite yet.

  • -Configure option: -InstallRust - Installs Rust and Cargo.
  • -Configure option: -InstallPoetry - Install Poetry (python project-level version manager)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment