...
 
Commits (3)
......@@ -5,7 +5,9 @@ variables:
before_script:
- apt-get update -qq
- apt-get install -qq build-essential curl git
format:
image: 'rustlang/rust:nightly'
cache:
paths:
- cargo/
......@@ -16,7 +18,7 @@ format:
- cargo +nightly fmt --all -- --check
linux:
image: 'rust:1.36.0'
image: 'rust:1.39.0'
cache:
paths:
- cargo/
......
......@@ -808,6 +808,7 @@ dependencies = [
"ion-ranges 0.1.0",
"itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lexical 4.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"mktemp 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"nix 0.15.0 (git+https://github.com/AdminXVII/nix?branch=add-redox-support)",
"object-pool 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"permutate 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
......@@ -979,6 +980,14 @@ dependencies = [
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "mktemp"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "nix"
version = "0.14.1"
......@@ -2025,6 +2034,14 @@ dependencies = [
"rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "uuid"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "vec_map"
version = "0.8.1"
......@@ -2299,6 +2316,7 @@ source = "git+https://gitlab.redox-os.org/redox-os/termion#c27678efc2ed14576361c
"checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e"
"checksum memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b"
"checksum memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ce6075db033bbbb7ee5a0bbd3a3186bbae616f57fb001c485c7ff77955f8177f"
"checksum mktemp 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "edf4fc746c5c977923b802d86fc9a95ca79a27d8c487613f68830d68d07c7b27"
"checksum nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6c722bee1037d430d0f8e687bbdbf222f27cc6e4e68d5caf630857bb2b6dbdce"
"checksum nix 0.15.0 (git+https://github.com/AdminXVII/nix?branch=add-redox-support)" = "<none>"
"checksum nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
......@@ -2419,6 +2437,7 @@ source = "git+https://gitlab.redox-os.org/redox-os/termion#c27678efc2ed14576361c
"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
"checksum users 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c72f4267aea0c3ec6d07eaabea6ead7c5ddacfafc5e22bcf8d186706851fb4cf"
"checksum uuid 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e1436e58182935dcd9ce0add9ea0b558e8a87befe01c1a301e6020aeb0876363"
"checksum uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "90dbc611eb48397705a6b0f6e917da23ae517e4d127123d2cf7674206627d32a"
"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
"checksum vecmath 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "956ae1e0d85bca567dee1dcf87fb1ca2e792792f66f87dced8381f99cd91156a"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
......
......@@ -80,6 +80,7 @@ atty = "0.2"
permutate = "0.3"
xdg = "2.1"
nix = { git = "https://github.com/AdminXVII/nix", branch = "add-redox-support" }
mktemp = "0.4"
# window example
piston-ai_behavior = { version = "0.31", optional = true }
......
prefix ?= usr/local
BINARY = $(prefix)/bin/ion
RELEASE = debug
TOOLCHAIN ?= 1.36.0
TOOLCHAIN ?= 1.39.0
GIT_REVISION=git_revision.txt
SRC=Cargo.toml Cargo.lock $(shell find src members -type f -wholename '*src/*.rs')
......
......@@ -45,7 +45,7 @@ See the [examples folder](https://gitlab.redox-os.org/redox-os/ion/tree/master/e
## Pop!\_OS / Ubuntu
The following PPA supports the 18.04 (bionic) and 19.04 (disco) releases. Bionic builds were made using the Pop\_OS PPA's rustc 1.36.0 package.
The following PPA supports the 18.04 (bionic) and 19.04 (disco) releases. Bionic builds were made using the Pop\_OS PPA's rustc 1.39.0 package.
```
sudo add-apt-repository ppa:mmstick76/ion-shell
......@@ -54,13 +54,13 @@ sudo add-apt-repository ppa:mmstick76/ion-shell
# Developer set up
Those who are developing software with Rust should install the [Rustup toolchain manager](https://rustup.rs/).
After installing rustup, run `rustup override set 1.36.0` to set your Rust toolchain to the version that Ion is
After installing rustup, run `rustup override set 1.39.0` to set your Rust toolchain to the version that Ion is
targeting at the moment. To build for Redox OS, `rustup override set nightly` is required to build the Redox
dependencies.
# Build dependencies
Please ensure that both cargo and rustc 1.36.0 or higher is installed for your system.
Please ensure that both cargo and rustc 1.39.0 or higher is installed for your system.
Release tarballs have not been made yet due to Ion being incomplete in a few remaining areas.
# Compile instructions for distribution
......
......@@ -112,11 +112,8 @@ impl<'a> InteractiveShell<'a> {
// pipline just executed to the the file and context histories. At the
// moment, this means record how long it took.
if Some("1".into()) == shell.variables().get_str("RECORD_SUMMARY").ok() {
let summary = format!(
"#summary# elapsed real time: {}.{:09} seconds",
elapsed.as_secs(),
elapsed.subsec_nanos()
);
let summary =
format!("#summary# elapsed real time: {:.9} seconds", elapsed.as_secs_f32(),);
println!("{}", summary);
context.borrow_mut().history.push(summary.into()).unwrap_or_else(|err| {
eprintln!("ion: history append: {}", err);
......@@ -304,7 +301,7 @@ impl<'a> InteractiveShell<'a> {
self.terminated.set(true);
{
let mut shell = self.shell.borrow_mut();
match shell.on_command(&cmd) {
match shell.on_command(&cmd, true) {
Ok(_) => (),
Err(IonError::PipelineExecutionError(
PipelineError::CommandNotFound(command),
......
......@@ -13,30 +13,31 @@ impl<'a> InteractiveShell<'a> {
let blocks = if self.terminated.get() { shell.block_len() } else { shell.block_len() + 1 };
if blocks == 0 {
let out = shell.command("PROMPT").map(|res| res.to_string()).unwrap_or_else(|err| {
if let expansion::Error::Subprocess(err) = err {
if let IonError::PipelineExecutionError(PipelineError::CommandNotFound(_)) =
*err
{
match shell
.variables()
.get_str("PROMPT")
.and_then(|prompt| shell.get_string(&prompt))
let out =
shell.command("PROMPT", false).map(|res| res.to_string()).unwrap_or_else(|err| {
if let expansion::Error::Subprocess(err) = err {
if let IonError::PipelineExecutionError(PipelineError::CommandNotFound(_)) =
*err
{
Ok(prompt) => prompt.to_string(),
Err(err) => {
eprintln!("ion: prompt expansion failed: {}", err);
">>> ".into()
match shell
.variables()
.get_str("PROMPT")
.and_then(|prompt| shell.get_string(&prompt))
{
Ok(prompt) => prompt.to_string(),
Err(err) => {
eprintln!("ion: prompt expansion failed: {}", err);
">>> ".into()
}
}
} else {
eprintln!("ion: prompt expansion failed: {}", err);
">>> ".into()
}
} else {
eprintln!("ion: prompt expansion failed: {}", err);
">>> ".into()
panic!("Only a subprocess error should happen inside the pipeline");
}
} else {
panic!("Only a subprocess error should happen inside the pipeline");
}
});
});
shell.set_previous_status(previous_status); // Set the previous exit code again
let key_bindings = self.context.borrow().key_bindings;
match key_bindings {
......
......@@ -41,11 +41,14 @@ use crate::{
use builtins_proc::builtin;
use itertools::Itertools;
use liner::{Completer, Context, Prompt};
use mktemp::Temp;
use std::{
borrow::Cow,
collections::HashMap,
io::{self, BufRead},
fs::File,
io::{self, BufRead, BufReader},
path::{Path, PathBuf},
process::{Command, Stdio},
};
const HELP_DESC: &str = "Display helpful information about a given command or list commands if \
......@@ -240,11 +243,73 @@ impl<'a> BuiltinMap<'a> {
///
/// Contains `eval`, `set`
pub fn with_unsafe(&mut self) -> &mut Self {
self.add("eval", &builtin_eval, "Evaluates the evaluated expression").add(
"set",
&builtin_set,
"Set or unset values of shell options and positional parameters.",
)
self.add("eval", &builtin_eval, "Evaluates the evaluated expression")
.add(
"set",
&builtin_set,
"Set or unset values of shell options and positional parameters.",
)
.add("source-sh", &builtin_source_sh, "Execute a sh script and load the env diff")
}
}
#[builtin(
desc = "execute a sh script and source environment variables",
man = "
SYNOPSYS
source-sh SCRIPT
DESCRIPTION
Execute the script literal given in argument and apply env vars diff to the current shell"
)]
pub fn source_sh(args: &[types::Str], _shell: &mut Shell<'_>) -> Status {
let arg = match args.get(1) {
None => return Status::bad_argument("Please pass a shell script as option"),
Some(arg) => arg,
};
let temp = match Temp::new_file() {
Ok(f) => f,
Err(e) => return Status::error(format!("Could not create temp file for source-sh: {}", e)),
};
let script = format!("{};env | sort > {}", arg, temp.as_path().display());
match Command::new("sh")
.args(&["-c", &script])
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
{
Ok(_) => {
let env = match File::open(temp) {
Ok(env) => env,
Err(e) => {
return Status::error(format!("Could not read script environment: {}", e))
}
};
for var in BufReader::new(env).lines() {
let var = match var {
Ok(v) => v,
Err(e) => return Status::error(format!("Could not read env: {}", e)),
};
let mut iter = var.splitn(2, "=");
let name = iter.next().unwrap();
let val = match iter.next() {
Some(v) => v,
None => {
return Status::error(format!(
"Could not parse env file, no value for: '{}'",
name
))
}
};
let prev_val = std::env::var_os(name);
if prev_val.as_ref().and_then(|x| x.to_str()) != Some(val) {
std::env::set_var(name, val);
}
}
Status::SUCCESS
}
Err(e) => Status::error(format!("Could not execute sh script: {}", e)),
}
}
......
......@@ -127,7 +127,11 @@ pub trait Expander: Sized {
/// Expand a string variable given if it's quoted / unquoted
fn string(&self, _name: &str) -> Result<types::Str, Self::Error>;
/// Expand a subshell expression.
fn command(&mut self, _command: &str) -> Result<types::Str, Self::Error>;
fn command(
&mut self,
_command: &str,
_set_cmd_duration: bool,
) -> Result<types::Str, Self::Error>;
/// Iterating upon key-value maps.
fn map_keys(&self, _name: &str) -> Result<Args, Self::Error>;
/// Iterating upon key-value maps.
......@@ -171,7 +175,7 @@ trait ExpanderInternal: Expander {
command: &str,
selection: &Option<&'a str>,
) -> Result<(), Self::Error> {
let result = self.command(command)?;
let result = self.command(command, true)?;
self.slice(current, result.trim_end_matches('\n'), selection)
}
......@@ -703,7 +707,13 @@ pub(crate) mod test {
}
}
fn command(&mut self, cmd: &str) -> Result<types::Str, Self::Error> { Ok(cmd.into()) }
fn command(
&mut self,
cmd: &str,
_set_cmd_duration: bool,
) -> Result<types::Str, Self::Error> {
Ok(cmd.into())
}
fn tilde(&self, input: &str) -> Result<types::Str, Self::Error> { Ok(input.into()) }
......
......@@ -17,7 +17,7 @@ use crate::{
use err_derive::Error;
use itertools::Itertools;
use nix::unistd::Pid;
use std::rc::Rc;
use std::{rc::Rc, time::SystemTime};
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub enum Condition {
......@@ -405,13 +405,14 @@ impl<'a> Shell<'a> {
let condition = self.execute_statement(box_statement)?;
let duration = time.elapsed();
let seconds = duration.as_secs();
let nanoseconds = duration.subsec_nanos();
let duration = duration.as_secs_f32();
let seconds = duration.rem_euclid(60.);
let minutes = duration.div_euclid(60.);
if seconds > 60 {
println!("real {}m{:02}.{:09}s", seconds / 60, seconds % 60, nanoseconds);
if minutes != 0. {
println!("real {}m{:.9}s", minutes, seconds);
} else {
println!("real {}.{:09}s", seconds, nanoseconds);
println!("real {:.9}s", seconds);
}
if condition != Condition::NoOp {
return Ok(condition);
......@@ -531,7 +532,7 @@ impl<'a> Shell<'a> {
});
if let Some(statement) = case.conditional.as_ref() {
self.on_command(statement)?;
self.on_command(statement, true)?;
if self.previous_status.is_failure() {
continue;
}
......@@ -558,7 +559,13 @@ impl<'a> Shell<'a> {
}
/// Receives a command and attempts to execute the contents.
pub fn on_command(&mut self, command_string: &str) -> std::result::Result<(), IonError> {
pub fn on_command(
&mut self,
command_string: &str,
set_cmd_duration: bool,
) -> std::result::Result<(), IonError> {
let command_start_time = if set_cmd_duration { Some(SystemTime::now()) } else { None };
for stmt in command_string.bytes().batching(|cmd| Terminator::new(cmd).terminate()) {
// Go through all of the statements and build up the block stack
// When block is done return statement for execution.
......@@ -569,6 +576,13 @@ impl<'a> Shell<'a> {
}
}
}
if let Some(start_time) = command_start_time {
if let Ok(elapsed_time) = start_time.elapsed() {
self.variables_mut().set("ION_CMD_DURATION", elapsed_time.as_secs().to_string());
}
}
Ok(())
}
}
......
......@@ -271,7 +271,7 @@ impl<'a> Shell<'a> {
.filter_map(Result::ok)
.batching(|bytes| Terminator::new(bytes).terminate())
{
self.on_command(&cmd)?;
self.on_command(&cmd, true)?;
}
if let Some(block) = self.flow_control.last().map(Statement::to_string) {
......
......@@ -16,7 +16,11 @@ impl<'a, 'b> Expander for Shell<'b> {
type Error = IonError;
/// Uses a subshell to expand a given command.
fn command(&mut self, command: &str) -> Result<types::Str, Self::Error> {
fn command(
&mut self,
command: &str,
set_cmd_duration: bool,
) -> Result<types::Str, Self::Error> {
let (mut reader, writer) = create_pipe()
.map_err(|err| Error::Subprocess(Box::new(IonError::PipelineExecutionError(err))))?;
let null_file = File::open(NULL_PATH).map_err(|err| {
......@@ -30,7 +34,9 @@ impl<'a, 'b> Expander for Shell<'b> {
let prev_stderr = self.stderr(null_file);
// Execute the command
let result = self.on_command(command).map_err(|err| Error::Subprocess(Box::new(err)));
let result = self
.on_command(command, set_cmd_duration)
.map_err(|err| Error::Subprocess(Box::new(err)));
// Reset the pipes, droping the stdout
self.stdout(prev_stdout);
......
......@@ -286,7 +286,13 @@ pub(crate) mod tests {
Err(expansion::Error::VarNotFound)
}
fn command(&mut self, cmd: &str) -> Result<types::Str, Self::Error> { Ok(cmd.into()) }
fn command(
&mut self,
cmd: &str,
_set_cmd_duration: bool,
) -> Result<types::Str, Self::Error> {
Ok(cmd.into())
}
fn tilde(&self, input: &str) -> Result<types::Str, Self::Error> { Ok(input.into()) }
......