diff --git a/Cargo.lock b/Cargo.lock index 2045924..3fdd9c0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ [root] name = "parallel" -version = "0.10.2" +version = "0.10.3" dependencies = [ "arrayvec 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index ed73b67..cdbbc7d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "parallel" -version = "0.10.2" +version = "0.10.3" authors = ["Michael Aaron Murphy "] license = "MIT" description = "Command-line CPU load balancer for executing jobs in parallel" diff --git a/README.md b/README.md index d0fc877..f7eb4ba 100644 --- a/README.md +++ b/README.md @@ -252,6 +252,7 @@ operates: - **-h**, **--help**: Prints the manual for the application (recommended to pipe it to `less`). - **-j**, **--jobs**: Defines the number of jobs/threads to run in parallel. - **--joblog**: Logs job statistics to a designated file as they are completed. +- **--joblog-8601**: Writes the start time in the ISO 8601 format: `YYYY-MM-DD hh:mm:ss` - **--memfree**: Defines the minimum amount of memory available before starting the next job. - **-n**, **--max-args**: Groups up to a certain number of arguments together in the same command line. - **--num-cpu-cores**: Prints the number of CPU cores in the system and exits. diff --git a/src/arguments/man.rs b/src/arguments/man.rs index a701022..29ce30e 100644 --- a/src/arguments/man.rs +++ b/src/arguments/man.rs @@ -94,6 +94,9 @@ OPTIONS --joblog: Logs job statistics to a designated file as they are completed. + --joblog-8601: + Writes the start time in the ISO 8601 format: `YYYY-MM-DD hh:mm:ss` + --memfree: Defines the minimum amount of memory available before starting the next job. diff --git a/src/arguments/mod.rs b/src/arguments/mod.rs index d71f511..8e6468a 100644 --- a/src/arguments/mod.rs +++ b/src/arguments/mod.rs @@ -34,6 +34,7 @@ pub const DRY_RUN: u16 = 64; pub const SHELL_QUOTE: u16 = 128; pub const ETA: u16 = 256; pub const JOBLOG: u16 = 512; +pub const JOBLOG_8601: u16 = 1024; /// `Args` is a collection of critical options and arguments that were collected at /// startup of the application. @@ -142,6 +143,7 @@ impl Args { index += 1; self.flags |= JOBLOG; }, + "joblog-8601" => self.flags |= JOBLOG_8601, "jobs" => { let val = arguments.get(index).ok_or(ParseErr::JobsNoValue)?; self.ncores = jobs::parse(val)?; @@ -172,7 +174,7 @@ impl Args { }, "verbose" => self.flags |= VERBOSE_MODE, "version" => { - println!("MIT/Rust Parallel 0.10.1\n"); + println!("MIT/Rust Parallel 0.10.3\n"); exit(0); }, "tmpdir" | "tempdir" => { diff --git a/src/execute/exec_commands.rs b/src/execute/exec_commands.rs index 8f7dfc9..20eb811 100644 --- a/src/execute/exec_commands.rs +++ b/src/execute/exec_commands.rs @@ -86,6 +86,7 @@ impl ExecCommands { runtime: runtime.num_nanoseconds().unwrap_or(0) as u64, exit_value: exit_value, signal: signal, + flags: self.flags, command: command_buffer.clone(), })); } diff --git a/src/execute/exec_inputs.rs b/src/execute/exec_inputs.rs index b688d93..53f0e09 100644 --- a/src/execute/exec_inputs.rs +++ b/src/execute/exec_inputs.rs @@ -66,6 +66,7 @@ impl ExecInputs { runtime: runtime.num_nanoseconds().unwrap_or(0) as u64, exit_value: exit_value, signal: signal, + flags: flags, command: input.clone(), })); } diff --git a/src/execute/job_log.rs b/src/execute/job_log.rs index b0d727e..98cc2d8 100644 --- a/src/execute/job_log.rs +++ b/src/execute/job_log.rs @@ -1,7 +1,8 @@ +use arguments::JOBLOG_8601; use misc::NumToA; use std::fs::File; use std::io::{Write, BufWriter}; -use time::Timespec; +use time::{at, Timespec}; // Each `JobLog` consists of a single job's statistics ready to be written to the job log file. pub struct JobLog { @@ -15,6 +16,8 @@ pub struct JobLog { pub exit_value: i32, /// The `signal` contains a non-zero value if the job was killed by a signal pub signal: i32, + /// Contains the configuration parameters for the joblog + pub flags: u16, /// The actual `command` that was executed for this job pub command: String } @@ -30,29 +33,37 @@ impl JobLog { let _ = joblog.write(b" "); } - // 2: StartTime in seconds, with up to two decimal places - let bytes_written = self.start_time.sec.numtoa(10, id_buffer); - let _ = joblog.write(&id_buffer[0..bytes_written]); - let _ = joblog.write(b"."); - let decimal = (self.start_time.nsec % 1_000_000_000) / 1_000_000; - if decimal == 0 { - let _ = joblog.write(b"000"); + // 2: StartTime + if self.flags & JOBLOG_8601 != 0 { + // ISO 8601 representation of the time + let tm = at(self.start_time); + let message = format!("{}-{:02}-{:02} {:02}:{:02}:{:02} ", 1900+tm.tm_year, 1+tm.tm_mon, + tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); + let _ = joblog.write(message.as_bytes()); + } else { - let bytes_written = decimal.numtoa(10, id_buffer); - match bytes_written { - 1 => { let _ = joblog.write(b"00"); }, - 2 => { let _ = joblog.write(b"0"); }, - _ => (), - }; + // Represented in seconds, with two decimal places + let bytes_written = self.start_time.sec.numtoa(10, id_buffer); let _ = joblog.write(&id_buffer[0..bytes_written]); - } - for _ in 0..16-(bytes_written+4) { - let _ = joblog.write(b" "); + let _ = joblog.write(b"."); + let decimal = (self.start_time.nsec % 1_000_000_000) / 1_000_000; + if decimal == 0 { + let _ = joblog.write(b"000"); + } else { + let bytes_written = decimal.numtoa(10, id_buffer); + match bytes_written { + 1 => { let _ = joblog.write(b"00"); }, + 2 => { let _ = joblog.write(b"0"); }, + _ => (), + }; + let _ = joblog.write(&id_buffer[0..bytes_written]); + } + let _ = joblog.write(b" "); } // 3: Runtime in seconds, with up to three decimal places. let bytes_written = (self.runtime / 1_000_000_000).numtoa(10, id_buffer); - for _ in 0..10-(bytes_written + 4) { + for _ in 0..6-bytes_written { let _ = joblog.write(b" "); } let _ = joblog.write(&id_buffer[0..bytes_written]); @@ -92,7 +103,7 @@ impl JobLog { } /// Creates the column headers in the first line of the job log file -pub fn create(file: &mut File, padding: usize) { +pub fn create(file: &mut File, padding: usize, flags: u16) { let mut joblog = BufWriter::new(file); // Sequence column is at least 10 chars long, counting space separator. @@ -100,8 +111,12 @@ pub fn create(file: &mut File, padding: usize) { let _ = joblog.write(b"Sequence "); for _ in 0..id_column_resize { let _ = joblog.write(b" "); } - // StartTime column is always 17 chars long - let _ = joblog.write(b"StartTime(s) "); + if flags & JOBLOG_8601 != 0 { + let _ = joblog.write(b"StartTime(ISO-8601) "); + } else { + let _ = joblog.write(b"StartTime(s) "); + } + // Remaining columns, with the runtim column left-padded. let _ = joblog.write(b"Runtime(s) ExitVal Signal Command\n"); diff --git a/src/execute/receive.rs b/src/execute/receive.rs index 1fc812e..1bdf84e 100644 --- a/src/execute/receive.rs +++ b/src/execute/receive.rs @@ -75,6 +75,8 @@ pub fn receive_messages(input_rx: Receiver, args: Args, base: &str, proce let stdout = io::stdout(); let stderr = io::stderr(); + // Store the flags value outside of the `args` structure + let flags = args.flags; // Keeps track of which job is currently allowed to print to standard output/error. let mut counter = 0; // In the event that the joblog parameter was passed, a counter will be needed for jobs. @@ -107,7 +109,7 @@ pub fn receive_messages(input_rx: Receiver, args: Args, base: &str, proce if id_pad_length < 10 { id_pad_length = 10; } let _ = fs::remove_file(&path); let mut file = fs::OpenOptions::new().create(true).write(true).open(path).unwrap(); - job_log::create(&mut file, id_pad_length); + job_log::create(&mut file, id_pad_length, flags); file });