Mike Gerwitz

Activist for User Freedom

aboutsummaryrefslogtreecommitdiffstats
path: root/tamer/src
diff options
context:
space:
mode:
authorJoseph Frazer <joseph.frazer@ryansg.com>2020-03-04 15:31:20 -0500
committerJoseph Frazer <joseph.frazer@ryansg.com>2020-03-06 09:41:55 -0500
commite613bd8a8c931546fd10ef50338b5a5affa1b3c2 (patch)
tree0a81c9d7406830068fa4273762897c5c9353191a /tamer/src
parent777494a6026e6eabff9ca9f05f00aa92e2f58c3d (diff)
downloadtame-e613bd8a8c931546fd10ef50338b5a5affa1b3c2.tar.gz
tame-e613bd8a8c931546fd10ef50338b5a5affa1b3c2.tar.bz2
tame-e613bd8a8c931546fd10ef50338b5a5affa1b3c2.zip
[DEV-7081] Add options to tameld
We want to add an option to set the output file to the linker so we do not need to redirect output to awk any longer. This also adds integration tests for tameld.
Diffstat (limited to 'tamer/src')
-rw-r--r--tamer/src/bin/tameld.rs213
-rw-r--r--tamer/src/ld/poc.rs15
2 files changed, 217 insertions, 11 deletions
diff --git a/tamer/src/bin/tameld.rs b/tamer/src/bin/tameld.rs
index 40f51bc..c06aa1c 100644
--- a/tamer/src/bin/tameld.rs
+++ b/tamer/src/bin/tameld.rs
@@ -24,9 +24,220 @@
extern crate tamer;
+use getopts::{Fail, Options};
+use std::env;
use std::error::Error;
use tamer::ld::poc;
+/// Types of commands
+enum Command {
+ Link(String, String),
+ Usage,
+}
+
+/// Entrypoint for the linker
pub fn main() -> Result<(), Box<dyn Error>> {
- poc::main()
+ let args: Vec<String> = env::args().collect();
+ let program = &args[0];
+ let opts = get_opts();
+ let usage = opts.usage(&format!("Usage: {} -o OUTPUT FILE", program));
+
+ match parse_options(opts, args) {
+ Ok(Command::Link(input, output)) => poc::main(&input, &output),
+ Ok(Command::Usage) => {
+ println!("{}", usage);
+ std::process::exit(exitcode::OK);
+ }
+ Err(e) => {
+ eprintln!("{}", e);
+ println!("{}", usage);
+ std::process::exit(exitcode::USAGE);
+ }
+ }
+}
+
+/// Get 'Options'
+///
+/// ```
+/// use getopts::Options;
+///
+/// let opts = get_opts();
+/// ```
+fn get_opts() -> Options {
+ let mut opts = Options::new();
+ opts.optopt("o", "output", "set output file name", "NAME");
+ opts.optflag("h", "help", "print this help menu");
+
+ opts
+}
+
+/// Option parser
+fn parse_options(opts: Options, args: Vec<String>) -> Result<Command, Fail> {
+ let matches = match opts.parse(&args[1..]) {
+ Ok(m) => m,
+ Err(f) => {
+ return Err(f);
+ }
+ };
+
+ if matches.opt_present("h") {
+ return Ok(Command::Usage);
+ }
+
+ let input = match matches.free.len() {
+ 0 => return Err(Fail::OptionMissing(String::from("FILE"))),
+ 1 => matches.free[0].clone(),
+ _ => return Err(Fail::UnrecognizedOption(matches.free[1].clone())),
+ };
+
+ let output = match matches.opt_str("o") {
+ Some(m) => m,
+ None => {
+ return Err(Fail::OptionMissing(String::from("-o OUTPUT")));
+ }
+ };
+
+ Ok(Command::Link(input, output))
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn parse_options_help() {
+ let opts = get_opts();
+ let result = parse_options(
+ opts,
+ vec![String::from("program"), String::from("-h")],
+ );
+
+ match result {
+ Ok(Command::Usage) => {}
+ _ => panic!("Long help option did not parse"),
+ }
+ }
+
+ #[test]
+ fn parse_options_help_long() {
+ let opts = get_opts();
+ let result = parse_options(
+ opts,
+ vec![String::from("program"), String::from("--help")],
+ );
+
+ match result {
+ Ok(Command::Usage) => {}
+ _ => panic!("Help option did not parse"),
+ }
+ }
+
+ #[test]
+ fn parse_options_invalid() {
+ let opts = get_opts();
+ let result = parse_options(
+ opts,
+ vec![String::from("program"), String::from("-q")],
+ );
+
+ match result {
+ Err(Fail::UnrecognizedOption(_)) => {}
+ _ => panic!("Invalid option not caught"),
+ }
+ }
+
+ #[test]
+ fn parse_options_missing_input() {
+ let opts = get_opts();
+ let result = parse_options(opts, vec![String::from("program")]);
+
+ match result {
+ Err(Fail::OptionMissing(message)) => {
+ assert_eq!("FILE", message);
+ }
+ _ => panic!("Missing input not caught"),
+ }
+ }
+
+ #[test]
+ fn parse_options_missing_output() {
+ let opts = get_opts();
+ let result = parse_options(
+ opts,
+ vec![String::from("program"), String::from("foo")],
+ );
+
+ match result {
+ Err(Fail::OptionMissing(message)) => {
+ assert_eq!("-o OUTPUT", message);
+ }
+ _ => panic!("Missing output not caught"),
+ }
+ }
+
+ #[test]
+ fn parse_options_too_many_args() {
+ let opts = get_opts();
+ let result = parse_options(
+ opts,
+ vec![
+ String::from("program"),
+ String::from("foo"),
+ String::from("-o"),
+ String::from("bar"),
+ String::from("baz"),
+ ],
+ );
+
+ match result {
+ Err(Fail::UnrecognizedOption(message)) => {
+ assert_eq!("baz", message);
+ }
+ _ => panic!("Extra option not caught"),
+ }
+ }
+
+ #[test]
+ fn parse_options_valid() {
+ let opts = get_opts();
+ let result = parse_options(
+ opts,
+ vec![
+ String::from("program"),
+ String::from("foo"),
+ String::from("-o"),
+ String::from("bar"),
+ ],
+ );
+
+ match result {
+ Ok(Command::Link(infile, outfile)) => {
+ assert_eq!("foo", infile);
+ assert_eq!("bar", outfile);
+ }
+ _ => panic!("Unexpected result"),
+ }
+ }
+
+ #[test]
+ fn parse_options_valid_long() {
+ let opts = get_opts();
+ let result = parse_options(
+ opts,
+ vec![
+ String::from("program"),
+ String::from("foo"),
+ String::from("--output"),
+ String::from("bar"),
+ ],
+ );
+
+ match result {
+ Ok(Command::Link(infile, outfile)) => {
+ assert_eq!("foo", infile);
+ assert_eq!("bar", outfile);
+ }
+ _ => panic!("Unexpected result"),
+ }
+ }
}
diff --git a/tamer/src/ld/poc.rs b/tamer/src/ld/poc.rs
index 53f9f7b..645a098 100644
--- a/tamer/src/ld/poc.rs
+++ b/tamer/src/ld/poc.rs
@@ -30,19 +30,17 @@ use std::convert::TryInto;
use std::error::Error;
use std::fs;
use std::io::BufReader;
-use std::io::Cursor;
type LinkerAsg<'i> = DefaultAsg<'i, global::ProgIdentSize>;
type LinkerObjectRef = ObjectRef<global::ProgIdentSize>;
-pub fn main() -> Result<(), Box<dyn Error>> {
+pub fn main(package_path: &str, output: &str) -> Result<(), Box<dyn Error>> {
let mut pkgs_seen: FxHashSet<String> = Default::default();
let mut fragments: FxHashMap<&str, String> = Default::default();
let mut depgraph = LinkerAsg::with_capacity(65536, 65536);
let mut roots = Vec::new();
let interner = DefaultInterner::new();
- let package_path = std::env::args().nth(1).expect("Missing argument");
let abs_path = fs::canonicalize(package_path).unwrap();
println!("WARNING: This is proof-of-concept; do not use!");
@@ -84,6 +82,7 @@ pub fn main() -> Result<(), Box<dyn Error>> {
&mut sorted,
name.expect("missing root package name"),
relroot.expect("missing root package relroot"),
+ output,
)?;
Ok(())
@@ -305,6 +304,7 @@ fn output_xmle<'a, 'i, I: Interner<'i>>(
sorted: &mut Sections<'a, 'i>,
name: &'i Symbol<'i>,
relroot: String,
+ output: &str,
) -> Result<(), Box<dyn Error>> {
if !sorted.map.is_empty() {
sorted.map.push_head(get_interner_value(
@@ -332,17 +332,12 @@ fn output_xmle<'a, 'i, I: Interner<'i>>(
));
}
- let writer = Cursor::new(Vec::new());
- let mut xmle_writer = XmleWriter::new(writer);
+ let file = fs::File::create(output)?;
+ let mut xmle_writer = XmleWriter::new(file);
xmle_writer
.write(&sorted, name, &relroot)
.expect("Could not write xmle output");
- print!(
- "{}",
- String::from_utf8(xmle_writer.into_inner().into_inner())?
- );
-
Ok(())
}