mirror of
https://github.com/pesde-pkg/pesde.git
synced 2025-05-04 10:33:47 +01:00
feat: add cross runtime script execution
Adds a new mechanism which simplifies executing scripts for different runtimes. Additionally, supports Lune's 0.9.0 change of removing "--" from the args.
This commit is contained in:
parent
6aeedd8602
commit
5c74a3762b
19 changed files with 434 additions and 147 deletions
|
@ -1,5 +1,7 @@
|
||||||
use crate::cli::{
|
use crate::cli::{
|
||||||
|
compatible_runtime,
|
||||||
config::read_config,
|
config::read_config,
|
||||||
|
get_project_engines,
|
||||||
reporters::{self, CliReporter},
|
reporters::{self, CliReporter},
|
||||||
VersionedPackageName,
|
VersionedPackageName,
|
||||||
};
|
};
|
||||||
|
@ -28,7 +30,6 @@ use std::{
|
||||||
env::current_dir,
|
env::current_dir,
|
||||||
ffi::OsString,
|
ffi::OsString,
|
||||||
io::{Stderr, Write as _},
|
io::{Stderr, Write as _},
|
||||||
process::Command,
|
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -57,7 +58,7 @@ impl ExecuteCommand {
|
||||||
|
|
||||||
let refreshed_sources = RefreshedSources::new();
|
let refreshed_sources = RefreshedSources::new();
|
||||||
|
|
||||||
let (tempdir, bin_path) = reporters::run_with_reporter_and_writer(
|
let (tempdir, runtime, bin_path) = reporters::run_with_reporter_and_writer(
|
||||||
std::io::stderr(),
|
std::io::stderr(),
|
||||||
|multi_progress, root_progress, reporter| async {
|
|multi_progress, root_progress, reporter| async {
|
||||||
let multi_progress = multi_progress;
|
let multi_progress = multi_progress;
|
||||||
|
@ -152,6 +153,8 @@ impl ExecuteCommand {
|
||||||
project: project.clone(),
|
project: project.clone(),
|
||||||
path: tempdir.path().into(),
|
path: tempdir.path().into(),
|
||||||
id: id.clone(),
|
id: id.clone(),
|
||||||
|
// HACK: the pesde package source doesn't use the engines, so we can just use an empty map
|
||||||
|
engines: Default::default(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -175,7 +178,7 @@ impl ExecuteCommand {
|
||||||
project
|
project
|
||||||
.download_and_link(
|
.download_and_link(
|
||||||
&graph,
|
&graph,
|
||||||
DownloadAndLinkOptions::<CliReporter<Stderr>, ()>::new(reqwest)
|
DownloadAndLinkOptions::<CliReporter<Stderr>, ()>::new(reqwest.clone())
|
||||||
.reporter(reporter)
|
.reporter(reporter)
|
||||||
.refreshed_sources(refreshed_sources)
|
.refreshed_sources(refreshed_sources)
|
||||||
.install_dependencies_mode(InstallDependenciesMode::Prod),
|
.install_dependencies_mode(InstallDependenciesMode::Prod),
|
||||||
|
@ -183,7 +186,18 @@ impl ExecuteCommand {
|
||||||
.await
|
.await
|
||||||
.context("failed to download and link dependencies")?;
|
.context("failed to download and link dependencies")?;
|
||||||
|
|
||||||
anyhow::Ok((tempdir, bin_path.to_relative_path_buf()))
|
let manifest = project
|
||||||
|
.deser_manifest()
|
||||||
|
.await
|
||||||
|
.context("failed to deserialize manifest")?;
|
||||||
|
|
||||||
|
let engines = get_project_engines(&manifest, &reqwest).await?;
|
||||||
|
|
||||||
|
anyhow::Ok((
|
||||||
|
tempdir,
|
||||||
|
compatible_runtime(target.kind(), &engines)?,
|
||||||
|
bin_path.to_relative_path_buf(),
|
||||||
|
))
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -200,13 +214,11 @@ impl ExecuteCommand {
|
||||||
)
|
)
|
||||||
.context("failed to write to tempfile")?;
|
.context("failed to write to tempfile")?;
|
||||||
|
|
||||||
let status = Command::new("lune")
|
let status = runtime
|
||||||
.arg("run")
|
.prepare_command(caller.path().as_os_str(), self.args)
|
||||||
.arg(caller.path())
|
|
||||||
.arg("--")
|
|
||||||
.args(&self.args)
|
|
||||||
.current_dir(current_dir().context("failed to get current directory")?)
|
.current_dir(current_dir().context("failed to get current directory")?)
|
||||||
.status()
|
.status()
|
||||||
|
.await
|
||||||
.context("failed to run script")?;
|
.context("failed to run script")?;
|
||||||
|
|
||||||
drop(caller);
|
drop(caller);
|
||||||
|
|
|
@ -225,6 +225,8 @@ impl InitCommand {
|
||||||
// HACK: the pesde package source doesn't use the path, so we can just use an empty one
|
// HACK: the pesde package source doesn't use the path, so we can just use an empty one
|
||||||
path: Path::new("").into(),
|
path: Path::new("").into(),
|
||||||
id: id.clone(),
|
id: id.clone(),
|
||||||
|
// HACK: the pesde package source doesn't use the engines, so we can just use an empty map
|
||||||
|
engines: Default::default(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -268,6 +270,8 @@ impl InitCommand {
|
||||||
.to_string(),
|
.to_string(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: add engines
|
||||||
} else {
|
} else {
|
||||||
println!(
|
println!(
|
||||||
"{ERROR_PREFIX}: no scripts package configured, this can cause issues with Roblox compatibility"
|
"{ERROR_PREFIX}: no scripts package configured, this can cause issues with Roblox compatibility"
|
||||||
|
|
|
@ -105,7 +105,7 @@ impl Subcommand {
|
||||||
Subcommand::Update(update) => update.run(project, reqwest).await,
|
Subcommand::Update(update) => update.run(project, reqwest).await,
|
||||||
Subcommand::Outdated(outdated) => outdated.run(project).await,
|
Subcommand::Outdated(outdated) => outdated.run(project).await,
|
||||||
Subcommand::List(list) => list.run(project).await,
|
Subcommand::List(list) => list.run(project).await,
|
||||||
Subcommand::Run(run) => run.run(project).await,
|
Subcommand::Run(run) => run.run(project, reqwest).await,
|
||||||
Subcommand::Publish(publish) => publish.run(project, reqwest).await,
|
Subcommand::Publish(publish) => publish.run(project, reqwest).await,
|
||||||
Subcommand::Yank(yank) => yank.run(project, reqwest).await,
|
Subcommand::Yank(yank) => yank.run(project, reqwest).await,
|
||||||
Subcommand::Deprecate(deprecate) => deprecate.run(project, reqwest).await,
|
Subcommand::Deprecate(deprecate) => deprecate.run(project, reqwest).await,
|
||||||
|
|
|
@ -127,6 +127,9 @@ impl PublishCommand {
|
||||||
|
|
||||||
match up_to_date_lockfile(project).await? {
|
match up_to_date_lockfile(project).await? {
|
||||||
Some(lockfile) => {
|
Some(lockfile) => {
|
||||||
|
let engines =
|
||||||
|
Arc::new(crate::cli::get_project_engines(&manifest, &reqwest).await?);
|
||||||
|
|
||||||
let mut tasks = lockfile
|
let mut tasks = lockfile
|
||||||
.graph
|
.graph
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -142,6 +145,7 @@ impl PublishCommand {
|
||||||
let id = Arc::new(id.clone());
|
let id = Arc::new(id.clone());
|
||||||
let node = node.clone();
|
let node = node.clone();
|
||||||
let refreshed_sources = refreshed_sources.clone();
|
let refreshed_sources = refreshed_sources.clone();
|
||||||
|
let engines = engines.clone();
|
||||||
|
|
||||||
async move {
|
async move {
|
||||||
let source = node.pkg_ref.source();
|
let source = node.pkg_ref.source();
|
||||||
|
@ -161,6 +165,7 @@ impl PublishCommand {
|
||||||
project,
|
project,
|
||||||
path: container_folder.into(),
|
path: container_folder.into(),
|
||||||
id,
|
id,
|
||||||
|
engines,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
|
@ -1,20 +1,21 @@
|
||||||
use crate::cli::{style::WARN_STYLE, up_to_date_lockfile};
|
use crate::cli::{compatible_runtime, get_project_engines, style::WARN_STYLE, up_to_date_lockfile};
|
||||||
use anyhow::Context as _;
|
use anyhow::Context as _;
|
||||||
use clap::Args;
|
use clap::Args;
|
||||||
use fs_err::tokio as fs;
|
use fs_err::tokio as fs;
|
||||||
use futures::{StreamExt as _, TryStreamExt as _};
|
use futures::{StreamExt as _, TryStreamExt as _};
|
||||||
use pesde::{
|
use pesde::{
|
||||||
|
engine::runtime::Runtime,
|
||||||
errors::{ManifestReadError, WorkspaceMembersError},
|
errors::{ManifestReadError, WorkspaceMembersError},
|
||||||
linking::generator::generate_bin_linking_module,
|
linking::generator::generate_bin_linking_module,
|
||||||
manifest::Alias,
|
manifest::{Alias, Manifest},
|
||||||
names::{PackageName, PackageNames},
|
names::{PackageName, PackageNames},
|
||||||
|
scripts::parse_script,
|
||||||
source::traits::{GetTargetOptions, PackageRef as _, PackageSource as _, RefreshOptions},
|
source::traits::{GetTargetOptions, PackageRef as _, PackageSource as _, RefreshOptions},
|
||||||
Project, MANIFEST_FILE_NAME,
|
Project, MANIFEST_FILE_NAME,
|
||||||
};
|
};
|
||||||
use relative_path::RelativePathBuf;
|
use relative_path::RelativePathBuf;
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashSet, env::current_dir, ffi::OsString, io::Write as _, path::Path,
|
collections::HashSet, env::current_dir, ffi::OsString, io::Write as _, path::Path, sync::Arc,
|
||||||
process::Command,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Args)]
|
#[derive(Debug, Args)]
|
||||||
|
@ -29,8 +30,15 @@ pub struct RunCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RunCommand {
|
impl RunCommand {
|
||||||
pub async fn run(self, project: Project) -> anyhow::Result<()> {
|
pub async fn run(self, project: Project, reqwest: reqwest::Client) -> anyhow::Result<()> {
|
||||||
let run = |root: &Path, file_path: &Path| -> ! {
|
let manifest = project
|
||||||
|
.deser_manifest()
|
||||||
|
.await
|
||||||
|
.context("failed to deserialize manifest")?;
|
||||||
|
|
||||||
|
let engines = Arc::new(get_project_engines(&manifest, &reqwest).await?);
|
||||||
|
|
||||||
|
let run = async |runtime: Runtime, root: &Path, file_path: &Path| -> ! {
|
||||||
let mut caller = tempfile::NamedTempFile::new().expect("failed to create tempfile");
|
let mut caller = tempfile::NamedTempFile::new().expect("failed to create tempfile");
|
||||||
caller
|
caller
|
||||||
.write_all(
|
.write_all(
|
||||||
|
@ -42,13 +50,11 @@ impl RunCommand {
|
||||||
)
|
)
|
||||||
.expect("failed to write to tempfile");
|
.expect("failed to write to tempfile");
|
||||||
|
|
||||||
let status = Command::new("lune")
|
let status = runtime
|
||||||
.arg("run")
|
.prepare_command(caller.path().as_os_str(), self.args)
|
||||||
.arg(caller.path())
|
|
||||||
.arg("--")
|
|
||||||
.args(&self.args)
|
|
||||||
.current_dir(current_dir().expect("failed to get current directory"))
|
.current_dir(current_dir().expect("failed to get current directory"))
|
||||||
.status()
|
.status()
|
||||||
|
.await
|
||||||
.expect("failed to run script");
|
.expect("failed to run script");
|
||||||
|
|
||||||
drop(caller);
|
drop(caller);
|
||||||
|
@ -57,11 +63,13 @@ impl RunCommand {
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(package_or_script) = self.package_or_script else {
|
let Some(package_or_script) = self.package_or_script else {
|
||||||
if let Some(script_path) = project.deser_manifest().await?.target.bin_path() {
|
if let Some(script_path) = manifest.target.bin_path() {
|
||||||
run(
|
run(
|
||||||
|
compatible_runtime(manifest.target.kind(), &engines)?,
|
||||||
project.package_dir(),
|
project.package_dir(),
|
||||||
&script_path.to_path(project.package_dir()),
|
&script_path.to_path(project.package_dir()),
|
||||||
);
|
)
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
anyhow::bail!("no package or script specified, and no bin path found in manifest")
|
anyhow::bail!("no package or script specified, and no bin path found in manifest")
|
||||||
|
@ -130,6 +138,7 @@ impl RunCommand {
|
||||||
project,
|
project,
|
||||||
path: container_folder.as_path().into(),
|
path: container_folder.as_path().into(),
|
||||||
id: id.into(),
|
id: id.into(),
|
||||||
|
engines: engines.clone(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -140,15 +149,20 @@ impl RunCommand {
|
||||||
|
|
||||||
let path = bin_path.to_path(&container_folder);
|
let path = bin_path.to_path(&container_folder);
|
||||||
|
|
||||||
run(&path, &path);
|
run(compatible_runtime(target.kind(), &engines)?, &path, &path).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(manifest) = project.deser_manifest().await {
|
if let Ok(mut manifest) = project.deser_manifest().await {
|
||||||
if let Some(script_path) = manifest.scripts.get(&package_or_script) {
|
if let Some(script) = manifest.scripts.remove(&package_or_script) {
|
||||||
|
let (runtime, script_path) =
|
||||||
|
parse_script(script, &engines).context("failed to get script info")?;
|
||||||
|
|
||||||
run(
|
run(
|
||||||
|
runtime,
|
||||||
project.package_dir(),
|
project.package_dir(),
|
||||||
&script_path.to_path(project.package_dir()),
|
&script_path.to_path(project.package_dir()),
|
||||||
);
|
)
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,6 +226,17 @@ impl RunCommand {
|
||||||
project.package_dir().to_path_buf()
|
project.package_dir().to_path_buf()
|
||||||
};
|
};
|
||||||
|
|
||||||
run(&root, &path);
|
let manifest = fs::read_to_string(root.join(MANIFEST_FILE_NAME))
|
||||||
|
.await
|
||||||
|
.context("failed to read manifest at root")?;
|
||||||
|
let manifest = toml::de::from_str::<Manifest>(&manifest)
|
||||||
|
.context("failed to deserialize manifest at root")?;
|
||||||
|
|
||||||
|
run(
|
||||||
|
compatible_runtime(manifest.target.kind(), &engines)?,
|
||||||
|
&root,
|
||||||
|
&path,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,11 @@ pub struct SelfInstallCommand {
|
||||||
|
|
||||||
impl SelfInstallCommand {
|
impl SelfInstallCommand {
|
||||||
pub async fn run(self) -> anyhow::Result<()> {
|
pub async fn run(self) -> anyhow::Result<()> {
|
||||||
|
let bin_dir = crate::cli::bin_dir()?;
|
||||||
|
let bin_dir = bin_dir
|
||||||
|
.to_str()
|
||||||
|
.context("bin directory path contains invalid characters")?;
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
{
|
{
|
||||||
if !self.skip_add_to_path {
|
if !self.skip_add_to_path {
|
||||||
|
@ -24,17 +29,11 @@ impl SelfInstallCommand {
|
||||||
use anyhow::Context as _;
|
use anyhow::Context as _;
|
||||||
use windows_registry::CURRENT_USER;
|
use windows_registry::CURRENT_USER;
|
||||||
|
|
||||||
let bin_dir = crate::cli::bin_dir()?;
|
|
||||||
|
|
||||||
let env = CURRENT_USER
|
let env = CURRENT_USER
|
||||||
.create("Environment")
|
.create("Environment")
|
||||||
.context("failed to open Environment key")?;
|
.context("failed to open Environment key")?;
|
||||||
let path = env.get_string("Path").context("failed to get Path value")?;
|
let path = env.get_string("Path").context("failed to get Path value")?;
|
||||||
|
|
||||||
let bin_dir = bin_dir
|
|
||||||
.to_str()
|
|
||||||
.context("bin directory path contains invalid characters")?;
|
|
||||||
|
|
||||||
let exists = path.split(';').any(|part| part == bin_dir);
|
let exists = path.split(';').any(|part| part == bin_dir);
|
||||||
|
|
||||||
if !exists {
|
if !exists {
|
||||||
|
@ -68,7 +67,7 @@ and then restart your shell.
|
||||||
",
|
",
|
||||||
CLI_STYLE.apply_to(env!("CARGO_BIN_NAME")),
|
CLI_STYLE.apply_to(env!("CARGO_BIN_NAME")),
|
||||||
ADDED_STYLE.apply_to(env!("CARGO_PKG_VERSION")),
|
ADDED_STYLE.apply_to(env!("CARGO_PKG_VERSION")),
|
||||||
style(format!(r#"export PATH="$PATH:$HOME/{HOME_DIR}/bin""#)).green(),
|
style(format!(r#"export PATH="$PATH:{bin_dir}""#)).green(),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use super::files::make_executable;
|
use super::files::make_executable;
|
||||||
use crate::cli::{
|
use crate::cli::{
|
||||||
bin_dir, dep_type_to_key,
|
bin_dir, dep_type_to_key,
|
||||||
reporters::{self, run_with_reporter, CliReporter},
|
reporters::{self, CliReporter},
|
||||||
resolve_overrides, run_on_workspace_members,
|
resolve_overrides, run_on_workspace_members,
|
||||||
style::{ADDED_STYLE, REMOVED_STYLE, WARN_PREFIX},
|
style::{ADDED_STYLE, REMOVED_STYLE, WARN_PREFIX},
|
||||||
up_to_date_lockfile,
|
up_to_date_lockfile,
|
||||||
|
@ -25,7 +25,7 @@ use pesde::{
|
||||||
version_matches, Project, RefreshedSources, MANIFEST_FILE_NAME,
|
version_matches, Project, RefreshedSources, MANIFEST_FILE_NAME,
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
collections::{BTreeMap, BTreeSet, HashMap, HashSet},
|
collections::{BTreeMap, BTreeSet, HashSet},
|
||||||
num::NonZeroUsize,
|
num::NonZeroUsize,
|
||||||
path::Path,
|
path::Path,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
|
@ -181,49 +181,7 @@ pub async fn install(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let progress_prefix = format!("{} {}: ", manifest.name, manifest.target);
|
let resolved_engine_versions = Arc::new(super::get_project_engines(&manifest, &reqwest).await?);
|
||||||
|
|
||||||
#[cfg(feature = "version-management")]
|
|
||||||
let resolved_engine_versions = run_with_reporter(|_, root_progress, reporter| async {
|
|
||||||
let root_progress = root_progress;
|
|
||||||
let reporter = reporter;
|
|
||||||
|
|
||||||
root_progress.set_prefix(progress_prefix.clone());
|
|
||||||
root_progress.reset();
|
|
||||||
root_progress.set_message("update engines");
|
|
||||||
|
|
||||||
let mut tasks = manifest
|
|
||||||
.engines
|
|
||||||
.iter()
|
|
||||||
.map(|(engine, req)| {
|
|
||||||
let engine = *engine;
|
|
||||||
let req = req.clone();
|
|
||||||
let reqwest = reqwest.clone();
|
|
||||||
let reporter = reporter.clone();
|
|
||||||
|
|
||||||
async move {
|
|
||||||
let version = crate::cli::version::get_or_download_engine(
|
|
||||||
&reqwest, engine, req, reporter,
|
|
||||||
)
|
|
||||||
.await?
|
|
||||||
.1;
|
|
||||||
crate::cli::version::make_linker_if_needed(engine).await?;
|
|
||||||
|
|
||||||
Ok::<_, anyhow::Error>((engine, version))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect::<JoinSet<_>>();
|
|
||||||
|
|
||||||
let mut resolved_engine_versions = HashMap::new();
|
|
||||||
|
|
||||||
while let Some(task) = tasks.join_next().await {
|
|
||||||
let (engine, version) = task.unwrap()?;
|
|
||||||
resolved_engine_versions.insert(engine, version);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok::<_, anyhow::Error>(resolved_engine_versions)
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let overrides = resolve_overrides(&manifest)?;
|
let overrides = resolve_overrides(&manifest)?;
|
||||||
|
|
||||||
|
@ -232,7 +190,7 @@ pub async fn install(
|
||||||
let multi = multi;
|
let multi = multi;
|
||||||
let root_progress = root_progress;
|
let root_progress = root_progress;
|
||||||
|
|
||||||
root_progress.set_prefix(progress_prefix);
|
root_progress.set_prefix(format!("{} {}: ", manifest.name, manifest.target));
|
||||||
root_progress.reset();
|
root_progress.reset();
|
||||||
root_progress.set_message("resolve");
|
root_progress.set_message("resolve");
|
||||||
|
|
||||||
|
@ -318,7 +276,8 @@ pub async fn install(
|
||||||
.refreshed_sources(refreshed_sources.clone())
|
.refreshed_sources(refreshed_sources.clone())
|
||||||
.install_dependencies_mode(options.install_dependencies_mode)
|
.install_dependencies_mode(options.install_dependencies_mode)
|
||||||
.network_concurrency(options.network_concurrency)
|
.network_concurrency(options.network_concurrency)
|
||||||
.force(options.force || has_irrecoverable_changes),
|
.force(options.force || has_irrecoverable_changes)
|
||||||
|
.engines(resolved_engine_versions.clone()),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.context("failed to download and link dependencies")?;
|
.context("failed to download and link dependencies")?;
|
||||||
|
|
|
@ -5,6 +5,10 @@ use crate::cli::{
|
||||||
use anyhow::Context as _;
|
use anyhow::Context as _;
|
||||||
use futures::StreamExt as _;
|
use futures::StreamExt as _;
|
||||||
use pesde::{
|
use pesde::{
|
||||||
|
engine::{
|
||||||
|
runtime::{Runtime, RuntimeKind},
|
||||||
|
EngineKind,
|
||||||
|
},
|
||||||
errors::ManifestReadError,
|
errors::ManifestReadError,
|
||||||
lockfile::Lockfile,
|
lockfile::Lockfile,
|
||||||
manifest::{
|
manifest::{
|
||||||
|
@ -19,8 +23,10 @@ use pesde::{
|
||||||
Project, DEFAULT_INDEX_NAME,
|
Project, DEFAULT_INDEX_NAME,
|
||||||
};
|
};
|
||||||
use relative_path::RelativePathBuf;
|
use relative_path::RelativePathBuf;
|
||||||
|
use reporters::run_with_reporter;
|
||||||
|
use semver::Version;
|
||||||
use std::{
|
use std::{
|
||||||
collections::{BTreeMap, HashSet},
|
collections::{BTreeMap, HashMap, HashSet},
|
||||||
future::Future,
|
future::Future,
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
|
@ -361,3 +367,88 @@ pub fn dep_type_to_key(dep_type: DependencyType) -> &'static str {
|
||||||
DependencyType::Peer => "peer_dependencies",
|
DependencyType::Peer => "peer_dependencies",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "version-management")]
|
||||||
|
pub async fn get_project_engines(
|
||||||
|
manifest: &Manifest,
|
||||||
|
reqwest: &reqwest::Client,
|
||||||
|
) -> anyhow::Result<HashMap<EngineKind, Version>> {
|
||||||
|
use tokio::task::JoinSet;
|
||||||
|
|
||||||
|
run_with_reporter(|_, root_progress, reporter| async {
|
||||||
|
let root_progress = root_progress;
|
||||||
|
let reporter = reporter;
|
||||||
|
|
||||||
|
root_progress.set_prefix(format!("{} {}: ", manifest.name, manifest.target));
|
||||||
|
root_progress.reset();
|
||||||
|
root_progress.set_message("update engines");
|
||||||
|
|
||||||
|
let mut tasks = manifest
|
||||||
|
.engines
|
||||||
|
.iter()
|
||||||
|
.map(|(engine, req)| {
|
||||||
|
let engine = *engine;
|
||||||
|
let req = req.clone();
|
||||||
|
let reqwest = reqwest.clone();
|
||||||
|
let reporter = reporter.clone();
|
||||||
|
|
||||||
|
async move {
|
||||||
|
let version = crate::cli::version::get_or_download_engine(
|
||||||
|
&reqwest, engine, req, reporter,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.context("failed to install engine")?
|
||||||
|
.1;
|
||||||
|
|
||||||
|
crate::cli::version::make_linker_if_needed(engine)
|
||||||
|
.await
|
||||||
|
.context("failed to make engine linker")?;
|
||||||
|
|
||||||
|
Ok::<_, anyhow::Error>((engine, version))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<JoinSet<_>>();
|
||||||
|
|
||||||
|
let mut resolved_engine_versions = HashMap::new();
|
||||||
|
|
||||||
|
while let Some(task) = tasks.join_next().await {
|
||||||
|
let (engine, version) = task.unwrap()?;
|
||||||
|
resolved_engine_versions.insert(engine, version);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok::<_, anyhow::Error>(resolved_engine_versions)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "version-management"))]
|
||||||
|
pub async fn get_project_engines(
|
||||||
|
_manifest: &Manifest,
|
||||||
|
_reqwest: &reqwest::Client,
|
||||||
|
) -> anyhow::Result<HashMap<EngineKind, Version>> {
|
||||||
|
Ok(Default::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compatible_runtime(
|
||||||
|
target: TargetKind,
|
||||||
|
engines: &HashMap<EngineKind, Version>,
|
||||||
|
) -> anyhow::Result<Runtime> {
|
||||||
|
let runtime = match target {
|
||||||
|
TargetKind::Lune => RuntimeKind::Lune,
|
||||||
|
TargetKind::Luau => engines
|
||||||
|
.keys()
|
||||||
|
.find_map(|e| e.as_runtime())
|
||||||
|
.context("no runtime available")?,
|
||||||
|
TargetKind::Roblox | TargetKind::RobloxServer => {
|
||||||
|
anyhow::bail!("roblox targets cannot be ran!")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Runtime::new(
|
||||||
|
runtime,
|
||||||
|
engines
|
||||||
|
.get(&runtime.into())
|
||||||
|
.with_context(|| format!("{runtime} not available!"))?
|
||||||
|
.clone(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
all_packages_dirs,
|
all_packages_dirs,
|
||||||
download::DownloadGraphOptions,
|
download::DownloadGraphOptions,
|
||||||
|
engine::runtime::Engines,
|
||||||
graph::{
|
graph::{
|
||||||
DependencyGraph, DependencyGraphNode, DependencyGraphNodeWithTarget,
|
DependencyGraph, DependencyGraphNode, DependencyGraphNodeWithTarget,
|
||||||
DependencyGraphWithTarget,
|
DependencyGraphWithTarget,
|
||||||
|
@ -104,6 +105,8 @@ pub struct DownloadAndLinkOptions<Reporter = (), Hooks = ()> {
|
||||||
pub network_concurrency: NonZeroUsize,
|
pub network_concurrency: NonZeroUsize,
|
||||||
/// Whether to re-install all dependencies even if they are already installed
|
/// Whether to re-install all dependencies even if they are already installed
|
||||||
pub force: bool,
|
pub force: bool,
|
||||||
|
/// The engines this project is using
|
||||||
|
pub engines: Arc<Engines>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Reporter, Hooks> DownloadAndLinkOptions<Reporter, Hooks>
|
impl<Reporter, Hooks> DownloadAndLinkOptions<Reporter, Hooks>
|
||||||
|
@ -122,6 +125,7 @@ where
|
||||||
install_dependencies_mode: InstallDependenciesMode::All,
|
install_dependencies_mode: InstallDependenciesMode::All,
|
||||||
network_concurrency: NonZeroUsize::new(16).unwrap(),
|
network_concurrency: NonZeroUsize::new(16).unwrap(),
|
||||||
force: false,
|
force: false,
|
||||||
|
engines: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,6 +173,13 @@ where
|
||||||
self.force = force;
|
self.force = force;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the engines this project is using
|
||||||
|
#[must_use]
|
||||||
|
pub fn engines(mut self, engines: impl Into<Arc<Engines>>) -> Self {
|
||||||
|
self.engines = engines.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for DownloadAndLinkOptions {
|
impl Clone for DownloadAndLinkOptions {
|
||||||
|
@ -181,6 +192,7 @@ impl Clone for DownloadAndLinkOptions {
|
||||||
install_dependencies_mode: self.install_dependencies_mode,
|
install_dependencies_mode: self.install_dependencies_mode,
|
||||||
network_concurrency: self.network_concurrency,
|
network_concurrency: self.network_concurrency,
|
||||||
force: self.force,
|
force: self.force,
|
||||||
|
engines: self.engines.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -209,6 +221,7 @@ impl Project {
|
||||||
install_dependencies_mode,
|
install_dependencies_mode,
|
||||||
network_concurrency,
|
network_concurrency,
|
||||||
force,
|
force,
|
||||||
|
engines,
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
let reqwest = reqwest.clone();
|
let reqwest = reqwest.clone();
|
||||||
|
@ -346,6 +359,7 @@ impl Project {
|
||||||
project: &Project,
|
project: &Project,
|
||||||
manifest_target_kind: TargetKind,
|
manifest_target_kind: TargetKind,
|
||||||
downloaded_graph: HashMap<PackageId, DependencyGraphNode>,
|
downloaded_graph: HashMap<PackageId, DependencyGraphNode>,
|
||||||
|
engines: &Arc<Engines>,
|
||||||
) -> Result<(), errors::DownloadAndLinkError<Hooks::Error>> {
|
) -> Result<(), errors::DownloadAndLinkError<Hooks::Error>> {
|
||||||
let mut tasks = downloaded_graph
|
let mut tasks = downloaded_graph
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -357,6 +371,7 @@ impl Project {
|
||||||
.into();
|
.into();
|
||||||
let id = Arc::new(id);
|
let id = Arc::new(id);
|
||||||
let project = project.clone();
|
let project = project.clone();
|
||||||
|
let engines = engines.clone();
|
||||||
|
|
||||||
async move {
|
async move {
|
||||||
let target = source
|
let target = source
|
||||||
|
@ -366,6 +381,7 @@ impl Project {
|
||||||
project,
|
project,
|
||||||
path,
|
path,
|
||||||
id: id.clone(),
|
id: id.clone(),
|
||||||
|
engines,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -392,11 +408,12 @@ impl Project {
|
||||||
self,
|
self,
|
||||||
manifest.target.kind(),
|
manifest.target.kind(),
|
||||||
other_graph_to_download,
|
other_graph_to_download,
|
||||||
|
&engines,
|
||||||
)
|
)
|
||||||
.instrument(tracing::debug_span!("get targets (non-wally)"))
|
.instrument(tracing::debug_span!("get targets (non-wally)"))
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
self.link_dependencies(&graph, false)
|
self.link_dependencies(&graph, &engines, false)
|
||||||
.instrument(tracing::debug_span!("link (non-wally)"))
|
.instrument(tracing::debug_span!("link (non-wally)"))
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
@ -418,6 +435,7 @@ impl Project {
|
||||||
self,
|
self,
|
||||||
manifest.target.kind(),
|
manifest.target.kind(),
|
||||||
wally_graph_to_download,
|
wally_graph_to_download,
|
||||||
|
&engines,
|
||||||
)
|
)
|
||||||
.instrument(tracing::debug_span!("get targets (wally)"))
|
.instrument(tracing::debug_span!("get targets (wally)"))
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -461,7 +479,7 @@ impl Project {
|
||||||
}
|
}
|
||||||
|
|
||||||
// step 4. link ALL dependencies. do so with types
|
// step 4. link ALL dependencies. do so with types
|
||||||
self.link_dependencies(&graph, true)
|
self.link_dependencies(&graph, &engines, true)
|
||||||
.instrument(tracing::debug_span!("link (all)"))
|
.instrument(tracing::debug_span!("link (all)"))
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
/// Engines as runtimes
|
||||||
|
pub mod runtime;
|
||||||
/// Sources of engines
|
/// Sources of engines
|
||||||
pub mod source;
|
pub mod source;
|
||||||
|
|
||||||
|
|
92
src/engine/runtime.rs
Normal file
92
src/engine/runtime.rs
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
ffi::OsStr,
|
||||||
|
fmt::{Debug, Display},
|
||||||
|
};
|
||||||
|
|
||||||
|
use semver::Version;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use tokio::process::Command;
|
||||||
|
|
||||||
|
use super::EngineKind;
|
||||||
|
|
||||||
|
pub(crate) type Engines = HashMap<EngineKind, Version>;
|
||||||
|
|
||||||
|
/// A runtime
|
||||||
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||||
|
#[cfg_attr(test, derive(schemars::JsonSchema))]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum RuntimeKind {
|
||||||
|
/// The Lune runtime
|
||||||
|
Lune,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for RuntimeKind {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Lune => write!(f, "lune"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Supported runtimes
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Runtime {
|
||||||
|
/// The [EngineKind::Lune] runtime
|
||||||
|
Lune(Version),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Runtime {
|
||||||
|
/// Prepares a [Command] to execute the given script with the given arguments
|
||||||
|
pub fn prepare_command<A: IntoIterator<Item = S> + Debug, S: AsRef<OsStr> + Debug>(
|
||||||
|
&self,
|
||||||
|
script_path: &OsStr,
|
||||||
|
args: A,
|
||||||
|
) -> Command {
|
||||||
|
let mut command = Command::new(match self {
|
||||||
|
Self::Lune(..) => "lune",
|
||||||
|
});
|
||||||
|
|
||||||
|
match self {
|
||||||
|
Self::Lune(version) => {
|
||||||
|
command.arg("run");
|
||||||
|
command.arg(script_path);
|
||||||
|
if *version < Version::new(0, 9, 0) {
|
||||||
|
command.arg("--");
|
||||||
|
}
|
||||||
|
command.args(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
command
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Runtime {
|
||||||
|
/// Creates a [Runtime] from the [RuntimeKind] and [Version]
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(kind: RuntimeKind, version: Version) -> Self {
|
||||||
|
match kind {
|
||||||
|
RuntimeKind::Lune => Runtime::Lune(version),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EngineKind {
|
||||||
|
/// Returns this engine as a [RuntimeKind], if it is one
|
||||||
|
#[must_use]
|
||||||
|
pub fn as_runtime(self) -> Option<RuntimeKind> {
|
||||||
|
Some(match self {
|
||||||
|
EngineKind::Pesde => return None,
|
||||||
|
EngineKind::Lune => RuntimeKind::Lune,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<RuntimeKind> for EngineKind {
|
||||||
|
fn from(value: RuntimeKind) -> Self {
|
||||||
|
match value {
|
||||||
|
RuntimeKind::Lune => EngineKind::Lune,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -377,7 +377,7 @@ impl RefreshedSources {
|
||||||
|
|
||||||
async fn deser_manifest(path: &Path) -> Result<Manifest, errors::ManifestReadError> {
|
async fn deser_manifest(path: &Path) -> Result<Manifest, errors::ManifestReadError> {
|
||||||
let string = fs::read_to_string(path.join(MANIFEST_FILE_NAME)).await?;
|
let string = fs::read_to_string(path.join(MANIFEST_FILE_NAME)).await?;
|
||||||
toml::from_str(&string).map_err(|e| errors::ManifestReadError::Serde(path.to_path_buf(), e))
|
toml::from_str(&string).map_err(|e| errors::ManifestReadError::Serde(path.into(), e))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find the project & workspace directory roots
|
/// Find the project & workspace directory roots
|
||||||
|
@ -398,7 +398,7 @@ pub async fn find_roots(
|
||||||
.await
|
.await
|
||||||
.map_err(errors::ManifestReadError::Io)?;
|
.map_err(errors::ManifestReadError::Io)?;
|
||||||
let manifest: Manifest = toml::from_str(&manifest)
|
let manifest: Manifest = toml::from_str(&manifest)
|
||||||
.map_err(|e| errors::ManifestReadError::Serde(path.to_path_buf(), e))?;
|
.map_err(|e| errors::ManifestReadError::Serde(path.into(), e))?;
|
||||||
|
|
||||||
if manifest.workspace_members.is_empty() {
|
if manifest.workspace_members.is_empty() {
|
||||||
return Ok(HashSet::new());
|
return Ok(HashSet::new());
|
||||||
|
@ -479,7 +479,7 @@ pub(crate) fn all_packages_dirs() -> HashSet<String> {
|
||||||
|
|
||||||
/// Errors that can occur when using the pesde library
|
/// Errors that can occur when using the pesde library
|
||||||
pub mod errors {
|
pub mod errors {
|
||||||
use std::path::PathBuf;
|
use std::path::Path;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
/// Errors that can occur when reading the manifest file
|
/// Errors that can occur when reading the manifest file
|
||||||
|
@ -492,7 +492,7 @@ pub mod errors {
|
||||||
|
|
||||||
/// An error occurred while deserializing the manifest file
|
/// An error occurred while deserializing the manifest file
|
||||||
#[error("error deserializing manifest file at {0}")]
|
#[error("error deserializing manifest file at {0}")]
|
||||||
Serde(PathBuf, #[source] toml::de::Error),
|
Serde(Box<Path>, #[source] toml::de::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Errors that can occur when reading the lockfile
|
/// Errors that can occur when reading the lockfile
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
|
engine::runtime::Engines,
|
||||||
graph::{DependencyGraphNodeWithTarget, DependencyGraphWithTarget},
|
graph::{DependencyGraphNodeWithTarget, DependencyGraphWithTarget},
|
||||||
linking::generator::get_file_types,
|
linking::generator::get_file_types,
|
||||||
manifest::{Alias, Manifest},
|
manifest::{Alias, Manifest},
|
||||||
|
@ -15,6 +16,7 @@ use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
ffi::OsStr,
|
ffi::OsStr,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
|
sync::Arc,
|
||||||
};
|
};
|
||||||
use tokio::task::{spawn_blocking, JoinSet};
|
use tokio::task::{spawn_blocking, JoinSet};
|
||||||
use tracing::{instrument, Instrument as _};
|
use tracing::{instrument, Instrument as _};
|
||||||
|
@ -64,6 +66,7 @@ impl Project {
|
||||||
pub(crate) async fn link_dependencies(
|
pub(crate) async fn link_dependencies(
|
||||||
&self,
|
&self,
|
||||||
graph: &DependencyGraphWithTarget,
|
graph: &DependencyGraphWithTarget,
|
||||||
|
engines: &Arc<Engines>,
|
||||||
with_types: bool,
|
with_types: bool,
|
||||||
) -> Result<(), errors::LinkingError> {
|
) -> Result<(), errors::LinkingError> {
|
||||||
let manifest = self.deser_manifest().await?;
|
let manifest = self.deser_manifest().await?;
|
||||||
|
@ -88,6 +91,7 @@ impl Project {
|
||||||
let package_id = package_id.clone();
|
let package_id = package_id.clone();
|
||||||
let node = node.clone();
|
let node = node.clone();
|
||||||
let project = self.clone();
|
let project = self.clone();
|
||||||
|
let engines = engines.clone();
|
||||||
|
|
||||||
async move {
|
async move {
|
||||||
let Some(lib_file) = node.target.lib_path() else {
|
let Some(lib_file) = node.target.lib_path() else {
|
||||||
|
@ -131,6 +135,7 @@ impl Project {
|
||||||
execute_script(
|
execute_script(
|
||||||
ScriptName::RobloxSyncConfigGenerator,
|
ScriptName::RobloxSyncConfigGenerator,
|
||||||
&project,
|
&project,
|
||||||
|
&engines,
|
||||||
LinkingExecuteScriptHooks,
|
LinkingExecuteScriptHooks,
|
||||||
std::iter::once(container_folder.as_os_str())
|
std::iter::once(container_folder.as_os_str())
|
||||||
.chain(build_files.iter().map(OsStr::new)),
|
.chain(build_files.iter().map(OsStr::new)),
|
||||||
|
|
75
src/main.rs
75
src/main.rs
|
@ -3,16 +3,16 @@ use crate::cli::version::{check_for_updates, current_version, get_or_download_en
|
||||||
use crate::cli::{auth::get_tokens, display_err, style::ERROR_STYLE, PESDE_DIR};
|
use crate::cli::{auth::get_tokens, display_err, style::ERROR_STYLE, PESDE_DIR};
|
||||||
use anyhow::Context as _;
|
use anyhow::Context as _;
|
||||||
use clap::{builder::styling::AnsiColor, Parser};
|
use clap::{builder::styling::AnsiColor, Parser};
|
||||||
use cli::data_dir;
|
use cli::{compatible_runtime, data_dir, get_project_engines};
|
||||||
use fs_err::tokio as fs;
|
use fs_err::tokio as fs;
|
||||||
use indicatif::MultiProgress;
|
use indicatif::MultiProgress;
|
||||||
use pesde::{
|
use pesde::{
|
||||||
engine::EngineKind, find_roots, manifest::target::TargetKind, version_matches, AuthConfig,
|
engine::EngineKind, find_roots, manifest::target::TargetKind, version_matches, AuthConfig,
|
||||||
Project,
|
Project, MANIFEST_FILE_NAME,
|
||||||
};
|
};
|
||||||
use semver::VersionReq;
|
use semver::VersionReq;
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashSet,
|
collections::HashMap,
|
||||||
io,
|
io,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
str::FromStr as _,
|
str::FromStr as _,
|
||||||
|
@ -197,6 +197,14 @@ async fn run() -> anyhow::Result<()> {
|
||||||
.map_or_else(|| "none".to_string(), |p| p.display().to_string())
|
.map_or_else(|| "none".to_string(), |p| p.display().to_string())
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let reqwest = reqwest::Client::builder()
|
||||||
|
.user_agent(concat!(
|
||||||
|
env!("CARGO_PKG_NAME"),
|
||||||
|
"/",
|
||||||
|
env!("CARGO_PKG_VERSION")
|
||||||
|
))
|
||||||
|
.build()?;
|
||||||
|
|
||||||
'scripts: {
|
'scripts: {
|
||||||
// if we're an engine, we don't want to run any scripts
|
// if we're an engine, we don't want to run any scripts
|
||||||
if exe_name_engine.is_ok() {
|
if exe_name_engine.is_ok() {
|
||||||
|
@ -212,15 +220,23 @@ async fn run() -> anyhow::Result<()> {
|
||||||
|
|
||||||
let linker_file_name = format!("{exe_name}.bin.luau");
|
let linker_file_name = format!("{exe_name}.bin.luau");
|
||||||
|
|
||||||
let path = 'finder: {
|
let (path, target) = 'finder: {
|
||||||
let all_folders = TargetKind::VARIANTS
|
let all_folders = TargetKind::VARIANTS
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|a| TargetKind::VARIANTS.iter().map(|b| a.packages_folder(*b)))
|
.copied()
|
||||||
.collect::<HashSet<_>>();
|
.filter(|t| t.has_bin())
|
||||||
|
.flat_map(|a| {
|
||||||
|
TargetKind::VARIANTS
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.filter(|t| t.has_bin())
|
||||||
|
.map(move |b| (a.packages_folder(b), b))
|
||||||
|
})
|
||||||
|
.collect::<HashMap<_, _>>();
|
||||||
|
|
||||||
let mut tasks = all_folders
|
let mut tasks = all_folders
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|folder| {
|
.map(|(folder, target)| {
|
||||||
let package_path = project_root_dir.join(&folder).join(&linker_file_name);
|
let package_path = project_root_dir.join(&folder).join(&linker_file_name);
|
||||||
let workspace_path = project_workspace_dir
|
let workspace_path = project_workspace_dir
|
||||||
.as_deref()
|
.as_deref()
|
||||||
|
@ -228,12 +244,12 @@ async fn run() -> anyhow::Result<()> {
|
||||||
|
|
||||||
async move {
|
async move {
|
||||||
if fs::metadata(&package_path).await.is_ok() {
|
if fs::metadata(&package_path).await.is_ok() {
|
||||||
return Some((true, package_path));
|
return Some((true, package_path, target));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(workspace_path) = workspace_path {
|
if let Some(workspace_path) = workspace_path {
|
||||||
if fs::metadata(&workspace_path).await.is_ok() {
|
if fs::metadata(&workspace_path).await.is_ok() {
|
||||||
return Some((false, workspace_path));
|
return Some((false, workspace_path, target));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -245,12 +261,12 @@ async fn run() -> anyhow::Result<()> {
|
||||||
let mut workspace_path = None;
|
let mut workspace_path = None;
|
||||||
|
|
||||||
while let Some(res) = tasks.join_next().await {
|
while let Some(res) = tasks.join_next().await {
|
||||||
if let Some((primary, path)) = res.unwrap() {
|
if let Some((primary, path, target)) = res.unwrap() {
|
||||||
if primary {
|
if primary {
|
||||||
break 'finder path;
|
break 'finder (path, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
workspace_path = Some(path);
|
workspace_path = Some((path, target));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,13 +283,18 @@ async fn run() -> anyhow::Result<()> {
|
||||||
std::process::exit(1i32);
|
std::process::exit(1i32);
|
||||||
};
|
};
|
||||||
|
|
||||||
let status = std::process::Command::new("lune")
|
let manifest = fs::read_to_string(project_root_dir.join(MANIFEST_FILE_NAME))
|
||||||
.arg("run")
|
.await
|
||||||
.arg(path)
|
.context("failed to read manifest")?;
|
||||||
.arg("--")
|
let manifest = toml::de::from_str(&manifest).context("failed to deserialize manifest")?;
|
||||||
.args(std::env::args_os().skip(1))
|
|
||||||
|
let engines = get_project_engines(&manifest, &reqwest).await?;
|
||||||
|
|
||||||
|
let status = compatible_runtime(target, &engines)?
|
||||||
|
.prepare_command(path.as_os_str(), std::env::args_os().skip(1))
|
||||||
.current_dir(cwd)
|
.current_dir(cwd)
|
||||||
.status()
|
.status()
|
||||||
|
.await
|
||||||
.expect("failed to run lune");
|
.expect("failed to run lune");
|
||||||
|
|
||||||
std::process::exit(status.code().unwrap_or(1i32));
|
std::process::exit(status.code().unwrap_or(1i32));
|
||||||
|
@ -294,26 +315,6 @@ async fn run() -> anyhow::Result<()> {
|
||||||
AuthConfig::new().with_tokens(get_tokens().await?.0),
|
AuthConfig::new().with_tokens(get_tokens().await?.0),
|
||||||
);
|
);
|
||||||
|
|
||||||
let reqwest = {
|
|
||||||
let mut headers = reqwest::header::HeaderMap::new();
|
|
||||||
|
|
||||||
headers.insert(
|
|
||||||
reqwest::header::ACCEPT,
|
|
||||||
"application/json"
|
|
||||||
.parse()
|
|
||||||
.context("failed to create accept header")?,
|
|
||||||
);
|
|
||||||
|
|
||||||
reqwest::Client::builder()
|
|
||||||
.user_agent(concat!(
|
|
||||||
env!("CARGO_PKG_NAME"),
|
|
||||||
"/",
|
|
||||||
env!("CARGO_PKG_VERSION")
|
|
||||||
))
|
|
||||||
.default_headers(headers)
|
|
||||||
.build()?
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(feature = "version-management")]
|
#[cfg(feature = "version-management")]
|
||||||
'engines: {
|
'engines: {
|
||||||
let Ok(engine) = exe_name_engine else {
|
let Ok(engine) = exe_name_engine else {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
engine::EngineKind,
|
engine::{runtime::RuntimeKind, EngineKind},
|
||||||
manifest::{
|
manifest::{
|
||||||
overrides::{OverrideKey, OverrideSpecifier},
|
overrides::{OverrideKey, OverrideSpecifier},
|
||||||
target::Target,
|
target::Target,
|
||||||
|
@ -51,8 +51,7 @@ pub struct Manifest {
|
||||||
pub private: bool,
|
pub private: bool,
|
||||||
/// The scripts of the package
|
/// The scripts of the package
|
||||||
#[serde(default, skip_serializing)]
|
#[serde(default, skip_serializing)]
|
||||||
#[cfg_attr(test, schemars(with = "BTreeMap<String, std::path::PathBuf>"))]
|
pub scripts: BTreeMap<String, Script>,
|
||||||
pub scripts: BTreeMap<String, RelativePathBuf>,
|
|
||||||
/// The indices to use for the package
|
/// The indices to use for the package
|
||||||
#[serde(
|
#[serde(
|
||||||
default,
|
default,
|
||||||
|
@ -210,6 +209,24 @@ impl Alias {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A script
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
#[cfg_attr(test, derive(schemars::JsonSchema))]
|
||||||
|
pub enum Script {
|
||||||
|
/// A path only script
|
||||||
|
#[cfg_attr(test, schemars(with = "std::path::PathBuf"))]
|
||||||
|
Path(RelativePathBuf),
|
||||||
|
/// A script which specifies both its path and its runtime
|
||||||
|
RuntimePath {
|
||||||
|
/// The runtime to execute this script with
|
||||||
|
runtime: RuntimeKind,
|
||||||
|
/// The path of the script to run
|
||||||
|
#[cfg_attr(test, schemars(with = "std::path::PathBuf"))]
|
||||||
|
path: RelativePathBuf,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
/// A dependency type
|
/// A dependency type
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
|
|
|
@ -76,6 +76,12 @@ impl TargetKind {
|
||||||
pub fn is_roblox(self) -> bool {
|
pub fn is_roblox(self) -> bool {
|
||||||
matches!(self, TargetKind::Roblox | TargetKind::RobloxServer)
|
matches!(self, TargetKind::Roblox | TargetKind::RobloxServer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns whether this target supports bin exports
|
||||||
|
#[must_use]
|
||||||
|
pub fn has_bin(self) -> bool {
|
||||||
|
!self.is_roblox()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A target of a package
|
/// A target of a package
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
use crate::Project;
|
use crate::{
|
||||||
|
engine::runtime::{Engines, Runtime},
|
||||||
|
manifest::Script,
|
||||||
|
Project,
|
||||||
|
};
|
||||||
use futures::FutureExt as _;
|
use futures::FutureExt as _;
|
||||||
|
use relative_path::RelativePathBuf;
|
||||||
use std::{
|
use std::{
|
||||||
ffi::OsStr,
|
ffi::OsStr,
|
||||||
fmt::{Debug, Display, Formatter},
|
fmt::{Debug, Display, Formatter},
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
process::Stdio,
|
process::Stdio,
|
||||||
};
|
};
|
||||||
use tokio::{
|
use tokio::io::{AsyncBufReadExt as _, BufReader};
|
||||||
io::{AsyncBufReadExt as _, BufReader},
|
|
||||||
process::Command,
|
|
||||||
};
|
|
||||||
use tracing::instrument;
|
use tracing::instrument;
|
||||||
|
|
||||||
/// Script names used by pesde
|
/// Script names used by pesde
|
||||||
|
@ -32,33 +34,63 @@ impl Display for ScriptName {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Extracts a script and a runtime out of a [Script]
|
||||||
|
pub fn parse_script(
|
||||||
|
script: Script,
|
||||||
|
engines: &Engines,
|
||||||
|
) -> Result<(Runtime, RelativePathBuf), errors::FindScriptError> {
|
||||||
|
Ok(match script {
|
||||||
|
Script::Path(path) => {
|
||||||
|
let runtime = engines
|
||||||
|
.iter()
|
||||||
|
.filter_map(|(engine, ver)| engine.as_runtime().map(|rt| (rt, ver)))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
if runtime.len() != 1 {
|
||||||
|
return Err(errors::FindScriptError::AmbiguousRuntime);
|
||||||
|
}
|
||||||
|
|
||||||
|
let (runtime, version) = runtime[0];
|
||||||
|
|
||||||
|
(Runtime::new(runtime, version.clone()), path)
|
||||||
|
}
|
||||||
|
Script::RuntimePath { runtime, path } => {
|
||||||
|
let Some(version) = engines.get(&runtime.into()) else {
|
||||||
|
return Err(errors::FindScriptError::SpecifiedRuntimeUnknown(runtime));
|
||||||
|
};
|
||||||
|
|
||||||
|
(Runtime::new(runtime, version.clone()), path)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Finds a script in the project, whether it be in the current package or it's workspace
|
/// Finds a script in the project, whether it be in the current package or it's workspace
|
||||||
pub async fn find_script(
|
pub async fn find_script(
|
||||||
project: &Project,
|
project: &Project,
|
||||||
|
engines: &Engines,
|
||||||
script_name: ScriptName,
|
script_name: ScriptName,
|
||||||
) -> Result<Option<PathBuf>, errors::FindScriptError> {
|
) -> Result<Option<(Runtime, PathBuf)>, errors::FindScriptError> {
|
||||||
let script_name_str = script_name.to_string();
|
let script_name_str = script_name.to_string();
|
||||||
|
|
||||||
let script_path = match project
|
let (script, base) = match project
|
||||||
.deser_manifest()
|
.deser_manifest()
|
||||||
.await?
|
.await?
|
||||||
.scripts
|
.scripts
|
||||||
.remove(&script_name_str)
|
.remove(&script_name_str)
|
||||||
{
|
{
|
||||||
Some(script) => script.to_path(project.package_dir()),
|
Some(script) => (script, project.package_dir()),
|
||||||
None => match project
|
None => match project
|
||||||
.deser_workspace_manifest()
|
.deser_workspace_manifest()
|
||||||
.await?
|
.await?
|
||||||
.and_then(|mut manifest| manifest.scripts.remove(&script_name_str))
|
.and_then(|mut manifest| manifest.scripts.remove(&script_name_str))
|
||||||
{
|
{
|
||||||
Some(script) => script.to_path(project.workspace_dir().unwrap()),
|
Some(script) => (script, project.workspace_dir().unwrap()),
|
||||||
None => {
|
None => {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Some(script_path))
|
parse_script(script, engines).map(|(rt, path)| Some((rt, path.to_path(base))))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
|
@ -74,20 +106,18 @@ pub(crate) async fn execute_script<
|
||||||
>(
|
>(
|
||||||
script_name: ScriptName,
|
script_name: ScriptName,
|
||||||
project: &Project,
|
project: &Project,
|
||||||
|
engines: &Engines,
|
||||||
hooks: H,
|
hooks: H,
|
||||||
args: A,
|
args: A,
|
||||||
return_stdout: bool,
|
return_stdout: bool,
|
||||||
) -> Result<Option<String>, errors::ExecuteScriptError> {
|
) -> Result<Option<String>, errors::ExecuteScriptError> {
|
||||||
let Some(script_path) = find_script(project, script_name).await? else {
|
let Some((runtime, script_path)) = find_script(project, engines, script_name).await? else {
|
||||||
hooks.not_found(script_name);
|
hooks.not_found(script_name);
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
|
|
||||||
match Command::new("lune")
|
match runtime
|
||||||
.arg("run")
|
.prepare_command(script_path.as_os_str(), args)
|
||||||
.arg(script_path.as_os_str())
|
|
||||||
.arg("--")
|
|
||||||
.args(args)
|
|
||||||
.current_dir(project.package_dir())
|
.current_dir(project.package_dir())
|
||||||
.stdin(Stdio::inherit())
|
.stdin(Stdio::inherit())
|
||||||
.stdout(Stdio::piped())
|
.stdout(Stdio::piped())
|
||||||
|
@ -146,6 +176,8 @@ pub(crate) async fn execute_script<
|
||||||
pub mod errors {
|
pub mod errors {
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use crate::engine::runtime::RuntimeKind;
|
||||||
|
|
||||||
/// Errors that can occur when finding a script
|
/// Errors that can occur when finding a script
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum FindScriptError {
|
pub enum FindScriptError {
|
||||||
|
@ -156,6 +188,14 @@ pub mod errors {
|
||||||
/// An IO error occurred
|
/// An IO error occurred
|
||||||
#[error("IO error")]
|
#[error("IO error")]
|
||||||
Io(#[from] std::io::Error),
|
Io(#[from] std::io::Error),
|
||||||
|
|
||||||
|
/// Ambiguous runtime
|
||||||
|
#[error("don't know which runtime to use. use specific form and specify the runtime")]
|
||||||
|
AmbiguousRuntime,
|
||||||
|
|
||||||
|
/// Runtime specified in script not in engines
|
||||||
|
#[error("runtime `{0}` was specified in the script, but it is not present in engines")]
|
||||||
|
SpecifiedRuntimeUnknown(RuntimeKind),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Errors which can occur while executing a script
|
/// Errors which can occur while executing a script
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
|
engine::runtime::Engines,
|
||||||
manifest::{
|
manifest::{
|
||||||
target::{Target, TargetKind},
|
target::{Target, TargetKind},
|
||||||
Alias, DependencyType,
|
Alias, DependencyType,
|
||||||
|
@ -71,6 +72,8 @@ pub struct GetTargetOptions {
|
||||||
pub path: Arc<Path>,
|
pub path: Arc<Path>,
|
||||||
/// The package ID of the package to be downloaded
|
/// The package ID of the package to be downloaded
|
||||||
pub id: Arc<PackageId>,
|
pub id: Arc<PackageId>,
|
||||||
|
/// The engines this project is using
|
||||||
|
pub engines: Arc<Engines>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A source of packages
|
/// A source of packages
|
||||||
|
|
|
@ -4,6 +4,7 @@ use relative_path::RelativePathBuf;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
engine::runtime::Engines,
|
||||||
manifest::target::Target,
|
manifest::target::Target,
|
||||||
scripts::{execute_script, ExecuteScriptHooks, ScriptName},
|
scripts::{execute_script, ExecuteScriptHooks, ScriptName},
|
||||||
source::{
|
source::{
|
||||||
|
@ -33,11 +34,13 @@ impl ExecuteScriptHooks for CompatExecuteScriptHooks {
|
||||||
|
|
||||||
async fn find_lib_path(
|
async fn find_lib_path(
|
||||||
project: &Project,
|
project: &Project,
|
||||||
|
engines: &Engines,
|
||||||
package_dir: &Path,
|
package_dir: &Path,
|
||||||
) -> Result<Option<RelativePathBuf>, errors::GetTargetError> {
|
) -> Result<Option<RelativePathBuf>, errors::GetTargetError> {
|
||||||
let Some(result) = execute_script(
|
let Some(result) = execute_script(
|
||||||
ScriptName::SourcemapGenerator,
|
ScriptName::SourcemapGenerator,
|
||||||
project,
|
project,
|
||||||
|
engines,
|
||||||
CompatExecuteScriptHooks,
|
CompatExecuteScriptHooks,
|
||||||
[package_dir],
|
[package_dir],
|
||||||
true,
|
true,
|
||||||
|
@ -60,9 +63,14 @@ pub(crate) const WALLY_MANIFEST_FILE_NAME: &str = "wally.toml";
|
||||||
pub(crate) async fn get_target(
|
pub(crate) async fn get_target(
|
||||||
options: &GetTargetOptions,
|
options: &GetTargetOptions,
|
||||||
) -> Result<Target, errors::GetTargetError> {
|
) -> Result<Target, errors::GetTargetError> {
|
||||||
let GetTargetOptions { project, path, .. } = options;
|
let GetTargetOptions {
|
||||||
|
project,
|
||||||
|
path,
|
||||||
|
engines,
|
||||||
|
..
|
||||||
|
} = options;
|
||||||
|
|
||||||
let lib = find_lib_path(project, path)
|
let lib = find_lib_path(project, engines, path)
|
||||||
.await?
|
.await?
|
||||||
.or_else(|| Some(RelativePathBuf::from(LINK_LIB_NO_FILE_FOUND)));
|
.or_else(|| Some(RelativePathBuf::from(LINK_LIB_NO_FILE_FOUND)));
|
||||||
let build_files = Default::default();
|
let build_files = Default::default();
|
||||||
|
|
Loading…
Add table
Reference in a new issue