Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge cargo-clone subcommand into cargo itself. #5501

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions src/bin/cargo/commands/clone.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
use command_prelude::*;

use cargo::core::{GitReference, SourceId};
use cargo::util::ToUrl;
use cargo::ops::{self, CloneOptions};

pub fn cli() -> App {
subcommand("clone")
.about("Clone source code of a Rust crate")
.arg(opt("prefix", "Directory to clone the package into").value_name("DIR"))
.arg(opt("force", "Force overwriting existing directory").short("f"))
.arg(Arg::with_name("crate").empty_values(false))
.arg(opt("version", "Specify a version to install from crates.io")
.alias("vers")
.value_name("VERSION"),
)
.arg(opt("git", "Git URL to clone the specified crate from").value_name("URL"))
.arg(opt("branch", "Branch to use when cloning from git").value_name("BRANCH"))
.arg(opt("tag", "Tag to use when cloning from git").value_name("TAG"))
.arg(opt("rev", "Specific commit to use when cloning from git").value_name("SHA"))
.arg(opt("path", "Filesystem path to local crate to clone").value_name("PATH"))
}

pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
let source = if let Some(url) = args.value_of("git") {
let url = url.to_url()?;
let gitref = if let Some(branch) = args.value_of("branch") {
GitReference::Branch(branch.to_string())
} else if let Some(tag) = args.value_of("tag") {
GitReference::Tag(tag.to_string())
} else if let Some(rev) = args.value_of("rev") {
GitReference::Rev(rev.to_string())
} else {
GitReference::Branch("master".to_string())
};
SourceId::for_git(&url, gitref)?
} else if let Some(path) = args.value_of_path("path", config) {
SourceId::for_path(&path)?
} else {
SourceId::crates_io(config)?
};

let opts = CloneOptions {
config,
name: args.value_of("crate"),
source_id: source,
prefix: args.value_of("prefix"),
force: args.is_present("force"),
version: args.value_of("version"),
};
ops::clone(opts)?;
Ok(())
}
3 changes: 3 additions & 0 deletions src/bin/cargo/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub fn builtin() -> Vec<App> {
build::cli(),
check::cli(),
clean::cli(),
clone::cli(),
doc::cli(),
fetch::cli(),
generate_lockfile::cli(),
Expand Down Expand Up @@ -40,6 +41,7 @@ pub fn builtin_exec(cmd: &str) -> Option<fn(&mut Config, &ArgMatches) -> CliResu
"build" => build::exec,
"check" => check::exec,
"clean" => clean::exec,
"clone" => clone::exec,
"doc" => doc::exec,
"fetch" => fetch::exec,
"generate-lockfile" => generate_lockfile::exec,
Expand Down Expand Up @@ -74,6 +76,7 @@ pub mod bench;
pub mod build;
pub mod check;
pub mod clean;
pub mod clone;
pub mod doc;
pub mod fetch;
pub mod generate_lockfile;
Expand Down
112 changes: 112 additions & 0 deletions src/cargo/ops/cargo_clone.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
use std::path::{Path, PathBuf};
use std::fs;
use std::env;

use util::Config;
use core::{Source, SourceId};
use sources::{GitSource, PathSource, SourceConfigMap};
use util::errors::{CargoResult, CargoResultExt};
use ops::select_pkg;


pub struct CloneOptions<'a> {
pub config: &'a Config,
/// Crate's name
pub name: Option<&'a str>,
/// Source ID to clone from
pub source_id: SourceId,
/// Path to clone to
pub prefix: Option<&'a str>,
/// Overwrite an existing directory with crate's content
pub force: bool,
/// Crate's version
pub version: Option<&'a str>,
}

pub fn clone(opts: CloneOptions) -> CargoResult<()> {
let CloneOptions {
config,
name,
source_id,
prefix,
force,
version,
} = opts;

let (pkg, _) = if source_id.is_path() {
let path = source_id
.url()
.to_file_path()
.map_err(|()| format_err!("path sources must have a valid path"))?;
let mut src = PathSource::new(&path, &source_id, config);
src.update().chain_err(|| {
format_err!(
"`{}` is not a crate root; specify a crate to \
install from crates.io, or use --path or --git to \
specify an alternate source",
path.display()
)
})?;

select_pkg(src, name, version, config, true, &mut |path| path.read_packages())?
} else if source_id.is_git() {
select_pkg(GitSource::new(&source_id, config)?,
name, version, config, true, &mut |git| git.read_packages())?
} else {
let map = SourceConfigMap::new(config)?;
select_pkg(map.load(&source_id)?,
name, version, config, true,
&mut |_| {
bail!("must specify a crate to clone from \
crates.io, or use --path or --git to \
specify alternate source")
}
)?
};

config.shell().status("Cloning", &pkg)?;

// If prefix was not supplied, clone into current dir
let mut dest_path = match prefix {
Some(prefix) => PathBuf::from(prefix),
None => env::current_dir()?,
};

dest_path.push(pkg.name().as_str());

if dest_path.exists() {
if force {
config.shell().status("Replacing", dest_path.display())?;
fs::remove_dir_all(&dest_path)?;
} else {
bail!(format!("Directory `{}` already exists. Add --force to overwrite",
dest_path.display()));
}
}

clone_directory(&pkg.root(), &dest_path)
}

fn clone_directory(from: &Path, to: &Path) -> CargoResult<()> {
fs::create_dir_all(&to)?;

let entries = from.read_dir()
.chain_err(|| format!("failed to read directory `{}`", from.display()))?;

for entry in entries {
let entry = entry?;
let path = entry.path();
let mut to = to.to_owned();
to.push(path.strip_prefix(from)?);

if path.is_file() && &entry.file_name() != ".cargo-ok" {
// .cargo-ok is not wanted in this context
fs::copy(&path, &to)?;
} else if path.is_dir() {
fs::create_dir(&to)?;
clone_directory(&path, &to)?
}
}

Ok(())
}
2 changes: 1 addition & 1 deletion src/cargo/ops/cargo_install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,7 @@ fn install_one(
Ok(())
}

fn select_pkg<'a, T>(
pub fn select_pkg<'a, T>(
mut source: T,
name: Option<&str>,
vers: Option<&str>,
Expand Down
4 changes: 3 additions & 1 deletion src/cargo/ops/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
pub use self::cargo_clean::{clean, CleanOptions};
pub use self::cargo_clone::{clone, CloneOptions};
pub use self::cargo_compile::{compile, compile_with_exec, compile_ws, CompileOptions};
pub use self::cargo_compile::{CompileFilter, FilterRule, Packages};
pub use self::cargo_read_manifest::{read_package, read_packages};
pub use self::cargo_run::run;
pub use self::cargo_install::{install, install_list, uninstall};
pub use self::cargo_install::{install, install_list, uninstall, select_pkg};
pub use self::cargo_new::{init, new, NewOptions, VersionControl};
pub use self::cargo_doc::{doc, DocOptions};
pub use self::cargo_generate_lockfile::generate_lockfile;
Expand All @@ -23,6 +24,7 @@ pub use self::resolve::{resolve_with_previous, resolve_ws, resolve_ws_precisely,
pub use self::cargo_output_metadata::{output_metadata, ExportInfo, OutputMetadataOptions};

mod cargo_clean;
mod cargo_clone;
mod cargo_compile;
mod cargo_doc;
mod cargo_fetch;
Expand Down
1 change: 1 addition & 0 deletions tests/testsuite/cargotest/support/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1054,6 +1054,7 @@ fn substitute_macros(input: &str) -> String {
("[RUNNING]", " Running"),
("[COMPILING]", " Compiling"),
("[CHECKING]", " Checking"),
("[CLONING]", " Cloning"),
("[CREATED]", " Created"),
("[FINISHED]", " Finished"),
("[ERROR]", "error:"),
Expand Down
Loading