| use crate::{ |
| autotest, build_binary, build_doc, |
| build_rustc::build_rustc, |
| check_binary, |
| cross::run_pkg_config, |
| device::{netls, shell, start_emulator, stop_emulator, StartEmulatorOptions}, |
| enable_networking, format_project, |
| linking::extract_linkage_information, |
| manifest::load_manifest, |
| package::is_v2_sandbox_file, |
| run_binary, run_cargo, run_configure, run_program_on_target, run_switches_to_mode, run_tests, |
| sdk::TargetOptions, |
| write_config, FuchsiaConfig, RunCargoOptions, RunMode, |
| }; |
| use anyhow::{bail, Error, Context as _}; |
| use std::path::PathBuf; |
| use structopt::StructOpt; |
| |
| #[derive(Debug, StructOpt)] |
| enum FargoCommand { |
| /// Auto build and test in Fuchsia device or emulator |
| Autotest(Autotest), |
| /// Build binary targeting Fuchsia device or emulator |
| Build(Build), |
| /// Build rustc targeting Fuchsia |
| BuildRustc(BuildRustc), |
| /// Run a cargo command for Fuchsia. Use -- to indicate that all following arguments should be |
| /// passed to cargo. |
| Cargo(Cargo), |
| /// Check binary targeting Fuchsia device or emulator |
| Check(Build), |
| /// Run a configure script for the cross compilation environment |
| Configure(Configure), |
| /// Build a package's documentation |
| Doc(Doc), |
| /// Enable networking for a running emulator |
| EnableNetworking, |
| /// Extract linkage information |
| ExtractLinkage, |
| /// Run cargo fmt using the Fuchsia toolchain |
| Fmt, |
| /// List visible Fuchsia devices |
| ListDevices, |
| /// Run pkg-config for the cross compilation environment |
| PkgConfig(PkgConfig), |
| /// Stop all Fuchsia emulators and start a new one |
| Restart(Start), |
| /// Act as as custom runner for Cargo targeting a Fuchsia device |
| RunOnTarget(RunOnTarget), |
| /// Run binary on Fuchsia device or emulator |
| Run(Run), |
| /// Open a shell on Fuchsia device or emulator |
| Shell, |
| /// Start a Fuchsia emulator |
| Start(Start), |
| /// Stop all Fuchsia emulators |
| Stop, |
| /// Run unit tests on Fuchsia device or emulator |
| Test(Test), |
| /// Write a .cargo/config file to allow cargo to operate correctly for Fuchsia |
| WriteConfig, |
| } |
| |
| #[derive(Debug, StructOpt)] |
| struct Autotest { |
| /// Build artifacts in release mode, with optimizations. |
| #[structopt(long)] |
| release: bool, |
| |
| /// Path to sandbox file to use when running. |
| #[structopt(long, alias = "cmx-path")] |
| sandbox_file_path: Option<PathBuf>, |
| |
| /// Directory of app as it appears in the manifest file. |
| #[structopt(long, default_value = "bin")] |
| app_dir: String, |
| |
| /// Name of app as it appears in the manifest file. |
| #[structopt(long, default_value = "app")] |
| app_name: String, |
| |
| /// Display all output when running tests. |
| #[structopt(long)] |
| nocapture: bool, |
| } |
| |
| #[derive(Debug, StructOpt)] |
| struct Build { |
| /// Build artifacts in release mode, with optimizations. |
| #[structopt(long)] |
| release: bool, |
| |
| /// Build artifacts in release mode, with optimizations. |
| #[structopt(short = "p", long)] |
| package: Option<String>, |
| |
| /// Name of the bin target to run. |
| #[structopt(long)] |
| bin: Option<String>, |
| |
| /// Build only the specified test target. |
| #[structopt(long)] |
| test: Option<String>, |
| |
| /// Build all the tests. |
| #[structopt(long)] |
| tests: bool, |
| |
| /// Build a specific example from the examples/ dir. |
| #[structopt(long)] |
| example: Option<String>, |
| |
| /// Build all the examples. |
| #[structopt(long)] |
| examples: bool, |
| } |
| |
| #[derive(Debug, StructOpt)] |
| struct BuildRustc { |
| #[structopt(long)] |
| rust_root: PathBuf, |
| } |
| |
| #[derive(Debug, StructOpt)] |
| struct Cargo { |
| /// Pass options only needed for linking to cargo via RUSTFLAGS. |
| #[structopt(long)] |
| link: bool, |
| subcommand: String, |
| cargo_params: Vec<String>, |
| } |
| |
| #[derive(Debug, StructOpt)] |
| struct Configure { |
| /// Don't pass --host to configure. |
| #[structopt(long)] |
| no_host: bool, |
| |
| configure_params: Vec<String>, |
| } |
| |
| #[derive(Debug, StructOpt)] |
| struct Doc { |
| /// Opens the docs in a browser after the operation |
| #[structopt(long)] |
| open: bool, |
| |
| /// Don't build documentation for dependencies |
| #[structopt(long)] |
| no_deps: bool, |
| } |
| |
| #[derive(Debug, StructOpt)] |
| #[allow(unused)] |
| struct MakePackage { |
| /// Path to the binary to package |
| #[structopt(long)] |
| binary_path: PathBuf, |
| |
| /// Path to sandbox file to use when running. |
| #[structopt(long)] |
| cmx_path: Option<PathBuf>, |
| |
| /// Directory of app as it appears in the manifest file. |
| #[structopt(long, default_value = "bin")] |
| app_dir: String, |
| |
| /// Name of app as it appears in the manifest file. |
| #[structopt(long, default_value = "app")] |
| app_name: String, |
| } |
| |
| #[derive(Debug, StructOpt)] |
| struct PkgConfig { |
| pkg_config_params: Vec<String>, |
| } |
| |
| #[derive(Debug, StructOpt)] |
| struct Run { |
| /// Build artifacts in release mode, with optimizations. |
| #[structopt(long)] |
| release: bool, |
| |
| /// Use run to run the binary. |
| #[structopt(long)] |
| #[allow(unused)] |
| run_with_run: bool, |
| |
| /// Use tiles_ctl add to run the binary. |
| #[structopt(long)] |
| run_with_tiles: bool, |
| |
| /// Use tiles_ctl add --flatland to run the binary. |
| #[structopt(long)] |
| run_with_flatland_tiles: bool, |
| |
| /// Use session_control add to run the binary. |
| #[structopt(long)] |
| run_with_session_control: bool, |
| |
| /// Use ffx component run to run the binary. |
| #[structopt(long)] |
| run_with_ffx_component: bool, |
| |
| /// Use ffx test run to run the binary. |
| #[structopt(long)] |
| run_with_ffx_test: bool, |
| |
| /// Use Use sessionctl to run the binary. |
| #[structopt(short = "K", long)] |
| kill_all: bool, |
| |
| /// Directory of app as it appears in the manifest file. |
| #[structopt(long, default_value = "bin")] |
| app_dir: String, |
| |
| /// Name of app as it appears in the manifest file. |
| #[structopt(long, default_value = "app")] |
| app_name: String, |
| |
| /// Path to sandbox file to use when running. |
| #[structopt(long, alias = "cmx-path")] |
| sandbox_file_path: Option<PathBuf>, |
| |
| /// Package to build. |
| #[structopt(short, long)] |
| package: Option<String>, |
| |
| /// Name of the bin target to run. |
| #[structopt(long)] |
| bin: Option<String>, |
| |
| /// Run a specific example from the examples/ dir. |
| #[structopt(long)] |
| example: Option<String>, |
| } |
| |
| #[derive(Debug, StructOpt)] |
| struct RunOnTarget { |
| /// Path to the binary to run. |
| binary_to_run: Option<String>, |
| |
| /// Use run to run the binary. |
| // Allow multiple occurences since fargo writes out a .cargo/config runner |
| // command that includes "--run-with-run", but some fargo commands also |
| // append their own "--run-with-run" when calling fargo recursively. |
| #[structopt(long, multiple = true)] |
| #[allow(unused)] |
| run_with_run: bool, |
| |
| /// Use tiles_ctl add to run the binary. |
| #[structopt(long)] |
| run_with_tiles: bool, |
| |
| /// Use tiles_ctl add --flatland to run the binary. |
| #[structopt(long)] |
| run_with_flatland_tiles: bool, |
| |
| /// Use Use sessionctl to run the binary. |
| #[structopt(long)] |
| run_with_session_control: bool, |
| |
| /// Use ffx component run to run the binary. |
| #[structopt(long)] |
| run_with_ffx_component: bool, |
| |
| /// Use ffx test run to run the binary. |
| #[structopt(long)] |
| run_with_ffx_test: bool, |
| |
| /// Directory of app as it appears in the manifest file. |
| #[structopt(long, default_value = "bin")] |
| app_dir: String, |
| |
| /// Name of app as it appears in the manifest file. |
| #[structopt(long, default_value = "app")] |
| app_name: String, |
| |
| /// Path to sandbox file to use when running. |
| #[structopt(long, alias = "cmx-path")] |
| sandbox_file_path: Option<PathBuf>, |
| |
| /// Additional arguments for the binary. |
| runner_args: Vec<String>, |
| |
| /// Display all output when running tests. |
| // Allow multiple occurences since fargo writes out a .cargo/config runner |
| // command that includes "--nocapture", but some tools like VSCode additionally |
| // append their own "--nocapture" when running. |
| #[structopt(long, multiple = true)] |
| nocapture: bool, |
| } |
| |
| #[derive(Debug, StructOpt)] |
| struct Start { |
| /// Start aemu instead of qemu |
| #[structopt(short = "a")] |
| aemu: bool, |
| |
| /// Start a simulator with graphics enabled. |
| #[structopt(short = "g")] |
| graphics: bool, |
| |
| /// Start a simulator with acceleration (for qemu, Linux only) |
| #[structopt(short = "k")] |
| with_acceleration: bool, |
| |
| /// Don't set up networking |
| #[structopt(long)] |
| no_net: bool, |
| |
| /// Do not launch the virtual console service if this option is present |
| #[structopt(long)] |
| disable_virtcon: bool, |
| start_params: Vec<String>, |
| } |
| |
| #[derive(Debug, StructOpt)] |
| struct Test { |
| /// Build artifacts in release mode, with optimizations. |
| #[structopt(long)] |
| release: bool, |
| |
| /// Use run to run the test. |
| #[structopt(long)] |
| #[allow(unused)] |
| run_with_run: bool, |
| |
| #[structopt(long)] |
| /// Test only the specified test target. |
| test: Option<String>, |
| |
| #[structopt(long)] |
| /// Test only this package's library unit tests. |
| lib: bool, |
| |
| /// Display all output when running tests. |
| // Allow multiple occurences since fargo writes out a .cargo/config runner |
| // command that includes "--nocapture", but some tools like VSCode additionally |
| // append their own "--nocapture" when running. |
| #[structopt(long, multiple = true)] |
| nocapture: bool, |
| |
| /// Name of the bin target to test. |
| #[structopt(long)] |
| bin: Option<String>, |
| |
| /// Test all binaries. |
| #[structopt(long)] |
| bins: bool, |
| |
| /// RunTest a specific example from the examples/ dir. |
| #[structopt(long)] |
| example: Option<String>, |
| |
| /// Test only this library's documentation. |
| #[structopt(long)] |
| doc: bool, |
| |
| /// Compile, but don't run tests. |
| #[structopt(long)] |
| no_run: bool, |
| |
| /// Arguments to pass to the test runner. |
| #[structopt(long)] |
| test_args: Option<String>, |
| |
| /// Path to sandbox file to use when running. |
| #[structopt(long, alias = "cmx-path")] |
| sandbox_file_path: Option<PathBuf>, |
| |
| /// Directory of app as it appears in the manifest file. |
| #[structopt(long, default_value = "bin")] |
| app_dir: String, |
| |
| /// Name of app as it appears in the manifest file. |
| #[structopt(long, default_value = "app")] |
| app_name: String, |
| |
| /// Package to build. |
| #[structopt(short, long)] |
| package: Option<String>, |
| |
| test_params: Vec<String>, |
| } |
| |
| #[derive(Debug, StructOpt)] |
| #[structopt(name = "fargo", about = "Fargo is a prototype Fuchsia-specific wrapper around Cargo.")] |
| struct FargoOption { |
| /// Print verbose output while performing commands |
| #[structopt(short, long)] |
| verbose: bool, |
| |
| /// Disable the setting of CC, AR and such environmental variables |
| #[structopt(long)] |
| disable_cross_env: bool, |
| |
| /// Name of device to target, needed if there are multiple devices visible on the network |
| #[structopt(short = "N", long)] |
| device_name: Option<String>, |
| |
| /// Path to Cargo.toml |
| #[structopt(long, global = true)] |
| manifest_path: Option<PathBuf>, |
| |
| run_target_first: Option<String>, |
| |
| #[structopt(subcommand)] |
| command: FargoCommand, |
| } |
| |
| fn to_opt_str(value: &Option<String>) -> Option<&str> { |
| value.as_ref().map(String::as_str) |
| } |
| |
| fn build_params(build_opts: &Build) -> Vec<&str> { |
| let mut params = vec![]; |
| if let Some(package) = build_opts.package.as_ref() { |
| params.push("--package"); |
| params.push(package); |
| } |
| if let Some(bin) = build_opts.bin.as_ref() { |
| params.push("--bin"); |
| params.push(bin); |
| } |
| if let Some(test) = build_opts.test.as_ref() { |
| params.push("--test"); |
| params.push(&test); |
| } |
| if build_opts.tests { |
| params.push("--tests"); |
| } |
| if let Some(example) = build_opts.example.as_ref() { |
| params.push("--example"); |
| params.push(example); |
| } |
| if build_opts.examples { |
| params.push("--examples"); |
| } |
| params |
| } |
| |
| #[doc(hidden)] |
| pub fn run() -> Result<(), Error> { |
| let opt = FargoOption::from_args(); |
| let fargo_manifest = load_manifest(&opt.manifest_path)?; |
| let verbose = opt.verbose; |
| let fuchsia_config = FuchsiaConfig::new_from_fx_exec()?; |
| if verbose { |
| println!("fuchsia_config = {:#?}", fuchsia_config); |
| } |
| |
| let target_options = |
| TargetOptions::new(&fuchsia_config, opt.device_name.as_ref().map(String::as_str)); |
| let run_cargo_options = RunCargoOptions { |
| verbose, |
| fargo_manifest: fargo_manifest.clone(), |
| manifest_path: opt.manifest_path.clone(), |
| ..RunCargoOptions::default() |
| }; |
| |
| match opt.command { |
| FargoCommand::Autotest(autotest_opts) => { |
| return autotest( |
| &run_cargo_options |
| .release(autotest_opts.release) |
| .linking(true) |
| .manifest_path(opt.manifest_path) |
| .sandbox_file_path(autotest_opts.sandbox_file_path) |
| .app_dir(&Some(&autotest_opts.app_dir)) |
| .app_name(&Some(&autotest_opts.app_name)) |
| .nocapture(autotest_opts.nocapture), |
| &target_options, |
| ); |
| } |
| |
| FargoCommand::Build(build_opts) => { |
| let params = build_params(&build_opts); |
| build_binary( |
| &run_cargo_options |
| .release(build_opts.release) |
| .manifest_path(opt.manifest_path) |
| .linking(true), |
| &target_options, |
| ¶ms, |
| )?; |
| return Ok(()); |
| } |
| |
| FargoCommand::BuildRustc(build_rustc_opts) => { |
| return build_rustc(&build_rustc_opts.rust_root, &target_options); |
| } |
| |
| FargoCommand::Cargo(cargo_opts) => { |
| let cargo_params: Vec<&str> = |
| cargo_opts.cargo_params.iter().map(String::as_str).collect(); |
| return run_cargo( |
| &RunCargoOptions { |
| fargo_manifest, |
| verbose, |
| release: false, |
| linking: cargo_opts.link, |
| nocapture: false, |
| run_mode: RunMode::Run, |
| story_name: None, |
| mod_name: None, |
| disable_cross: opt.disable_cross_env, |
| manifest_path: None, |
| sandbox_file_path: None, |
| app_dir: None, |
| app_name: None, |
| }, |
| cargo_opts.subcommand.as_ref(), |
| &cargo_params, |
| &target_options, |
| None, |
| None, |
| ); |
| } |
| |
| FargoCommand::Check(build_opts) => { |
| let params = build_params(&build_opts); |
| check_binary( |
| &run_cargo_options.release(build_opts.release).manifest_path(opt.manifest_path), |
| &target_options, |
| ¶ms, |
| )?; |
| return Ok(()); |
| } |
| |
| FargoCommand::Configure(config_opts) => { |
| let configure_params: Vec<&str> = |
| config_opts.configure_params.iter().map(String::as_str).collect(); |
| run_configure(opt.verbose, config_opts.no_host, &configure_params, &target_options)?; |
| return Ok(()); |
| } |
| |
| FargoCommand::Doc(doc_opts) => { |
| return build_doc( |
| &run_cargo_options.manifest_path(opt.manifest_path), |
| &target_options, |
| doc_opts.no_deps, |
| doc_opts.open, |
| ); |
| } |
| |
| FargoCommand::EnableNetworking => { |
| return enable_networking(); |
| } |
| |
| FargoCommand::ExtractLinkage => { |
| return extract_linkage_information(); |
| } |
| |
| FargoCommand::Fmt => { |
| format_project(opt.manifest_path)?; |
| return Ok(()); |
| } |
| |
| FargoCommand::ListDevices => { |
| return netls(opt.verbose); |
| } |
| |
| FargoCommand::PkgConfig(pkg_config_opts) => { |
| let pkg_params: Vec<&str> = |
| pkg_config_opts.pkg_config_params.iter().map(String::as_str).collect(); |
| let exit_code = run_pkg_config(verbose, &pkg_params, &target_options)?; |
| if exit_code != 0 { |
| ::std::process::exit(exit_code); |
| } |
| return Ok(()); |
| } |
| |
| FargoCommand::Restart(start_opts) => { |
| stop_emulator()?; |
| |
| let fx_run_params: Vec<&str> = |
| start_opts.start_params.iter().map(String::as_str).collect(); |
| |
| return start_emulator( |
| &StartEmulatorOptions { |
| verbose: verbose, |
| aemu: start_opts.aemu, |
| with_graphics: start_opts.graphics, |
| with_acceleration: start_opts.with_acceleration, |
| with_networking: !start_opts.no_net, |
| disable_virtcon: start_opts.disable_virtcon, |
| }, |
| &fx_run_params, |
| ); |
| } |
| |
| FargoCommand::Run(run) => { |
| let mut params = vec![]; |
| |
| if run.kill_all { |
| shell(opt.verbose, &target_options, "killall *fargo.cmx").unwrap_or_default(); |
| } |
| |
| if let Some(package) = run.package.as_ref() { |
| params.push("--package"); |
| params.push(package); |
| } |
| if let Some(bin) = run.bin.as_ref() { |
| params.push("--bin"); |
| params.push(bin); |
| } |
| |
| if let Some(example) = run.example.as_ref() { |
| params.push("--example"); |
| params.push(example); |
| } |
| |
| let run_mode = run_switches_to_mode( |
| run.run_with_ffx_component, |
| run.run_with_ffx_test, |
| run.run_with_tiles, |
| run.run_with_flatland_tiles, |
| run.run_with_session_control, |
| ); |
| return run_binary( |
| &run_cargo_options |
| .release(run.release) |
| .run_mode(run_mode) |
| .linking(true) |
| .app_dir(&Some(&run.app_dir)) |
| .app_name(&Some(&run.app_name)) |
| .manifest_path(opt.manifest_path) |
| .sandbox_file_path(run.sandbox_file_path), |
| &target_options, |
| ¶ms, |
| ); |
| } |
| |
| FargoCommand::RunOnTarget(run_on_target) => { |
| let run_cargo_options = |
| run_cargo_options.sandbox_file_path(run_on_target.sandbox_file_path); |
| let run_params: Vec<&str> = run_on_target.runner_args.iter().map(|s| &**s).collect(); |
| let run_mode = run_switches_to_mode( |
| run_on_target.run_with_ffx_component, |
| run_on_target.run_with_ffx_test, |
| run_on_target.run_with_tiles, |
| run_on_target.run_with_flatland_tiles, |
| run_on_target.run_with_session_control, |
| ); |
| |
| let binary_to_run = if let Some(binary_to_run) = run_on_target.binary_to_run { |
| binary_to_run |
| } else { |
| if let Some(binary_to_run) = opt.run_target_first { |
| binary_to_run |
| } else { |
| bail!("The fargo test runner was not passed a binary to run") |
| } |
| }; |
| |
| return run_program_on_target( |
| &binary_to_run, |
| verbose, |
| run_on_target.nocapture, |
| &target_options, |
| run_mode, |
| &run_cargo_options, |
| &run_on_target.app_dir, |
| &run_on_target.app_name, |
| &run_params, |
| None, |
| ); |
| } |
| |
| FargoCommand::Shell => { |
| return shell(opt.verbose, &target_options, ""); |
| } |
| |
| FargoCommand::Start(start_opts) => { |
| let fx_run_params: Vec<&str> = |
| start_opts.start_params.iter().map(String::as_str).collect(); |
| |
| return start_emulator( |
| &StartEmulatorOptions { |
| verbose: verbose, |
| aemu: start_opts.aemu, |
| with_graphics: start_opts.graphics, |
| with_acceleration: start_opts.with_acceleration, |
| with_networking: !start_opts.no_net, |
| disable_virtcon: start_opts.disable_virtcon, |
| }, |
| &fx_run_params, |
| ); |
| } |
| |
| FargoCommand::Stop => { |
| return stop_emulator(); |
| } |
| |
| FargoCommand::Test(test_opts) => { |
| let mut params = vec![]; |
| if let Some(package) = test_opts.package.as_ref() { |
| params.push("--package"); |
| params.push(package); |
| } |
| |
| if let Some(bin) = test_opts.bin.as_ref() { |
| params.push("--bin"); |
| params.push(bin); |
| } |
| |
| if let Some(example) = test_opts.example.as_ref() { |
| params.push("--example"); |
| params.push(example); |
| } |
| |
| let test_params: Vec<&str> = test_opts.test_params.iter().map(String::as_str).collect(); |
| |
| params.extend(test_params); |
| |
| if let Some(test) = test_opts.test.as_ref() { |
| params.push("--test"); |
| params.push(test); |
| } |
| |
| let sandbox_file_path = match test_opts.sandbox_file_path { |
| Some(sandbox_file_path) => Some(sandbox_file_path), |
| None => { |
| let cwd: PathBuf = if let Some(actual_manifest_path) = |
| opt.manifest_path.as_ref() |
| { |
| actual_manifest_path.parent().expect("manifest_path parent").to_path_buf() |
| } else { |
| std::fs::canonicalize(std::env::current_dir()?) |
| .context("autotest: canonicalize working directory")? |
| }; |
| let test_cmx_file = cwd.join("meta").join("test.cmx"); |
| if test_cmx_file.exists() { |
| Some(test_cmx_file) |
| } else { |
| None |
| } |
| } |
| }; |
| |
| let test_args = test_opts.test_args; |
| |
| let is_v2_component = if let Some(ref sandbox_file_path) = sandbox_file_path { |
| is_v2_sandbox_file(sandbox_file_path)? |
| } else { |
| // If no sandbox file is provided, default to running as a v1 component (.cmx), |
| // because the autogenerated sandbox file in package.rs is currently a v1 (.cmx) |
| // file. |
| false |
| }; |
| let run_mode = run_switches_to_mode(false, is_v2_component, false, false, false); |
| |
| return run_tests( |
| &run_cargo_options |
| .sandbox_file_path(sandbox_file_path) |
| .app_dir(&Some(&test_opts.app_dir)) |
| .app_name(&Some(&test_opts.app_name)) |
| .run_mode(run_mode) |
| .release(test_opts.release) |
| .linking(true) |
| .manifest_path(opt.manifest_path) |
| .nocapture(test_opts.nocapture), |
| test_opts.no_run, |
| test_opts.doc, |
| test_opts.lib, |
| test_opts.bins, |
| &target_options, |
| ¶ms, |
| to_opt_str(&test_args), |
| ); |
| } |
| |
| FargoCommand::WriteConfig => { |
| return write_config(&run_cargo_options, &target_options); |
| } |
| } |
| } |