|
|
@ -2,8 +2,13 @@ use async_recursion::async_recursion;
|
|
|
|
use aur_rpc::PackageInfo;
|
|
|
|
use aur_rpc::PackageInfo;
|
|
|
|
use crossterm::style::Stylize;
|
|
|
|
use crossterm::style::Stylize;
|
|
|
|
use futures::future;
|
|
|
|
use futures::future;
|
|
|
|
use std::collections::HashSet;
|
|
|
|
use indicatif::ProgressBar;
|
|
|
|
|
|
|
|
use std::collections::{HashMap, HashSet};
|
|
|
|
|
|
|
|
use std::mem;
|
|
|
|
use std::path::{Path, PathBuf};
|
|
|
|
use std::path::{Path, PathBuf};
|
|
|
|
|
|
|
|
use std::sync::Arc;
|
|
|
|
|
|
|
|
use tokio::io::{AsyncRead, AsyncReadExt, BufReader};
|
|
|
|
|
|
|
|
use tokio::task::JoinHandle;
|
|
|
|
|
|
|
|
|
|
|
|
use crate::builder::git::{GitCloneBuilder, GitPullBuilder};
|
|
|
|
use crate::builder::git::{GitCloneBuilder, GitPullBuilder};
|
|
|
|
use crate::builder::makepkg::MakePkgBuilder;
|
|
|
|
use crate::builder::makepkg::MakePkgBuilder;
|
|
|
@ -11,9 +16,10 @@ use crate::builder::pacman::PacmanInstallBuilder;
|
|
|
|
use crate::internal::dependencies::DependencyInformation;
|
|
|
|
use crate::internal::dependencies::DependencyInformation;
|
|
|
|
use crate::internal::error::{AppError, AppResult, SilentUnwrap};
|
|
|
|
use crate::internal::error::{AppError, AppResult, SilentUnwrap};
|
|
|
|
use crate::internal::exit_code::AppExitCode;
|
|
|
|
use crate::internal::exit_code::AppExitCode;
|
|
|
|
use crate::internal::utils::get_cache_dir;
|
|
|
|
use crate::internal::utils::{get_cache_dir, wrap_text};
|
|
|
|
use crate::logging::get_logger;
|
|
|
|
use crate::logging::get_logger;
|
|
|
|
use crate::{crash, Options};
|
|
|
|
use crate::logging::output::{print_aur_package_list, print_dependency_list};
|
|
|
|
|
|
|
|
use crate::{cancelled, crash, prompt, Options};
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct BuildContext {
|
|
|
|
pub struct BuildContext {
|
|
|
@ -66,12 +72,8 @@ impl BuildContext {
|
|
|
|
#[tracing::instrument(level = "trace")]
|
|
|
|
#[tracing::instrument(level = "trace")]
|
|
|
|
#[async_recursion]
|
|
|
|
#[async_recursion]
|
|
|
|
pub async fn aur_install(packages: Vec<String>, options: Options) {
|
|
|
|
pub async fn aur_install(packages: Vec<String>, options: Options) {
|
|
|
|
let noconfirm = options.noconfirm;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tracing::debug!("Installing from AUR: {:?}", &packages);
|
|
|
|
tracing::debug!("Installing from AUR: {:?}", &packages);
|
|
|
|
|
|
|
|
|
|
|
|
tracing::info!("Installing packages {} from the AUR", packages.join(", "));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let pb = get_logger().new_progress_spinner();
|
|
|
|
let pb = get_logger().new_progress_spinner();
|
|
|
|
pb.set_message("Fetching package information");
|
|
|
|
pb.set_message("Fetching package information");
|
|
|
|
|
|
|
|
|
|
|
@ -97,18 +99,33 @@ pub async fn aur_install(packages: Vec<String>, options: Options) {
|
|
|
|
get_logger().reset_output_type();
|
|
|
|
get_logger().reset_output_type();
|
|
|
|
|
|
|
|
|
|
|
|
pb.finish_with_message("Found all packages in the aur");
|
|
|
|
pb.finish_with_message("Found all packages in the aur");
|
|
|
|
|
|
|
|
print_aur_package_list(&package_info);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if !options.noconfirm
|
|
|
|
|
|
|
|
&& !prompt!(default yes, "Do you want to install those packages from the AUR?")
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
cancelled!();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tracing::info!("Downloading aur packages");
|
|
|
|
get_logger().new_multi_progress();
|
|
|
|
get_logger().new_multi_progress();
|
|
|
|
|
|
|
|
|
|
|
|
let dependencies = future::try_join_all(package_info.iter().map(|pkg| async {
|
|
|
|
let dependencies = future::try_join_all(package_info.iter().map(|pkg| async {
|
|
|
|
get_logger()
|
|
|
|
get_logger().new_progress_spinner().set_message(format!(
|
|
|
|
.new_progress_spinner()
|
|
|
|
"{}: Fetching dependencies",
|
|
|
|
.set_message(format!("{}: Fetching dependencies", pkg.metadata.name));
|
|
|
|
pkg.metadata.name.clone().bold()
|
|
|
|
|
|
|
|
));
|
|
|
|
DependencyInformation::for_package(pkg).await
|
|
|
|
DependencyInformation::for_package(pkg).await
|
|
|
|
}))
|
|
|
|
}))
|
|
|
|
.await
|
|
|
|
.await
|
|
|
|
.silent_unwrap(AppExitCode::RpcError);
|
|
|
|
.silent_unwrap(AppExitCode::RpcError);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if !print_dependency_list(&dependencies) && !options.noconfirm {
|
|
|
|
|
|
|
|
get_logger().print_newline();
|
|
|
|
|
|
|
|
if !prompt!(default yes, "Do you want to install those dependencies?") {
|
|
|
|
|
|
|
|
cancelled!();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
get_logger().new_multi_progress();
|
|
|
|
get_logger().new_multi_progress();
|
|
|
|
|
|
|
|
|
|
|
|
let contexts = future::try_join_all(
|
|
|
|
let contexts = future::try_join_all(
|
|
|
@ -123,9 +140,14 @@ pub async fn aur_install(packages: Vec<String>, options: Options) {
|
|
|
|
get_logger().reset_output_type();
|
|
|
|
get_logger().reset_output_type();
|
|
|
|
tracing::info!("All sources are ready.");
|
|
|
|
tracing::info!("All sources are ready.");
|
|
|
|
|
|
|
|
|
|
|
|
let aur_build_dependencies: Vec<PackageInfo> = dependencies
|
|
|
|
let aur_dependencies: Vec<PackageInfo> = dependencies
|
|
|
|
.iter()
|
|
|
|
.iter()
|
|
|
|
.flat_map(|d| d.make_depends.aur.clone())
|
|
|
|
.flat_map(|d| {
|
|
|
|
|
|
|
|
let mut deps = d.make_depends.aur.clone();
|
|
|
|
|
|
|
|
deps.append(&mut d.depends.aur.clone());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
deps
|
|
|
|
|
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
|
|
let repo_dependencies: HashSet<String> = dependencies
|
|
|
|
let repo_dependencies: HashSet<String> = dependencies
|
|
|
@ -138,26 +160,28 @@ pub async fn aur_install(packages: Vec<String>, options: Options) {
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
|
|
get_logger().reset_output_type();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if !repo_dependencies.is_empty() {
|
|
|
|
if !repo_dependencies.is_empty() {
|
|
|
|
tracing::info!("Installing repo dependencies");
|
|
|
|
tracing::info!("Installing repo dependencies");
|
|
|
|
PacmanInstallBuilder::default()
|
|
|
|
PacmanInstallBuilder::default()
|
|
|
|
.as_deps(true)
|
|
|
|
.as_deps(true)
|
|
|
|
|
|
|
|
.no_confirm(true)
|
|
|
|
.packages(repo_dependencies)
|
|
|
|
.packages(repo_dependencies)
|
|
|
|
.install()
|
|
|
|
.install()
|
|
|
|
.await
|
|
|
|
.await
|
|
|
|
.silent_unwrap(AppExitCode::PacmanError);
|
|
|
|
.silent_unwrap(AppExitCode::PacmanError);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if !aur_build_dependencies.is_empty() {
|
|
|
|
if !aur_dependencies.is_empty() {
|
|
|
|
tracing::info!(
|
|
|
|
tracing::info!(
|
|
|
|
"Installing {} build dependencies from the aur",
|
|
|
|
"Installing {} dependencies from the aur",
|
|
|
|
aur_build_dependencies.len()
|
|
|
|
aur_dependencies.len()
|
|
|
|
);
|
|
|
|
);
|
|
|
|
install_aur_build_dependencies(aur_build_dependencies)
|
|
|
|
let batches = create_dependency_batches(aur_dependencies);
|
|
|
|
.await
|
|
|
|
tracing::debug!("aur install batches: {batches:?}");
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
|
|
|
|
for batch in batches {
|
|
|
|
|
|
|
|
install_aur_deps(batch).await.unwrap();
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
tracing::info!("Installing {} packages", contexts.len());
|
|
|
|
tracing::info!("Installing {} packages", contexts.len());
|
|
|
@ -165,13 +189,14 @@ pub async fn aur_install(packages: Vec<String>, options: Options) {
|
|
|
|
build_and_install(
|
|
|
|
build_and_install(
|
|
|
|
contexts,
|
|
|
|
contexts,
|
|
|
|
MakePkgBuilder::default(),
|
|
|
|
MakePkgBuilder::default(),
|
|
|
|
PacmanInstallBuilder::default(),
|
|
|
|
PacmanInstallBuilder::default().no_confirm(true),
|
|
|
|
)
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.await
|
|
|
|
.silent_unwrap(AppExitCode::MakePkgError);
|
|
|
|
.silent_unwrap(AppExitCode::MakePkgError);
|
|
|
|
|
|
|
|
tracing::info!("Done!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async fn install_aur_build_dependencies(deps: Vec<PackageInfo>) -> AppResult<()> {
|
|
|
|
async fn install_aur_deps(deps: Vec<PackageInfo>) -> AppResult<()> {
|
|
|
|
get_logger().new_multi_progress();
|
|
|
|
get_logger().new_multi_progress();
|
|
|
|
|
|
|
|
|
|
|
|
let dep_contexts = future::try_join_all(
|
|
|
|
let dep_contexts = future::try_join_all(
|
|
|
@ -221,18 +246,24 @@ async fn build_and_install(
|
|
|
|
async fn download_aur_source(mut ctx: BuildContext) -> AppResult<BuildContext> {
|
|
|
|
async fn download_aur_source(mut ctx: BuildContext) -> AppResult<BuildContext> {
|
|
|
|
let pb = get_logger().new_progress_spinner();
|
|
|
|
let pb = get_logger().new_progress_spinner();
|
|
|
|
let pkg_name = &ctx.package.metadata.name;
|
|
|
|
let pkg_name = &ctx.package.metadata.name;
|
|
|
|
pb.set_message(format!("{pkg_name}: Downloading sources"));
|
|
|
|
pb.set_message(format!("{}: Downloading sources", pkg_name.clone().bold()));
|
|
|
|
|
|
|
|
|
|
|
|
let cache_dir = get_cache_dir();
|
|
|
|
let cache_dir = get_cache_dir();
|
|
|
|
let pkg_dir = cache_dir.join(&pkg_name);
|
|
|
|
let pkg_dir = cache_dir.join(&pkg_name);
|
|
|
|
|
|
|
|
|
|
|
|
if pkg_dir.exists() {
|
|
|
|
if pkg_dir.exists() {
|
|
|
|
pb.set_message(format!("{pkg_name}: Pulling latest changes {pkg_name}"));
|
|
|
|
pb.set_message(format!(
|
|
|
|
|
|
|
|
"{}: Pulling latest changes",
|
|
|
|
|
|
|
|
pkg_name.clone().bold()
|
|
|
|
|
|
|
|
));
|
|
|
|
GitPullBuilder::default().directory(&pkg_dir).pull().await?;
|
|
|
|
GitPullBuilder::default().directory(&pkg_dir).pull().await?;
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
let aur_url = crate::internal::rpc::URL;
|
|
|
|
let aur_url = crate::internal::rpc::URL;
|
|
|
|
let repository_url = format!("{aur_url}/{pkg_name}");
|
|
|
|
let repository_url = format!("{aur_url}/{pkg_name}");
|
|
|
|
pb.set_message(format!("{pkg_name}: Cloning aur repository"));
|
|
|
|
pb.set_message(format!(
|
|
|
|
|
|
|
|
"{}: Cloning aur repository",
|
|
|
|
|
|
|
|
pkg_name.clone().bold()
|
|
|
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
|
|
GitCloneBuilder::default()
|
|
|
|
GitCloneBuilder::default()
|
|
|
|
.url(repository_url)
|
|
|
|
.url(repository_url)
|
|
|
@ -240,7 +271,10 @@ async fn download_aur_source(mut ctx: BuildContext) -> AppResult<BuildContext> {
|
|
|
|
.clone()
|
|
|
|
.clone()
|
|
|
|
.await?;
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
|
|
|
|
pb.set_message(format!("{pkg_name}: Downloading and extracting files"));
|
|
|
|
pb.set_message(format!(
|
|
|
|
|
|
|
|
"{}: Downloading and extracting files",
|
|
|
|
|
|
|
|
pkg_name.clone().bold()
|
|
|
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
|
|
MakePkgBuilder::default()
|
|
|
|
MakePkgBuilder::default()
|
|
|
|
.directory(&pkg_dir)
|
|
|
|
.directory(&pkg_dir)
|
|
|
@ -251,7 +285,11 @@ async fn download_aur_source(mut ctx: BuildContext) -> AppResult<BuildContext> {
|
|
|
|
.run()
|
|
|
|
.run()
|
|
|
|
.await?;
|
|
|
|
.await?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pb.finish_with_message(format!("{pkg_name}: Downloaded!"));
|
|
|
|
pb.finish_with_message(format!(
|
|
|
|
|
|
|
|
"{}: {}",
|
|
|
|
|
|
|
|
pkg_name.clone().bold(),
|
|
|
|
|
|
|
|
"Downloaded!".green()
|
|
|
|
|
|
|
|
));
|
|
|
|
ctx.step = BuildStep::Build(BuildPath(pkg_dir));
|
|
|
|
ctx.step = BuildStep::Build(BuildPath(pkg_dir));
|
|
|
|
|
|
|
|
|
|
|
|
Ok(ctx)
|
|
|
|
Ok(ctx)
|
|
|
@ -264,19 +302,47 @@ async fn build_package(
|
|
|
|
let pb = get_logger().new_progress_spinner();
|
|
|
|
let pb = get_logger().new_progress_spinner();
|
|
|
|
let pkg_name = &ctx.package.metadata.name;
|
|
|
|
let pkg_name = &ctx.package.metadata.name;
|
|
|
|
let build_path = ctx.build_path()?;
|
|
|
|
let build_path = ctx.build_path()?;
|
|
|
|
pb.set_message(format!("{pkg_name}: Building Package"));
|
|
|
|
pb.set_message(format!("{}: Building Package", pkg_name.clone().bold()));
|
|
|
|
|
|
|
|
|
|
|
|
make_opts
|
|
|
|
let mut child = make_opts
|
|
|
|
.directory(build_path)
|
|
|
|
.directory(build_path)
|
|
|
|
.clean(true)
|
|
|
|
.clean(true)
|
|
|
|
.no_deps(true)
|
|
|
|
.no_deps(true)
|
|
|
|
.skip_pgp(true)
|
|
|
|
.skip_pgp(true)
|
|
|
|
.needed(true)
|
|
|
|
.needed(true)
|
|
|
|
.run()
|
|
|
|
.spawn()?;
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
|
|
|
|
let stderr = child.stderr.take().unwrap();
|
|
|
|
|
|
|
|
let stdout = child.stdout.take().unwrap();
|
|
|
|
|
|
|
|
let h1 = show_stdio_on_pb(stdout, pb.clone(), {
|
|
|
|
|
|
|
|
let pkg_name = pkg_name.clone();
|
|
|
|
|
|
|
|
move |s| format!("{}: {s}", pkg_name.clone().bold())
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
let h2 = show_stdio_on_pb(stderr, pb.clone(), {
|
|
|
|
|
|
|
|
let pkg_name = pkg_name.clone();
|
|
|
|
|
|
|
|
move |s| format!("{}: {s}", pkg_name.clone().bold())
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
loop {
|
|
|
|
|
|
|
|
if let Some(exit_code) = child.try_wait()? {
|
|
|
|
|
|
|
|
h1.abort();
|
|
|
|
|
|
|
|
h2.abort();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if !exit_code.success() {
|
|
|
|
|
|
|
|
pb.set_message(format!(
|
|
|
|
|
|
|
|
"{}: {}",
|
|
|
|
|
|
|
|
"Build failed!".red(),
|
|
|
|
|
|
|
|
pkg_name.clone().bold()
|
|
|
|
|
|
|
|
));
|
|
|
|
|
|
|
|
return Err(AppError::from("Failed to build package"));
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let packages = MakePkgBuilder::package_list(build_path).await?;
|
|
|
|
let packages = MakePkgBuilder::package_list(build_path).await?;
|
|
|
|
pb.finish_with_message(format!("{pkg_name}: Built!"));
|
|
|
|
pb.finish_with_message(format!("{}: {}", pkg_name.clone().bold(), "Built!".green()));
|
|
|
|
ctx.step = BuildStep::Install(PackageArchives(packages));
|
|
|
|
ctx.step = BuildStep::Install(PackageArchives(packages));
|
|
|
|
|
|
|
|
|
|
|
|
Ok(ctx)
|
|
|
|
Ok(ctx)
|
|
|
@ -297,3 +363,64 @@ async fn install_packages(
|
|
|
|
|
|
|
|
|
|
|
|
Ok(ctxs)
|
|
|
|
Ok(ctxs)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[tracing::instrument(level = "trace")]
|
|
|
|
|
|
|
|
fn create_dependency_batches(deps: Vec<PackageInfo>) -> Vec<Vec<PackageInfo>> {
|
|
|
|
|
|
|
|
let mut deps: HashMap<String, PackageInfo> = deps
|
|
|
|
|
|
|
|
.into_iter()
|
|
|
|
|
|
|
|
.map(|d| (d.metadata.name.clone(), d))
|
|
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
let mut batches = Vec::new();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
while !deps.is_empty() {
|
|
|
|
|
|
|
|
let mut current_batch = HashMap::new();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (key, info) in deps.clone() {
|
|
|
|
|
|
|
|
let contains_make_dep = info
|
|
|
|
|
|
|
|
.make_depends
|
|
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
|
|
.any(|d| current_batch.contains_key(d) || deps.contains_key(d));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let contains_dep = info
|
|
|
|
|
|
|
|
.depends
|
|
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
|
|
.any(|d| current_batch.contains_key(d) || deps.contains_key(d));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if !contains_dep && !contains_make_dep {
|
|
|
|
|
|
|
|
deps.remove(&key);
|
|
|
|
|
|
|
|
current_batch.insert(key, info);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
batches.push(current_batch.into_iter().map(|(_, v)| v).collect());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
batches
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn show_stdio_on_pb<
|
|
|
|
|
|
|
|
R: AsyncRead + Unpin + Send + 'static,
|
|
|
|
|
|
|
|
F: Fn(String) -> String + 'static + Send,
|
|
|
|
|
|
|
|
>(
|
|
|
|
|
|
|
|
stdout: R,
|
|
|
|
|
|
|
|
pb: Arc<ProgressBar>,
|
|
|
|
|
|
|
|
fmt: F,
|
|
|
|
|
|
|
|
) -> JoinHandle<()> {
|
|
|
|
|
|
|
|
tokio::task::spawn({
|
|
|
|
|
|
|
|
async move {
|
|
|
|
|
|
|
|
let mut stdout = BufReader::new(stdout);
|
|
|
|
|
|
|
|
let mut line = String::new();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
while let Ok(ch) = stdout.read_u8().await {
|
|
|
|
|
|
|
|
if ch == b'\n' {
|
|
|
|
|
|
|
|
let line = fmt(mem::take(&mut line));
|
|
|
|
|
|
|
|
let lines = wrap_text(line);
|
|
|
|
|
|
|
|
let line = lines.into_iter().next().unwrap();
|
|
|
|
|
|
|
|
pb.set_message(line);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
line.push(ch as char);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
}
|
|
|
|