mirror of
https://github.com/rust-lang/cargo.git
synced 2025-10-01 11:30:39 +00:00
Add a cargo run
command
This currently only supports executing the `src/main.rs` convention, no other. Close #149
This commit is contained in:
parent
ec0895b460
commit
a64b9073f4
1
Makefile
1
Makefile
@ -22,6 +22,7 @@ BINS = cargo \
|
|||||||
cargo-verify-project \
|
cargo-verify-project \
|
||||||
cargo-git-checkout \
|
cargo-git-checkout \
|
||||||
cargo-test \
|
cargo-test \
|
||||||
|
cargo-run
|
||||||
|
|
||||||
SRC = $(shell find src -name '*.rs' -not -path 'src/bin*')
|
SRC = $(shell find src -name '*.rs' -not -path 'src/bin*')
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ fn execute(options: Options, shell: &mut MultiShell) -> CliResult<Option<()>> {
|
|||||||
"compile"
|
"compile"
|
||||||
};
|
};
|
||||||
|
|
||||||
let opts = CompileOptions {
|
let mut opts = CompileOptions {
|
||||||
update: options.update_remotes,
|
update: options.update_remotes,
|
||||||
env: env,
|
env: env,
|
||||||
shell: shell,
|
shell: shell,
|
||||||
@ -64,7 +64,7 @@ fn execute(options: Options, shell: &mut MultiShell) -> CliResult<Option<()>> {
|
|||||||
target: options.target.as_ref().map(|t| t.as_slice()),
|
target: options.target.as_ref().map(|t| t.as_slice()),
|
||||||
};
|
};
|
||||||
|
|
||||||
ops::compile(&root, opts).map(|_| None).map_err(|err| {
|
ops::compile(&root, &mut opts).map(|_| None).map_err(|err| {
|
||||||
CliError::from_boxed(err, 101)
|
CliError::from_boxed(err, 101)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
69
src/bin/cargo-run.rs
Normal file
69
src/bin/cargo-run.rs
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
#![crate_name = "cargo-run"]
|
||||||
|
#![feature(phase)]
|
||||||
|
|
||||||
|
#[phase(plugin, link)]
|
||||||
|
extern crate cargo;
|
||||||
|
extern crate serialize;
|
||||||
|
|
||||||
|
#[phase(plugin, link)]
|
||||||
|
extern crate hammer;
|
||||||
|
|
||||||
|
use std::os;
|
||||||
|
use std::io::process::ExitStatus;
|
||||||
|
|
||||||
|
use cargo::ops;
|
||||||
|
use cargo::{execute_main_without_stdin};
|
||||||
|
use cargo::core::{MultiShell};
|
||||||
|
use cargo::util::{CliResult, CliError};
|
||||||
|
use cargo::util::important_paths::find_project_manifest;
|
||||||
|
|
||||||
|
#[deriving(PartialEq,Clone,Decodable)]
|
||||||
|
struct Options {
|
||||||
|
manifest_path: Option<String>,
|
||||||
|
jobs: Option<uint>,
|
||||||
|
update: bool,
|
||||||
|
rest: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
hammer_config!(Options "Run the package's main executable", |c| {
|
||||||
|
c.short("jobs", 'j').short("update", 'u')
|
||||||
|
})
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
execute_main_without_stdin(execute);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn execute(options: Options, shell: &mut MultiShell) -> CliResult<Option<()>> {
|
||||||
|
let root = match options.manifest_path {
|
||||||
|
Some(path) => Path::new(path),
|
||||||
|
None => try!(find_project_manifest(&os::getcwd(), "Cargo.toml")
|
||||||
|
.map_err(|_| {
|
||||||
|
CliError::new("Could not find Cargo.toml in this \
|
||||||
|
directory or any parent directory",
|
||||||
|
102)
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut compile_opts = ops::CompileOptions {
|
||||||
|
update: options.update,
|
||||||
|
env: "compile",
|
||||||
|
shell: shell,
|
||||||
|
jobs: options.jobs,
|
||||||
|
target: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let err = try!(ops::run(&root, &mut compile_opts,
|
||||||
|
options.rest.as_slice()).map_err(|err| {
|
||||||
|
CliError::from_boxed(err, 101)
|
||||||
|
}));
|
||||||
|
match err {
|
||||||
|
None => Ok(None),
|
||||||
|
Some(err) => {
|
||||||
|
Err(match err.exit {
|
||||||
|
Some(ExitStatus(i)) => CliError::from_boxed(box err, i as uint),
|
||||||
|
_ => CliError::from_boxed(box err, 101),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -44,7 +44,7 @@ fn execute(options: Options, shell: &mut MultiShell) -> CliResult<Option<()>> {
|
|||||||
}))
|
}))
|
||||||
};
|
};
|
||||||
|
|
||||||
let compile_opts = ops::CompileOptions {
|
let mut compile_opts = ops::CompileOptions {
|
||||||
update: options.update,
|
update: options.update,
|
||||||
env: "test",
|
env: "test",
|
||||||
shell: shell,
|
shell: shell,
|
||||||
@ -52,7 +52,8 @@ fn execute(options: Options, shell: &mut MultiShell) -> CliResult<Option<()>> {
|
|||||||
target: None,
|
target: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let test_executables = try!(ops::compile(&root, compile_opts).map_err(|err| {
|
let test_executables = try!(ops::compile(&root,
|
||||||
|
&mut compile_opts).map_err(|err| {
|
||||||
CliError::from_boxed(err, 101)
|
CliError::from_boxed(err, 101)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -51,7 +51,9 @@ fn execute() {
|
|||||||
println!("Commands:");
|
println!("Commands:");
|
||||||
println!(" build # compile the current project");
|
println!(" build # compile the current project");
|
||||||
println!(" test # run the tests");
|
println!(" test # run the tests");
|
||||||
println!(" clean # remove the target directory\n");
|
println!(" clean # remove the target directory");
|
||||||
|
println!(" run # build and execute src/main.rs");
|
||||||
|
println!("");
|
||||||
|
|
||||||
|
|
||||||
let (_, options) = hammer::usage::<GlobalFlags>(false);
|
let (_, options) = hammer::usage::<GlobalFlags>(false);
|
||||||
|
@ -38,8 +38,9 @@ pub struct CompileOptions<'a> {
|
|||||||
pub target: Option<&'a str>,
|
pub target: Option<&'a str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compile(manifest_path: &Path, options: CompileOptions) -> CargoResult<Vec<String>> {
|
pub fn compile(manifest_path: &Path,
|
||||||
let CompileOptions { update, env, shell, jobs, target } = options;
|
options: &mut CompileOptions) -> CargoResult<Vec<String>> {
|
||||||
|
let CompileOptions { update, env, ref mut shell, jobs, target } = *options;
|
||||||
let target = target.map(|s| s.to_string());
|
let target = target.map(|s| s.to_string());
|
||||||
|
|
||||||
log!(4, "compile; manifest-path={}", manifest_path.display());
|
log!(4, "compile; manifest-path={}", manifest_path.display());
|
||||||
@ -60,7 +61,7 @@ pub fn compile(manifest_path: &Path, options: CompileOptions) -> CargoResult<Vec
|
|||||||
let source_ids = package.get_source_ids();
|
let source_ids = package.get_source_ids();
|
||||||
|
|
||||||
let (packages, resolve) = {
|
let (packages, resolve) = {
|
||||||
let mut config = try!(Config::new(shell, update, jobs, target.clone()));
|
let mut config = try!(Config::new(*shell, update, jobs, target.clone()));
|
||||||
|
|
||||||
let mut registry =
|
let mut registry =
|
||||||
try!(PackageRegistry::new(source_ids, override_ids, &mut config));
|
try!(PackageRegistry::new(source_ids, override_ids, &mut config));
|
||||||
@ -83,7 +84,7 @@ pub fn compile(manifest_path: &Path, options: CompileOptions) -> CargoResult<Vec
|
|||||||
target.get_profile().get_env() == env
|
target.get_profile().get_env() == env
|
||||||
}).collect::<Vec<&Target>>();
|
}).collect::<Vec<&Target>>();
|
||||||
|
|
||||||
let mut config = try!(Config::new(shell, update, jobs, target));
|
let mut config = try!(Config::new(*shell, update, jobs, target));
|
||||||
|
|
||||||
try!(ops::compile_targets(env.as_slice(), targets.as_slice(), &package,
|
try!(ops::compile_targets(env.as_slice(), targets.as_slice(), &package,
|
||||||
&PackageSet::new(packages.as_slice()), &resolve, &mut config));
|
&PackageSet::new(packages.as_slice()), &resolve, &mut config));
|
||||||
|
23
src/cargo/ops/cargo_run.rs
Normal file
23
src/cargo/ops/cargo_run.rs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
use std::os;
|
||||||
|
|
||||||
|
use ops;
|
||||||
|
use util::{CargoResult, human, process, ProcessError};
|
||||||
|
|
||||||
|
pub fn run(manifest_path: &Path,
|
||||||
|
options: &mut ops::CompileOptions,
|
||||||
|
args: &[String]) -> CargoResult<Option<ProcessError>> {
|
||||||
|
if !manifest_path.dir_path().join("src").join("main.rs").exists() {
|
||||||
|
return Err(human("`src/main.rs` must be present for `cargo run`"))
|
||||||
|
}
|
||||||
|
|
||||||
|
try!(ops::compile(manifest_path, options));
|
||||||
|
let exe = manifest_path.dir_path().join("target/main");
|
||||||
|
let exe = match exe.path_relative_from(&os::getcwd()) {
|
||||||
|
Some(path) => path,
|
||||||
|
None => exe,
|
||||||
|
};
|
||||||
|
let process = process(exe).args(args);
|
||||||
|
|
||||||
|
try!(options.shell.status("Running", process.to_string()));
|
||||||
|
Ok(process.exec().err())
|
||||||
|
}
|
@ -2,8 +2,10 @@ pub use self::cargo_clean::clean;
|
|||||||
pub use self::cargo_compile::{compile, CompileOptions};
|
pub use self::cargo_compile::{compile, CompileOptions};
|
||||||
pub use self::cargo_read_manifest::{read_manifest,read_package,read_packages};
|
pub use self::cargo_read_manifest::{read_manifest,read_package,read_packages};
|
||||||
pub use self::cargo_rustc::compile_targets;
|
pub use self::cargo_rustc::compile_targets;
|
||||||
|
pub use self::cargo_run::run;
|
||||||
|
|
||||||
mod cargo_clean;
|
mod cargo_clean;
|
||||||
mod cargo_compile;
|
mod cargo_compile;
|
||||||
mod cargo_read_manifest;
|
mod cargo_read_manifest;
|
||||||
mod cargo_rustc;
|
mod cargo_rustc;
|
||||||
|
mod cargo_run;
|
||||||
|
84
tests/test_cargo_run.rs
Normal file
84
tests/test_cargo_run.rs
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
use std::path;
|
||||||
|
|
||||||
|
use support::{project, execs};
|
||||||
|
use support::{COMPILING, RUNNING};
|
||||||
|
use hamcrest::{assert_that, existing_file};
|
||||||
|
|
||||||
|
fn setup() {
|
||||||
|
}
|
||||||
|
|
||||||
|
test!(simple {
|
||||||
|
let p = project("foo")
|
||||||
|
.file("Cargo.toml", r#"
|
||||||
|
[project]
|
||||||
|
name = "foo"
|
||||||
|
version = "0.0.1"
|
||||||
|
authors = []
|
||||||
|
"#)
|
||||||
|
.file("src/main.rs", r#"
|
||||||
|
fn main() { println!("hello"); }
|
||||||
|
"#);
|
||||||
|
|
||||||
|
assert_that(p.cargo_process("cargo-run"),
|
||||||
|
execs().with_status(0).with_stdout(format!("\
|
||||||
|
{compiling} foo v0.0.1 (file:{dir})
|
||||||
|
{running} `target{sep}main`
|
||||||
|
hello
|
||||||
|
",
|
||||||
|
compiling = COMPILING,
|
||||||
|
running = RUNNING,
|
||||||
|
dir = p.root().display(),
|
||||||
|
sep = path::SEP).as_slice()));
|
||||||
|
assert_that(&p.bin("main"), existing_file());
|
||||||
|
})
|
||||||
|
|
||||||
|
test!(simple_with_args {
|
||||||
|
let p = project("foo")
|
||||||
|
.file("Cargo.toml", r#"
|
||||||
|
[project]
|
||||||
|
name = "foo"
|
||||||
|
version = "0.0.1"
|
||||||
|
authors = []
|
||||||
|
"#)
|
||||||
|
.file("src/main.rs", r#"
|
||||||
|
fn main() {
|
||||||
|
assert_eq!(std::os::args().get(1).as_slice(), "hello");
|
||||||
|
assert_eq!(std::os::args().get(2).as_slice(), "world");
|
||||||
|
}
|
||||||
|
"#);
|
||||||
|
|
||||||
|
assert_that(p.cargo_process("cargo-run").arg("hello").arg("world"),
|
||||||
|
execs().with_status(0));
|
||||||
|
})
|
||||||
|
|
||||||
|
test!(exit_code {
|
||||||
|
let p = project("foo")
|
||||||
|
.file("Cargo.toml", r#"
|
||||||
|
[project]
|
||||||
|
name = "foo"
|
||||||
|
version = "0.0.1"
|
||||||
|
authors = []
|
||||||
|
"#)
|
||||||
|
.file("src/main.rs", r#"
|
||||||
|
fn main() { std::os::set_exit_status(2); }
|
||||||
|
"#);
|
||||||
|
|
||||||
|
assert_that(p.cargo_process("cargo-run"),
|
||||||
|
execs().with_status(2));
|
||||||
|
})
|
||||||
|
|
||||||
|
test!(no_main_file {
|
||||||
|
let p = project("foo")
|
||||||
|
.file("Cargo.toml", r#"
|
||||||
|
[project]
|
||||||
|
name = "foo"
|
||||||
|
version = "0.0.1"
|
||||||
|
authors = []
|
||||||
|
"#)
|
||||||
|
.file("src/lib.rs", "");
|
||||||
|
|
||||||
|
assert_that(p.cargo_process("cargo-run"),
|
||||||
|
execs().with_status(101)
|
||||||
|
.with_stderr("`src/main.rs` must be present for \
|
||||||
|
`cargo run`\n"));
|
||||||
|
})
|
@ -27,3 +27,4 @@ mod test_cargo_compile_path_deps;
|
|||||||
mod test_cargo_test;
|
mod test_cargo_test;
|
||||||
mod test_shell;
|
mod test_shell;
|
||||||
mod test_cargo_cross_compile;
|
mod test_cargo_cross_compile;
|
||||||
|
mod test_cargo_run;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user