cub: full AUR package manager + Phase 1-5 native build tools
cub redesign (local/recipes/system/cub/): - AUR RPC v5 client (serde_json) with search/info - ~/.cub/ user-local recipe/source/repo storage - Enhanced PKGBUILD parser: optdepends, .SRCINFO, split packages, 19 linuxism patterns - Recipe generation: host: prefix on dev-deps, shallow_clone, cargopath, installs, optional-packages - Dependency resolver: scans build errors for missing commands/headers/libs/pkgconfig, maps to packages - Dependency installation: checks installed packages, fetches AUR deps, interactive prompt - ~110 Arc→Redox dependency mappings - ratatui TUI: search, info, install, build, query views - 14 Arch-style CLI switches (-S/-Si/-Syu/-G/-R/-Q/-Qi/-Ql) - 65 tests, 0 failures, clean build Phase 1-5 native build tools (local/recipes/dev/): - P1 Substrate: tar, m4, diffutils (gnulib bypass), mkfifo kernel patch (1085 lines) - P2 Build Systems: bison, flex, meson (standalone wrapper), ninja-build, libtool - P3 Native GCC: gcc-native, binutils-native (cross-compiled for redox host) - P4 Native LLVM: llvm-native (clang + lld from monorepo) - P5 Native Rust: rust-native (rustc + cargo) - Groups: build-essential-native, dev-essential expanded Config: - redbear-mini: +7 tools (diffutils, tar, bison, flex, meson, ninja, m4) - redbear-full: +4 native tools (gcc, binutils, llvm, rust) - All recipes moved to local/ with symlinks for cookbook discovery (Red Bear policy) Docs: - BUILD-TOOLS-PORTING-PLAN.md: phased porting roadmap - CUB-WORKFLOW-ASSESSMENT.md: gap analysis and integration assessment
This commit is contained in:
@@ -3,11 +3,14 @@ use std::env;
|
||||
use std::ffi::OsString;
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::io::Write;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
use std::rc::Rc;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
use clap::{CommandFactory, Parser, Subcommand};
|
||||
use cub::aur::{AurClient, AurPackage};
|
||||
use cub::cook;
|
||||
@@ -475,6 +478,27 @@ fn build_local_dir(context: &AppContext, dir: &Path) -> Result<(), Box<dyn std::
|
||||
let rbpkg = RbPkgBuild::from_file(&rbpkg_path)?;
|
||||
rbpkg.validate()?;
|
||||
|
||||
let missing = check_missing_dependencies(context, &rbpkg)?;
|
||||
if !missing.is_empty() {
|
||||
println!(
|
||||
"The following dependencies are not installed and must be resolved before building {}:",
|
||||
rbpkg.package.name
|
||||
);
|
||||
for (dep, kind) in &missing {
|
||||
println!(" - {} ({})", dep, kind);
|
||||
}
|
||||
println!();
|
||||
println!("Would you like to try installing missing dependencies? [y/N]");
|
||||
|
||||
let mut answer = String::new();
|
||||
io::stdin().read_line(&mut answer)?;
|
||||
if answer.trim().to_ascii_lowercase().starts_with('y') {
|
||||
resolve_dependencies_interactive(context, &missing)?;
|
||||
} else {
|
||||
println!("Proceeding with build — it may fail if dependencies are missing at cook time.");
|
||||
}
|
||||
}
|
||||
|
||||
let work_dir = create_temp_dir("cub-build")?;
|
||||
let recipe_dir = work_dir.join(&rbpkg.package.name);
|
||||
CookbookAdapter::write_recipe_dir(&rbpkg, &recipe_dir)?;
|
||||
@@ -525,6 +549,126 @@ fn build_local_dir(context: &AppContext, dir: &Path) -> Result<(), Box<dyn std::
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn check_missing_dependencies(
|
||||
context: &AppContext,
|
||||
rbpkg: &RbPkgBuild,
|
||||
) -> Result<Vec<(String, String)>, Box<dyn std::error::Error>> {
|
||||
let library = context.open_library()?;
|
||||
let installed: Vec<String> = library
|
||||
.get_installed_packages()?
|
||||
.into_iter()
|
||||
.map(|p| p.to_string().to_ascii_lowercase())
|
||||
.collect();
|
||||
|
||||
let mut missing = Vec::new();
|
||||
let all_deps: Vec<(&String, &str)> = rbpkg
|
||||
.dependencies
|
||||
.build
|
||||
.iter()
|
||||
.map(|d| (d, "build dependency"))
|
||||
.chain(
|
||||
rbpkg
|
||||
.dependencies
|
||||
.runtime
|
||||
.iter()
|
||||
.map(|d| (d, "runtime dependency")),
|
||||
)
|
||||
.collect();
|
||||
|
||||
let mut seen = HashSet::new();
|
||||
for (dep, kind) in all_deps {
|
||||
let lower = dep.to_ascii_lowercase();
|
||||
if !seen.insert(lower.clone()) {
|
||||
continue;
|
||||
}
|
||||
if installed.contains(&lower) {
|
||||
continue;
|
||||
}
|
||||
missing.push((dep.clone(), kind.to_string()));
|
||||
}
|
||||
|
||||
Ok(missing)
|
||||
}
|
||||
|
||||
fn resolve_dependencies_interactive(
|
||||
context: &AppContext,
|
||||
missing: &[(String, String)],
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut library = context.open_library()?;
|
||||
|
||||
for (dep, _kind) in missing {
|
||||
let package_name = match PackageName::new(dep.clone()) {
|
||||
Ok(name) => name,
|
||||
Err(_) => {
|
||||
eprintln!(" skipping invalid package name: {dep}");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
print!(" installing {dep} from official repo... ");
|
||||
io::stdout().flush()?;
|
||||
|
||||
match library.install(vec![package_name.clone()]) {
|
||||
Ok(()) => {
|
||||
println!("done");
|
||||
}
|
||||
Err(pkg::backend::Error::PackageNotFound(_)) => {
|
||||
println!("not found in official repo — trying AUR");
|
||||
print!(" fetching {dep} from AUR into ~/.cub/... ");
|
||||
io::stdout().flush()?;
|
||||
|
||||
match fetch_aur_to_store(dep) {
|
||||
Ok(_) => println!("done (recipe saved, build with `cub -B ~/.cub/recipes/{dep}`)"),
|
||||
Err(e) => println!("failed: {e}"),
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("failed: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let _ = apply_library_changes(&mut library);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fetch_aur_to_store(package: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let store = CubStore::new()?;
|
||||
store.init()?;
|
||||
let recipe_dir = store.recipes_dir().join(package);
|
||||
if recipe_dir.exists() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let repo_url = aur_repo_url(package);
|
||||
let clone_dir = create_temp_dir("cub-dep-aur")?;
|
||||
|
||||
let status = Command::new("git")
|
||||
.arg("clone")
|
||||
.arg("--depth")
|
||||
.arg("1")
|
||||
.arg("--")
|
||||
.arg(&repo_url)
|
||||
.arg(&clone_dir)
|
||||
.status()?;
|
||||
|
||||
if !status.success() {
|
||||
return Err(Box::new(CubError::BuildFailed(format!(
|
||||
"failed to clone AUR source from {repo_url}"
|
||||
))));
|
||||
}
|
||||
|
||||
let pkgbuild_path = clone_dir.join("PKGBUILD");
|
||||
let pkgbuild_content = fs::read_to_string(&pkgbuild_path)?;
|
||||
let conversion = pkgbuild::convert_pkgbuild(&pkgbuild_content)?;
|
||||
|
||||
fs::create_dir_all(&recipe_dir)?;
|
||||
fs::write(recipe_dir.join("RBPKGBUILD"), conversion.rbpkg.to_toml()?)?;
|
||||
cub::recipe::save_recipe_to_store(&conversion.rbpkg, &store)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fetch_bur_recipe(package: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let source_dir = ensure_bur_package_dir(package)?;
|
||||
let destination = env::current_dir()?.join(package);
|
||||
|
||||
Reference in New Issue
Block a user