diff --git a/cranelift/Cargo.toml b/cranelift/Cargo.toml index f27fe9c2eb..93f35f0c69 100644 --- a/cranelift/Cargo.toml +++ b/cranelift/Cargo.toml @@ -27,9 +27,8 @@ cranelift-faerie = { path = "lib/faerie", version = "0.19.0" } cranelift-simplejit = { path = "lib/simplejit", version = "0.19.0" } cranelift = { path = "lib/umbrella", version = "0.19.0" } filecheck = "0.3.0" -docopt = "1" +clap = "2.32.0" serde = "1.0.8" -serde_derive = "1.0.8" term = "0.5.1" capstone = { version = "0.4", optional = true } wabt = { version = "0.4", optional = true } diff --git a/cranelift/src/clif-util.rs b/cranelift/src/clif-util.rs index 5b8eb2f3c7..2a7ae79d30 100644 --- a/cranelift/src/clif-util.rs +++ b/cranelift/src/clif-util.rs @@ -8,23 +8,20 @@ ) )] +extern crate file_per_thread_logger; #[macro_use] extern crate cfg_if; -extern crate cranelift_codegen; -extern crate cranelift_filetests; -extern crate cranelift_reader; -extern crate docopt; -extern crate file_per_thread_logger; -extern crate filecheck; -#[macro_use] -extern crate serde_derive; #[cfg(feature = "disas")] extern crate capstone; +extern crate clap; +extern crate cranelift_codegen; +extern crate cranelift_entity; +extern crate cranelift_filetests; +extern crate cranelift_reader; extern crate pretty_env_logger; cfg_if! { if #[cfg(feature = "wasm")] { - extern crate cranelift_entity; extern crate cranelift_wasm; extern crate term; extern crate wabt; @@ -33,137 +30,200 @@ cfg_if! { } extern crate target_lexicon; +use clap::{App, Arg, SubCommand}; use cranelift_codegen::dbg::LOG_FILENAME_PREFIX; -use cranelift_codegen::{timing, VERSION}; -use docopt::Docopt; +use cranelift_codegen::VERSION; use std::io::{self, Write}; +use std::option::Option; use std::process; mod cat; mod compile; mod print_cfg; -mod rsfilecheck; mod utils; -const USAGE: &str = " -Cranelift code generator utility - -Usage: - clif-util test [-vTd] ... - clif-util cat [-d] ... - clif-util filecheck [-vd] - clif-util print-cfg [-d] ... - clif-util compile [-vpTd] [--set ]... [--target ] ... - clif-util wasm [-ctvpTsd] [--set ]... [--target ] ... - clif-util --help | --version - -Options: - -v, --verbose be more verbose - -d, --debug enable debug output on stderr/stdout - -T, --time-passes - print pass timing report - -t, --just-decode - just decode WebAssembly to Cranelift IR - -s, --print-size - prints generated code size - -c, --check-translation - just checks the correctness of Cranelift IR translated from WebAssembly - -p, --print print the resulting Cranelift IR - -h, --help print this help message - --set= configure Cranelift settings - --target= - specify the Cranelift target - --version print the Cranelift version - -"; - -#[derive(Deserialize, Debug)] -struct Args { - cmd_test: bool, - cmd_cat: bool, - cmd_filecheck: bool, - cmd_print_cfg: bool, - cmd_compile: bool, - cmd_wasm: bool, - arg_file: Vec, - flag_just_decode: bool, - flag_check_translation: bool, - flag_print: bool, - flag_verbose: bool, - flag_set: Vec, - flag_target: String, - flag_time_passes: bool, - flag_print_size: bool, - flag_debug: bool, -} - /// A command either succeeds or fails with an error message. pub type CommandResult = Result<(), String>; -/// Parse the command line arguments and run the requested command. -fn clif_util() -> CommandResult { - // Parse command line arguments. - let args: Args = Docopt::new(USAGE) - .and_then(|d| { - d.help(true) - .version(Some(format!("Cranelift {}", VERSION))) - .deserialize() - }) - .unwrap_or_else(|e| e.exit()); +fn add_input_file_arg<'a>() -> clap::Arg<'a, 'a> { + Arg::with_name("file") + .required(true) + .multiple(true) + .value_name("file") + .help("Specify file(s) to be used for test") +} - if args.flag_debug { +fn add_verbose_flag<'a>() -> clap::Arg<'a, 'a> { + Arg::with_name("verbose").short("v").help("Be more verbose") +} + +fn add_time_flag<'a>() -> clap::Arg<'a, 'a> { + Arg::with_name("time-passes") + .short("T") + .help("Print pass timing report for test") +} + +fn add_set_flag<'a>() -> clap::Arg<'a, 'a> { + Arg::with_name("set") + .long("set") + .takes_value(true) + .multiple(true) + .help("Configure Cranelift settings") +} + +fn add_target_flag<'a>() -> clap::Arg<'a, 'a> { + Arg::with_name("target") + .takes_value(true) + .long("target") + .help("Specify the Cranelift target") +} + +fn add_print_flag<'a>() -> clap::Arg<'a, 'a> { + Arg::with_name("print") + .short("p") + .help("Print the resulting Cranelift IR") +} + +fn add_debug_flag<'a>() -> clap::Arg<'a, 'a> { + Arg::with_name("debug") + .short("d") + .help("enable debug output on stderr/stdout") +} + +/// Returns a vector of clap value options and changes these options into a vector of strings +fn get_vec<'a>(argument_vec: Option>) -> Vec { + let mut ret_vec: Vec = Vec::new(); + if let Some(clap_vec) = argument_vec { + for val in clap_vec { + ret_vec.push(val.to_string()); + } + } + + ret_vec +} + +fn add_wasm_or_compile<'a>(cmd: &str) -> clap::App<'a, 'a> { + let about_str = match cmd { + "wasm" => "Compiles Cranelift IR into target language", + "compile" => "Compiles Cranelift IR into target language", + _ => panic!("Invalid command"), + }; + + SubCommand::with_name(cmd) + .about(about_str) + .arg(add_verbose_flag()) + .arg(add_print_flag()) + .arg(add_time_flag()) + .arg(add_set_flag()) + .arg(add_target_flag()) + .arg(add_input_file_arg()) + .arg(add_debug_flag()) +} + +fn handle_debug_flag(debug: bool) { + if debug { pretty_env_logger::init(); } else { file_per_thread_logger::initialize(LOG_FILENAME_PREFIX); } - - // Find the sub-command to execute. - let result = if args.cmd_test { - cranelift_filetests::run(args.flag_verbose, &args.arg_file).map(|_time| ()) - } else if args.cmd_cat { - cat::run(&args.arg_file) - } else if args.cmd_filecheck { - rsfilecheck::run(&args.arg_file, args.flag_verbose) - } else if args.cmd_print_cfg { - print_cfg::run(&args.arg_file) - } else if args.cmd_compile { - compile::run( - args.arg_file, - args.flag_print, - &args.flag_set, - &args.flag_target, - ) - } else if args.cmd_wasm { - #[cfg(feature = "wasm")] - let result = wasm::run( - args.arg_file, - args.flag_verbose, - args.flag_just_decode, - args.flag_check_translation, - args.flag_print, - &args.flag_set, - &args.flag_target, - args.flag_print_size, - ); - - #[cfg(not(feature = "wasm"))] - let result = Err("Error: clif-util was compiled without wasm support.".to_owned()); - - result - } else { - // Debugging / shouldn't happen with proper command line handling above. - Err(format!("Unhandled args: {:?}", args)) - }; - - if args.flag_time_passes { - print!("{}", timing::take_current()); - } - - result } fn main() { - if let Err(mut msg) = clif_util() { + let app_cmds = App::new("Cranelift code generator utility") + .version(VERSION) + .subcommand( + SubCommand::with_name("test") + .about("Run Cranelift tests") + .arg(add_verbose_flag()) + .arg(add_time_flag()) + .arg(add_input_file_arg()) + .arg(add_debug_flag()), + ) + .subcommand( + SubCommand::with_name("cat") + .about("Outputs .clif file") + .arg(add_input_file_arg()) + .arg(add_debug_flag()), + ) + .subcommand( + SubCommand::with_name("print-cfg") + .about("Prints out cfg in dot format") + .arg(add_input_file_arg()) + .arg(add_debug_flag()), + ) + .subcommand( + add_wasm_or_compile("compile") + .arg( + Arg::with_name("just-decode") + .short("t") + .help("Just decode WebAssembly to Cranelift IR"), + ) + .arg(Arg::with_name("check-translation").short("c").help( + "Just checks the correctness of Cranelift IR translated from WebAssembly", + )), + ) + .subcommand(add_wasm_or_compile("wasm")); + + let res_util = match app_cmds.get_matches().subcommand() { + ("cat", Some(rest_cmd)) => { + handle_debug_flag(rest_cmd.is_present("debug")); + cat::run(&get_vec(rest_cmd.values_of("file"))) + } + ("test", Some(rest_cmd)) => { + handle_debug_flag(rest_cmd.is_present("debug")); + cranelift_filetests::run( + rest_cmd.is_present("time-passes"), + &get_vec(rest_cmd.values_of("file")), + ).map(|_time| ()) + } + ("print-cfg", Some(rest_cmd)) => { + handle_debug_flag(rest_cmd.is_present("debug")); + print_cfg::run(&get_vec(rest_cmd.values_of("file"))) + } + ("compile", Some(rest_cmd)) => { + handle_debug_flag(rest_cmd.is_present("debug")); + + let mut target_val: &str = ""; + if let Some(clap_target) = rest_cmd.value_of("target") { + target_val = clap_target; + } + + compile::run( + get_vec(rest_cmd.values_of("file")), + rest_cmd.is_present("print"), + &get_vec(rest_cmd.values_of("set")), + target_val, + ) + } + ("wasm", Some(rest_cmd)) => { + handle_debug_flag(rest_cmd.is_present("debug")); + + let mut target_val: &str = ""; + if let Some(clap_target) = rest_cmd.value_of("target") { + target_val = clap_target; + } + + #[cfg(feature = "wasm")] + let result = wasm::run( + get_vec(rest_cmd.values_of("file")), + rest_cmd.is_present("verbose"), + rest_cmd.is_present("just-decode"), + rest_cmd.is_present("check-translation"), + rest_cmd.is_present("print"), + &get_vec(rest_cmd.values_of("set")), + target_val, + rest_cmd.is_present("print-size"), + ); + + #[cfg(not(feature = "wasm"))] + let result = Err("Error: clif-util was compiled without wasm support.".to_owned()); + + result + } + _ => Err(format!("Invalid subcommand.")), + }; + + if let Err(mut msg) = res_util { if !msg.ends_with('\n') { msg.push('\n'); } diff --git a/cranelift/src/rsfilecheck.rs b/cranelift/src/rsfilecheck.rs deleted file mode 100644 index 48e25b3f12..0000000000 --- a/cranelift/src/rsfilecheck.rs +++ /dev/null @@ -1,61 +0,0 @@ -//! The `filecheck` sub-command. -//! -//! This file is named to avoid a name collision with the filecheck crate. - -use filecheck::{Checker, CheckerBuilder, NO_VARIABLES}; -use std::io::{self, Read}; -use utils::read_to_string; -use CommandResult; - -pub fn run(files: &[String], verbose: bool) -> CommandResult { - if files.is_empty() { - return Err("No check files".to_string()); - } - let checker = read_checkfile(&files[0])?; - if checker.is_empty() { - return Err(format!("{}: no filecheck directives found", files[0])); - } - - // Print out the directives under --verbose. - if verbose { - println!("{}", checker); - } - - let mut buffer = String::new(); - io::stdin() - .read_to_string(&mut buffer) - .map_err(|e| format!("stdin: {}", e))?; - - if verbose { - let (success, explain) = checker - .explain(&buffer, NO_VARIABLES) - .map_err(|e| e.to_string())?; - print!("{}", explain); - if success { - println!("OK"); - Ok(()) - } else { - Err("Check failed".to_string()) - } - } else if checker - .check(&buffer, NO_VARIABLES) - .map_err(|e| e.to_string())? - { - Ok(()) - } else { - let (_, explain) = checker - .explain(&buffer, NO_VARIABLES) - .map_err(|e| e.to_string())?; - print!("{}", explain); - Err("Check failed".to_string()) - } -} - -fn read_checkfile(filename: &str) -> Result { - let buffer = read_to_string(&filename).map_err(|e| format!("{}: {}", filename, e))?; - let mut builder = CheckerBuilder::new(); - builder - .text(&buffer) - .map_err(|e| format!("{}: {}", filename, e))?; - Ok(builder.finish()) -}