Skip to content

Commit 7a599f0

Browse files
rashil2000niheaven
andauthored
feat(install): Add support for ARM64 architecture (#5154)
* 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 <niheaven@gmail.com>
1 parent 146dab6 commit 7a599f0

22 files changed

+197
-120
lines changed

.github/ISSUE_TEMPLATE/Bug_report.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ labels: "bug"
3131

3232
### System details
3333

34-
**Windows version:** [e.g. 7, 8, 10]
34+
**Windows version:** [e.g. 7, 8, 10, 11]
3535

36-
**OS architecture:** [e.g. 32bit, 64bit]
36+
**OS architecture:** [e.g. 32bit, 64bit, arm64]
3737

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

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
### Features
44

5+
- **install:** Add support for ARM64 architecture ([#5154](https://github.com/ScoopInstaller/Scoop/issues/5154))
56
- **getopt:** Support option terminator (`--`) ([#5121](https://github.com/ScoopInstaller/Scoop/issues/5121))
67
- **scoop-(un)hold:** Support `scoop (un)hold scoop` ([#5089](https://github.com/ScoopInstaller/Scoop/issues/5089))
78
- **scoop-config:** Allow 'hold_update_until' be set manually ([#5100](https://github.com/ScoopInstaller/Scoop/issues/5100))

bin/checkhashes.ps1

+9-3
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ foreach ($single in Get-ChildItem $Dir "$App.json") {
7979
hash $manifest '64bit' | ForEach-Object { $hashes += $_ }
8080
script:url $manifest '32bit' | ForEach-Object { $urls += $_ }
8181
hash $manifest '32bit' | ForEach-Object { $hashes += $_ }
82+
script:url $manifest 'arm64' | ForEach-Object { $urls += $_ }
83+
hash $manifest 'arm64' | ForEach-Object { $hashes += $_ }
8284
} else {
8385
err $name 'Manifest does not contain URL property.'
8486
continue
@@ -158,16 +160,20 @@ foreach ($current in $MANIFESTS) {
158160
# Defaults to zero, don't know, which architecture is available
159161
$64bit_count = 0
160162
$32bit_count = 0
163+
$arm64_count = 0
161164

165+
# 64bit is get, donwloaded and added first
162166
if ($platforms.Contains('64bit')) {
163167
$64bit_count = $current.manifest.architecture.'64bit'.hash.Count
164-
# 64bit is get, donwloaded and added first
165168
$current.manifest.architecture.'64bit'.hash = $actuals[0..($64bit_count - 1)]
166169
}
167170
if ($platforms.Contains('32bit')) {
168171
$32bit_count = $current.manifest.architecture.'32bit'.hash.Count
169-
$max = $64bit_count + $32bit_count - 1 # Edge case if manifest contains 64bit and 32bit.
170-
$current.manifest.architecture.'32bit'.hash = $actuals[($64bit_count)..$max]
172+
$current.manifest.architecture.'32bit'.hash = $actuals[($64bit_count)..($64bit_count + $32bit_count - 1)]
173+
}
174+
if ($platforms.Contains('arm64')) {
175+
$arm64_count = $current.manifest.architecture.'arm64'.hash.Count
176+
$current.manifest.architecture.'arm64'.hash = $actuals[($64bit_count + $32bit_count)..($64bit_count + $32bit_count + $arm64_count - 1)]
171177
}
172178
}
173179

bin/checkurls.ps1

+1
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ foreach ($man in $Queue) {
9898
} else {
9999
script:url $manifest '64bit' | ForEach-Object { $urls += $_ }
100100
script:url $manifest '32bit' | ForEach-Object { $urls += $_ }
101+
script:url $manifest 'arm64' | ForEach-Object { $urls += $_ }
101102
}
102103

103104
$urls | ForEach-Object {

lib/autoupdate.ps1

+1-1
Original file line numberDiff line numberDiff line change
@@ -457,7 +457,7 @@ function Invoke-AutoUpdate {
457457
$hasNote = $true
458458
}
459459
if ($Manifest.autoupdate.architecture) {
460-
'64bit', '32bit' | ForEach-Object {
460+
'64bit', '32bit', 'arm64' | ForEach-Object {
461461
if ($Manifest.autoupdate.architecture.$_.note) {
462462
$note += "`n$_-arch: $($Manifest.autoupdate.architecture.$_.note)"
463463
$hasNote = $true

lib/core.ps1

+35-9
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ function Get-Encoding($wc) {
2424
}
2525

2626
function Get-UserAgent() {
27-
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)"
27+
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)"
2828
}
2929

3030
function Show-DeprecatedWarning {
@@ -857,15 +857,38 @@ function ensure_in_path($dir, $global) {
857857
}
858858
}
859859

860-
function ensure_architecture($architecture_opt) {
861-
if(!$architecture_opt) {
862-
return default_architecture
860+
function Get-DefaultArchitecture {
861+
$arch = get_config DEFAULT_ARCHITECTURE
862+
$system = if (${env:ProgramFiles(Arm)}) {
863+
'arm64'
864+
} elseif ([System.Environment]::Is64BitOperatingSystem) {
865+
'64bit'
866+
} else {
867+
'32bit'
868+
}
869+
if ($null -eq $arch) {
870+
$arch = $system
871+
} else {
872+
try {
873+
$arch = Format-ArchitectureString $arch
874+
} catch {
875+
warn 'Invalid default architecture configured. Determining default system architecture'
876+
$arch = $system
877+
}
863878
}
864-
$architecture_opt = $architecture_opt.ToString().ToLower()
865-
switch($architecture_opt) {
866-
{ @('64bit', '64', 'x64', 'amd64', 'x86_64', 'x86-64') -contains $_ } { return '64bit' }
867-
{ @('32bit', '32', 'x86', 'i386', '386', 'i686') -contains $_ } { return '32bit' }
868-
default { throw [System.ArgumentException] "Invalid architecture: '$architecture_opt'"}
879+
return $arch
880+
}
881+
882+
function Format-ArchitectureString($Architecture) {
883+
if (!$Architecture) {
884+
return Get-DefaultArchitecture
885+
}
886+
$Architecture = $Architecture.ToString().ToLower()
887+
switch ($Architecture) {
888+
{ @('64bit', '64', 'x64', 'amd64', 'x86_64', 'x86-64') -contains $_ } { return '64bit' }
889+
{ @('32bit', '32', 'x86', 'i386', '386', 'i686') -contains $_ } { return '32bit' }
890+
{ @('arm64', 'arm', 'aarch64') -contains $_ } { return 'arm64' }
891+
default { throw [System.ArgumentException] "Invalid architecture: '$Architecture'" }
869892
}
870893
}
871894

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

1266+
# OS information
1267+
$WindowsBuild = [System.Environment]::OSVersion.Version.Build
1268+
12431269
# Setup proxy globally
12441270
setup_proxy

lib/install.ps1

+4-3
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@ function install_app($app, $architecture, $global, $suggested, $use_cache = $tru
2525
$check_hash = $false
2626
}
2727

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

@@ -1046,7 +1047,7 @@ function Invoke-HookScript {
10461047
[ValidateNotNullOrEmpty()]
10471048
[PSCustomObject] $Manifest,
10481049
[Parameter(Mandatory = $true)]
1049-
[ValidateSet('32bit', '64bit')]
1050+
[ValidateSet('32bit', '64bit', 'arm64')]
10501051
[String] $Arch
10511052
)
10521053

lib/manifest.ps1

+16-19
Original file line numberDiff line numberDiff line change
@@ -102,23 +102,6 @@ function install_info($app, $version, $global) {
102102
parse_json $path
103103
}
104104

105-
function default_architecture {
106-
$arch = get_config DEFAULT_ARCHITECTURE
107-
$system = if ([Environment]::Is64BitOperatingSystem) { '64bit' } else { '32bit' }
108-
if ($null -eq $arch) {
109-
$arch = $system
110-
} else {
111-
try {
112-
$arch = ensure_architecture $arch
113-
} catch {
114-
warn 'Invalid default architecture configured. Determining default system architecture'
115-
$arch = $system
116-
}
117-
}
118-
119-
return $arch
120-
}
121-
122105
function arch_specific($prop, $manifest, $architecture) {
123106
if ($manifest.architecture) {
124107
$val = $manifest.architecture.$architecture.$prop
@@ -128,8 +111,22 @@ function arch_specific($prop, $manifest, $architecture) {
128111
if ($manifest.$prop) { return $manifest.$prop }
129112
}
130113

131-
function supports_architecture($manifest, $architecture) {
132-
return -not [String]::IsNullOrEmpty((arch_specific 'url' $manifest $architecture))
114+
function Get-SupportedArchitecture($manifest, $architecture) {
115+
if ($architecture -eq 'arm64' -and ($manifest | ConvertToPrettyJson) -notmatch '[''"]arm64["'']') {
116+
# Windows 10 enables existing unmodified x86 apps to run on Arm devices.
117+
# Windows 11 adds the ability to run unmodified x64 Windows apps on Arm devices!
118+
# Ref: https://learn.microsoft.com/en-us/windows/arm/overview
119+
if ($WindowsBuild -ge 22000) {
120+
# Windows 11
121+
$architecture = '64bit'
122+
} else {
123+
# Windows 10
124+
$architecture = '32bit'
125+
}
126+
}
127+
if (![String]::IsNullOrEmpty((arch_specific 'url' $manifest $architecture))) {
128+
return $architecture
129+
}
133130
}
134131

135132
function generate_user_manifest($app, $bucket, $version) {

libexec/scoop-config.ps1

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
# When a conflict is detected during updating, Scoop will auto-stash the uncommitted changes.
5252
# (Default is $false, which will abort the update)
5353
#
54-
# default_architecture: 64bit|32bit
54+
# default_architecture: 64bit|32bit|arm64
5555
# Allow to configure preferred architecture for application installation.
5656
# If not specified, architecture is determined be system.
5757
#

libexec/scoop-depends.ps1

+3-3
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,16 @@
33

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

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

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

13-
$architecture = default_architecture
13+
$architecture = Get-DefaultArchitecture
1414
try {
15-
$architecture = ensure_architecture ($opt.a + $opt.arch)
15+
$architecture = Format-ArchitectureString ($opt.a + $opt.arch)
1616
} catch {
1717
abort "ERROR: $_"
1818
}

libexec/scoop-download.ps1

+10-9
Original file line numberDiff line numberDiff line change
@@ -14,25 +14,25 @@
1414
# scoop download path\to\app.json
1515
#
1616
# Options:
17-
# -f, --force Force download (overwrite cache)
18-
# -h, --no-hash-check Skip hash verification (use with caution!)
19-
# -u, --no-update-scoop Don't update Scoop before downloading if it's outdated
20-
# -a, --arch <32bit|64bit> Use the specified architecture, if the app supports it
17+
# -f, --force Force download (overwrite cache)
18+
# -h, --no-hash-check Skip hash verification (use with caution!)
19+
# -u, --no-update-scoop Don't update Scoop before downloading if it's outdated
20+
# -a, --arch <32bit|64bit|arm64> Use the specified architecture, if the app supports it
2121

2222
. "$PSScriptRoot\..\lib\getopt.ps1"
2323
. "$PSScriptRoot\..\lib\json.ps1" # 'autoupdate.ps1' (indirectly)
2424
. "$PSScriptRoot\..\lib\autoupdate.ps1" # 'generate_user_manifest' (indirectly)
25-
. "$PSScriptRoot\..\lib\manifest.ps1" # 'default_architecture' 'generate_user_manifest' 'Get-Manifest'
25+
. "$PSScriptRoot\..\lib\manifest.ps1" # 'generate_user_manifest' 'Get-Manifest'
2626
. "$PSScriptRoot\..\lib\install.ps1"
2727

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

3131
$check_hash = !($opt.h -or $opt.'no-hash-check')
3232
$use_cache = !($opt.f -or $opt.force)
33-
$architecture = default_architecture
33+
$architecture = Get-DefaultArchitecture
3434
try {
35-
$architecture = ensure_architecture ($opt.a + $opt.arch)
35+
$architecture = Format-ArchitectureString ($opt.a + $opt.arch)
3636
} catch {
3737
abort "ERROR: $_"
3838
}
@@ -89,8 +89,9 @@ foreach ($curr_app in $apps) {
8989
$curr_check_hash = $false
9090
}
9191

92-
if(!(supports_architecture $manifest $architecture)) {
93-
error "'$app' doesn't support $architecture architecture!"
92+
$architecture = Get-SupportedArchitecture $manifest $architecture
93+
if ($null -eq $architecture) {
94+
error "'$app' doesn't support current architecture!"
9495
continue
9596
}
9697

libexec/scoop-import.ps1

+4-4
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ param(
1313

1414
$import = $null
1515
$bucket_names = @()
16-
$def_arch = default_architecture
16+
$def_arch = Get-DefaultArchitecture
1717

1818
if (Test-Path $scoopfile) {
1919
$import = parse_json $scoopfile
@@ -40,12 +40,12 @@ foreach ($item in $import.apps) {
4040
} else {
4141
''
4242
}
43-
$arch = if ('64bit' -in $info -and '32bit' -eq $def_arch) {
43+
$arch = if ('64bit' -in $info) {
4444
' --arch 64bit'
45-
} elseif ('32bit' -in $info -and '64bit' -eq $def_arch) {
45+
} elseif ('32bit' -in $info) {
4646
' --arch 32bit'
4747
} else {
48-
''
48+
' --arch arm64'
4949
}
5050

5151
$app = if ($item.Source -in $bucket_names) {

libexec/scoop-info.ps1

+1-1
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ if ($status.installed) {
158158
if ($verbose) {
159159
# Get download size if app not installed
160160
$totalPackage = 0
161-
foreach ($url in @(url $manifest (default_architecture))) {
161+
foreach ($url in @(url $manifest (Get-DefaultArchitecture))) {
162162
try {
163163
if (Test-Path (fullpath (cache_path $app $manifest.version $url))) {
164164
$cached = " (latest version is cached)"

libexec/scoop-install.ps1

+9-9
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,17 @@
1414
# scoop install \path\to\app.json
1515
#
1616
# Options:
17-
# -g, --global Install the app globally
18-
# -i, --independent Don't install dependencies automatically
19-
# -k, --no-cache Don't use the download cache
20-
# -u, --no-update-scoop Don't update Scoop before installing if it's outdated
21-
# -s, --skip Skip hash validation (use with caution!)
22-
# -a, --arch <32bit|64bit> Use the specified architecture, if the app supports it
17+
# -g, --global Install the app globally
18+
# -i, --independent Don't install dependencies automatically
19+
# -k, --no-cache Don't use the download cache
20+
# -u, --no-update-scoop Don't update Scoop before installing if it's outdated
21+
# -s, --skip Skip hash validation (use with caution!)
22+
# -a, --arch <32bit|64bit|arm64> Use the specified architecture, if the app supports it
2323

2424
. "$PSScriptRoot\..\lib\getopt.ps1"
2525
. "$PSScriptRoot\..\lib\json.ps1" # 'autoupdate.ps1' 'manifest.ps1' (indirectly)
2626
. "$PSScriptRoot\..\lib\autoupdate.ps1" # 'generate_user_manifest' (indirectly)
27-
. "$PSScriptRoot\..\lib\manifest.ps1" # 'default_architecture' 'generate_user_manifest' 'Get-Manifest' 'Select-CurrentVersion' (indirectly)
27+
. "$PSScriptRoot\..\lib\manifest.ps1" # 'generate_user_manifest' 'Get-Manifest' 'Select-CurrentVersion' (indirectly)
2828
. "$PSScriptRoot\..\lib\install.ps1"
2929
. "$PSScriptRoot\..\lib\decompress.ps1"
3030
. "$PSScriptRoot\..\lib\shortcuts.ps1"
@@ -39,9 +39,9 @@ $global = $opt.g -or $opt.global
3939
$check_hash = !($opt.s -or $opt.skip)
4040
$independent = $opt.i -or $opt.independent
4141
$use_cache = !($opt.k -or $opt.'no-cache')
42-
$architecture = default_architecture
42+
$architecture = Get-DefaultArchitecture
4343
try {
44-
$architecture = ensure_architecture ($opt.a + $opt.arch)
44+
$architecture = Format-ArchitectureString ($opt.a + $opt.arch)
4545
} catch {
4646
abort "ERROR: $_"
4747
}

libexec/scoop-list.ps1

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ param($query)
66
. "$PSScriptRoot\..\lib\versions.ps1" # 'Select-CurrentVersion'
77
. "$PSScriptRoot\..\lib\manifest.ps1" # 'parse_json' 'Select-CurrentVersion' (indirectly)
88

9-
$def_arch = default_architecture
9+
$def_arch = Get-DefaultArchitecture
1010
if (-not (Get-FormatData ScoopApps)) {
1111
Update-FormatData "$PSScriptRoot\..\supporting\formats\ScoopTypes.Format.ps1xml"
1212
}

libexec/scoop-update.ps1

+1-1
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ function update($app, $global, $quiet = $false, $independent, $suggested, $use_c
191191
$install = install_info $app $old_version $global
192192

193193
# re-use architecture, bucket and url from first install
194-
$architecture = ensure_architecture $install.architecture
194+
$architecture = Format-ArchitectureString $install.architecture
195195
$bucket = $install.bucket
196196
if ($null -eq $bucket) {
197197
$bucket = 'main'

libexec/scoop-virustotal.ps1

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
$opt, $apps, $err = getopt $args 'asnup' @('all', 'scan', 'no-depends', 'no-update-scoop', 'passthru')
3838
if ($err) { "scoop virustotal: $err"; exit 1 }
3939
if (!$apps) { my_usage; exit 1 }
40-
$architecture = ensure_architecture
40+
$architecture = Format-ArchitectureString
4141

4242
if (is_scoop_outdated) {
4343
if ($opt.u -or $opt.'no-update-scoop') {

0 commit comments

Comments
 (0)