Skip to content

Commit

Permalink
feat(install): Add support for ARM64 architecture (ScoopInstaller#5154)
Browse files Browse the repository at this point in the history
* Initial support for ARMv8

* Add fallback mechanism

* Update changelog

* Update useragent

* Some typo and format changes

* Use `env:ProgramFiles(Arm)` to detect ARM64

- Move `default_architecture()` to `core.ps1`

* Rename 'ensure_architecture()' and 'default_architecture()'

* Refactor 'supports_architecture()' to 'Get-SupportedArchitecture()'

Co-authored-by: Hsiao-nan Cheung <[email protected]>
  • Loading branch information
2 people authored and pynappo committed Sep 29, 2022
1 parent 1dc4f8b commit bac0a3a
Show file tree
Hide file tree
Showing 22 changed files with 197 additions and 120 deletions.
4 changes: 2 additions & 2 deletions .github/ISSUE_TEMPLATE/Bug_report.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ labels: "bug"

### System details

**Windows version:** [e.g. 7, 8, 10]
**Windows version:** [e.g. 7, 8, 10, 11]

**OS architecture:** [e.g. 32bit, 64bit]
**OS architecture:** [e.g. 32bit, 64bit, arm64]

**PowerShell version:** [output of `"$($PSVersionTable.PSVersion)"`]

Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

### Features

- **install:** Add support for ARM64 architecture ([#5154](https://github.com/ScoopInstaller/Scoop/issues/5154))
- **getopt:** Support option terminator (`--`) ([#5121](https://github.com/ScoopInstaller/Scoop/issues/5121))
- **scoop-(un)hold:** Support `scoop (un)hold scoop` ([#5089](https://github.com/ScoopInstaller/Scoop/issues/5089))
- **scoop-config:** Allow 'hold_update_until' be set manually ([#5100](https://github.com/ScoopInstaller/Scoop/issues/5100))
Expand Down
12 changes: 9 additions & 3 deletions bin/checkhashes.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ foreach ($single in Get-ChildItem $Dir "$App.json") {
hash $manifest '64bit' | ForEach-Object { $hashes += $_ }
script:url $manifest '32bit' | ForEach-Object { $urls += $_ }
hash $manifest '32bit' | ForEach-Object { $hashes += $_ }
script:url $manifest 'arm64' | ForEach-Object { $urls += $_ }
hash $manifest 'arm64' | ForEach-Object { $hashes += $_ }
} else {
err $name 'Manifest does not contain URL property.'
continue
Expand Down Expand Up @@ -158,16 +160,20 @@ foreach ($current in $MANIFESTS) {
# Defaults to zero, don't know, which architecture is available
$64bit_count = 0
$32bit_count = 0
$arm64_count = 0

# 64bit is get, donwloaded and added first
if ($platforms.Contains('64bit')) {
$64bit_count = $current.manifest.architecture.'64bit'.hash.Count
# 64bit is get, donwloaded and added first
$current.manifest.architecture.'64bit'.hash = $actuals[0..($64bit_count - 1)]
}
if ($platforms.Contains('32bit')) {
$32bit_count = $current.manifest.architecture.'32bit'.hash.Count
$max = $64bit_count + $32bit_count - 1 # Edge case if manifest contains 64bit and 32bit.
$current.manifest.architecture.'32bit'.hash = $actuals[($64bit_count)..$max]
$current.manifest.architecture.'32bit'.hash = $actuals[($64bit_count)..($64bit_count + $32bit_count - 1)]
}
if ($platforms.Contains('arm64')) {
$arm64_count = $current.manifest.architecture.'arm64'.hash.Count
$current.manifest.architecture.'arm64'.hash = $actuals[($64bit_count + $32bit_count)..($64bit_count + $32bit_count + $arm64_count - 1)]
}
}

Expand Down
1 change: 1 addition & 0 deletions bin/checkurls.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ foreach ($man in $Queue) {
} else {
script:url $manifest '64bit' | ForEach-Object { $urls += $_ }
script:url $manifest '32bit' | ForEach-Object { $urls += $_ }
script:url $manifest 'arm64' | ForEach-Object { $urls += $_ }
}

$urls | ForEach-Object {
Expand Down
2 changes: 1 addition & 1 deletion lib/autoupdate.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,7 @@ function Invoke-AutoUpdate {
$hasNote = $true
}
if ($Manifest.autoupdate.architecture) {
'64bit', '32bit' | ForEach-Object {
'64bit', '32bit', 'arm64' | ForEach-Object {
if ($Manifest.autoupdate.architecture.$_.note) {
$note += "`n$_-arch: $($Manifest.autoupdate.architecture.$_.note)"
$hasNote = $true
Expand Down
44 changes: 35 additions & 9 deletions lib/core.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ function Get-Encoding($wc) {
}

function Get-UserAgent() {
return "Scoop/1.0 (+http://scoop.sh/) PowerShell/$($PSVersionTable.PSVersion.Major).$($PSVersionTable.PSVersion.Minor) (Windows NT $([System.Environment]::OSVersion.Version.Major).$([System.Environment]::OSVersion.Version.Minor); $(if($env:PROCESSOR_ARCHITECTURE -eq 'AMD64'){'Win64; x64; '})$(if($env:PROCESSOR_ARCHITEW6432 -eq 'AMD64'){'WOW64; '})$PSEdition)"
return "Scoop/1.0 (+http://scoop.sh/) PowerShell/$($PSVersionTable.PSVersion.Major).$($PSVersionTable.PSVersion.Minor) (Windows NT $([System.Environment]::OSVersion.Version.Major).$([System.Environment]::OSVersion.Version.Minor); $(if(${env:ProgramFiles(Arm)}){'ARM64; '}elseif($env:PROCESSOR_ARCHITECTURE -eq 'AMD64'){'Win64; x64; '})$(if($env:PROCESSOR_ARCHITEW6432 -in 'AMD64','ARM64'){'WOW64; '})$PSEdition)"
}

function Show-DeprecatedWarning {
Expand Down Expand Up @@ -857,15 +857,38 @@ function ensure_in_path($dir, $global) {
}
}

function ensure_architecture($architecture_opt) {
if(!$architecture_opt) {
return default_architecture
function Get-DefaultArchitecture {
$arch = get_config DEFAULT_ARCHITECTURE
$system = if (${env:ProgramFiles(Arm)}) {
'arm64'
} elseif ([System.Environment]::Is64BitOperatingSystem) {
'64bit'
} else {
'32bit'
}
if ($null -eq $arch) {
$arch = $system
} else {
try {
$arch = Format-ArchitectureString $arch
} catch {
warn 'Invalid default architecture configured. Determining default system architecture'
$arch = $system
}
}
$architecture_opt = $architecture_opt.ToString().ToLower()
switch($architecture_opt) {
{ @('64bit', '64', 'x64', 'amd64', 'x86_64', 'x86-64') -contains $_ } { return '64bit' }
{ @('32bit', '32', 'x86', 'i386', '386', 'i686') -contains $_ } { return '32bit' }
default { throw [System.ArgumentException] "Invalid architecture: '$architecture_opt'"}
return $arch
}

function Format-ArchitectureString($Architecture) {
if (!$Architecture) {
return Get-DefaultArchitecture
}
$Architecture = $Architecture.ToString().ToLower()
switch ($Architecture) {
{ @('64bit', '64', 'x64', 'amd64', 'x86_64', 'x86-64') -contains $_ } { return '64bit' }
{ @('32bit', '32', 'x86', 'i386', '386', 'i686') -contains $_ } { return '32bit' }
{ @('arm64', 'arm', 'aarch64') -contains $_ } { return 'arm64' }
default { throw [System.ArgumentException] "Invalid architecture: '$Architecture'" }
}
}

Expand Down Expand Up @@ -1240,5 +1263,8 @@ $globaldir = $env:SCOOP_GLOBAL, (get_config GLOBAL_PATH), "$env:ProgramData\scoo
# Use at your own risk.
$cachedir = $env:SCOOP_CACHE, (get_config CACHE_PATH), "$scoopdir\cache" | Where-Object { -not [String]::IsNullOrEmpty($_) } | Select-Object -First 1

# OS information
$WindowsBuild = [System.Environment]::OSVersion.Version.Build

# Setup proxy globally
setup_proxy
7 changes: 4 additions & 3 deletions lib/install.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ function install_app($app, $architecture, $global, $suggested, $use_cache = $tru
$check_hash = $false
}

if(!(supports_architecture $manifest $architecture)) {
write-host -f DarkRed "'$app' doesn't support $architecture architecture!"
$architecture = Get-SupportedArchitecture $manifest $architecture
if ($null -eq $architecture) {
error "'$app' doesn't support current architecture!"
return
}

Expand Down Expand Up @@ -1046,7 +1047,7 @@ function Invoke-HookScript {
[ValidateNotNullOrEmpty()]
[PSCustomObject] $Manifest,
[Parameter(Mandatory = $true)]
[ValidateSet('32bit', '64bit')]
[ValidateSet('32bit', '64bit', 'arm64')]
[String] $Arch
)

Expand Down
35 changes: 16 additions & 19 deletions lib/manifest.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -102,23 +102,6 @@ function install_info($app, $version, $global) {
parse_json $path
}

function default_architecture {
$arch = get_config DEFAULT_ARCHITECTURE
$system = if ([Environment]::Is64BitOperatingSystem) { '64bit' } else { '32bit' }
if ($null -eq $arch) {
$arch = $system
} else {
try {
$arch = ensure_architecture $arch
} catch {
warn 'Invalid default architecture configured. Determining default system architecture'
$arch = $system
}
}

return $arch
}

function arch_specific($prop, $manifest, $architecture) {
if ($manifest.architecture) {
$val = $manifest.architecture.$architecture.$prop
Expand All @@ -128,8 +111,22 @@ function arch_specific($prop, $manifest, $architecture) {
if ($manifest.$prop) { return $manifest.$prop }
}

function supports_architecture($manifest, $architecture) {
return -not [String]::IsNullOrEmpty((arch_specific 'url' $manifest $architecture))
function Get-SupportedArchitecture($manifest, $architecture) {
if ($architecture -eq 'arm64' -and ($manifest | ConvertToPrettyJson) -notmatch '[''"]arm64["'']') {
# Windows 10 enables existing unmodified x86 apps to run on Arm devices.
# Windows 11 adds the ability to run unmodified x64 Windows apps on Arm devices!
# Ref: https://learn.microsoft.com/en-us/windows/arm/overview
if ($WindowsBuild -ge 22000) {
# Windows 11
$architecture = '64bit'
} else {
# Windows 10
$architecture = '32bit'
}
}
if (![String]::IsNullOrEmpty((arch_specific 'url' $manifest $architecture))) {
return $architecture
}
}

function generate_user_manifest($app, $bucket, $version) {
Expand Down
2 changes: 1 addition & 1 deletion libexec/scoop-config.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
# When a conflict is detected during updating, Scoop will auto-stash the uncommitted changes.
# (Default is $false, which will abort the update)
#
# default_architecture: 64bit|32bit
# default_architecture: 64bit|32bit|arm64
# Allow to configure preferred architecture for application installation.
# If not specified, architecture is determined be system.
#
Expand Down
6 changes: 3 additions & 3 deletions libexec/scoop-depends.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@

. "$PSScriptRoot\..\lib\getopt.ps1"
. "$PSScriptRoot\..\lib\depends.ps1" # 'Get-Dependency'
. "$PSScriptRoot\..\lib\manifest.ps1" # 'default_architecture'
. "$PSScriptRoot\..\lib\manifest.ps1" # 'Get-Manifest' (indirectly)

$opt, $apps, $err = getopt $args 'a:' 'arch='
$app = $apps[0]

if(!$app) { error '<app> missing'; my_usage; exit 1 }

$architecture = default_architecture
$architecture = Get-DefaultArchitecture
try {
$architecture = ensure_architecture ($opt.a + $opt.arch)
$architecture = Format-ArchitectureString ($opt.a + $opt.arch)
} catch {
abort "ERROR: $_"
}
Expand Down
19 changes: 10 additions & 9 deletions libexec/scoop-download.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,25 @@
# scoop download path\to\app.json
#
# Options:
# -f, --force Force download (overwrite cache)
# -h, --no-hash-check Skip hash verification (use with caution!)
# -u, --no-update-scoop Don't update Scoop before downloading if it's outdated
# -a, --arch <32bit|64bit> Use the specified architecture, if the app supports it
# -f, --force Force download (overwrite cache)
# -h, --no-hash-check Skip hash verification (use with caution!)
# -u, --no-update-scoop Don't update Scoop before downloading if it's outdated
# -a, --arch <32bit|64bit|arm64> Use the specified architecture, if the app supports it

. "$PSScriptRoot\..\lib\getopt.ps1"
. "$PSScriptRoot\..\lib\json.ps1" # 'autoupdate.ps1' (indirectly)
. "$PSScriptRoot\..\lib\autoupdate.ps1" # 'generate_user_manifest' (indirectly)
. "$PSScriptRoot\..\lib\manifest.ps1" # 'default_architecture' 'generate_user_manifest' 'Get-Manifest'
. "$PSScriptRoot\..\lib\manifest.ps1" # 'generate_user_manifest' 'Get-Manifest'
. "$PSScriptRoot\..\lib\install.ps1"

$opt, $apps, $err = getopt $args 'fhua:' 'force', 'no-hash-check', 'no-update-scoop', 'arch='
if ($err) { error "scoop download: $err"; exit 1 }

$check_hash = !($opt.h -or $opt.'no-hash-check')
$use_cache = !($opt.f -or $opt.force)
$architecture = default_architecture
$architecture = Get-DefaultArchitecture
try {
$architecture = ensure_architecture ($opt.a + $opt.arch)
$architecture = Format-ArchitectureString ($opt.a + $opt.arch)
} catch {
abort "ERROR: $_"
}
Expand Down Expand Up @@ -89,8 +89,9 @@ foreach ($curr_app in $apps) {
$curr_check_hash = $false
}

if(!(supports_architecture $manifest $architecture)) {
error "'$app' doesn't support $architecture architecture!"
$architecture = Get-SupportedArchitecture $manifest $architecture
if ($null -eq $architecture) {
error "'$app' doesn't support current architecture!"
continue
}

Expand Down
8 changes: 4 additions & 4 deletions libexec/scoop-import.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ param(

$import = $null
$bucket_names = @()
$def_arch = default_architecture
$def_arch = Get-DefaultArchitecture

if (Test-Path $scoopfile) {
$import = parse_json $scoopfile
Expand All @@ -40,12 +40,12 @@ foreach ($item in $import.apps) {
} else {
''
}
$arch = if ('64bit' -in $info -and '32bit' -eq $def_arch) {
$arch = if ('64bit' -in $info) {
' --arch 64bit'
} elseif ('32bit' -in $info -and '64bit' -eq $def_arch) {
} elseif ('32bit' -in $info) {
' --arch 32bit'
} else {
''
' --arch arm64'
}

$app = if ($item.Source -in $bucket_names) {
Expand Down
2 changes: 1 addition & 1 deletion libexec/scoop-info.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ if ($status.installed) {
if ($verbose) {
# Get download size if app not installed
$totalPackage = 0
foreach ($url in @(url $manifest (default_architecture))) {
foreach ($url in @(url $manifest (Get-DefaultArchitecture))) {
try {
if (Test-Path (fullpath (cache_path $app $manifest.version $url))) {
$cached = " (latest version is cached)"
Expand Down
18 changes: 9 additions & 9 deletions libexec/scoop-install.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,17 @@
# scoop install \path\to\app.json
#
# Options:
# -g, --global Install the app globally
# -i, --independent Don't install dependencies automatically
# -k, --no-cache Don't use the download cache
# -u, --no-update-scoop Don't update Scoop before installing if it's outdated
# -s, --skip Skip hash validation (use with caution!)
# -a, --arch <32bit|64bit> Use the specified architecture, if the app supports it
# -g, --global Install the app globally
# -i, --independent Don't install dependencies automatically
# -k, --no-cache Don't use the download cache
# -u, --no-update-scoop Don't update Scoop before installing if it's outdated
# -s, --skip Skip hash validation (use with caution!)
# -a, --arch <32bit|64bit|arm64> Use the specified architecture, if the app supports it

. "$PSScriptRoot\..\lib\getopt.ps1"
. "$PSScriptRoot\..\lib\json.ps1" # 'autoupdate.ps1' 'manifest.ps1' (indirectly)
. "$PSScriptRoot\..\lib\autoupdate.ps1" # 'generate_user_manifest' (indirectly)
. "$PSScriptRoot\..\lib\manifest.ps1" # 'default_architecture' 'generate_user_manifest' 'Get-Manifest' 'Select-CurrentVersion' (indirectly)
. "$PSScriptRoot\..\lib\manifest.ps1" # 'generate_user_manifest' 'Get-Manifest' 'Select-CurrentVersion' (indirectly)
. "$PSScriptRoot\..\lib\install.ps1"
. "$PSScriptRoot\..\lib\decompress.ps1"
. "$PSScriptRoot\..\lib\shortcuts.ps1"
Expand All @@ -39,9 +39,9 @@ $global = $opt.g -or $opt.global
$check_hash = !($opt.s -or $opt.skip)
$independent = $opt.i -or $opt.independent
$use_cache = !($opt.k -or $opt.'no-cache')
$architecture = default_architecture
$architecture = Get-DefaultArchitecture
try {
$architecture = ensure_architecture ($opt.a + $opt.arch)
$architecture = Format-ArchitectureString ($opt.a + $opt.arch)
} catch {
abort "ERROR: $_"
}
Expand Down
2 changes: 1 addition & 1 deletion libexec/scoop-list.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ param($query)
. "$PSScriptRoot\..\lib\versions.ps1" # 'Select-CurrentVersion'
. "$PSScriptRoot\..\lib\manifest.ps1" # 'parse_json' 'Select-CurrentVersion' (indirectly)

$def_arch = default_architecture
$def_arch = Get-DefaultArchitecture
if (-not (Get-FormatData ScoopApps)) {
Update-FormatData "$PSScriptRoot\..\supporting\formats\ScoopTypes.Format.ps1xml"
}
Expand Down
2 changes: 1 addition & 1 deletion libexec/scoop-update.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ function update($app, $global, $quiet = $false, $independent, $suggested, $use_c
$install = install_info $app $old_version $global

# re-use architecture, bucket and url from first install
$architecture = ensure_architecture $install.architecture
$architecture = Format-ArchitectureString $install.architecture
$bucket = $install.bucket
if ($null -eq $bucket) {
$bucket = 'main'
Expand Down
2 changes: 1 addition & 1 deletion libexec/scoop-virustotal.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
$opt, $apps, $err = getopt $args 'asnup' @('all', 'scan', 'no-depends', 'no-update-scoop', 'passthru')
if ($err) { "scoop virustotal: $err"; exit 1 }
if (!$apps) { my_usage; exit 1 }
$architecture = ensure_architecture
$architecture = Format-ArchitectureString

if (is_scoop_outdated) {
if ($opt.u -or $opt.'no-update-scoop') {
Expand Down
Loading

0 comments on commit bac0a3a

Please sign in to comment.