Skip to content

Commit

Permalink
node: compilation options in .rtx.toml (#1040)
Browse files Browse the repository at this point in the history
  • Loading branch information
jdx authored Dec 1, 2023
1 parent 7dac73e commit d547412
Show file tree
Hide file tree
Showing 20 changed files with 271 additions and 193 deletions.
17 changes: 16 additions & 1 deletion docs/node.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ required system dependencies.
- `RTX_NODE_BUILD_REPO` [string]: the default is `https://github.com/nodenv/node-build.git`
- `RTX_NODE_VERIFY` [bool]: Verify the downloaded assets using GPG. Defaults to `true`.
- `RTX_NODE_NINJA` [bool]: Use ninja instead of make to compile nodel. Defaults to `true` if installed.
- `RTX_NODE_FORCE_COMPILE` [bool]: Forces compilation from source instead of preferring pre-compiled binaries. Can also be set across all languages with [`RTX_NODE_FORCE_COMPILE`](https://github.com/jdx/rtx#rtx_node_force_compile1)
- `RTX_NODE_COMPILE` [bool]: Forces compilation from source instead of preferring pre-compiled binaries. Can also be set across all languages with [`RTX_NODE__COMPILE`](https://github.com/jdx/rtx#rtx_node_compile1)
- `RTX_NODE_CONCURRENCY` [uint]: How many jobs should be used in compilation. Defaults to half the computer cores
- `RTX_NODE_DEFAULT_PACKAGES_FILE` [string]: location of default packages file, defaults to `$HOME/.default-npm-packages`
- `RTX_NODE_MIRROR_URL` [string]: overrides the default mirror used for downloading the distributions
Expand All @@ -37,6 +37,21 @@ required system dependencies.
- `RTX_NODE_MAKE_OPTS` [string]: Additional `make` options.
- `RTX_NODE_MAKE_INSTALL_OPTS` [string]: Additional `make install` options.

## Tool Options

The following are example options that can be set in `.rtx.toml` files.
Note env vars take precedence over these.

```toml
[tool.node]
version = "20"
compile = true # do not use pre-compiled binaries
cflags = "-O3 -march=native" # additional CFLAGS options
configure_opts = "--debug" # command line options to pass to ./configure
make_opts = "-j 4" # command line options to pass to make
make_install_opts = "-j 4" # command line options to pass to make install
```

## Default node packages

rtx-node can automatically install a default set of npm packages right after installing a node version. To enable this feature, provide a `$HOME/.default-npm-packages` file that lists one package per line, for example:
Expand Down
2 changes: 1 addition & 1 deletion e2e/test_nodejs
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,5 @@ rtx uninstall node
rtx i node
assert "rtx x -- node --version" "v20.0.0"
# rtx uninstall node
# RTX_NODE_FORCE_COMPILE=1 rtx i node
# RTX_NODE_COMPILE=1 rtx i node
# assert "rtx x -- node --version" "v20.0.0"
5 changes: 3 additions & 2 deletions src/cli/direnv/exec.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use color_eyre::eyre::{eyre, Result};
use duct::Expression;
use eyre::Result;
use eyre::WrapErr;
use serde_derive::Deserialize;

use crate::cli::command::Command;
Expand Down Expand Up @@ -37,7 +38,7 @@ impl Command for DirenvExec {

let json = cmd!("direnv", "watch", "json", ".tool-versions")
.read()
.map_err(|err| eyre!("error running direnv watch: {}", err))?;
.wrap_err("error running direnv watch")?;
let w: DirenvWatches = serde_json::from_str(&json)?;
cmd = cmd.env("DIRENV_WATCHES", w.watches);

Expand Down
2 changes: 1 addition & 1 deletion src/cli/local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ pub fn get_parent_path() -> Result<PathBuf> {
filenames.push(RTX_DEFAULT_TOOL_VERSIONS_FILENAME.as_str());
}
file::find_up(&dirs::CURRENT, &filenames)
.with_context(|| eyre!("no {} file found", filenames.join(" or "),))
.wrap_err_with(|| eyre!("no {} file found", filenames.join(" or "),))
}

#[allow(clippy::too_many_arguments)]
Expand Down
6 changes: 3 additions & 3 deletions src/cli/uninstall.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use color_eyre::eyre::{bail, eyre, Result};
use console::style;
use eyre::{Result, WrapErr};

use crate::cli::args::tool::{ToolArg, ToolArgParser};
use crate::cli::command::Command;
Expand Down Expand Up @@ -92,13 +92,13 @@ impl Command for Uninstall {
plugin.decorate_progress_bar(&mut pr, Some(&tv));
if let Err(err) = plugin.uninstall_version(&config, &tv, &pr, self.dry_run) {
pr.error(err.to_string());
return Err(eyre!(err).wrap_err(format!("failed to uninstall {}", &tv)));
return Err(eyre!(err).wrap_err(format!("failed to uninstall {tv}")));
}
pr.finish_with_message("uninstalled");
}

let ts = ToolsetBuilder::new().build(&mut config)?;
shims::reshim(&config, &ts).map_err(|err| eyre!("failed to reshim: {}", err))?;
shims::reshim(&config, &ts).wrap_err("failed to reshim")?;
runtime_symlinks::rebuild(&config)?;

Ok(())
Expand Down
9 changes: 5 additions & 4 deletions src/cli/upgrade.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use std::collections::HashSet;
use std::sync::Arc;

use color_eyre::eyre::{eyre, Result};
use eyre::Result;
use eyre::WrapErr;
use itertools::Itertools;
use rayon::prelude::*;
use rayon::ThreadPoolBuilder;
Expand Down Expand Up @@ -64,7 +65,7 @@ impl Upgrade {
self.install_new_versions(config, &mpr, outdated)?;

let ts = ToolsetBuilder::new().with_args(&self.tool).build(config)?;
shims::reshim(config, &ts).map_err(|err| eyre!("failed to reshim: {}", err))?;
shims::reshim(config, &ts).wrap_err("failed to reshim")?;
runtime_symlinks::rebuild(config)?;

Ok(())
Expand Down Expand Up @@ -112,7 +113,7 @@ impl Upgrade {
Ok(_) => Ok(()),
Err(err) => {
pr.error(err.to_string());
Err(err.wrap_err(format!("failed to install {}", tv)))
Err(err.wrap_err(format!("failed to install {tv}")))
}
}
}
Expand All @@ -132,7 +133,7 @@ impl Upgrade {
}
Err(err) => {
pr.error(err.to_string());
Err(err.wrap_err(format!("failed to uninstall {}", tv)))
Err(err.wrap_err(format!("failed to uninstall {tv}")))
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ impl<'a> CmdLineRunner<'a> {
let mut cp = self
.cmd
.spawn()
.with_context(|| format!("failed to execute command: {self}"))?;
.wrap_err_with(|| format!("failed to execute command: {self}"))?;
let stdout = BufReader::new(cp.stdout.take().unwrap());
let stderr = BufReader::new(cp.stderr.take().unwrap());
let (tx, rx) = channel();
Expand Down
26 changes: 15 additions & 11 deletions src/config/config_file/rtx_toml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ use std::time::Duration;

use color_eyre::eyre::eyre;
use color_eyre::{Result, Section};
use console::style;
use eyre::WrapErr;
use log::LevelFilter;
use tera::Context;
use toml_edit::{table, value, Array, Document, Item, Value};
Expand All @@ -13,7 +15,7 @@ use crate::config::config_file::{ConfigFile, ConfigFileType};
use crate::config::settings::SettingsBuilder;
use crate::config::{config_file, global_config_files, AliasMap, MissingRuntimeBehavior};
use crate::errors::Error::UntrustedConfig;
use crate::file::create_dir_all;
use crate::file::{create_dir_all, display_path};
use crate::plugins::{unalias_plugin, PluginName};
use crate::tera::{get_tera, BASE_CONTEXT};
use crate::toolset::{
Expand Down Expand Up @@ -334,13 +336,14 @@ impl RtxToml {
if k == "version" || k == "path" || k == "prefix" || k == "ref" {
continue;
}
match v.as_str() {
Some(s) => {
let s = self.parse_template(key, s)?;
opts.insert(k.into(), s);
}
_ => parse_error!(format!("{}.{}", key, k), v, "string")?,
}
let s = if let Some(s) = v.as_str() {
self.parse_template(key, s)?
} else if let Some(b) = v.as_bool() {
b.to_string()
} else {
parse_error!(key, v, "string or bool")?
};
opts.insert(k.into(), s);
}
}
_ => match v {
Expand Down Expand Up @@ -631,7 +634,7 @@ impl RtxToml {
let dir = self.path.parent().unwrap();
let output = get_tera(dir)
.render_str(input, &self.context)
.map_err(|err| eyre!("failed to parse template: {k}='{}': {}", input, err))?;
.wrap_err_with(|| eyre!("failed to parse template: {k}='{input}'"))?;
Ok(output)
}

Expand All @@ -643,8 +646,9 @@ impl RtxToml {
}
if cmd != "hook-env" {
let ans = prompt::confirm(&format!(
"Config file {} is not trusted. Would you like to trust it?",
self.path.display()
"{} {} is not trusted. Trust it?",
style("rtx").yellow().for_stderr(),
display_path(&self.path)
))?;
if ans {
config_file::trust(self.path.as_path())?;
Expand Down
22 changes: 9 additions & 13 deletions src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ use std::path::PathBuf;
use std::sync::Arc;
use std::thread;

use color_eyre::eyre::{eyre, Result};
use eyre::Context;
use eyre::Result;
use indexmap::IndexMap;
use itertools::Itertools;
use once_cell::sync::OnceCell;
Expand Down Expand Up @@ -266,14 +267,8 @@ fn load_rtxrc() -> Result<RtxToml> {
trace!("settings does not exist {:?}", settings_path);
Ok(RtxToml::init(&settings_path, is_trusted))
}
true => match RtxToml::from_file(&settings_path, is_trusted) {
Ok(cf) => Ok(cf),
Err(err) => Err(eyre!(
"Error parsing {}: {:#}",
display_path(&settings_path),
err
)),
},
true => RtxToml::from_file(&settings_path, is_trusted)
.wrap_err_with(|| format!("Error parsing {}", display_path(&settings_path))),
}
}

Expand Down Expand Up @@ -402,10 +397,11 @@ fn load_all_config_files(
// already parsed so just return it
Some(cf) => Ok((f, cf)),
// need to parse this config file
None => match parse_config_file(&f, settings, legacy_filenames, tools) {
Ok(cf) => Ok((f, cf)),
Err(err) => Err(eyre!("error reading config: {}\n{err:#}", display_path(&f))),
},
None => {
let cf = parse_config_file(&f, settings, legacy_filenames, tools)
.wrap_err_with(|| format!("error parsing config file: {}", display_path(&f)))?;
Ok((f, cf))
}
})
.collect::<Vec<Result<_>>>()
.into_iter()
Expand Down
20 changes: 10 additions & 10 deletions src/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,14 +116,13 @@ pub static RTX_ALWAYS_KEEP_DOWNLOAD: Lazy<bool> =
Lazy::new(|| var_is_true("RTX_ALWAYS_KEEP_DOWNLOAD"));
pub static RTX_ALWAYS_KEEP_INSTALL: Lazy<bool> =
Lazy::new(|| var_is_true("RTX_ALWAYS_KEEP_INSTALL"));
pub static RTX_ALL_FORCE_COMPILE: Lazy<bool> =
Lazy::new(|| match var_option_bool("RTX_ALL_FORCE_COMPILE") {
Some(v) => v,
None => matches!(
linux_distro().unwrap_or_default().as_str(),
"alpine" | "nixos"
),
});
pub static RTX_ALL_COMPILE: Lazy<bool> = Lazy::new(|| match var_option_bool("RTX_ALL_COMPILE") {
Some(v) => v,
None => matches!(
linux_distro().unwrap_or_default().as_str(),
"alpine" | "nixos"
),
});
#[allow(unused)]
pub static GITHUB_API_TOKEN: Lazy<Option<String>> = Lazy::new(|| var("GITHUB_API_TOKEN").ok());

Expand Down Expand Up @@ -177,8 +176,9 @@ pub static RTX_NODE_NINJA: Lazy<bool> = Lazy::new(|| match var_option_bool("RTX_
None => is_ninja_on_path(),
});
pub static RTX_NODE_VERIFY: Lazy<bool> = Lazy::new(|| !var_is_false("RTX_NODE_VERIFY"));
pub static RTX_NODE_FORCE_COMPILE: Lazy<bool> =
Lazy::new(|| *RTX_ALL_FORCE_COMPILE || var_is_true("RTX_NODE_FORCE_COMPILE"));
pub static RTX_NODE_COMPILE: Lazy<bool> = Lazy::new(|| {
*RTX_ALL_COMPILE || var_is_true("RTX_NODE_COMPILE") || var_is_true("RTX_NODE_FORCE_COMPILE")
});
pub static RTX_NODE_CFLAGS: Lazy<Option<String>> =
Lazy::new(|| var("RTX_NODE_CFLAGS").or_else(|_| var("NODE_CFLAGS")).ok());
pub static RTX_NODE_CONFIGURE_OPTS: Lazy<Option<String>> = Lazy::new(|| {
Expand Down
38 changes: 23 additions & 15 deletions src/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ pub fn remove_all<P: AsRef<Path>>(path: P) -> Result<()> {
remove_file(path)?;
}
Ok(x) if x.is_dir() => {
trace!("rm -rf {}", path.display());
trace!("rm -rf {}", display_path(path));
fs::remove_dir_all(path)
.with_context(|| format!("failed rm -rf: {}", path.display()))?;
.wrap_err_with(|| format!("failed rm -rf: {}", display_path(path)))?;
}
_ => {}
};
Expand All @@ -30,14 +30,14 @@ pub fn remove_all<P: AsRef<Path>>(path: P) -> Result<()> {

pub fn remove_file<P: AsRef<Path>>(path: P) -> Result<()> {
let path = path.as_ref();
trace!("rm {}", path.display());
fs::remove_file(path).with_context(|| format!("failed rm: {}", path.display()))
trace!("rm {}", display_path(path));
fs::remove_file(path).wrap_err_with(|| format!("failed rm: {}", display_path(path)))
}

pub fn remove_dir<P: AsRef<Path>>(path: P) -> Result<()> {
let path = path.as_ref();
trace!("rmdir {}", path.display());
fs::remove_dir(path).with_context(|| format!("failed rmdir: {}", path.display()))
trace!("rmdir {}", display_path(path));
fs::remove_dir(path).wrap_err_with(|| format!("failed rmdir: {}", display_path(path)))
}

pub fn remove_all_with_warning<P: AsRef<Path>>(path: P) -> Result<()> {
Expand All @@ -51,26 +51,33 @@ pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> Result<()> {
let from = from.as_ref();
let to = to.as_ref();
trace!("mv {} {}", from.display(), to.display());
fs::rename(from, to)
.with_context(|| format!("failed rename: {} -> {}", from.display(), to.display()))
fs::rename(from, to).wrap_err_with(|| {
format!(
"failed rename: {} -> {}",
display_path(from),
display_path(to)
)
})
}

pub fn write<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> Result<()> {
let path = path.as_ref();
trace!("write {}", path.display());
fs::write(path, contents).with_context(|| format!("failed write: {}", path.display()))
trace!("write {}", display_path(path));
fs::write(path, contents).wrap_err_with(|| format!("failed write: {}", display_path(path)))
}

pub fn read_to_string<P: AsRef<Path>>(path: P) -> Result<String> {
let path = path.as_ref();
trace!("cat {}", path.display());
fs::read_to_string(path).with_context(|| format!("failed read_to_string: {}", path.display()))
trace!("cat {}", display_path(path));
fs::read_to_string(path)
.wrap_err_with(|| format!("failed read_to_string: {}", display_path(path)))
}

pub fn create_dir_all<P: AsRef<Path>>(path: P) -> Result<()> {
let path = path.as_ref();
trace!("mkdir -p {}", path.display());
fs::create_dir_all(path).with_context(|| format!("failed create_dir_all: {}", path.display()))
trace!("mkdir -p {}", display_path(path));
fs::create_dir_all(path)
.wrap_err_with(|| format!("failed create_dir_all: {}", display_path(path)))
}

pub fn basename(path: &Path) -> Option<String> {
Expand Down Expand Up @@ -98,7 +105,8 @@ pub fn replace_path<P: AsRef<Path>>(path: P) -> PathBuf {
pub fn touch_dir(dir: &Path) -> Result<()> {
trace!("touch {}", dir.display());
let now = FileTime::now();
set_file_times(dir, now, now).with_context(|| format!("failed to touch dir: {}", dir.display()))
set_file_times(dir, now, now)
.wrap_err_with(|| format!("failed to touch dir: {}", display_path(dir)))
}

pub fn modified_duration(path: &Path) -> Result<Duration> {
Expand Down
19 changes: 9 additions & 10 deletions src/hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ use std::fs::File;
use std::hash::{Hash, Hasher};
use std::path::Path;

use color_eyre::eyre::{bail, Result};
use eyre::Result;
use rayon::prelude::*;
use sha2::{Digest, Sha256};

use crate::file::display_path;

pub fn hash_to_str<T: Hash>(t: &T) -> String {
let mut s = DefaultHasher::new();
t.hash(&mut s);
Expand All @@ -20,19 +22,16 @@ pub fn file_hash_sha256(path: &Path) -> Result<String> {
let mut hasher = Sha256::new();
std::io::copy(&mut file, &mut hasher)?;
let hash = hasher.finalize();
Ok(format!("{:x}", hash))
Ok(format!("{hash:x}"))
}

pub fn ensure_checksum_sha256(path: &Path, checksum: &str) -> Result<()> {
let actual = file_hash_sha256(path)?;
if actual != checksum {
bail!(
"Checksum mismatch for file {:?}:\nExpected: {}\nActual: {}",
path,
checksum,
actual
);
}
ensure!(
actual == checksum,
"Checksum mismatch for file {}:\nExpected: {checksum}\nActual: {actual}",
display_path(path),
);
Ok(())
}

Expand Down
Loading

0 comments on commit d547412

Please sign in to comment.