blob: d2d05c1753e169ca7c4d683f023560723a1450ce [file] [log] [blame]
use crate::{
autotest, build_binary, build_doc,
build_rustc::build_rustc,
check_binary,
cross::run_pkg_config,
device::{netls, ssh, start_emulator, stop_emulator, StartEmulatorOptions},
enable_networking, format_project,
manifest::load_manifest,
random_story_name, run_binary, run_cargo, run_configure, run_program_on_target,
run_switches_to_mode, run_tests,
sdk::TargetOptions,
write_config, FuchsiaConfig, RunCargoOptions, RunMode, DEFAULT_MOD_NAME,
};
use failure::{bail, Error, ResultExt};
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,
/// 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
Ssh,
/// 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)]
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,
/// 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 {
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)]
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)]
run_with_run: bool,
/// Use tiles_ctl add to run the binary.
#[structopt(long)]
run_with_tiles: bool,
/// Use Use sessionctl to run the binary.
#[structopt(long)]
run_with_sessionctl: 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,
/// Name of story to pass to sessionctl.
#[structopt(short = "n", long)]
story_name: Option<String>,
/// Name of mod to pass to sessionctl.
#[structopt(short, long)]
mod_name: Option<String>,
/// Path to sandbox file to use when running.
#[structopt(long)]
cmx_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.
#[structopt(long)]
run_with_run: bool,
/// Use tiles_ctl add to run the binary.
#[structopt(long)]
run_with_tiles: bool,
/// Use Use sessionctl to run the binary.
#[structopt(long)]
run_with_sessionctl: 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,
/// Name of story to pass to sessionctl.
#[structopt(short = "n", long)]
story_name: Option<String>,
/// Name of mod to pass to sessionctl.
#[structopt(short, long)]
mod_name: Option<String>,
/// Path to sandbox file to use when running.
#[structopt(long)]
cmx_path: Option<PathBuf>,
/// Additional arguments for the binary.
runner_args: Vec<String>,
/// Display all output when running tests.
#[structopt(long)]
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)]
run_with_run: bool,
#[structopt(long)]
/// Test only the specified test target.
test: Option<String>,
/// Display all output when running tests.
#[structopt(long)]
nocapture: bool,
/// Name of the bin target to test.
#[structopt(long)]
bin: Option<String>,
/// 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 testing via run.
#[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,
/// 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)
.manifest_path(opt.manifest_path)
.cmx_path(autotest_opts.cmx_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),
&target_options,
&params,
)?;
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,
nocapture: false,
run_mode: RunMode::Run,
story_name: None,
mod_name: None,
disable_cross: opt.disable_cross_env,
manifest_path: None,
cmx_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,
&params,
)?;
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::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 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_tiles, run.run_with_sessionctl);
return run_binary(
&run_cargo_options
.release(run.release)
.run_mode(run_mode)
.story_name(&to_opt_str(&run.story_name))
.mod_name(&to_opt_str(&run.mod_name))
.app_dir(&Some(&run.app_dir))
.app_name(&Some(&run.app_name))
.manifest_path(opt.manifest_path)
.cmx_path(run.cmx_path),
&target_options,
&params,
);
}
FargoCommand::RunOnTarget(run_on_target) => {
let run_cargo_options = run_cargo_options.cmx_path(run_on_target.cmx_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_tiles,
run_on_target.run_with_sessionctl,
);
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,
&fuchsia_config,
&target_options,
run_mode,
&run_cargo_options,
&run_on_target.story_name.unwrap_or(random_story_name()),
&run_on_target.mod_name.unwrap_or(DEFAULT_MOD_NAME.to_string()),
&run_on_target.app_dir,
&run_on_target.app_name,
&run_params,
None,
);
}
FargoCommand::Ssh => {
return ssh(opt.verbose, &fuchsia_config, &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 cmx_path = if let Some(cmx_path) = test_opts.cmx_path {
Some(cmx_path)
} else {
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 run_mode = run_switches_to_mode(false, false);
return run_tests(
&run_cargo_options
.cmx_path(cmx_path)
.app_dir(&Some(&test_opts.app_dir))
.app_name(&Some(&test_opts.app_name))
.run_mode(run_mode)
.release(test_opts.release)
.manifest_path(opt.manifest_path)
.nocapture(test_opts.nocapture),
test_opts.no_run,
test_opts.doc,
&target_options,
&params,
to_opt_str(&test_args),
);
}
FargoCommand::WriteConfig => {
return write_config(&run_cargo_options, &target_options);
}
}
}