| // Copyright 2017 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #![recursion_limit = "1024"] |
| |
| extern crate clap; |
| #[macro_use] |
| extern crate error_chain; |
| extern crate serde; |
| #[macro_use] |
| extern crate serde_derive; |
| extern crate serde_json; |
| extern crate toml; |
| extern crate uname; |
| |
| mod device; |
| mod sdk; |
| mod utils; |
| mod update_crates; |
| |
| mod errors { |
| // Create the Error, ErrorKind, ResultExt, and Result types |
| error_chain!{ |
| links { |
| Device(::device::Error, ::device::ErrorKind); |
| SDK(::sdk::Error, ::sdk::ErrorKind); |
| Utils(::utils::Error, ::utils::ErrorKind); |
| } |
| |
| foreign_links { |
| Io(::std::io::Error); |
| } |
| } |
| } |
| |
| use errors::*; |
| |
| use clap::{App, AppSettings, Arg, SubCommand}; |
| use device::{netaddr, scp_to_device, ssh, start_emulator, stop_emulator}; |
| use sdk::{rust_c_path, rust_linker_path, TargetOptions}; |
| use std::path::PathBuf; |
| use std::process::Command; |
| use std::fs; |
| use update_crates::update_crates; |
| use utils::strip_binary; |
| |
| fn run_program_on_target(filename: &str, |
| verbose: bool, |
| target_options: &TargetOptions, |
| launch: bool, |
| params: &[&str]) |
| -> Result<()> { |
| let netaddr = netaddr(verbose)?; |
| if verbose { |
| println!("netaddr {}", netaddr); |
| } |
| let source_path = PathBuf::from(&filename); |
| let stripped_source_path = strip_binary(&source_path)?; |
| let destination_path = format!("/tmp/{}", |
| stripped_source_path.file_name() |
| .unwrap() |
| .to_string_lossy()); |
| println!("copying {} to {}", |
| source_path.to_string_lossy(), |
| destination_path); |
| scp_to_device(verbose, |
| &target_options, |
| &netaddr, |
| &stripped_source_path, |
| &destination_path)?; |
| let mut command_string = (if launch { "launch " } else { "" }).to_string(); |
| command_string.push_str(&destination_path); |
| for param in params { |
| command_string.push(' '); |
| command_string.push_str(param); |
| } |
| |
| if verbose { |
| println!("running {}", command_string); |
| } |
| ssh(verbose, &target_options, &command_string)?; |
| Ok(()) |
| } |
| |
| fn build_tests(verbose: bool, |
| release: bool, |
| target_options: &TargetOptions, |
| test_target: &str) |
| -> Result<bool> { |
| run_tests(verbose, release, true, target_options, test_target, &vec![])?; |
| Ok(true) |
| } |
| |
| fn run_tests(verbose: bool, |
| release: bool, |
| no_run: bool, |
| target_options: &TargetOptions, |
| test_target: &str, |
| params: &[&str]) |
| -> Result<()> { |
| |
| let mut args = vec!["test"]; |
| |
| if test_target.len() > 0 { |
| args.push("--test"); |
| args.push(test_target); |
| } |
| |
| if no_run { |
| args.push("--no-run"); |
| } |
| |
| for param in params { |
| args.push(param); |
| } |
| |
| run_cargo(verbose, release, false, &args, &target_options)?; |
| Ok(()) |
| } |
| |
| fn build_binary(verbose: bool, release: bool, target_options: &TargetOptions) -> Result<(bool)> { |
| run_cargo(verbose, release, false, &vec!["build"], &target_options) |
| } |
| |
| fn run_binary(verbose: bool, |
| release: bool, |
| launch: bool, |
| target_options: &TargetOptions) |
| -> Result<()> { |
| run_cargo(verbose, release, launch, &vec!["run"], &target_options)?; |
| return Ok(()); |
| } |
| |
| fn run_cargo(verbose: bool, |
| release: bool, |
| launch: bool, |
| args: &[&str], |
| target_options: &TargetOptions) |
| -> Result<(bool)> { |
| let mut target_args = vec!["--target", "x86_64-unknown-fuchsia"]; |
| |
| if release { |
| target_args.push("--release"); |
| } |
| |
| if verbose { |
| println!("target_args = {:?}", target_args); |
| } |
| |
| let fargo_path = fs::canonicalize(std::env::current_exe()?)?; |
| |
| let mut runner_args = vec![fargo_path.to_str() |
| .ok_or_else(|| "unable to convert path to utf8 encoding")?]; |
| |
| if verbose { |
| runner_args.push("-v"); |
| } |
| |
| runner_args.push("run-on-target"); |
| |
| if launch { |
| runner_args.push("--launch"); |
| } |
| |
| let fargo_command = runner_args.join(" "); |
| |
| if verbose { |
| println!("fargo_command: {:?}", fargo_command); |
| } |
| |
| let mut cmd = Command::new("cargo"); |
| |
| cmd.env("RUSTC", rust_c_path()?.to_str().unwrap()) |
| .env("CARGO_TARGET_X86_64_UNKNOWN_FUCHSIA_LINKER", |
| rust_linker_path(&target_options)?.to_str().unwrap()) |
| .env("CARGO_TARGET_X86_64_UNKNOWN_FUCHSIA_RUNNER", fargo_command) |
| .args(args) |
| .args(target_args); |
| |
| if verbose { |
| println!("cargo cmd: {:?}", cmd); |
| } |
| |
| cmd.status().chain_err(|| "Unable to run cargo").map(|s| s.success()) |
| } |
| |
| |
| fn run() -> Result<()> { |
| let matches = App::new("fargo") |
| .version("v0.1.0") |
| .setting(AppSettings::GlobalVersion) |
| .arg(Arg::with_name("verbose") |
| .short("v") |
| .help("Print verbose output while performing commands")) |
| .arg(Arg::with_name("debug-os") |
| .long("debug-os") |
| .help("Use debug user.bootfs and ssh keys")) |
| .subcommand(SubCommand::with_name("build-tests") |
| .about("Build for Fuchsia device or emulator") |
| .arg(Arg::with_name("test") |
| .long("test") |
| .value_name("test") |
| .help("Test only the specified test target")) |
| .arg(Arg::with_name("release") |
| .long("release") |
| .help("Build release"))) |
| .subcommand(SubCommand::with_name("test") |
| .about("Run unit tests on Fuchsia device or emulator") |
| .arg(Arg::with_name("release") |
| .long("release") |
| .help("Build release")) |
| .arg(Arg::with_name("test") |
| .long("test") |
| .value_name("test") |
| .help("Test only the specified test target")) |
| .arg(Arg::with_name("test_params").index(1).multiple(true))) |
| .subcommand(SubCommand::with_name("build") |
| .about("Build binary targeting Fuchsia device or emulator") |
| .arg(Arg::with_name("release") |
| .long("release") |
| .help("Build release"))) |
| .subcommand(SubCommand::with_name("run") |
| .about("Run binary on Fuchsia device or emulator") |
| .arg(Arg::with_name("release") |
| .long("release") |
| .help("Build release")) |
| .arg(Arg::with_name("launch") |
| .long("launch") |
| .help("Use launch to run binary."))) |
| .subcommand(SubCommand::with_name("start") |
| .about("Start a Fuchsia emulator") |
| .arg(Arg::with_name("graphics") |
| .short("g") |
| .help("Start a simulator with graphics enabled"))) |
| .subcommand(SubCommand::with_name("stop").about("Stop all Fuchsia emulators")) |
| .subcommand(SubCommand::with_name("restart") |
| .about("Stop all Fuchsia emulators and start a new one") |
| .arg(Arg::with_name("graphics") |
| .short("g") |
| .help("Start a simulator with graphics enabled"))) |
| .subcommand(SubCommand::with_name("ssh") |
| .about("Open a shell on Fuchsia device or emulator")) |
| .subcommand(SubCommand::with_name("cargo") |
| .about("Run a cargo command for Fuchsia. Use -- to indicate that all \ |
| following arguments should be passed to cargo.") |
| .arg(Arg::with_name("cargo_params").index(1).multiple(true))) |
| .subcommand(SubCommand::with_name("run-on-target") |
| .about("Act as a test runner for cargo") |
| .arg(Arg::with_name("launch") |
| .long("launch") |
| .help("Use launch to run binary.")) |
| .arg(Arg::with_name("run_on_target_params").index(1).multiple(true)) |
| .setting(AppSettings::Hidden)) |
| .subcommand(SubCommand::with_name("update-crates") |
| .about("Update the FIDL generated crates") |
| .arg(Arg::with_name("target") |
| .long("target") |
| .value_name("target") |
| .required(true) |
| .help("Target directory for updated crates")) |
| .setting(AppSettings::Hidden)) |
| .get_matches(); |
| |
| let verbose = matches.is_present("verbose"); |
| let target_options = TargetOptions::new(!matches.is_present("debug-os")); |
| |
| if let Some(test_matches) = matches.subcommand_matches("test") { |
| let test_params = |
| test_matches.values_of("test_params").map(|x| x.collect()).unwrap_or(vec![]); |
| let test_target = test_matches.value_of("test").unwrap_or(""); |
| return run_tests(verbose, |
| test_matches.is_present("release"), |
| false, |
| &target_options, |
| &test_target, |
| &test_params) |
| .chain_err(|| "running tests failed"); |
| } |
| |
| if let Some(build_matches) = matches.subcommand_matches("build") { |
| build_binary(verbose, |
| build_matches.is_present("release"), |
| &target_options).chain_err(|| "building binary failed")?; |
| return Ok(()); |
| } |
| |
| if let Some(run_matches) = matches.subcommand_matches("run") { |
| return run_binary(verbose, |
| run_matches.is_present("release"), |
| run_matches.is_present("launch"), |
| &target_options) |
| .chain_err(|| "running binary failed"); |
| } |
| |
| if let Some(build_test_matches) = matches.subcommand_matches("build-tests") { |
| let test_target = build_test_matches.value_of("test").unwrap_or(""); |
| build_tests(verbose, |
| build_test_matches.is_present("release"), |
| &target_options, |
| &test_target).chain_err(|| "building tests failed")?; |
| return Ok(()); |
| } |
| |
| if let Some(start_matches) = matches.subcommand_matches("start") { |
| return start_emulator(start_matches.is_present("graphics"), &target_options) |
| .chain_err(|| "starting emulator failed"); |
| } |
| |
| if let Some(_) = matches.subcommand_matches("stop") { |
| return stop_emulator().chain_err(|| "stopping emulator failed"); |
| } |
| |
| if let Some(restart_matches) = matches.subcommand_matches("restart") { |
| stop_emulator().chain_err(|| "in restart, stopping emulator failed")?; |
| |
| return start_emulator(restart_matches.is_present("graphics"), &target_options) |
| .chain_err(|| "in restart, starting emulator failed"); |
| } |
| |
| if let Some(_) = matches.subcommand_matches("ssh") { |
| return ssh(verbose, &target_options, "").chain_err(|| "ssh failed"); |
| } |
| |
| if let Some(cargo_matches) = matches.subcommand_matches("cargo") { |
| let cargo_params = |
| cargo_matches.values_of("cargo_params").map(|x| x.collect()).unwrap_or(vec![]); |
| run_cargo(verbose, false, false, &cargo_params, |
| &target_options).chain_err(|| "run cargo failed")?; |
| return Ok(()); |
| } |
| |
| if let Some(run_on_target_matches) = matches.subcommand_matches("run-on-target") { |
| let run_params = run_on_target_matches.values_of("run_on_target_params") |
| .map(|x| x.collect()) |
| .unwrap_or(vec![]); |
| let (program, args) = run_params.split_first().unwrap(); |
| return run_program_on_target(&program, verbose, &target_options, false, &args); |
| } |
| |
| if let Some(update_matches) = matches.subcommand_matches("update-crates") { |
| let update_target = update_matches.value_of("target").unwrap(); |
| return update_crates(&update_target).chain_err(|| "update-crates failed"); |
| } |
| |
| Ok(()) |
| } |
| |
| fn main() { |
| if let Err(ref e) = run() { |
| println!("error: {}", e); |
| |
| for e in e.iter().skip(1) { |
| println!("caused by: {}", e); |
| } |
| |
| // The backtrace is not always generated. Try to run this example |
| // with `RUST_BACKTRACE=1`. |
| if let Some(backtrace) = e.backtrace() { |
| println!("backtrace: {:?}", backtrace); |
| } |
| |
| ::std::process::exit(1); |
| } |
| } |