Skip to content

Commit e6856e9

Browse files
authored
Merge pull request #974 from charlespierce/native_apple_silicon
[Medium] Fetch Native Apple Silicon Node builds for Node 16+
2 parents 5f39583 + cfd717f commit e6856e9

File tree

5 files changed

+70
-26
lines changed

5 files changed

+70
-26
lines changed

crates/volta-core/src/tool/node/fetch.rs

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ cfg_if! {
3030
}
3131
}
3232

33-
fn npm_manifest_path(version: &str) -> PathBuf {
33+
fn npm_manifest_path(version: &Version) -> PathBuf {
3434
let mut manifest = PathBuf::from(Node::archive_basename(version));
3535

3636
#[cfg(unix)]
@@ -46,7 +46,7 @@ fn npm_manifest_path(version: &str) -> PathBuf {
4646
pub fn fetch(version: &Version, hooks: Option<&ToolHooks<Node>>) -> Fallible<NodeVersion> {
4747
let home = volta_home()?;
4848
let node_dir = home.node_inventory_dir();
49-
let cache_file = node_dir.join(Node::archive_filename(&version.to_string()));
49+
let cache_file = node_dir.join(Node::archive_filename(version));
5050

5151
let (archive, staging) = match load_cached_distro(&cache_file) {
5252
Some(archive) => {
@@ -107,22 +107,20 @@ fn unpack_archive(archive: Box<dyn Archive>, version: &Version) -> Fallible<Node
107107
})?;
108108

109109
// Save the npm version number in the npm version file for this distro
110-
let npm_package_json = temp.path().join(npm_manifest_path(&version_string));
110+
let npm_package_json = temp.path().join(npm_manifest_path(version));
111111
let npm = Manifest::version(&npm_package_json)?;
112112
save_default_npm_version(&version, &npm)?;
113113

114114
let dest = volta_home()?.node_image_dir(&version_string);
115115
ensure_containing_dir_exists(&dest)
116116
.with_context(|| ErrorKind::ContainingDirError { path: dest.clone() })?;
117117

118-
rename(
119-
temp.path().join(Node::archive_basename(&version_string)),
120-
&dest,
121-
)
122-
.with_context(|| ErrorKind::SetupToolImageError {
123-
tool: "Node".into(),
124-
version: version_string,
125-
dir: dest.clone(),
118+
rename(temp.path().join(Node::archive_basename(version)), &dest).with_context(|| {
119+
ErrorKind::SetupToolImageError {
120+
tool: "Node".into(),
121+
version: version_string,
122+
dir: dest.clone(),
123+
}
126124
})?;
127125

128126
progress.finish_and_clear();
@@ -151,8 +149,7 @@ fn load_cached_distro(file: &Path) -> Option<Box<dyn Archive>> {
151149

152150
/// Determine the remote URL to download from, using the hooks if available
153151
fn determine_remote_url(version: &Version, hooks: Option<&ToolHooks<Node>>) -> Fallible<String> {
154-
let version_str = version.to_string();
155-
let distro_file_name = Node::archive_filename(&version_str);
152+
let distro_file_name = Node::archive_filename(version);
156153
match hooks {
157154
Some(&ToolHooks {
158155
distro: Some(ref hook),

crates/volta-core/src/tool/node/metadata.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use std::collections::HashSet;
22

33
use super::NODE_DISTRO_IDENTIFIER;
4+
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
5+
use super::NODE_DISTRO_IDENTIFIER_FALLBACK;
46
use crate::version::{option_version_serde, version_serde};
57
use semver::Version;
68
use serde::{Deserialize, Deserializer};
@@ -37,6 +39,7 @@ impl From<RawNodeIndex> for NodeIndex {
3739
.0
3840
.into_iter()
3941
.filter_map(|entry| {
42+
#[cfg(not(all(target_os = "macos", target_arch = "aarch64")))]
4043
if entry.npm.is_some() && entry.files.contains(NODE_DISTRO_IDENTIFIER) {
4144
Some(NodeEntry {
4245
version: entry.version,
@@ -45,6 +48,19 @@ impl From<RawNodeIndex> for NodeIndex {
4548
} else {
4649
None
4750
}
51+
52+
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
53+
if entry.npm.is_some()
54+
&& (entry.files.contains(NODE_DISTRO_IDENTIFIER)
55+
|| entry.files.contains(NODE_DISTRO_IDENTIFIER_FALLBACK))
56+
{
57+
Some(NodeEntry {
58+
version: entry.version,
59+
lts: entry.lts,
60+
})
61+
} else {
62+
None
63+
}
4864
})
4965
.collect();
5066

crates/volta-core/src/tool/node/mod.rs

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,7 @@ cfg_if! {
3939
pub const NODE_DISTRO_EXTENSION: &str = "zip";
4040
/// The file identifier in the Node index `files` array
4141
pub const NODE_DISTRO_IDENTIFIER: &str = "win-x64-zip";
42-
} else if #[cfg(all(target_os = "macos", any(target_arch = "x86_64", target_arch = "aarch64")))] {
43-
// NOTE: Currently, Node does not provide prebuilt binaries for Apple M1 machines, so we
44-
// fall back to using the x64 binaries through Rosetta 2. When Node starts shipping M1
45-
// binaries, then we will need to adjust our logic to search for those first and only fall
46-
// back if they aren't found
47-
42+
} else if #[cfg(all(target_os = "macos", target_arch = "x86_64"))] {
4843
/// The OS component of a Node distro filename
4944
pub const NODE_DISTRO_OS: &str = "darwin";
5045
/// The architecture component of a Node distro filename
@@ -53,6 +48,23 @@ cfg_if! {
5348
pub const NODE_DISTRO_EXTENSION: &str = "tar.gz";
5449
/// The file identifier in the Node index `files` array
5550
pub const NODE_DISTRO_IDENTIFIER: &str = "osx-x64-tar";
51+
} else if #[cfg(all(target_os = "macos", target_arch = "aarch64"))] {
52+
/// The OS component of a Node distro filename
53+
pub const NODE_DISTRO_OS: &str = "darwin";
54+
/// The architecture component of a Node distro filename
55+
pub const NODE_DISTRO_ARCH: &str = "arm64";
56+
/// The extension for Node distro files
57+
pub const NODE_DISTRO_EXTENSION: &str = "tar.gz";
58+
/// The file identifier in the Node index `files` array
59+
pub const NODE_DISTRO_IDENTIFIER: &str = "osx-arm64-tar";
60+
61+
// NOTE: Node support for pre-built Apple Silicon binaries was added in major version 16
62+
// For versions prior to that, we need to fall back on the x64 binaries via Rosetta 2
63+
64+
/// The fallback architecture component of a Node distro filename
65+
pub const NODE_DISTRO_ARCH_FALLBACK: &str = "x64";
66+
/// The fallback file identifier in the Node index `files` array
67+
pub const NODE_DISTRO_IDENTIFIER_FALLBACK: &str = "osx-x64-tar";
5668
} else if #[cfg(all(target_os = "linux", target_arch = "x86_64"))] {
5769
/// The OS component of a Node distro filename
5870
pub const NODE_DISTRO_OS: &str = "linux";
@@ -117,11 +129,28 @@ impl Node {
117129
Node { version }
118130
}
119131

120-
pub fn archive_basename(version: &str) -> String {
132+
#[cfg(not(all(target_os = "macos", target_arch = "aarch64")))]
133+
pub fn archive_basename(version: &Version) -> String {
121134
format!("node-v{}-{}-{}", version, NODE_DISTRO_OS, NODE_DISTRO_ARCH)
122135
}
123136

124-
pub fn archive_filename(version: &str) -> String {
137+
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
138+
pub fn archive_basename(version: &Version) -> String {
139+
// Note: Node began shipping pre-built binaries for Apple Silicon with Major version 16
140+
// Prior to that, we need to fall back on the x64 binaries
141+
format!(
142+
"node-v{}-{}-{}",
143+
version,
144+
NODE_DISTRO_OS,
145+
if version.major >= 16 {
146+
NODE_DISTRO_ARCH
147+
} else {
148+
NODE_DISTRO_ARCH_FALLBACK
149+
}
150+
)
151+
}
152+
153+
pub fn archive_filename(version: &Version) -> String {
125154
format!(
126155
"{}.{}",
127156
Node::archive_basename(version),
@@ -230,15 +259,15 @@ mod tests {
230259
#[test]
231260
fn test_node_archive_basename() {
232261
assert_eq!(
233-
Node::archive_basename("1.2.3"),
262+
Node::archive_basename(&Version::new(1, 2, 3)),
234263
format!("node-v1.2.3-{}-{}", NODE_DISTRO_OS, NODE_DISTRO_ARCH)
235264
);
236265
}
237266

238267
#[test]
239268
fn test_node_archive_filename() {
240269
assert_eq!(
241-
Node::archive_filename("1.2.3"),
270+
Node::archive_filename(&Version::new(1, 2, 3)),
242271
format!(
243272
"node-v1.2.3-{}-{}.{}",
244273
NODE_DISTRO_OS, NODE_DISTRO_ARCH, NODE_DISTRO_EXTENSION

tests/acceptance/corrupted_download.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::support::sandbox::{sandbox, DistroMetadata, NodeFixture, YarnFixture};
22
use hamcrest2::assert_that;
33
use hamcrest2::prelude::*;
4+
use semver::Version;
45
use test_support::matchers::execs;
56

67
use volta_core::error::ExitCode;
@@ -58,7 +59,7 @@ fn install_corrupted_node_leaves_inventory_unchanged() {
5859
execs().with_status(ExitCode::UnknownError as i32)
5960
);
6061

61-
assert!(!s.node_inventory_archive_exists("0.0.1"));
62+
assert!(!s.node_inventory_archive_exists(&Version::new(0, 0, 1)));
6263
}
6364

6465
#[test]
@@ -73,7 +74,7 @@ fn install_valid_node_saves_to_inventory() {
7374
execs().with_status(ExitCode::Success as i32)
7475
);
7576

76-
assert!(s.node_inventory_archive_exists("10.99.1040"));
77+
assert!(s.node_inventory_archive_exists(&Version::new(10, 99, 1040)));
7778
}
7879

7980
#[test]

tests/acceptance/support/sandbox.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use std::time::{Duration, SystemTime};
77

88
use hyperx::header::HttpDate;
99
use mockito::{self, mock, Matcher};
10+
use semver::Version;
1011
use test_support::{self, ok_or_panic, paths, paths::PathExt, process::ProcessBuilder};
1112
use volta_core::fs::symlink_file;
1213
use volta_core::tool::{Node, Yarn, NODE_DISTRO_ARCH, NODE_DISTRO_EXTENSION, NODE_DISTRO_OS};
@@ -631,7 +632,7 @@ impl Sandbox {
631632

632633
// check that files in the sandbox exist
633634

634-
pub fn node_inventory_archive_exists(&self, version: &str) -> bool {
635+
pub fn node_inventory_archive_exists(&self, version: &Version) -> bool {
635636
node_inventory_dir()
636637
.join(Node::archive_filename(version))
637638
.exists()

0 commit comments

Comments
 (0)