2019-11-18 15:12:34 -05:00
|
|
|
// TAME linker
|
|
|
|
//
|
2021-07-22 15:00:15 -04:00
|
|
|
// Copyright (C) 2014-2021 Ryan Specialty Group, LLC.
|
2020-03-06 11:05:18 -05:00
|
|
|
//
|
|
|
|
// This file is part of TAME.
|
2019-11-18 15:12:34 -05:00
|
|
|
//
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU General Public License as published by
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
// (at your option) any later version.
|
|
|
|
//
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
//! This is the TAME linker, so named after the traditional `ld` Unix
|
|
|
|
//! utility. Its job is to take each of the compiled object files and
|
|
|
|
//! produce a final executable.
|
|
|
|
//!
|
2019-12-10 00:33:53 -05:00
|
|
|
//! For more information about the linker,
|
|
|
|
//! see the [`tamer::ld`] module.
|
2019-11-18 15:12:34 -05:00
|
|
|
|
2019-11-27 09:18:17 -05:00
|
|
|
extern crate tamer;
|
|
|
|
|
2020-03-04 15:31:20 -05:00
|
|
|
use getopts::{Fail, Options};
|
|
|
|
use std::env;
|
tamer: tameld (TameldError): Error sum type
This aggregates all non-panic errors that can occur during link time, making
`Box<dyn Error>` unnecessary. I've been wanting to do this for a long time,
so it's nice seeing this come together. This is a powerful tool, in that we
know, at compile time, all errors that can occur, and properly report on
them and compose them. This method of error composition ensures that all
errors have a chance to be handled within their context, though it'll take
time to do so in a decent way.
This just maintains compatibility with the dynamic dispatch that was
previous occurring. This work is being done to introduce the initial
diagnostic system, which was really difficult/confusing to do without proper
errors types at the top level, considering the toplevel is responsible for
triggering the diagnostic reporting.
The cycle error is in particular going to be interesting once the system is
in place, especially once it provides spans in the future, since it will
guide the user through the code to understand how the cycle formed.
More to come.
DEV-10935
2022-04-11 15:15:04 -04:00
|
|
|
use tamer::ld::poc::{self, TameldError};
|
2019-11-18 15:12:34 -05:00
|
|
|
|
2020-03-04 15:31:20 -05:00
|
|
|
/// Types of commands
|
|
|
|
enum Command {
|
2020-04-30 14:33:10 -04:00
|
|
|
Link(String, String, Emit),
|
2020-03-04 15:31:20 -05:00
|
|
|
Usage,
|
|
|
|
}
|
|
|
|
|
2020-04-30 14:33:10 -04:00
|
|
|
/// Ways to emit the linked objects
|
|
|
|
enum Emit {
|
|
|
|
/// The typical desired `Emit`
|
|
|
|
///
|
|
|
|
/// Outputs the linked object files in a format that can be used in an
|
|
|
|
/// application.
|
|
|
|
Xmle,
|
|
|
|
|
|
|
|
/// Used for exploring the linked graph
|
|
|
|
Graphml,
|
|
|
|
}
|
|
|
|
|
2020-03-04 15:31:20 -05:00
|
|
|
/// Entrypoint for the linker
|
tamer: tameld (TameldError): Error sum type
This aggregates all non-panic errors that can occur during link time, making
`Box<dyn Error>` unnecessary. I've been wanting to do this for a long time,
so it's nice seeing this come together. This is a powerful tool, in that we
know, at compile time, all errors that can occur, and properly report on
them and compose them. This method of error composition ensures that all
errors have a chance to be handled within their context, though it'll take
time to do so in a decent way.
This just maintains compatibility with the dynamic dispatch that was
previous occurring. This work is being done to introduce the initial
diagnostic system, which was really difficult/confusing to do without proper
errors types at the top level, considering the toplevel is responsible for
triggering the diagnostic reporting.
The cycle error is in particular going to be interesting once the system is
in place, especially once it provides spans in the future, since it will
guide the user through the code to understand how the cycle formed.
More to come.
DEV-10935
2022-04-11 15:15:04 -04:00
|
|
|
pub fn main() -> Result<(), TameldError> {
|
2020-03-04 15:31:20 -05:00
|
|
|
let args: Vec<String> = env::args().collect();
|
|
|
|
let program = &args[0];
|
|
|
|
let opts = get_opts();
|
2020-04-30 14:33:10 -04:00
|
|
|
let usage =
|
|
|
|
opts.usage(&format!("Usage: {} [OPTIONS] -o OUTPUT FILE", program));
|
2020-03-04 15:31:20 -05:00
|
|
|
|
|
|
|
match parse_options(opts, args) {
|
2020-04-30 14:33:10 -04:00
|
|
|
Ok(Command::Link(input, output, emit)) => match emit {
|
|
|
|
Emit::Xmle => poc::xmle(&input, &output),
|
|
|
|
Emit::Graphml => poc::graphml(&input, &output),
|
|
|
|
}
|
|
|
|
.or_else(|e| {
|
|
|
|
eprintln!("error: {}", e);
|
|
|
|
eprintln!("fatal: failed to link `{}`", output);
|
2020-03-25 14:27:10 -04:00
|
|
|
|
2020-04-30 14:33:10 -04:00
|
|
|
std::process::exit(1);
|
|
|
|
}),
|
2020-03-04 15:31:20 -05:00
|
|
|
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");
|
2020-04-30 14:33:10 -04:00
|
|
|
opts.optopt(
|
|
|
|
"",
|
|
|
|
"emit",
|
|
|
|
"set the output to be emitted",
|
|
|
|
"--emit xmle|graphml",
|
|
|
|
);
|
2020-03-04 15:31:20 -05:00
|
|
|
|
|
|
|
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")));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-04-30 14:33:10 -04:00
|
|
|
let emit = match matches.opt_str("emit") {
|
|
|
|
Some(m) => match &m[..] {
|
|
|
|
"xmle" => Emit::Xmle,
|
|
|
|
"graphml" => Emit::Graphml,
|
|
|
|
em => return Err(Fail::ArgumentMissing(format!("--emit {}", em))),
|
|
|
|
},
|
|
|
|
None => Emit::Xmle,
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(Command::Link(input, output, emit))
|
2020-03-04 15:31:20 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[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"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-30 14:33:10 -04:00
|
|
|
#[test]
|
|
|
|
fn parse_options_valid_long_emit_invalid() {
|
|
|
|
let opts = get_opts();
|
|
|
|
let result = parse_options(
|
|
|
|
opts,
|
|
|
|
vec![
|
|
|
|
String::from("program"),
|
|
|
|
String::from("foo"),
|
|
|
|
String::from("--output"),
|
|
|
|
String::from("bar"),
|
|
|
|
String::from("--emit"),
|
|
|
|
String::from("foo"),
|
|
|
|
],
|
|
|
|
);
|
|
|
|
|
|
|
|
match result {
|
|
|
|
Err(Fail::ArgumentMissing(message)) => {
|
|
|
|
assert_eq!("--emit foo", message);
|
|
|
|
}
|
|
|
|
_ => panic!("Extra option not caught"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-04 15:31:20 -05:00
|
|
|
#[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 {
|
2020-04-30 14:33:10 -04:00
|
|
|
Ok(Command::Link(infile, outfile, Emit::Xmle)) => {
|
2020-03-04 15:31:20 -05:00
|
|
|
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"),
|
2020-04-30 14:33:10 -04:00
|
|
|
String::from("--emit"),
|
|
|
|
String::from("xmle"),
|
|
|
|
],
|
|
|
|
);
|
|
|
|
|
|
|
|
match result {
|
|
|
|
Ok(Command::Link(infile, outfile, Emit::Xmle)) => {
|
|
|
|
assert_eq!("foo", infile);
|
|
|
|
assert_eq!("bar", outfile);
|
|
|
|
}
|
|
|
|
_ => panic!("Unexpected result"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn parse_options_valid_long_emit_graphml() {
|
|
|
|
let opts = get_opts();
|
|
|
|
let result = parse_options(
|
|
|
|
opts,
|
|
|
|
vec![
|
|
|
|
String::from("program"),
|
|
|
|
String::from("foo"),
|
|
|
|
String::from("--output"),
|
|
|
|
String::from("bar"),
|
|
|
|
String::from("--emit"),
|
|
|
|
String::from("graphml"),
|
2020-03-04 15:31:20 -05:00
|
|
|
],
|
|
|
|
);
|
|
|
|
|
|
|
|
match result {
|
2020-04-30 14:33:10 -04:00
|
|
|
Ok(Command::Link(infile, outfile, Emit::Graphml)) => {
|
2020-03-04 15:31:20 -05:00
|
|
|
assert_eq!("foo", infile);
|
|
|
|
assert_eq!("bar", outfile);
|
|
|
|
}
|
|
|
|
_ => panic!("Unexpected result"),
|
|
|
|
}
|
|
|
|
}
|
2019-11-18 15:12:34 -05:00
|
|
|
}
|