Initial work on the git source

This commit is contained in:
Yehuda Katz 2014-05-21 17:53:05 -07:00
parent ca668c6605
commit 452654250d
8 changed files with 157 additions and 8 deletions

View file

@ -6,7 +6,8 @@ BINS = cargo \
cargo-compile \
cargo-read-manifest \
cargo-rustc \
cargo-verify-project
cargo-verify-project \
cargo-git-checkout \
SRC = $(shell find src -name '*.rs')

View file

@ -0,0 +1,34 @@
#![crate_id="cargo-git-checkout"]
extern crate cargo;
extern crate serialize;
extern crate hammer;
extern crate url;
use hammer::FlagConfig;
use cargo::{execute_main_without_stdin,CLIResult,CLIError,ToResult};
use cargo::core::Package;
use cargo::util::{Require,ToCLI,simple_human};
use cargo::sources::git::{GitCommand,GitRepo};
use url::Url;
#[deriving(Eq,Clone,Decodable)]
struct Options {
directory: StrBuf,
url: StrBuf,
reference: StrBuf
}
impl FlagConfig for Options {}
fn main() {
execute_main_without_stdin(execute);
}
fn execute(options: Options) -> CLIResult<Option<GitRepo>> {
let url: Url = try!(from_str(options.url.as_slice()).to_result(|_|
CLIError::new(format!("The URL `{}` you passed was not a valid URL", options.url), None::<&str>, 1)));
let cmd = GitCommand::new(Path::new(options.directory.clone()), url, options.reference);
cmd.checkout().to_cli(1).map(|repo| Some(repo))
}

View file

@ -5,6 +5,7 @@
#![feature(macro_rules)]
extern crate collections;
extern crate url;
extern crate hammer;
extern crate serialize;
extern crate semver;

103
src/cargo/sources/git.rs Normal file
View file

@ -0,0 +1,103 @@
use url::Url;
use util::{CargoResult,ProcessBuilder,io_error,human_error,process};
use std::str;
use std::io::UserDir;
use std::io::fs::mkdir_recursive;
use serialize::{Encodable,Encoder};
macro_rules! git(
($config:expr, $str:expr, $($rest:expr),*) => (
try!(git_inherit($config, format_strbuf!($str, $($rest),*)))
);
)
macro_rules! git_output(
($config:expr, $str:expr, $($rest:expr),*) => (
try!(git_output($config, format_strbuf!($str, $($rest),*)))
);
)
#[deriving(Eq,Clone)]
struct GitConfig {
path: Path,
uri: Url,
reference: StrBuf
}
#[deriving(Eq,Clone,Encodable)]
struct EncodableGitConfig {
path: StrBuf,
uri: StrBuf,
reference: StrBuf
}
impl<E, S: Encoder<E>> Encodable<S, E> for GitConfig {
fn encode(&self, s: &mut S) -> Result<(), E> {
EncodableGitConfig {
path: format_strbuf!("{}", self.path.display()),
uri: format_strbuf!("{}", self.uri),
reference: self.reference.clone()
}.encode(s)
}
}
#[deriving(Eq,Clone)]
pub struct GitCommand {
config: GitConfig
}
#[deriving(Eq,Clone,Encodable)]
pub struct GitRepo {
config: GitConfig,
revision: StrBuf
}
impl GitCommand {
pub fn new(path: Path, uri: Url, reference: StrBuf) -> GitCommand {
GitCommand { config: GitConfig { path: path, uri: uri, reference: reference } }
}
pub fn checkout(&self) -> CargoResult<GitRepo> {
let config = &self.config;
if config.path.exists() {
git!(config, "fetch --force --quiet --tags {} refs/heads/*:refs/heads/*", config.uri);
} else {
let dirname = Path::new(config.path.dirname());
let mut checkout_config = self.config.clone();
checkout_config.path = dirname;
try!(mkdir_recursive(&checkout_config.path, UserDir).map_err(|err|
human_error(format_strbuf!("Couldn't recursively create `{}`", checkout_config.path.display()), format_strbuf!("path={}", checkout_config.path.display()), io_error(err))));
git!(&checkout_config, "clone {} {} --bare --no-hardlinks --quiet", config.uri, config.path.display());
}
Ok(GitRepo { config: config.clone(), revision: try!(rev_for(config)) })
}
}
fn rev_for(config: &GitConfig) -> CargoResult<StrBuf> {
Ok(git_output!(config, "rev-parse {}", config.reference))
}
fn git(config: &GitConfig, str: &str) -> ProcessBuilder {
println!("Executing git {} @ {}", str, config.path.display());
process("git").args(str.split(' ').collect::<Vec<&str>>().as_slice()).cwd(config.path.clone())
}
fn git_inherit(config: &GitConfig, str: StrBuf) -> CargoResult<()> {
git(config, str.as_slice()).exec().map_err(|err|
human_error(format_strbuf!("Couldn't execute `git {}`: {}", str, err), None::<&str>, err))
}
fn git_output(config: &GitConfig, str: StrBuf) -> CargoResult<StrBuf> {
let output = try!(git(config, str.as_slice()).exec_with_output().map_err(|err|
human_error(format_strbuf!("Couldn't execute `git {}`", str), None::<&str>, err)));
Ok(to_str(output.output.as_slice()))
}
fn to_str(vec: &[u8]) -> StrBuf {
format_strbuf!("{}", str::from_utf8_lossy(vec))
}

View file

@ -1 +1,2 @@
pub mod path;
pub mod git;

View file

@ -1,5 +1,5 @@
pub use self::process_builder::{process,ProcessBuilder};
pub use self::result::{CargoError,CargoResult,Wrap,Require,ToCLI,other_error,human_error,toml_error,io_error,process_error};
pub use self::result::{CargoError,CargoResult,Wrap,Require,ToCLI,other_error,human_error,simple_human,toml_error,io_error,process_error};
pub mod graph;
pub mod process_builder;

View file

@ -31,8 +31,8 @@ impl Show for ProcessBuilder {
static PATH_SEP : &'static str = ":";
impl ProcessBuilder {
pub fn args(mut self, arguments: &[StrBuf]) -> ProcessBuilder {
self.args = Vec::from_slice(arguments);
pub fn args<T: Show>(mut self, arguments: &[T]) -> ProcessBuilder {
self.args = arguments.iter().map(|a| format_strbuf!("{}", a)).collect();
self
}

View file

@ -42,15 +42,24 @@ pub fn process_error(detail: StrBuf, exit: ProcessExit, output: Option<ProcessOu
}
}
pub fn human_error(desc: StrBuf, detail: StrBuf, cause: CargoError) -> CargoError {
pub fn human_error<T: ToStr, U: ToStr>(desc: T, detail: U, cause: CargoError) -> CargoError {
CargoError {
kind: HumanReadableError,
desc: BoxedDescription(desc),
detail: Some(detail),
desc: BoxedDescription(desc.to_str().to_strbuf()),
detail: Some(detail.to_str().to_strbuf()),
cause: Some(box cause)
}
}
pub fn simple_human<T: Show>(desc: T) -> CargoError {
CargoError {
kind: HumanReadableError,
desc: BoxedDescription(format_strbuf!("{}", desc)),
detail: None,
cause: None
}
}
pub fn toml_error(desc: &'static str, error: toml::Error) -> CargoError {
CargoError {
kind: TomlError(error),