| // 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. |
| |
| //! While fargo is mainly intended to be a command line tool, this library |
| //! exposes one function, `run_cargo`, that could be integrated directly into |
| //! Rust programs that want to cross compile cargo crates on Fuchsia. |
| |
| #![recursion_limit = "1024"] |
| |
| mod cross; |
| mod device; |
| mod sdk; |
| mod utils; |
| |
| pub use crate::sdk::{FuchsiaConfig, TargetOptions}; |
| |
| use crate::cross::{pkg_config_path, run_configure, run_pkg_config}; |
| use crate::device::{enable_networking, netaddr, netls, scp_to_device, ssh, start_emulator, |
| stop_emulator, StartEmulatorOptions}; |
| use crate::sdk::{cargo_out_dir, cargo_path, clang_archiver_path, clang_c_compiler_path, |
| clang_cpp_compiler_path, clang_linker_path, clang_ranlib_path, rustc_path, |
| rustdoc_path, shared_libraries_path, sysroot_path, zircon_build_path}; |
| use crate::utils::{is_mac, strip_binary}; |
| |
| use clap::{App, AppSettings, Arg, SubCommand}; |
| use failure::{bail, err_msg, Error, ResultExt}; |
| use std::fs; |
| use std::fs::File; |
| use std::io::Write; |
| use std::path::{Path, PathBuf}; |
| use std::process::Command; |
| use std::time::SystemTime; |
| |
| fn copy_to_target( |
| source_path: &PathBuf, verbose: bool, config: &FuchsiaConfig, |
| target_options: &TargetOptions<'_, '_>, |
| ) -> Result<String, Error> { |
| let netaddr = netaddr(verbose, target_options)?; |
| if verbose { |
| println!("netaddr {}", netaddr); |
| } |
| let destination_path = format!( |
| "/tmp/{}", |
| source_path.file_name().unwrap().to_string_lossy() |
| ); |
| println!( |
| "copying {} to {}", |
| source_path.to_string_lossy(), |
| destination_path |
| ); |
| scp_to_device(verbose, config, &netaddr, &source_path, &destination_path)?; |
| Ok(destination_path) |
| } |
| |
| fn run_program_on_target( |
| filename: &str, verbose: bool, nocapture: bool, config: &FuchsiaConfig, |
| target_options: &TargetOptions<'_, '_>, run_mode: RunMode, story_name: &str, mod_name: &str, |
| params: &[&str], test_args: Option<&str>, |
| ) -> Result<(), Error> { |
| let source_path = PathBuf::from(&filename); |
| let stripped_source_path = strip_binary(&source_path)?; |
| let destination_path = copy_to_target(&stripped_source_path, verbose, config, target_options)?; |
| let mut command_string = match run_mode { |
| RunMode::Tiles => "tiles_ctl add ".to_string(), |
| RunMode::Ermine => format!( |
| "sessionctl --story_name={} --mod_name={} --mod_url=", |
| story_name, mod_name |
| ), |
| RunMode::Run => "run ".to_string(), |
| RunMode::Normal => "".to_string(), |
| }; |
| command_string.push_str(&destination_path); |
| |
| match run_mode { |
| RunMode::Ermine => { |
| command_string.push_str(" add_mod"); |
| } |
| _ => (), |
| } |
| |
| if nocapture { |
| command_string.push_str(" --"); |
| command_string.push_str(NOCAPTURE); |
| } |
| |
| for param in params { |
| command_string.push(' '); |
| command_string.push_str(param); |
| } |
| |
| if let Some(test_args_str) = test_args { |
| command_string.push_str(" -- "); |
| command_string.push_str(test_args_str); |
| } |
| |
| if verbose { |
| println!("running {}", command_string); |
| } |
| |
| ssh(verbose, config, target_options, &command_string)?; |
| Ok(()) |
| } |
| |
| extern crate notify; |
| |
| use notify::{RecommendedWatcher, RecursiveMode, Watcher}; |
| use std::sync::mpsc::channel; |
| use std::time::Duration; |
| |
| fn autotest( |
| run_cargo_options: &RunCargoOptions, target_options: &TargetOptions<'_, '_>, |
| ) -> Result<(), Error> { |
| let (tx, rx) = channel(); |
| let mut watcher: RecommendedWatcher = |
| Watcher::new(tx, Duration::from_secs(1)).context("autotest: watcher creation failed")?; |
| |
| let cwd = std::fs::canonicalize(std::env::current_dir()?) |
| .context("autotest: canonicalize working directory")?; |
| let tgt = cwd.join("target"); |
| let git = cwd.join(".git"); |
| |
| watcher |
| .watch(&cwd, RecursiveMode::Recursive) |
| .context("autotest: watch failed")?; |
| |
| println!("autotest: started"); |
| loop { |
| let event = rx.recv().context("autotest: watch recv failed")?; |
| match event { |
| notify::DebouncedEvent::Create(path) |
| | notify::DebouncedEvent::Write(path) |
| | notify::DebouncedEvent::Chmod(path) |
| | notify::DebouncedEvent::Remove(path) |
| | notify::DebouncedEvent::Rename(path, _) => { |
| // TODO(raggi): provide a fuller ignore flag/pattern match solution here. |
| if !path.starts_with(&tgt) && !path.starts_with(&git) { |
| println!("autotest: running tests because {:?}", path); |
| run_tests(run_cargo_options, false, target_options, "", &[], None).ok(); |
| } |
| } |
| _ => {} |
| } |
| } |
| } |
| |
| fn build_tests( |
| run_cargo_options: &RunCargoOptions, target_options: &TargetOptions<'_, '_>, test_target: &str, |
| ) -> Result<bool, Error> { |
| run_tests( |
| run_cargo_options, |
| true, |
| target_options, |
| test_target, |
| &[], |
| None, |
| )?; |
| Ok(true) |
| } |
| |
| fn run_tests( |
| run_cargo_options: &RunCargoOptions, no_run: bool, target_options: &TargetOptions<'_, '_>, |
| test_target: &str, params: &[&str], target_params: Option<&str>, |
| ) -> Result<(), Error> { |
| let mut args = vec![]; |
| |
| if !test_target.is_empty() { |
| args.push("--test"); |
| args.push(test_target); |
| } |
| |
| if no_run { |
| args.push("--no-run"); |
| } |
| |
| for param in params { |
| args.push(param); |
| } |
| |
| if target_params.is_some() { |
| let formatted_target_params = format!("--args={}", target_params.unwrap()); |
| run_cargo( |
| &run_cargo_options, |
| "test", |
| &args, |
| target_options, |
| None, |
| Some(&formatted_target_params), |
| )?; |
| } else { |
| run_cargo( |
| &run_cargo_options, |
| "test", |
| &args, |
| target_options, |
| None, |
| None, |
| )?; |
| } |
| |
| Ok(()) |
| } |
| |
| fn build_binary( |
| run_cargo_options: &RunCargoOptions, target_options: &TargetOptions<'_, '_>, params: &[&str], |
| ) -> Result<(), Error> { |
| run_cargo( |
| run_cargo_options, |
| "build", |
| params, |
| target_options, |
| None, |
| None, |
| ) |
| } |
| |
| fn check_binary( |
| run_cargo_options: &RunCargoOptions, target_options: &TargetOptions<'_, '_>, params: &[&str], |
| ) -> Result<(), Error> { |
| run_cargo( |
| run_cargo_options, |
| "check", |
| params, |
| target_options, |
| None, |
| None, |
| ) |
| } |
| |
| fn run_binary( |
| run_cargo_options: &RunCargoOptions, target_options: &TargetOptions<'_, '_>, params: &[&str], |
| ) -> Result<(), Error> { |
| run_cargo(run_cargo_options, RUN, params, target_options, None, None)?; |
| Ok(()) |
| } |
| |
| fn build_doc( |
| run_cargo_options: &RunCargoOptions, target_options: &TargetOptions<'_, '_>, no_deps: bool, |
| open: bool, |
| ) -> Result<(), Error> { |
| let mut args = vec![]; |
| if no_deps { |
| args.push("--no-deps"); |
| } |
| if open { |
| args.push("--open"); |
| } |
| run_cargo(run_cargo_options, DOC, &args, &target_options, None, None) |
| } |
| |
| fn load_driver( |
| run_cargo_options: &RunCargoOptions, config: &FuchsiaConfig, |
| target_options: &TargetOptions<'_, '_>, |
| ) -> Result<(), Error> { |
| let args = vec![]; |
| run_cargo( |
| run_cargo_options, |
| "build", |
| &args, |
| target_options, |
| None, |
| None, |
| )?; |
| let cwd = std::env::current_dir()?; |
| let package = cwd |
| .file_name() |
| .ok_or(err_msg("No current directory"))? |
| .to_str() |
| .ok_or(err_msg("Invalid current directory"))?; |
| let filename = cargo_out_dir(target_options)?.join(format!("lib{}.so", package)); |
| let destination_path = |
| copy_to_target(&filename, run_cargo_options.verbose, config, target_options)?; |
| let command_string = format!("dm add-driver:{}", destination_path); |
| if run_cargo_options.verbose { |
| println!("running {}", command_string); |
| } |
| ssh( |
| run_cargo_options.verbose, |
| config, |
| target_options, |
| &command_string, |
| )?; |
| Ok(()) |
| } |
| |
| #[derive(Clone, Copy, Debug)] |
| pub enum RunMode { |
| Normal, |
| Run, |
| Tiles, |
| Ermine, |
| } |
| |
| fn run_switches_to_mode(tiles: bool, run: bool, ermine: bool) -> RunMode { |
| if tiles { |
| RunMode::Tiles |
| } else if run { |
| RunMode::Run |
| } else if ermine { |
| RunMode::Ermine |
| } else { |
| RunMode::Normal |
| } |
| } |
| |
| fn random_story_name() -> String { |
| let secs = match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) { |
| Ok(n) => n.as_secs(), |
| Err(_) => panic!("SystemTime before UNIX EPOCH!"), |
| }; |
| format!("fargo-story-{}", secs) |
| } |
| |
| #[derive(Debug)] |
| pub struct RunCargoOptions { |
| pub verbose: bool, |
| pub release: bool, |
| pub run_mode: RunMode, |
| pub story_name: Option<String>, |
| pub mod_name: Option<String>, |
| pub disable_cross: bool, |
| pub nocapture: bool, |
| pub manifest_path: Option<PathBuf>, |
| } |
| |
| impl RunCargoOptions { |
| pub fn new(verbose: bool, release: bool) -> RunCargoOptions { |
| RunCargoOptions { |
| verbose, |
| release, |
| nocapture: false, |
| run_mode: RunMode::Normal, |
| story_name: None, |
| mod_name: None, |
| disable_cross: false, |
| manifest_path: None, |
| } |
| } |
| |
| pub fn disable_cross(&self, disable_cross: bool) -> RunCargoOptions { |
| RunCargoOptions { |
| verbose: self.verbose, |
| release: self.release, |
| nocapture: self.nocapture, |
| run_mode: self.run_mode, |
| story_name: self.story_name.clone(), |
| mod_name: self.mod_name.clone(), |
| disable_cross, |
| manifest_path: self.manifest_path.clone(), |
| } |
| } |
| |
| pub fn release(&self, release: bool) -> RunCargoOptions { |
| RunCargoOptions { |
| verbose: self.verbose, |
| release: release, |
| nocapture: self.nocapture, |
| run_mode: self.run_mode, |
| story_name: self.story_name.clone(), |
| mod_name: self.mod_name.clone(), |
| disable_cross: self.disable_cross, |
| manifest_path: self.manifest_path.clone(), |
| } |
| } |
| |
| pub fn nocapture(&self, nocapture: bool) -> RunCargoOptions { |
| RunCargoOptions { |
| verbose: self.verbose, |
| release: self.release, |
| nocapture: nocapture, |
| run_mode: self.run_mode, |
| story_name: self.story_name.clone(), |
| mod_name: self.mod_name.clone(), |
| disable_cross: self.disable_cross, |
| manifest_path: self.manifest_path.clone(), |
| } |
| } |
| |
| pub fn run_mode(&self, run_mode: RunMode) -> RunCargoOptions { |
| RunCargoOptions { |
| verbose: self.verbose, |
| release: self.release, |
| nocapture: self.nocapture, |
| run_mode: run_mode, |
| story_name: self.story_name.clone(), |
| mod_name: self.mod_name.clone(), |
| disable_cross: self.disable_cross, |
| manifest_path: self.manifest_path.clone(), |
| } |
| } |
| |
| pub fn story_name(&self, story_name: &Option<&str>) -> RunCargoOptions { |
| RunCargoOptions { |
| verbose: self.verbose, |
| release: self.release, |
| nocapture: self.nocapture, |
| run_mode: self.run_mode, |
| story_name: story_name.map(|name| name.to_string()), |
| mod_name: self.mod_name.clone(), |
| disable_cross: self.disable_cross, |
| manifest_path: self.manifest_path.clone(), |
| } |
| } |
| |
| pub fn mod_name(&self, mod_name: &Option<&str>) -> RunCargoOptions { |
| RunCargoOptions { |
| verbose: self.verbose, |
| release: self.release, |
| nocapture: self.nocapture, |
| run_mode: self.run_mode, |
| story_name: self.story_name.clone(), |
| mod_name: mod_name.map(|name| name.to_string()), |
| disable_cross: self.disable_cross, |
| manifest_path: self.manifest_path.clone(), |
| } |
| } |
| |
| pub fn manifest_path(&self, manifest_path: Option<PathBuf>) -> RunCargoOptions { |
| RunCargoOptions { |
| verbose: self.verbose, |
| release: self.release, |
| nocapture: self.nocapture, |
| run_mode: self.run_mode, |
| story_name: self.story_name.clone(), |
| mod_name: self.mod_name.clone(), |
| disable_cross: self.disable_cross, |
| manifest_path: manifest_path, |
| } |
| } |
| |
| pub fn get_story_name(&self) -> String { |
| if let Some(ref name) = self.story_name { |
| name.clone() |
| } else { |
| random_story_name() |
| } |
| } |
| |
| pub fn get_mod_name(&self) -> String { |
| if let Some(ref name) = self.mod_name { |
| name.clone() |
| } else { |
| DEFAULT_MOD_NAME.to_string() |
| } |
| } |
| } |
| |
| fn get_triple_cpu(target_options: &TargetOptions<'_, '_>) -> String { |
| if (target_options.config.fuchsia_arch) == X64 { |
| "x86_64" |
| } else { |
| "aarch64" |
| } |
| .to_string() |
| } |
| |
| fn get_target_triple(target_options: &TargetOptions<'_, '_>) -> String { |
| let triple_cpu = get_triple_cpu(target_options); |
| |
| format!("{}-fuchsia", triple_cpu) |
| } |
| |
| fn get_rustflags( |
| target_options: &TargetOptions<'_, '_>, sysroot_as_path: &PathBuf, |
| ) -> Result<String, Error> { |
| let mut rust_flags = vec![ |
| format!("-C link-arg=--target={}", get_target_triple(target_options)), |
| format!( |
| "-C link-arg=--sysroot={}", |
| sysroot_as_path.to_str().unwrap() |
| ), |
| format!( |
| "-Lnative={}", |
| shared_libraries_path(target_options)?.to_str().unwrap() |
| ), |
| "-Clink-arg=-Wl,--pack-dyn-relocs=relr".to_string(), |
| ]; |
| if !is_mac() { |
| rust_flags.push("-C link-arg=-Wl,--threads".to_string()); |
| } |
| if get_triple_cpu(target_options) == "aarch64" { |
| rust_flags.push("-Clink-arg=-Wl,--fix-cortex-a53-843419".to_string()); |
| } |
| Ok(rust_flags.join(" ")) |
| } |
| |
| fn make_fargo_command( |
| runner: Option<PathBuf>, options: &RunCargoOptions, nocapture: bool, |
| target_options: &TargetOptions<'_, '_>, additional_target_args: Option<&str>, |
| ) -> Result<String, Error> { |
| let tiles_arg = format!("--{}", RUN_WITH_TILES); |
| let ermine_arg = format!( |
| "--{} --story-name={} --mod-name={}", |
| RUN_WITH_SESSIONCTL, |
| options.get_story_name(), |
| options.get_mod_name() |
| ); |
| let run_arg = format!("--{}", RUN_WITH_RUN); |
| let nocapture_arg = format!("--{}", NOCAPTURE); |
| |
| let fargo_path = if runner.is_some() { |
| runner.unwrap() |
| } else { |
| fs::canonicalize(std::env::current_exe()?)? |
| }; |
| |
| let mut runner_args = vec![ |
| fargo_path |
| .to_str() |
| .ok_or_else(|| err_msg("unable to convert path to utf8 encoding"))?, |
| ]; |
| |
| if options.verbose { |
| runner_args.push("-v"); |
| } |
| |
| if let Some(device_name) = target_options.device_name { |
| runner_args.push("--device-name"); |
| runner_args.push(device_name); |
| } |
| |
| runner_args.push(RUN_ON_TARGET); |
| |
| if nocapture { |
| runner_args.push(&nocapture_arg); |
| } |
| |
| match options.run_mode { |
| RunMode::Normal => (), |
| RunMode::Tiles => runner_args.push(&tiles_arg), |
| RunMode::Ermine => runner_args.push(&ermine_arg), |
| RunMode::Run => runner_args.push(&run_arg), |
| } |
| |
| if let Some(args_for_target) = additional_target_args { |
| runner_args.push(&args_for_target); |
| } |
| |
| Ok(runner_args.join(" ")) |
| } |
| |
| fn convert_manifest_path(possible_path: &Option<&str>) -> Option<PathBuf> { |
| if let Some(path) = possible_path { |
| Some(PathBuf::from(path)) |
| } else { |
| None |
| } |
| } |
| |
| /// Runs the cargo tool configured to target Fuchsia. When used as a library, |
| /// the runner options must contain the path to fargo or some other program |
| /// that implements the `run-on-target` subcommand in a way compatible with |
| /// fargo. |
| /// |
| /// # Examples |
| /// |
| /// ``` |
| /// use fargo::{run_cargo, FuchsiaConfig, RunCargoOptions, RunMode, TargetOptions}; |
| /// |
| /// let config = FuchsiaConfig::default(); |
| /// let target_options = TargetOptions::new(&config, None); |
| /// run_cargo( |
| /// &RunCargoOptions { |
| /// verbose: false, |
| /// release: true, |
| /// nocapture: false, |
| /// run_mode: RunMode::Normal, |
| /// story_name: None, |
| /// mod_name: None, |
| /// disable_cross: false, |
| /// manifest_path: None, |
| /// }, |
| /// "help", |
| /// &[], |
| /// &target_options, |
| /// None, |
| /// None, |
| /// ); |
| /// ``` |
| pub fn run_cargo( |
| options: &RunCargoOptions, subcommand: &str, args: &[&str], |
| target_options: &TargetOptions<'_, '_>, runner: Option<PathBuf>, |
| additional_target_args: Option<&str>, |
| ) -> Result<(), Error> { |
| if options.verbose { |
| println!("target_options = {:?}", target_options); |
| } |
| |
| let triple_cpu = get_triple_cpu(target_options); |
| let target_triple = get_target_triple(target_options); |
| let mut target_args = vec!["--target", &target_triple]; |
| |
| if options.release { |
| target_args.push("--release"); |
| } |
| |
| if options.verbose { |
| println!( |
| "target_options.target_cpu = {:?}", |
| target_options.config.fuchsia_arch |
| ); |
| println!("triple_cpu = {:?}", triple_cpu); |
| println!("target_triple = {:?}", target_triple); |
| println!("target_args = {:?}", target_args); |
| println!("options = {:?}", options); |
| } |
| |
| let target_triple_uc = format!("{}_fuchsia", triple_cpu).to_uppercase(); |
| |
| let fargo_command = make_fargo_command( |
| runner, |
| &options, |
| options.nocapture, |
| target_options, |
| additional_target_args, |
| )?; |
| |
| if options.verbose { |
| println!("fargo_command: {:?}", fargo_command); |
| } |
| |
| let pkg_path = pkg_config_path(target_options)?; |
| let mut cmd = Command::new(cargo_path()?); |
| let sysroot_as_path = sysroot_path(target_options)?; |
| let sysroot_as_str = sysroot_as_path.to_str().unwrap(); |
| |
| let args: Vec<&str> = args |
| .iter() |
| .map(|a| if *a == "++" { "--" } else { *a }) |
| .collect(); |
| |
| let runner_env_name = format!("CARGO_TARGET_{}_RUNNER", target_triple_uc); |
| let rustflags_env_name = format!("CARGO_TARGET_{}_RUSTFLAGS", target_triple_uc); |
| let linker_env_name = format!("CARGO_TARGET_{}_LINKER", target_triple_uc); |
| |
| if options.verbose { |
| println!("runner_env_name: {:?}", runner_env_name); |
| println!("rustflags_env_name: {:?}", rustflags_env_name); |
| println!("linker_env_name: {:?}", linker_env_name); |
| println!("rustc_path: {:?}", rustc_path()?.to_str().unwrap()); |
| println!("cargo_path: {:?}", cargo_path()?.to_str().unwrap()); |
| } |
| |
| cmd.env(runner_env_name, fargo_command) |
| .env( |
| rustflags_env_name, |
| get_rustflags(target_options, &sysroot_as_path)?, |
| ) |
| .env(linker_env_name, clang_linker_path()?.to_str().unwrap()) |
| .env("RUSTC", rustc_path()?.to_str().unwrap()) |
| .env("RUSTDOC", rustdoc_path()?.to_str().unwrap()) |
| .env("RUSTDOCFLAGS", "--cap-lints allow -Z unstable-options") |
| .env( |
| "FUCHSIA_SHARED_ROOT", |
| shared_libraries_path(target_options)?, |
| ) |
| .env("ZIRCON_BUILD_ROOT", zircon_build_path(target_options)?) |
| .arg(subcommand) |
| .args(target_args) |
| .args(args); |
| |
| if let Some(ref manifest_path) = options.manifest_path { |
| let manifest_args: Vec<&str> = vec!["--manifest-path", manifest_path.to_str().unwrap()]; |
| cmd.args(manifest_args); |
| } |
| |
| if !options.disable_cross { |
| let cc_env_name = format!("CC_{}", target_triple_uc); |
| let cxx_env_name = format!("CXX_{}", target_triple_uc); |
| let cflags_env_name = format!("CFLAGS_{}", target_triple_uc); |
| let ar_env_name = format!("AR_{}", target_triple_uc); |
| cmd.env(cc_env_name, clang_c_compiler_path()?.to_str().unwrap()) |
| .env(cxx_env_name, clang_cpp_compiler_path()?.to_str().unwrap()) |
| .env(cflags_env_name, format!("--sysroot={}", sysroot_as_str)) |
| .env(ar_env_name, clang_archiver_path()?.to_str().unwrap()) |
| .env("RANLIB", clang_ranlib_path()?.to_str().unwrap()) |
| .env("PKG_CONFIG_ALL_STATIC", "1") |
| .env("PKG_CONFIG_ALLOW_CROSS", "1") |
| .env("PKG_CONFIG_PATH", "") |
| .env("PKG_CONFIG_LIBDIR", pkg_path); |
| } |
| |
| if options.verbose { |
| println!("cargo cmd: {:?}", cmd); |
| } |
| |
| let cargo_status = cmd.status()?; |
| if !cargo_status.success() { |
| bail!("cargo exited with status {:?}", cargo_status,); |
| } |
| |
| Ok(()) |
| } |
| |
| fn write_config( |
| options: &RunCargoOptions, target_options: &TargetOptions<'_, '_>, |
| ) -> Result<(), Error> { |
| let cargo_dir_path = Path::new(".cargo"); |
| if cargo_dir_path.exists() { |
| if !cargo_dir_path.is_dir() { |
| bail!( |
| "fargo wants to create a directory {:#?}, but there is an existing file in the way", |
| cargo_dir_path |
| ); |
| } |
| } else { |
| fs::create_dir(cargo_dir_path)?; |
| } |
| |
| let mut config = File::create(".cargo/config")?; |
| |
| let sysroot_as_path = sysroot_path(target_options)?; |
| writeln!(config, "[target.{}]", get_target_triple(target_options))?; |
| writeln!( |
| config, |
| "rustflags = \"{}\"", |
| get_rustflags(target_options, &sysroot_as_path)? |
| )?; |
| writeln!( |
| config, |
| "linker = \"{}\"", |
| clang_linker_path()?.to_str().unwrap() |
| )?; |
| writeln!( |
| config, |
| "runner = \"{}\"", |
| make_fargo_command(None, options, true, target_options, None)? |
| )?; |
| writeln!(config, "")?; |
| writeln!(config, "[build]")?; |
| writeln!(config, "rustc = \"{}\"", rustc_path()?.to_str().unwrap())?; |
| writeln!( |
| config, |
| "rustdoc = \"{}\"", |
| rustdoc_path()?.to_str().unwrap() |
| )?; |
| writeln!(config, "target = \"{}\"", get_target_triple(target_options))?; |
| Ok(()) |
| } |
| |
| static RUN: &str = "run"; |
| static RUN_WITH_TILES: &str = "run-with-tiles"; |
| static RUN_WITH_RUN: &str = "run-with-run"; |
| static RUN_WITH_SESSIONCTL: &str = "run-with-sessionctl"; |
| static STORY_NAME: &str = "story-name"; |
| static MOD_NAME: &str = "mod-name"; |
| static DEFAULT_MOD_NAME: &str = "fargo"; |
| |
| static CHECK: &str = "check"; |
| static RELEASE: &str = "release"; |
| static EXAMPLE: &str = "example"; |
| static EXAMPLES: &str = "examples"; |
| |
| static TEST: &str = "test"; |
| static TEST_TARGET_NAME: &str = "test"; |
| static NOCAPTURE: &str = "nocapture"; |
| |
| static DOC: &str = "doc"; |
| static DOC_OPEN: &str = "open"; |
| static DOC_NO_DEPS: &str = "no-deps"; |
| |
| static X64: &str = "x64"; |
| |
| static SUBCOMMAND: &str = "subcommand"; |
| |
| static DISABLE_CROSS_ENV: &str = "disable-cross-env"; |
| |
| static NO_NET: &str = "no-net"; |
| static FX_RUN_PARAMS: &str = "fx-run-params"; |
| static MANIFEST_PATH: &str = "manifest-path"; |
| |
| static RELEASE_HELP: &str = "Build artifacts in release mode, with optimizations"; |
| |
| static START: &str = "start"; |
| static RESTART: &str = "restart"; |
| static GRAPHICS: &str = "graphics"; |
| static DISABLE_VIRTCON: &str = "disable-virtcon"; |
| |
| static WRITE_CONFIG: &str = "write-config"; |
| |
| static RUN_ON_TARGET: &str = "run-on-target"; |
| |
| #[doc(hidden)] |
| pub fn run() -> Result<(), Error> { |
| let matches = |
| App::new("fargo") |
| .version("v0.2.0") |
| .setting(AppSettings::GlobalVersion) |
| .about("Fargo is a prototype Fuchsia-specific wrapper around Cargo") |
| .arg( |
| Arg::with_name("verbose") |
| .long("verbose") |
| .short("v") |
| .help("Print verbose output while performing commands"), |
| ) |
| .arg( |
| Arg::with_name(DISABLE_CROSS_ENV) |
| .long(DISABLE_CROSS_ENV) |
| .help("Disable the setting of CC, AR and such environmental variables."), |
| ) |
| .arg( |
| Arg::with_name("device-name") |
| .long("device-name") |
| .short("N") |
| .value_name("device-name") |
| .help( |
| "Name of device to target, needed if there are multiple devices visible \ |
| on the network", |
| ), |
| ) |
| .arg( |
| Arg::with_name(MANIFEST_PATH) |
| .long(MANIFEST_PATH) |
| .value_name(MANIFEST_PATH) |
| .global(true) |
| .help("Path to Cargo.toml"), |
| ) |
| .subcommand( |
| SubCommand::with_name("autotest") |
| .about("Auto build and test in Fuchsia device or emulator") |
| .arg(Arg::with_name(RELEASE).long(RELEASE).help("Build release")) |
| .arg( |
| Arg::with_name(NOCAPTURE) |
| .long(NOCAPTURE) |
| .help("Display all output when running tests."), |
| ), |
| ) |
| .subcommand( |
| SubCommand::with_name("build-tests") |
| .about("Build tests for Fuchsia device or emulator") |
| .arg( |
| Arg::with_name(TEST_TARGET_NAME) |
| .long(TEST_TARGET_NAME) |
| .value_name(TEST_TARGET_NAME) |
| .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(RELEASE_HELP)) |
| .arg( |
| Arg::with_name(NOCAPTURE) |
| .long(NOCAPTURE) |
| .help("Display all output when running tests."), |
| ) |
| .arg( |
| Arg::with_name(TEST_TARGET_NAME) |
| .long(TEST_TARGET_NAME) |
| .value_name(TEST_TARGET_NAME) |
| .help("Test only the specified test target"), |
| ) |
| .arg( |
| Arg::with_name("test_args") |
| .long("args") |
| .value_name("args") |
| .help("arguments to pass to the test runner"), |
| ) |
| .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(RELEASE_HELP)) |
| .arg( |
| Arg::with_name("example") |
| .long("example") |
| .takes_value(true) |
| .help("Build a specific example from the examples/ dir."), |
| ) |
| .arg( |
| Arg::with_name("examples") |
| .long("examples") |
| .help("Build all examples in the examples/ dir."), |
| ), |
| ) |
| .subcommand( |
| SubCommand::with_name(CHECK) |
| .about("Check binary targeting Fuchsia device or emulator") |
| .arg(Arg::with_name(RELEASE).long(RELEASE).help(RELEASE_HELP)) |
| .arg( |
| Arg::with_name(EXAMPLE) |
| .long(EXAMPLE) |
| .takes_value(true) |
| .help("Check a specific example from the examples/ dir."), |
| ) |
| .arg( |
| Arg::with_name(EXAMPLES) |
| .long(EXAMPLES) |
| .help("Check all examples in the examples/ dir."), |
| ), |
| ) |
| .subcommand( |
| SubCommand::with_name(DOC) |
| .about("Build a package's documentation") |
| .arg(Arg::with_name(RELEASE).long(RELEASE).help(RELEASE_HELP)) |
| .arg( |
| Arg::with_name(DOC_NO_DEPS) |
| .long(DOC_NO_DEPS) |
| .help("Don't build documentation for dependencies"), |
| ) |
| .arg( |
| Arg::with_name(DOC_OPEN) |
| .long(DOC_OPEN) |
| .help("Opens the docs in a browser after the operation"), |
| ), |
| ) |
| .subcommand( |
| SubCommand::with_name(RUN) |
| .about("Run binary on Fuchsia device or emulator") |
| .arg(Arg::with_name(RELEASE).long(RELEASE).help(RELEASE_HELP)) |
| .arg( |
| Arg::with_name(STORY_NAME) |
| .long(STORY_NAME) |
| .short("n") |
| .value_name(STORY_NAME) |
| .help("Name of story to pass to sessionctl"), |
| ) |
| .arg( |
| Arg::with_name(MOD_NAME) |
| .long(MOD_NAME) |
| .short("m") |
| .value_name(MOD_NAME) |
| .help("Name of mod to pass to sessionctl"), |
| ) |
| .arg( |
| Arg::with_name(RUN_WITH_TILES) |
| .long(RUN_WITH_TILES) |
| .help("Use tiles_ctl add to run binary."), |
| ) |
| .arg( |
| Arg::with_name(RUN_WITH_RUN) |
| .long(RUN_WITH_RUN) |
| .help("Use run to run binary."), |
| ) |
| .arg( |
| Arg::with_name(RUN_WITH_SESSIONCTL) |
| .long(RUN_WITH_SESSIONCTL) |
| .help("Use sessionctl to run binary."), |
| ) |
| .arg( |
| Arg::with_name("example") |
| .long("example") |
| .value_name("example") |
| .help("Run a specific example from the examples/ dir."), |
| ), |
| ) |
| .subcommand( |
| SubCommand::with_name("load-driver") |
| .about("Build driver and load it on Fuchsia device or emulator.") |
| .arg(Arg::with_name(RELEASE).long(RELEASE).help("Build release")), |
| ) |
| .subcommand(SubCommand::with_name("list-devices").about("List visible Fuchsia devices")) |
| .subcommand( |
| SubCommand::with_name(START) |
| .about("Start a Fuchsia emulator") |
| .arg( |
| Arg::with_name(GRAPHICS) |
| .short("g") |
| .help("Start a simulator with graphics enabled"), |
| ) |
| .arg(Arg::with_name(DISABLE_VIRTCON).long(DISABLE_VIRTCON).help( |
| "Do not launch the virtual console service if this option is present", |
| )) |
| .arg( |
| Arg::with_name(NO_NET) |
| .long(NO_NET) |
| .help("Don't set up networking."), |
| ) |
| .arg(Arg::with_name(FX_RUN_PARAMS).index(1).multiple(true)), |
| ) |
| .subcommand(SubCommand::with_name("stop").about("Stop all Fuchsia emulators")) |
| .subcommand( |
| SubCommand::with_name("enable-networking") |
| .about("Enable networking for a running emulator"), |
| ) |
| .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"), |
| ) |
| .arg(Arg::with_name(DISABLE_VIRTCON).long(DISABLE_VIRTCON).help( |
| "Do not launch the virtual console service if this option is present", |
| )) |
| .arg( |
| Arg::with_name(NO_NET) |
| .long(NO_NET) |
| .help("Don't set up networking."), |
| ) |
| .arg(Arg::with_name(FX_RUN_PARAMS).index(1).multiple(true)), |
| ) |
| .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(SUBCOMMAND).required(true)) |
| .arg(Arg::with_name("cargo_params").index(2).multiple(true)), |
| ) |
| .subcommand( |
| SubCommand::with_name(RUN_ON_TARGET) |
| .about("Act as a test runner for cargo") |
| .arg( |
| Arg::with_name("test_args") |
| .long("args") |
| .value_name("args") |
| .help("arguments to pass to the test runner"), |
| ) |
| .arg( |
| Arg::with_name(NOCAPTURE) |
| .long(NOCAPTURE) |
| .help("Display all output when running tests."), |
| ) |
| .arg( |
| Arg::with_name(RUN_WITH_TILES) |
| .long(RUN_WITH_TILES) |
| .help("Use tiles to run binary."), |
| ) |
| .arg( |
| Arg::with_name(RUN_WITH_RUN) |
| .long(RUN_WITH_RUN) |
| .help("Use run to run binary."), |
| ) |
| .arg( |
| Arg::with_name(RUN_WITH_SESSIONCTL) |
| .long(RUN_WITH_SESSIONCTL) |
| .help("Use sessionctl to run binary."), |
| ) |
| .arg( |
| Arg::with_name(STORY_NAME) |
| .long(STORY_NAME) |
| .short("n") |
| .value_name(STORY_NAME) |
| .help("Name of story to pass to sessionctl"), |
| ) |
| .arg( |
| Arg::with_name(MOD_NAME) |
| .long(MOD_NAME) |
| .short("m") |
| .value_name(MOD_NAME) |
| .help("Name of mod to pass to sessionctl"), |
| ) |
| .arg( |
| Arg::with_name("run_on_target_params") |
| .index(1) |
| .multiple(true), |
| ) |
| .setting(AppSettings::Hidden), |
| ) |
| .subcommand( |
| SubCommand::with_name("pkg-config") |
| .about("Run pkg-config for the cross compilation environment") |
| .arg(Arg::with_name("pkgconfig_param").index(1).multiple(true)), |
| ) |
| .subcommand( |
| SubCommand::with_name("configure") |
| .about("Run a configure script for the cross compilation environment") |
| .arg(Arg::with_name("configure_param").index(1).multiple(true)) |
| .arg( |
| Arg::with_name("no-host") |
| .long("no-host") |
| .help("Don't pass --host to configure"), |
| ), |
| ) |
| .subcommand(SubCommand::with_name(WRITE_CONFIG).about( |
| "Write a .cargo/config file to allow cargo to operate correctly for Fuchsia", |
| )) |
| .get_matches(); |
| |
| let verbose = matches.is_present("verbose"); |
| let disable_cross = matches.is_present(DISABLE_CROSS_ENV); |
| |
| let fuchsia_config = FuchsiaConfig::new()?; |
| if verbose { |
| println!("fuchsia_config = {:#?}", fuchsia_config); |
| } |
| |
| let target_options = TargetOptions::new(&fuchsia_config, matches.value_of("device-name")); |
| |
| let run_cargo_options = RunCargoOptions { |
| verbose, |
| release: false, |
| nocapture: false, |
| run_mode: RunMode::Normal, |
| story_name: None, |
| mod_name: None, |
| disable_cross, |
| manifest_path: None, |
| }; |
| |
| if verbose { |
| println!("target_options = {:#?}", target_options); |
| } |
| |
| if let Some(autotest_matches) = matches.subcommand_matches("autotest") { |
| let manifest_path = convert_manifest_path(&autotest_matches.value_of(MANIFEST_PATH)); |
| return autotest( |
| &run_cargo_options |
| .release(autotest_matches.is_present(RELEASE)) |
| .manifest_path(manifest_path) |
| .nocapture(autotest_matches.is_present(NOCAPTURE)), |
| &target_options, |
| ); |
| } |
| |
| if let Some(test_matches) = matches.subcommand_matches(TEST) { |
| let test_params = test_matches |
| .values_of("test_params") |
| .map(|x| x.collect()) |
| .unwrap_or_else(|| vec![]); |
| let test_target = test_matches.value_of(TEST_TARGET_NAME).unwrap_or(""); |
| let test_args = test_matches.value_of("test_args"); |
| let manifest_path = convert_manifest_path(&test_matches.value_of(MANIFEST_PATH)); |
| return run_tests( |
| &run_cargo_options |
| .release(test_matches.is_present(RELEASE)) |
| .manifest_path(manifest_path) |
| .nocapture(test_matches.is_present(NOCAPTURE)), |
| false, |
| &target_options, |
| test_target, |
| &test_params, |
| test_args, |
| ); |
| } |
| |
| if let Some(build_matches) = matches.subcommand_matches("build") { |
| let mut params = vec![]; |
| if let Some(example) = build_matches.value_of("example") { |
| params.push("--example"); |
| params.push(example); |
| } |
| |
| if build_matches.is_present("examples") { |
| params.push("--examples"); |
| } |
| |
| let manifest_path = convert_manifest_path(&build_matches.value_of(MANIFEST_PATH)); |
| build_binary( |
| &run_cargo_options |
| .release(build_matches.is_present(RELEASE)) |
| .manifest_path(manifest_path), |
| &target_options, |
| ¶ms, |
| )?; |
| return Ok(()); |
| } |
| |
| if let Some(check_matches) = matches.subcommand_matches(CHECK) { |
| let mut params = vec![]; |
| if let Some(example) = check_matches.value_of(EXAMPLE) { |
| params.push("--example"); |
| params.push(example); |
| } |
| |
| if check_matches.is_present(EXAMPLES) { |
| params.push("--examples"); |
| } |
| |
| let manifest_path = convert_manifest_path(&check_matches.value_of(MANIFEST_PATH)); |
| check_binary( |
| &run_cargo_options |
| .release(check_matches.is_present(RELEASE)) |
| .manifest_path(manifest_path), |
| &target_options, |
| ¶ms, |
| )?; |
| return Ok(()); |
| } |
| |
| if let Some(run_matches) = matches.subcommand_matches(RUN) { |
| let mut params = vec![]; |
| if let Some(example) = run_matches.value_of("example") { |
| params.push("--example"); |
| params.push(example); |
| } |
| |
| let manifest_path = convert_manifest_path(&run_matches.value_of(MANIFEST_PATH)); |
| let run_mode = run_switches_to_mode( |
| run_matches.is_present(RUN_WITH_TILES), |
| run_matches.is_present(RUN_WITH_RUN), |
| run_matches.is_present(RUN_WITH_SESSIONCTL), |
| ); |
| return run_binary( |
| &run_cargo_options |
| .release(run_matches.is_present(RELEASE)) |
| .run_mode(run_mode) |
| .story_name(&run_matches.value_of(STORY_NAME)) |
| .mod_name(&run_matches.value_of(MOD_NAME)) |
| .manifest_path(manifest_path), |
| &target_options, |
| ¶ms, |
| ); |
| } |
| |
| if let Some(load_driver_matches) = matches.subcommand_matches("load-driver") { |
| return load_driver( |
| &run_cargo_options.release(load_driver_matches.is_present(RELEASE)), |
| &fuchsia_config, |
| &target_options, |
| ); |
| } |
| |
| if let Some(build_test_matches) = matches.subcommand_matches("build-tests") { |
| let test_target = build_test_matches.value_of(TEST_TARGET_NAME).unwrap_or(""); |
| let manifest_path = convert_manifest_path(&build_test_matches.value_of(MANIFEST_PATH)); |
| build_tests( |
| &run_cargo_options |
| .release(build_test_matches.is_present(RELEASE)) |
| .manifest_path(manifest_path), |
| &target_options, |
| test_target, |
| )?; |
| return Ok(()); |
| } |
| |
| if let Some(doc_matches) = matches.subcommand_matches(DOC) { |
| let manifest_path = convert_manifest_path(&doc_matches.value_of(MANIFEST_PATH)); |
| return build_doc( |
| &run_cargo_options |
| .release(doc_matches.is_present(RELEASE)) |
| .manifest_path(manifest_path), |
| &target_options, |
| doc_matches.is_present(DOC_NO_DEPS), |
| doc_matches.is_present(DOC_OPEN), |
| ); |
| } |
| |
| if matches.subcommand_matches("list-devices").is_some() { |
| return netls(verbose); |
| } |
| |
| if let Some(start_matches) = matches.subcommand_matches(START) { |
| let fx_run_params = start_matches |
| .values_of(FX_RUN_PARAMS) |
| .map(|x| x.collect()) |
| .unwrap_or_else(|| vec![]); |
| |
| return start_emulator( |
| &StartEmulatorOptions { |
| verbose: verbose, |
| with_graphics: start_matches.is_present(GRAPHICS), |
| with_networking: !start_matches.is_present(NO_NET), |
| disable_virtcon: start_matches.is_present(DISABLE_VIRTCON), |
| }, |
| &fx_run_params, |
| ); |
| } |
| |
| if matches.subcommand_matches("stop").is_some() { |
| return stop_emulator(); |
| } |
| |
| if matches.subcommand_matches("enable-networking").is_some() { |
| return enable_networking(); |
| } |
| |
| if let Some(restart_matches) = matches.subcommand_matches(RESTART) { |
| stop_emulator()?; |
| |
| let fx_run_params = restart_matches |
| .values_of(FX_RUN_PARAMS) |
| .map(|x| x.collect()) |
| .unwrap_or_else(|| vec![]); |
| |
| return start_emulator( |
| &StartEmulatorOptions { |
| verbose: verbose, |
| with_graphics: restart_matches.is_present(GRAPHICS), |
| with_networking: !restart_matches.is_present(NO_NET), |
| disable_virtcon: restart_matches.is_present(DISABLE_VIRTCON), |
| }, |
| &fx_run_params, |
| ); |
| } |
| |
| if matches.subcommand_matches("ssh").is_some() { |
| return ssh(verbose, &fuchsia_config, &target_options, ""); |
| } |
| |
| if let Some(cargo_matches) = matches.subcommand_matches("cargo") { |
| let subcommand = cargo_matches.value_of(SUBCOMMAND).unwrap(); |
| let cargo_params = cargo_matches |
| .values_of("cargo_params") |
| .map(|x| x.collect()) |
| .unwrap_or_else(|| vec![]); |
| return run_cargo( |
| &RunCargoOptions { |
| verbose, |
| release: false, |
| nocapture: false, |
| run_mode: RunMode::Normal, |
| story_name: None, |
| mod_name: None, |
| disable_cross: disable_cross, |
| manifest_path: None, |
| }, |
| subcommand, |
| &cargo_params, |
| &target_options, |
| None, |
| None, |
| ); |
| } |
| |
| 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_else(|| vec![]); |
| let test_args = run_on_target_matches.value_of("test_args"); |
| let (program, args) = run_params.split_first().unwrap(); |
| let run_mode = run_switches_to_mode( |
| run_on_target_matches.is_present(RUN_WITH_TILES), |
| run_on_target_matches.is_present(RUN_WITH_RUN), |
| run_on_target_matches.is_present(RUN_WITH_SESSIONCTL), |
| ); |
| return run_program_on_target( |
| program, |
| verbose, |
| run_on_target_matches.is_present(NOCAPTURE), |
| &fuchsia_config, |
| &target_options, |
| run_mode, |
| run_on_target_matches |
| .value_of(STORY_NAME) |
| .unwrap_or(&random_story_name()), |
| run_on_target_matches |
| .value_of(MOD_NAME) |
| .unwrap_or(DEFAULT_MOD_NAME), |
| args, |
| test_args, |
| ); |
| } |
| |
| if let Some(pkg_matches) = matches.subcommand_matches("pkg-config") { |
| let pkg_params = pkg_matches |
| .values_of("pkgconfig_param") |
| .map(|x| x.collect()) |
| .unwrap_or_else(|| vec![]); |
| let exit_code = run_pkg_config(verbose, &pkg_params, &target_options)?; |
| if exit_code != 0 { |
| ::std::process::exit(exit_code); |
| } |
| return Ok(()); |
| } |
| |
| if let Some(configure_matches) = matches.subcommand_matches("configure") { |
| let configure_params = configure_matches |
| .values_of("configure_param") |
| .map(|x| x.collect()) |
| .unwrap_or_else(|| vec![]); |
| run_configure( |
| verbose, |
| !configure_matches.is_present("no-host"), |
| &configure_params, |
| &target_options, |
| )?; |
| return Ok(()); |
| } |
| |
| if let Some(_write_config_matches) = matches.subcommand_matches(WRITE_CONFIG) { |
| return write_config(&run_cargo_options, &target_options); |
| } |
| |
| Ok(()) |
| } |