mirror of
https://gitlab.redox-os.org/redox-os/redox.git
synced 2026-06-22 21:04:18 +08:00
Add command stdout pipe
This commit is contained in:
parent
e03e843abd
commit
2af2a5bd26
@ -21,8 +21,8 @@ fn cook(
|
||||
is_offline: bool,
|
||||
) -> Result<(), String> {
|
||||
let source_dir = match is_offline {
|
||||
true => fetch_offline(recipe_dir, recipe),
|
||||
false => fetch(recipe_dir, recipe),
|
||||
true => fetch_offline(recipe_dir, recipe, &None),
|
||||
false => fetch(recipe_dir, recipe, &None),
|
||||
}
|
||||
.map_err(|err| format!("failed to fetch: {}", err))?;
|
||||
|
||||
@ -40,6 +40,7 @@ fn cook(
|
||||
recipe,
|
||||
is_offline,
|
||||
!is_deps,
|
||||
&None,
|
||||
)
|
||||
.map_err(|err| format!("failed to build: {}", err))?;
|
||||
|
||||
|
||||
148
src/bin/repo.rs
148
src/bin/repo.rs
@ -1,4 +1,5 @@
|
||||
use std::io::stdout;
|
||||
use std::collections::HashMap;
|
||||
use std::io::{BufRead, BufReader, PipeReader, stdout};
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
use std::sync::mpsc;
|
||||
@ -11,7 +12,7 @@ use cookbook::WALK_DEPTH;
|
||||
use cookbook::config::{CookConfig, get_config, init_config};
|
||||
use cookbook::cook::cook_build::build;
|
||||
use cookbook::cook::fetch::{fetch, fetch_offline};
|
||||
use cookbook::cook::fs::create_target_dir;
|
||||
use cookbook::cook::fs::{Stdout, create_target_dir};
|
||||
use cookbook::cook::package::package;
|
||||
use cookbook::recipe::CookRecipe;
|
||||
use pkg::PackageName;
|
||||
@ -20,7 +21,7 @@ use ratatui::Terminal;
|
||||
use ratatui::layout::{Constraint, Direction, Layout};
|
||||
use ratatui::prelude::TermionBackend;
|
||||
use ratatui::style::{Color, Style};
|
||||
use ratatui::widgets::{Block, Borders, List, ListItem, Paragraph};
|
||||
use ratatui::widgets::{Block, Borders, List, ListItem, Paragraph, Wrap};
|
||||
use termion::screen::{ToAlternateScreen, ToMainScreen};
|
||||
|
||||
// A repo manager, to replace repo.sh
|
||||
@ -141,11 +142,11 @@ fn main_inner() -> anyhow::Result<()> {
|
||||
for recipe in &recipe_names {
|
||||
match command {
|
||||
CliCommand::Fetch => {
|
||||
handle_fetch(recipe, &config)?;
|
||||
handle_fetch(recipe, &config, &None)?;
|
||||
}
|
||||
CliCommand::Cook => {
|
||||
let source_dir = handle_fetch(recipe, &config)?;
|
||||
handle_cook(recipe, &config, source_dir, recipe.is_deps)?
|
||||
let source_dir = handle_fetch(recipe, &config, &None)?;
|
||||
handle_cook(recipe, &config, source_dir, recipe.is_deps, &None)?
|
||||
}
|
||||
CliCommand::Unfetch => handle_clean(recipe, &config, true, true)?,
|
||||
CliCommand::Clean => handle_clean(recipe, &config, false, true)?,
|
||||
@ -241,11 +242,15 @@ fn parse_args(args: Vec<String>) -> anyhow::Result<(CliConfig, CliCommand, Vec<C
|
||||
Ok((config, command, recipes))
|
||||
}
|
||||
|
||||
fn handle_fetch(recipe: &CookRecipe, config: &CliConfig) -> anyhow::Result<PathBuf> {
|
||||
fn handle_fetch(
|
||||
recipe: &CookRecipe,
|
||||
config: &CliConfig,
|
||||
logger: &Stdout,
|
||||
) -> anyhow::Result<PathBuf> {
|
||||
let recipe_dir = &recipe.dir;
|
||||
let source_dir = match config.cook.offline {
|
||||
true => fetch_offline(recipe_dir, &recipe.recipe),
|
||||
false => fetch(recipe_dir, &recipe.recipe),
|
||||
true => fetch_offline(recipe_dir, &recipe.recipe, logger),
|
||||
false => fetch(recipe_dir, &recipe.recipe, logger),
|
||||
}
|
||||
.map_err(|e| anyhow!(e))?;
|
||||
|
||||
@ -257,10 +262,10 @@ fn handle_cook(
|
||||
config: &CliConfig,
|
||||
source_dir: PathBuf,
|
||||
is_deps: bool,
|
||||
logger: &Stdout,
|
||||
) -> anyhow::Result<()> {
|
||||
let recipe_dir = &recipe.dir;
|
||||
let target_dir = create_target_dir(recipe_dir).map_err(|e| anyhow!(e))?;
|
||||
|
||||
let (stage_dir, auto_deps) = build(
|
||||
recipe_dir,
|
||||
&source_dir,
|
||||
@ -269,6 +274,7 @@ fn handle_cook(
|
||||
&recipe.recipe,
|
||||
config.cook.offline,
|
||||
!is_deps,
|
||||
logger,
|
||||
)
|
||||
.map_err(|err| anyhow!("failed to build: {}", err))?;
|
||||
|
||||
@ -332,6 +338,7 @@ enum StatusUpdate {
|
||||
Fetched(PackageName),
|
||||
FailFetch(PackageName, String),
|
||||
StartCook(PackageName),
|
||||
CookLog(PackageName, String),
|
||||
Cooked(PackageName),
|
||||
FailCook(PackageName, String),
|
||||
}
|
||||
@ -342,6 +349,9 @@ struct TuiApp {
|
||||
cook_queue: Vec<PackageName>,
|
||||
done: Vec<PackageName>,
|
||||
failed: Vec<PackageName>,
|
||||
active_fetch: Option<PackageName>,
|
||||
active_cook: Option<PackageName>,
|
||||
logs: HashMap<PackageName, Vec<String>>,
|
||||
}
|
||||
|
||||
impl TuiApp {
|
||||
@ -356,16 +366,36 @@ impl TuiApp {
|
||||
cook_queue: Vec::new(),
|
||||
done: Vec::new(),
|
||||
failed: Vec::new(),
|
||||
active_fetch: None,
|
||||
active_cook: None,
|
||||
logs: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
// Update the state based on a message from a worker thread
|
||||
fn update_status(&mut self, update: StatusUpdate) {
|
||||
let (name, new_status) = match update {
|
||||
StatusUpdate::StartFetch(name) => (name, RecipeStatus::Fetching),
|
||||
StatusUpdate::StartFetch(name) => {
|
||||
self.active_fetch = Some(name.clone());
|
||||
self.logs.insert(name.clone(), Vec::new()); // Clear old logs
|
||||
(name.clone(), RecipeStatus::Fetching)
|
||||
}
|
||||
StatusUpdate::Fetched(name) => (name, RecipeStatus::Fetched),
|
||||
StatusUpdate::FailFetch(name, err) => (name, RecipeStatus::Failed(err)),
|
||||
StatusUpdate::StartCook(name) => (name, RecipeStatus::Cooking),
|
||||
StatusUpdate::StartCook(name) => {
|
||||
self.active_cook = Some(name.clone()); // Set active cook
|
||||
self.logs.insert(name.clone(), Vec::new()); // Clear old logs
|
||||
(name.clone(), RecipeStatus::Cooking)
|
||||
}
|
||||
StatusUpdate::CookLog(name, line) => {
|
||||
self.logs.entry(name.clone()).or_default().push(line);
|
||||
// No status change, just return the current state
|
||||
if let Some((_, status)) = self.recipes.iter().find(|(r, _)| r.name == name) {
|
||||
(name, status.clone())
|
||||
} else {
|
||||
return; // Should not happen
|
||||
}
|
||||
}
|
||||
StatusUpdate::Cooked(name) => (name, RecipeStatus::Done),
|
||||
StatusUpdate::FailCook(name, err) => (name, RecipeStatus::Failed(err)),
|
||||
};
|
||||
@ -402,6 +432,26 @@ impl TuiApp {
|
||||
}
|
||||
}
|
||||
|
||||
fn spawn_log_reader(
|
||||
mut pipe_reader: PipeReader,
|
||||
package_name: PackageName,
|
||||
status_tx: mpsc::Sender<StatusUpdate>,
|
||||
) {
|
||||
thread::spawn(move || {
|
||||
let reader = BufReader::new(&mut pipe_reader);
|
||||
for line in reader.lines() {
|
||||
let line_str = line.unwrap_or_else(|e| format!("[IO Error] {}", e));
|
||||
if status_tx
|
||||
.send(StatusUpdate::CookLog(package_name.clone(), line_str))
|
||||
.is_err()
|
||||
{
|
||||
// TUI thread hung up
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn run_tui_cook(config: CliConfig, recipes: Vec<CookRecipe>) -> anyhow::Result<()> {
|
||||
let (work_tx, work_rx) = mpsc::channel::<(CookRecipe, PathBuf)>();
|
||||
let (status_tx, status_rx) = mpsc::channel::<StatusUpdate>();
|
||||
@ -416,8 +466,9 @@ fn run_tui_cook(config: CliConfig, recipes: Vec<CookRecipe>) -> anyhow::Result<(
|
||||
cooker_status_tx
|
||||
.send(StatusUpdate::StartCook(name.clone()))
|
||||
.unwrap();
|
||||
|
||||
match handle_cook(&recipe, &cooker_config, source_dir, is_deps) {
|
||||
let (mut stdout_writer, mut stderr_writer) = setup_logger(&cooker_status_tx, &name);
|
||||
let logger = Some((&mut stdout_writer, &mut stderr_writer));
|
||||
match handle_cook(&recipe, &cooker_config, source_dir, is_deps, &logger) {
|
||||
Ok(_) => cooker_status_tx.send(StatusUpdate::Cooked(name)).unwrap(),
|
||||
Err(e) => cooker_status_tx
|
||||
.send(StatusUpdate::FailCook(name, e.to_string()))
|
||||
@ -427,18 +478,22 @@ fn run_tui_cook(config: CliConfig, recipes: Vec<CookRecipe>) -> anyhow::Result<(
|
||||
});
|
||||
|
||||
// ---- Fetcher Thread ----
|
||||
let fetcher_recipes = recipes.clone();
|
||||
let fetcher_config = config.clone();
|
||||
let fetcher_handle = thread::spawn(move || {
|
||||
for recipe in recipes {
|
||||
for recipe in fetcher_recipes {
|
||||
let name = recipe.name.clone();
|
||||
status_tx
|
||||
.send(StatusUpdate::StartFetch(name.clone()))
|
||||
.unwrap();
|
||||
|
||||
match handle_fetch(&recipe, &fetcher_config) {
|
||||
let (mut stdout_writer, mut stderr_writer) = setup_logger(&status_tx, &name);
|
||||
let logger = Some((&mut stdout_writer, &mut stderr_writer));
|
||||
|
||||
match handle_fetch(&recipe, &fetcher_config, &logger) {
|
||||
Ok(source_dir) => {
|
||||
status_tx.send(StatusUpdate::Fetched(name)).unwrap();
|
||||
if work_tx.send((recipe, source_dir)).is_err() {
|
||||
if work_tx.send((recipe.clone(), source_dir)).is_err() {
|
||||
// Cooker thread died
|
||||
break;
|
||||
}
|
||||
@ -455,15 +510,22 @@ fn run_tui_cook(config: CliConfig, recipes: Vec<CookRecipe>) -> anyhow::Result<(
|
||||
let mut terminal = Terminal::new(TermionBackend::new(stdout()))?;
|
||||
terminal.clear()?;
|
||||
|
||||
let mut app = TuiApp::new(Vec::new());
|
||||
let total_recipes = app.recipes.len();
|
||||
let mut app = TuiApp::new(recipes);
|
||||
// let total_recipes = app.recipes.len();
|
||||
let mut running = true;
|
||||
|
||||
while running {
|
||||
terminal.draw(|f| {
|
||||
let chunks = Layout::default()
|
||||
.direction(Direction::Horizontal)
|
||||
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
|
||||
.constraints(
|
||||
[
|
||||
Constraint::Percentage(20),
|
||||
Constraint::Percentage(20),
|
||||
Constraint::Percentage(60),
|
||||
]
|
||||
.as_ref(),
|
||||
)
|
||||
.split(f.area());
|
||||
|
||||
// Left Pane
|
||||
@ -509,13 +571,34 @@ fn run_tui_cook(config: CliConfig, recipes: Vec<CookRecipe>) -> anyhow::Result<(
|
||||
.block(Block::default().title("Cook Queue").borders(Borders::ALL));
|
||||
f.render_widget(cook_list, chunks[1]);
|
||||
|
||||
let footer = Paragraph::new(format!(
|
||||
"Done: {}/{} | Failed: {}",
|
||||
app.done.len(),
|
||||
total_recipes,
|
||||
app.failed.len()
|
||||
));
|
||||
f.render_widget(footer, f.area());
|
||||
let log_title = if let Some(active_name) = &app.active_cook {
|
||||
format!("Build Log: {}", active_name.as_str())
|
||||
} else {
|
||||
"Build Log".to_string()
|
||||
};
|
||||
|
||||
let log_text: Vec<String> = if let Some(active_name) = &app.active_cook {
|
||||
app.logs
|
||||
.get(active_name)
|
||||
.cloned()
|
||||
.unwrap_or_else(|| vec!["Waiting for logs...".to_string()])
|
||||
} else {
|
||||
vec!["No active cook job.".to_string()]
|
||||
};
|
||||
|
||||
let log_paragraph = Paragraph::new(log_text.join("\n"))
|
||||
.block(Block::default().title(log_title).borders(Borders::ALL))
|
||||
.wrap(Wrap { trim: false });
|
||||
|
||||
f.render_widget(log_paragraph, chunks[2]);
|
||||
|
||||
// let footer = Paragraph::new(format!(
|
||||
// "Done: {}/{} | Failed: {}",
|
||||
// app.done.len(),
|
||||
// total_recipes,
|
||||
// app.failed.len()
|
||||
// ));
|
||||
// f.render_widget(footer, f.area());
|
||||
})?;
|
||||
|
||||
while let Ok(update) = status_rx.try_recv() {
|
||||
@ -536,3 +619,14 @@ fn run_tui_cook(config: CliConfig, recipes: Vec<CookRecipe>) -> anyhow::Result<(
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn setup_logger(
|
||||
cooker_status_tx: &mpsc::Sender<StatusUpdate>,
|
||||
name: &PackageName,
|
||||
) -> (std::io::PipeWriter, std::io::PipeWriter) {
|
||||
let (stdout_reader, stdout_writer) = std::io::pipe().expect("Failed to create stdout pipe");
|
||||
let (stderr_reader, stderr_writer) = std::io::pipe().expect("Failed to create stderr pipe");
|
||||
spawn_log_reader(stdout_reader, name.clone(), cooker_status_tx.clone());
|
||||
spawn_log_reader(stderr_reader, name.clone(), cooker_status_tx.clone());
|
||||
(stdout_writer, stderr_writer)
|
||||
}
|
||||
|
||||
@ -148,6 +148,7 @@ pub fn build(
|
||||
recipe: &Recipe,
|
||||
offline_mode: bool,
|
||||
check_source: bool,
|
||||
logger: &Stdout,
|
||||
) -> Result<(PathBuf, BTreeSet<PackageName>), String> {
|
||||
let sysroot_dir = target_dir.join("sysroot");
|
||||
let stage_dir = target_dir.join("stage");
|
||||
@ -296,7 +297,7 @@ pub fn build(
|
||||
flags_fn("COOKBOOK_MESON_FLAGS", mesonflags),
|
||||
),
|
||||
BuildKind::Custom { script } => script.clone(),
|
||||
BuildKind::Remote => return build_remote(target_dir, name, offline_mode),
|
||||
BuildKind::Remote => return build_remote(target_dir, name, offline_mode, logger),
|
||||
BuildKind::None => "".to_owned(),
|
||||
};
|
||||
|
||||
@ -341,7 +342,7 @@ pub fn build(
|
||||
"{}\n{}\n{}\n{}",
|
||||
BUILD_PRESCRIPT, SHARED_PRESCRIPT, script, BUILD_POSTSCRIPT
|
||||
);
|
||||
run_command_stdin(command, full_script.as_bytes())?;
|
||||
run_command_stdin(command, full_script.as_bytes(), logger)?;
|
||||
|
||||
// Move stage.tmp to stage atomically
|
||||
rename(&stage_dir_tmp, &stage_dir)?;
|
||||
@ -389,6 +390,7 @@ pub fn build_remote(
|
||||
target_dir: &Path,
|
||||
name: &PackageName,
|
||||
offline_mode: bool,
|
||||
logger: &Stdout,
|
||||
) -> Result<(PathBuf, BTreeSet<PackageName>), String> {
|
||||
// download straight from remote source then declare pkg dependencies as autodeps dependency
|
||||
let stage_dir = target_dir.join("stage");
|
||||
@ -398,9 +400,9 @@ pub fn build_remote(
|
||||
let source_pubkey = target_dir.join("id_ed25519.pub.toml");
|
||||
|
||||
if !offline_mode {
|
||||
download_wget(&get_remote_url(name, "pkgar"), &source_pkgar)?;
|
||||
download_wget(&get_remote_url(name, "toml"), &source_toml)?;
|
||||
download_wget(&get_pubkey_url(), &source_pubkey)?;
|
||||
download_wget(&get_remote_url(name, "pkgar"), &source_pkgar, logger)?;
|
||||
download_wget(&get_remote_url(name, "toml"), &source_toml, logger)?;
|
||||
download_wget(&get_pubkey_url(), &source_pubkey, logger)?;
|
||||
} else {
|
||||
offline_check_exists(&source_pkgar)?;
|
||||
offline_check_exists(&source_toml)?;
|
||||
|
||||
@ -25,7 +25,11 @@ pub(crate) fn get_blake3(path: &PathBuf, show_progress: bool) -> Result<String,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn fetch_offline(recipe_dir: &Path, recipe: &Recipe) -> Result<PathBuf, String> {
|
||||
pub fn fetch_offline(
|
||||
recipe_dir: &Path,
|
||||
recipe: &Recipe,
|
||||
logger: &Stdout,
|
||||
) -> Result<PathBuf, String> {
|
||||
let source_dir = recipe_dir.join("source");
|
||||
if recipe.build.kind == BuildKind::None || recipe.build.kind == BuildKind::Remote {
|
||||
// the build function doesn't need source dir exists
|
||||
@ -33,10 +37,10 @@ pub fn fetch_offline(recipe_dir: &Path, recipe: &Recipe) -> Result<PathBuf, Stri
|
||||
}
|
||||
match &recipe.source {
|
||||
Some(SourceRecipe::Path { path: _ }) | None => {
|
||||
return fetch(recipe_dir, recipe);
|
||||
return fetch(recipe_dir, recipe, logger);
|
||||
}
|
||||
Some(SourceRecipe::SameAs { same_as: _ }) => {
|
||||
return fetch(recipe_dir, recipe);
|
||||
return fetch(recipe_dir, recipe, logger);
|
||||
}
|
||||
Some(SourceRecipe::Git {
|
||||
git: _,
|
||||
@ -65,8 +69,8 @@ pub fn fetch_offline(recipe_dir: &Path, recipe: &Recipe) -> Result<PathBuf, Stri
|
||||
"The downloaded tar blake3 '{source_tar_blake3}' is not equal to blake3 in recipe.toml."
|
||||
));
|
||||
}
|
||||
fetch_extract_tar(source_tar, &source_dir)?;
|
||||
fetch_apply_patches(recipe_dir, patches, script, &source_dir)?;
|
||||
fetch_extract_tar(source_tar, &source_dir, logger)?;
|
||||
fetch_apply_patches(recipe_dir, patches, script, &source_dir, logger)?;
|
||||
} else {
|
||||
// need to trust this tar file
|
||||
return Err(format!(
|
||||
@ -84,7 +88,7 @@ pub fn fetch_offline(recipe_dir: &Path, recipe: &Recipe) -> Result<PathBuf, Stri
|
||||
Ok(source_dir)
|
||||
}
|
||||
|
||||
pub fn fetch(recipe_dir: &Path, recipe: &Recipe) -> Result<PathBuf, String> {
|
||||
pub fn fetch(recipe_dir: &Path, recipe: &Recipe, logger: &Stdout) -> Result<PathBuf, String> {
|
||||
let source_dir = recipe_dir.join("source");
|
||||
if recipe.build.kind == BuildKind::None || recipe.build.kind == BuildKind::Remote {
|
||||
// the build function doesn't need source dir exists
|
||||
@ -94,7 +98,7 @@ pub fn fetch(recipe_dir: &Path, recipe: &Recipe) -> Result<PathBuf, String> {
|
||||
Some(SourceRecipe::SameAs { same_as }) => {
|
||||
let (canon_dir, recipe) = fetch_resolve_canon(recipe_dir, &same_as)?;
|
||||
// recursively fetch
|
||||
fetch(&canon_dir, &recipe)?;
|
||||
fetch(&canon_dir, &recipe, logger)?;
|
||||
fetch_make_symlink(&source_dir, &same_as)?;
|
||||
}
|
||||
Some(SourceRecipe::Path { path }) => {
|
||||
@ -140,7 +144,7 @@ pub fn fetch(recipe_dir: &Path, recipe: &Recipe) -> Result<PathBuf, String> {
|
||||
command.arg("--depth").arg("1").arg("--shallow-submodules");
|
||||
}
|
||||
command.arg(&source_dir_tmp);
|
||||
run_command(command)?;
|
||||
run_command(command, logger)?;
|
||||
|
||||
// Move source.tmp to source atomically
|
||||
rename(&source_dir_tmp, &source_dir)?;
|
||||
@ -158,13 +162,13 @@ pub fn fetch(recipe_dir: &Path, recipe: &Recipe) -> Result<PathBuf, String> {
|
||||
let mut command = Command::new("git");
|
||||
command.arg("-C").arg(&source_dir);
|
||||
command.arg("remote").arg("set-url").arg("origin").arg(git);
|
||||
run_command(command)?;
|
||||
run_command(command, logger)?;
|
||||
|
||||
// Fetch origin
|
||||
let mut command = Command::new("git");
|
||||
command.arg("-C").arg(&source_dir);
|
||||
command.arg("fetch").arg("origin");
|
||||
run_command(command)?;
|
||||
run_command(command, logger)?;
|
||||
}
|
||||
|
||||
if let Some(_upstream) = upstream {
|
||||
@ -179,7 +183,7 @@ pub fn fetch(recipe_dir: &Path, recipe: &Recipe) -> Result<PathBuf, String> {
|
||||
let mut command = Command::new("git");
|
||||
command.arg("-C").arg(&source_dir);
|
||||
command.arg("checkout").arg(rev);
|
||||
run_command(command)?;
|
||||
run_command(command, logger)?;
|
||||
} else if !shallow_clone && !is_redox() {
|
||||
//TODO: complicated stuff to check and reset branch to origin
|
||||
//TODO: redox can't undestand this (got exit status 1)
|
||||
@ -189,7 +193,7 @@ pub fn fetch(recipe_dir: &Path, recipe: &Recipe) -> Result<PathBuf, String> {
|
||||
command.env("BRANCH", branch);
|
||||
}
|
||||
command.current_dir(&source_dir);
|
||||
run_command(command)?;
|
||||
run_command(command, logger)?;
|
||||
}
|
||||
|
||||
if !patches.is_empty() || script.is_some() {
|
||||
@ -197,7 +201,7 @@ pub fn fetch(recipe_dir: &Path, recipe: &Recipe) -> Result<PathBuf, String> {
|
||||
let mut command = Command::new("git");
|
||||
command.arg("-C").arg(&source_dir);
|
||||
command.arg("reset").arg("--hard");
|
||||
run_command(command)?;
|
||||
run_command(command, logger)?;
|
||||
}
|
||||
|
||||
if !shallow_clone {
|
||||
@ -205,7 +209,7 @@ pub fn fetch(recipe_dir: &Path, recipe: &Recipe) -> Result<PathBuf, String> {
|
||||
let mut command = Command::new("git");
|
||||
command.arg("-C").arg(&source_dir);
|
||||
command.arg("submodule").arg("sync").arg("--recursive");
|
||||
run_command(command)?;
|
||||
run_command(command, logger)?;
|
||||
|
||||
// Update submodules
|
||||
let mut command = Command::new("git");
|
||||
@ -215,10 +219,10 @@ pub fn fetch(recipe_dir: &Path, recipe: &Recipe) -> Result<PathBuf, String> {
|
||||
.arg("update")
|
||||
.arg("--init")
|
||||
.arg("--recursive");
|
||||
run_command(command)?;
|
||||
run_command(command, logger)?;
|
||||
}
|
||||
|
||||
fetch_apply_patches(recipe_dir, patches, script, &source_dir)?;
|
||||
fetch_apply_patches(recipe_dir, patches, script, &source_dir, logger)?;
|
||||
}
|
||||
Some(SourceRecipe::Tar {
|
||||
tar,
|
||||
@ -231,7 +235,7 @@ pub fn fetch(recipe_dir: &Path, recipe: &Recipe) -> Result<PathBuf, String> {
|
||||
while {
|
||||
if !source_tar.is_file() {
|
||||
tar_updated = true;
|
||||
download_wget(&tar, &source_tar)?;
|
||||
download_wget(&tar, &source_tar, logger)?;
|
||||
}
|
||||
let source_tar_blake3 = get_blake3(&source_tar, tar_updated)?;
|
||||
if let Some(blake3) = blake3 {
|
||||
@ -268,8 +272,8 @@ pub fn fetch(recipe_dir: &Path, recipe: &Recipe) -> Result<PathBuf, String> {
|
||||
// Create source.tmp
|
||||
let source_dir_tmp = recipe_dir.join("source.tmp");
|
||||
create_dir_clean(&source_dir_tmp)?;
|
||||
fetch_extract_tar(source_tar, &source_dir_tmp)?;
|
||||
fetch_apply_patches(recipe_dir, patches, script, &source_dir_tmp)?;
|
||||
fetch_extract_tar(source_tar, &source_dir_tmp, logger)?;
|
||||
fetch_apply_patches(recipe_dir, patches, script, &source_dir_tmp, logger)?;
|
||||
|
||||
// Move source.tmp to source atomically
|
||||
rename(&source_dir_tmp, &source_dir)?;
|
||||
@ -342,6 +346,7 @@ pub(crate) fn fetch_resolve_canon(
|
||||
pub(crate) fn fetch_extract_tar(
|
||||
source_tar: PathBuf,
|
||||
source_dir_tmp: &PathBuf,
|
||||
logger: &Stdout,
|
||||
) -> Result<(), String> {
|
||||
let mut command = Command::new("tar");
|
||||
if is_redox() {
|
||||
@ -354,7 +359,7 @@ pub(crate) fn fetch_extract_tar(
|
||||
command.arg(&source_tar);
|
||||
command.arg("--directory").arg(source_dir_tmp);
|
||||
command.arg("--strip-components").arg("1");
|
||||
run_command(command)?;
|
||||
run_command(command, logger)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -387,6 +392,7 @@ pub(crate) fn fetch_apply_patches(
|
||||
patches: &Vec<String>,
|
||||
script: &Option<String>,
|
||||
source_dir_tmp: &PathBuf,
|
||||
logger: &Stdout,
|
||||
) -> Result<(), String> {
|
||||
for patch_name in patches {
|
||||
let patch_file = recipe_dir.join(patch_name);
|
||||
@ -409,12 +415,16 @@ pub(crate) fn fetch_apply_patches(
|
||||
let mut command = Command::new("patch");
|
||||
command.arg("--directory").arg(source_dir_tmp);
|
||||
command.arg("--strip=1");
|
||||
run_command_stdin(command, patch.as_bytes())?;
|
||||
run_command_stdin(command, patch.as_bytes(), logger)?;
|
||||
}
|
||||
Ok(if let Some(script) = script {
|
||||
let mut command = Command::new("bash");
|
||||
command.arg("-ex");
|
||||
command.current_dir(source_dir_tmp);
|
||||
run_command_stdin(command, format!("{SHARED_PRESCRIPT}\n{script}").as_bytes())?;
|
||||
run_command_stdin(
|
||||
command,
|
||||
format!("{SHARED_PRESCRIPT}\n{script}").as_bytes(),
|
||||
logger,
|
||||
)?;
|
||||
})
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
use serde::Serialize;
|
||||
use std::{
|
||||
fs,
|
||||
io::{self, Write},
|
||||
io::{self, PipeWriter, Write},
|
||||
path::{Path, PathBuf},
|
||||
process::{self, Command, Stdio},
|
||||
time::SystemTime,
|
||||
@ -146,7 +146,25 @@ pub fn rename(src: &Path, dst: &Path) -> Result<(), String> {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn run_command(mut command: process::Command) -> Result<(), String> {
|
||||
pub type Stdout<'a> = Option<(&'a mut PipeWriter, &'a mut PipeWriter)>;
|
||||
|
||||
fn pipe_to_cmd(command: &mut Command, stdout_pipe: &Stdout) -> Result<(), String> {
|
||||
Ok(if let Some((stdout, stderr)) = stdout_pipe {
|
||||
command.stdout::<PipeWriter>(
|
||||
stdout
|
||||
.try_clone()
|
||||
.map_err(|e| format!("unable to clone stdout fd: {:?}", e))?,
|
||||
);
|
||||
command.stderr(
|
||||
stderr
|
||||
.try_clone()
|
||||
.map_err(|e| format!("unable to clone stderr fd: {:?}", e))?,
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
pub fn run_command(mut command: process::Command, stdout_pipe: &Stdout) -> Result<(), String> {
|
||||
pipe_to_cmd(&mut command, stdout_pipe)?;
|
||||
let status = command
|
||||
.status()
|
||||
.map_err(|err| format!("failed to run {:?}: {}\n{:#?}", command, err, err))?;
|
||||
@ -161,8 +179,13 @@ pub fn run_command(mut command: process::Command) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn run_command_stdin(mut command: process::Command, stdin_data: &[u8]) -> Result<(), String> {
|
||||
pub fn run_command_stdin(
|
||||
mut command: process::Command,
|
||||
stdin_data: &[u8],
|
||||
stdout_pipe: &Stdout,
|
||||
) -> Result<(), String> {
|
||||
command.stdin(Stdio::piped());
|
||||
pipe_to_cmd(&mut command, stdout_pipe)?;
|
||||
|
||||
let mut child = command
|
||||
.spawn()
|
||||
@ -217,13 +240,13 @@ pub fn offline_check_exists(path: &PathBuf) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn download_wget(url: &str, dest: &PathBuf) -> Result<(), String> {
|
||||
pub fn download_wget(url: &str, dest: &PathBuf, logger: &Stdout) -> Result<(), String> {
|
||||
if !dest.is_file() {
|
||||
let dest_tmp = PathBuf::from(format!("{}.tmp", dest.display()));
|
||||
let mut command = Command::new("wget");
|
||||
command.arg(translate_mirror(url));
|
||||
command.arg("--continue").arg("-O").arg(&dest_tmp);
|
||||
run_command(command)?;
|
||||
run_command(command, logger)?;
|
||||
rename(&dest_tmp, &dest)?;
|
||||
}
|
||||
Ok(())
|
||||
|
||||
@ -15,7 +15,7 @@ use serde::{
|
||||
use crate::WALK_DEPTH;
|
||||
|
||||
/// Specifies how to download the source for a recipe
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum SourceRecipe {
|
||||
/// Reuse the source directory of another package
|
||||
@ -88,7 +88,7 @@ impl SourceRecipe {
|
||||
}
|
||||
|
||||
/// Specifies how to build a recipe
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(tag = "template")]
|
||||
pub enum BuildKind {
|
||||
/// Will not build (for meta packages)
|
||||
@ -134,7 +134,7 @@ impl Default for BuildKind {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
|
||||
#[derive(Debug, Clone, Default, Deserialize, PartialEq, Serialize)]
|
||||
pub struct BuildRecipe {
|
||||
#[serde(flatten, default)]
|
||||
pub kind: BuildKind,
|
||||
@ -142,7 +142,7 @@ pub struct BuildRecipe {
|
||||
pub dependencies: Vec<PackageName>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
|
||||
#[derive(Debug, Clone, Default, Deserialize, PartialEq, Serialize)]
|
||||
pub struct PackageRecipe {
|
||||
#[serde(default)]
|
||||
pub dependencies: Vec<PackageName>,
|
||||
@ -151,7 +151,7 @@ pub struct PackageRecipe {
|
||||
}
|
||||
|
||||
/// Everything required to build a Redox package
|
||||
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
|
||||
#[derive(Debug, Clone, Default, Deserialize, PartialEq, Serialize)]
|
||||
pub struct Recipe {
|
||||
/// Specifies how to download the source for this recipe
|
||||
pub source: Option<SourceRecipe>,
|
||||
@ -175,7 +175,7 @@ impl Recipe {
|
||||
Ok(recipe)
|
||||
}
|
||||
}
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct CookRecipe {
|
||||
pub name: PackageName,
|
||||
pub dir: PathBuf,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user