mirror of
https://gitlab.redox-os.org/redox-os/redox.git
synced 2026-06-17 23:44:17 +08:00
Write build logs
This commit is contained in:
parent
32024767ee
commit
1e0b60eec5
130
src/bin/repo.rs
130
src/bin/repo.rs
@ -72,6 +72,7 @@ struct CliConfig {
|
||||
cookbook_dir: PathBuf,
|
||||
repo_dir: PathBuf,
|
||||
sysroot_dir: PathBuf,
|
||||
logs_dir: Option<PathBuf>,
|
||||
category: Option<PathBuf>,
|
||||
filesystem: Option<redox_installer::Config>,
|
||||
with_package_deps: bool,
|
||||
@ -140,6 +141,12 @@ impl CliConfig {
|
||||
//FIXME: This config is unused as redox-pkg harcoded this to $PWD/recipes
|
||||
cookbook_dir: current_dir.join("recipes"),
|
||||
repo_dir: current_dir.join("repo"),
|
||||
// build dir here is hardcoded in repo_builder as well
|
||||
logs_dir: if get_config().cook.tui_logs {
|
||||
Some(current_dir.join("build/logs"))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
category: None,
|
||||
sysroot_dir: if cfg!(target_os = "redox") {
|
||||
PathBuf::from("/")
|
||||
@ -342,6 +349,9 @@ fn parse_args(args: Vec<String>) -> anyhow::Result<(CliConfig, CliCommand, Vec<C
|
||||
// need to prefix by cookbook dir
|
||||
config.category = Some(PathBuf::from("recipes").join(c));
|
||||
}
|
||||
if let Some(c) = config.logs_dir.as_ref() {
|
||||
fs::create_dir_all(c).map_err(|e| anyhow!(e))?;
|
||||
}
|
||||
|
||||
let command = command.ok_or(anyhow!("Error: No command specified."))?;
|
||||
let command: CliCommand = str::parse(&command)?;
|
||||
@ -599,6 +609,7 @@ enum StatusUpdate {
|
||||
Cooked(CookRecipe),
|
||||
FailCook(CookRecipe, String),
|
||||
PushLog(PackageName, Vec<u8>),
|
||||
FlushLog(PackageName, PathBuf),
|
||||
FetchThreadFinished,
|
||||
CookThreadFinished,
|
||||
}
|
||||
@ -676,6 +687,52 @@ impl TuiApp {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_active_name(&self) -> Option<PackageName> {
|
||||
if self.log_view_job == JobType::Cook {
|
||||
self.active_cook.clone()
|
||||
} else {
|
||||
self.active_fetch.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_active_log(
|
||||
&self,
|
||||
) -> (
|
||||
Option<PackageName>,
|
||||
Option<&Vec<String>>,
|
||||
Option<Cow<'_, str>>,
|
||||
) {
|
||||
let active_name = self.get_active_name();
|
||||
let (log_text, log_line) = if let Some(active_name) = active_name.as_ref() {
|
||||
self.get_recipe_log(active_name)
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
|
||||
(active_name, log_text, log_line)
|
||||
}
|
||||
|
||||
pub fn get_recipe_log(
|
||||
&self,
|
||||
recipe_name: &PackageName,
|
||||
) -> (Option<&Vec<String>>, Option<Cow<'_, str>>) {
|
||||
let log_text = self.logs.get(recipe_name);
|
||||
let log_line = if let Some(b) = self.log_byte_buffer.get(recipe_name) {
|
||||
Some(String::from_utf8_lossy(b))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
(log_text, log_line)
|
||||
}
|
||||
|
||||
pub fn write_log(&self, recipe_name: &PackageName, log_path: &PathBuf) -> anyhow::Result<()> {
|
||||
let (Some(logs), line) = self.get_recipe_log(recipe_name) else {
|
||||
return Ok(());
|
||||
};
|
||||
fs::write(log_path, join_logs(logs, line))?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Update the state based on a message from a worker thread
|
||||
fn update_status(&mut self, update: StatusUpdate) {
|
||||
let (name, new_status) = match update {
|
||||
@ -711,6 +768,12 @@ impl TuiApp {
|
||||
}
|
||||
return;
|
||||
}
|
||||
StatusUpdate::FlushLog(name, path) => {
|
||||
// TODO: This blocks the TUI for a moment, maybe open separate thread?
|
||||
// FIXME: handle error here?
|
||||
let _ = self.write_log(&name, &path);
|
||||
return;
|
||||
}
|
||||
StatusUpdate::Cooked(recipe) => {
|
||||
if self.active_cook.as_ref() == Some(&recipe.name) {
|
||||
self.active_cook = None;
|
||||
@ -784,13 +847,20 @@ fn run_tui_cook(
|
||||
let (mut stdout_writer, mut stderr_writer) = setup_logger(&cooker_status_tx, &name);
|
||||
let logger = Some((&mut stdout_writer, &mut stderr_writer));
|
||||
'again: loop {
|
||||
match handle_cook(
|
||||
let handler = handle_cook(
|
||||
&recipe,
|
||||
&cooker_config,
|
||||
source_dir.clone(),
|
||||
is_deps,
|
||||
&logger,
|
||||
) {
|
||||
);
|
||||
if let Some(log_path) = cooker_config.logs_dir.as_ref() {
|
||||
let log_path = log_path.join(name.as_str());
|
||||
cooker_status_tx
|
||||
.send(StatusUpdate::FlushLog(name.clone(), log_path))
|
||||
.unwrap_or_default();
|
||||
}
|
||||
match handler {
|
||||
Ok(()) => {
|
||||
cooker_status_tx
|
||||
.send(StatusUpdate::Cooked(recipe))
|
||||
@ -870,7 +940,14 @@ fn run_tui_cook(
|
||||
let logger = Some((&mut stdout_writer, &mut stderr_writer));
|
||||
|
||||
'again: loop {
|
||||
match handle_fetch(&recipe, &fetcher_config, &logger) {
|
||||
let handler = handle_fetch(&recipe, &fetcher_config, &logger);
|
||||
if let Some(log_path) = fetcher_config.logs_dir.as_ref() {
|
||||
let log_path = log_path.join(name.as_str());
|
||||
fetcher_status_tx
|
||||
.send(StatusUpdate::FlushLog(name.clone(), log_path))
|
||||
.unwrap_or_default();
|
||||
}
|
||||
match handler {
|
||||
Ok(source_dir) => {
|
||||
fetcher_status_tx
|
||||
.send(StatusUpdate::Fetched(recipe.clone()))
|
||||
@ -1041,7 +1118,7 @@ fn run_tui_cook(
|
||||
);
|
||||
f.render_stateful_widget(cook_list, cook_chunk, &mut app.cook_list_state);
|
||||
|
||||
let (active_name, log_text, log_line) = get_active_log(&app);
|
||||
let (active_name, log_text, log_line) = app.get_active_log();
|
||||
let log_title = if let Some(active_name) = active_name {
|
||||
format!(
|
||||
" {} Log: {} ",
|
||||
@ -1152,16 +1229,11 @@ fn run_tui_cook(
|
||||
if let Some((app, res)) = handle_prompt_input(&event, &mut app) {
|
||||
prompting.swap(res as u32, Ordering::SeqCst);
|
||||
if res == PromptOption::Exit {
|
||||
let (name, log, line) = get_active_log(&app);
|
||||
let (name, log, line) = app.get_active_log();
|
||||
if let Some(name) = name
|
||||
&& let Some(log) = log
|
||||
{
|
||||
let mut logs = log.join("\n");
|
||||
if let Some(line) = line {
|
||||
logs.push_str("\n");
|
||||
logs.push_str(handle_cr(&line));
|
||||
}
|
||||
app.dump_logs_on_exit = Some((name.to_owned(), logs));
|
||||
app.dump_logs_on_exit = Some((name.to_owned(), join_logs(log, line)));
|
||||
}
|
||||
running.store(false, Ordering::SeqCst);
|
||||
}
|
||||
@ -1194,38 +1266,20 @@ fn run_tui_cook(
|
||||
Ok(app.dump_logs_on_exit)
|
||||
}
|
||||
|
||||
fn join_logs(log: &Vec<String>, line: Option<Cow<'_, str>>) -> String {
|
||||
let mut logs = log.join("\n");
|
||||
if let Some(line) = line {
|
||||
logs.push_str("\n");
|
||||
logs.push_str(handle_cr(&line));
|
||||
}
|
||||
logs
|
||||
}
|
||||
|
||||
fn handle_cr<'a>(buffer: &'a Cow<'_, str>) -> &'a str {
|
||||
let st = buffer.trim_end();
|
||||
st.rsplit('\r').next().unwrap_or(&st)
|
||||
}
|
||||
|
||||
fn get_active_log(
|
||||
app: &TuiApp,
|
||||
) -> (
|
||||
Option<PackageName>,
|
||||
Option<&Vec<String>>,
|
||||
Option<Cow<'_, str>>,
|
||||
) {
|
||||
let active_name = if app.log_view_job == JobType::Cook {
|
||||
app.active_cook.clone()
|
||||
} else {
|
||||
app.active_fetch.clone()
|
||||
};
|
||||
let log_text = if let Some(active_name) = &active_name {
|
||||
app.logs.get(active_name)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let log_line = if let Some(active_name) = &active_name
|
||||
&& let Some(b) = app.log_byte_buffer.get(active_name)
|
||||
{
|
||||
Some(String::from_utf8_lossy(b))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
(active_name, log_text, log_line)
|
||||
}
|
||||
|
||||
fn handle_main_event(app: &mut TuiApp, event: &Event) {
|
||||
match event {
|
||||
Event::Key(key) => match key {
|
||||
|
||||
@ -12,6 +12,9 @@ pub struct CookConfigOpt {
|
||||
/// whether to use TUI to allow parallel build
|
||||
/// default value is yes if "CI" env unset and STDIN is open.
|
||||
pub tui: Option<bool>,
|
||||
/// whether to write logs to build/logs dir
|
||||
/// only usable when tui is used
|
||||
pub tui_logs: Option<bool>,
|
||||
/// whether to ignore build errors
|
||||
pub nonstop: Option<bool>,
|
||||
/// whether to print success recipes info and warnings
|
||||
@ -24,6 +27,7 @@ pub struct CookConfig {
|
||||
pub offline: bool,
|
||||
pub jobs: usize,
|
||||
pub tui: bool,
|
||||
pub tui_logs: bool,
|
||||
pub nonstop: bool,
|
||||
pub verbose: bool,
|
||||
}
|
||||
@ -34,6 +38,7 @@ impl From<CookConfigOpt> for CookConfig {
|
||||
offline: value.offline.unwrap(),
|
||||
jobs: value.jobs.unwrap(),
|
||||
tui: value.tui.unwrap(),
|
||||
tui_logs: value.tui_logs.unwrap(),
|
||||
nonstop: value.nonstop.unwrap(),
|
||||
verbose: value.verbose.unwrap(),
|
||||
}
|
||||
@ -75,6 +80,9 @@ pub fn init_config() {
|
||||
.unwrap_or(1),
|
||||
));
|
||||
}
|
||||
if config.cook_opt.tui_logs.is_none() {
|
||||
config.cook_opt.tui_logs = config.cook_opt.tui;
|
||||
}
|
||||
if config.cook_opt.offline.is_none() {
|
||||
config.cook_opt.offline = Some(extract_env("COOKBOOK_OFFLINE", false));
|
||||
}
|
||||
|
||||
@ -19,25 +19,10 @@ use std::{
|
||||
time::SystemTime,
|
||||
};
|
||||
|
||||
use crate::is_redox;
|
||||
use crate::{is_redox, log_to_pty};
|
||||
|
||||
use crate::REMOTE_PKG_SOURCE;
|
||||
|
||||
macro_rules! log_warn {
|
||||
($logger:expr, $($arg:tt)+) => {
|
||||
use std::io::Write;
|
||||
|
||||
if $logger.is_some() {
|
||||
let _ = $logger.as_ref().unwrap().1.try_clone().unwrap().write(
|
||||
format!($($arg)+)
|
||||
.as_bytes(),
|
||||
);
|
||||
} else {
|
||||
eprintln!($($arg)+);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn auto_deps_from_dynamic_linking(
|
||||
stage_dir: &Path,
|
||||
dep_pkgars: &BTreeSet<(PackageName, PathBuf)>,
|
||||
@ -61,7 +46,7 @@ fn auto_deps_from_dynamic_linking(
|
||||
};
|
||||
if visited.contains(&dir) {
|
||||
#[cfg(debug_assertions)]
|
||||
log_warn!(
|
||||
log_to_pty!(
|
||||
logger,
|
||||
"DEBUG: auto_deps => Skipping `{dir:?}` (already visited)"
|
||||
);
|
||||
@ -111,7 +96,7 @@ fn auto_deps_from_dynamic_linking(
|
||||
continue;
|
||||
};
|
||||
if let Ok(relative_path) = path.strip_prefix(stage_dir) {
|
||||
log_warn!(logger, "DEBUG: {} needs {}", relative_path.display(), name);
|
||||
log_to_pty!(logger, "DEBUG: {} needs {}", relative_path.display(), name);
|
||||
}
|
||||
needed.insert(name.to_string());
|
||||
}
|
||||
@ -145,7 +130,7 @@ fn auto_deps_from_dynamic_linking(
|
||||
continue;
|
||||
};
|
||||
if needed.contains(child_name) {
|
||||
log_warn!(logger, "DEBUG: {} provides {}", dep, child_name);
|
||||
log_to_pty!(logger, "DEBUG: {} provides {}", dep, child_name);
|
||||
deps.insert(dep.clone());
|
||||
missing.remove(child_name);
|
||||
}
|
||||
@ -155,7 +140,7 @@ fn auto_deps_from_dynamic_linking(
|
||||
}
|
||||
|
||||
for name in missing {
|
||||
log_warn!(logger, "WARN: {} missing", name);
|
||||
log_to_pty!(logger, "WARN: {} missing", name);
|
||||
}
|
||||
|
||||
deps
|
||||
@ -225,7 +210,7 @@ pub fn build(
|
||||
if sysroot_dir.is_dir() {
|
||||
let sysroot_modified = modified_dir(&sysroot_dir)?;
|
||||
if sysroot_modified < source_modified || sysroot_modified < deps_modified {
|
||||
log_warn!(
|
||||
log_to_pty!(
|
||||
logger,
|
||||
"DEBUG: '{}' newer than '{}'",
|
||||
source_dir.display(),
|
||||
@ -275,7 +260,7 @@ pub fn build(
|
||||
if stage_dir.is_dir() {
|
||||
let stage_modified = modified_dir(&stage_dir)?;
|
||||
if stage_modified < source_modified || stage_modified < deps_modified {
|
||||
log_warn!(
|
||||
log_to_pty!(
|
||||
logger,
|
||||
"DEBUG: '{}' newer than '{}'",
|
||||
source_dir.display(),
|
||||
|
||||
@ -3,6 +3,7 @@ use crate::cook::fs::*;
|
||||
use crate::cook::pty::PtyOut;
|
||||
use crate::cook::script::*;
|
||||
use crate::is_redox;
|
||||
use crate::log_to_pty;
|
||||
use crate::recipe::BuildKind;
|
||||
use crate::recipe::Recipe;
|
||||
use crate::{blake3, recipe::SourceRecipe};
|
||||
@ -10,21 +11,6 @@ use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
|
||||
macro_rules! log_warn {
|
||||
($logger:expr, $($arg:tt)+) => {
|
||||
use std::io::Write;
|
||||
|
||||
if $logger.is_some() {
|
||||
let _ = $logger.as_ref().unwrap().1.try_clone().unwrap().write(
|
||||
format!($($arg)+)
|
||||
.as_bytes(),
|
||||
);
|
||||
} else {
|
||||
eprintln!($($arg)+);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) fn get_blake3(path: &PathBuf, show_progress: bool) -> Result<String, String> {
|
||||
if show_progress {
|
||||
blake3::blake3_progress(&path)
|
||||
@ -120,7 +106,7 @@ pub fn fetch(recipe_dir: &Path, recipe: &Recipe, logger: &PtyOut) -> Result<Path
|
||||
Some(SourceRecipe::Path { path }) => {
|
||||
if !source_dir.is_dir() || modified_dir(Path::new(&path))? > modified_dir(&source_dir)?
|
||||
{
|
||||
log_warn!(
|
||||
log_to_pty!(
|
||||
logger,
|
||||
"[DEBUG]: {} is newer than {}",
|
||||
path,
|
||||
@ -266,7 +252,7 @@ pub fn fetch(recipe_dir: &Path, recipe: &Recipe, logger: &PtyOut) -> Result<Path
|
||||
"The downloaded tar blake3 '{source_tar_blake3}' is not equal to blake3 in recipe.toml"
|
||||
));
|
||||
} else {
|
||||
log_warn!(
|
||||
log_to_pty!(
|
||||
logger,
|
||||
"DEBUG: source tar blake3 is different and need redownload"
|
||||
);
|
||||
@ -278,7 +264,7 @@ pub fn fetch(recipe_dir: &Path, recipe: &Recipe, logger: &PtyOut) -> Result<Path
|
||||
}
|
||||
} else {
|
||||
//TODO: set blake3 hash on the recipe with something like "cook fix"
|
||||
log_warn!(
|
||||
log_to_pty!(
|
||||
logger,
|
||||
"WARNING: set blake3 for '{}' to '{}'",
|
||||
source_tar.display(),
|
||||
@ -289,7 +275,7 @@ pub fn fetch(recipe_dir: &Path, recipe: &Recipe, logger: &PtyOut) -> Result<Path
|
||||
} {}
|
||||
if source_dir.is_dir() {
|
||||
if tar_updated || fetch_is_patches_newer(recipe_dir, patches, &source_dir)? {
|
||||
log_warn!(
|
||||
log_to_pty!(
|
||||
logger,
|
||||
"DEBUG: source tar or patches is newer than the source directory"
|
||||
);
|
||||
@ -310,7 +296,7 @@ pub fn fetch(recipe_dir: &Path, recipe: &Recipe, logger: &PtyOut) -> Result<Path
|
||||
// Local Sources
|
||||
None => {
|
||||
if !source_dir.is_dir() {
|
||||
log_warn!(
|
||||
log_to_pty!(
|
||||
logger,
|
||||
"WARNING: Recipe without source section expected source dir at '{}'",
|
||||
source_dir.display(),
|
||||
|
||||
@ -17,10 +17,9 @@ pub use std::os::unix::io::RawFd;
|
||||
#[macro_export]
|
||||
macro_rules! log_to_pty {
|
||||
($logger:expr, $($arg:tt)+) => {
|
||||
use std::io::Write;
|
||||
|
||||
if $logger.is_some() {
|
||||
let _ = $logger.as_ref().unwrap().1.try_clone().unwrap().write(
|
||||
use std::io::Write;
|
||||
let _ = $logger.as_ref().unwrap().1.try_clone().unwrap().write(
|
||||
format!($($arg)+)
|
||||
.as_bytes(),
|
||||
);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user