message_format,
target_rustdoc_args: None,
target_rustc_args: None,
+ export_dir: None,
};
Ok(opts)
}
.arg_release("Build artifacts in release mode, with optimizations")
.arg_features()
.arg_target_triple("Build for the target triple")
+ .arg(opt("out-dir", "Copy final artifacts to this directory").value_name("PATH"))
.arg_manifest_path()
.arg_message_format()
.after_help(
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
let ws = args.workspace(config)?;
- let compile_opts = args.compile_options(config, CompileMode::Build)?;
+ let mut compile_opts = args.compile_options(config, CompileMode::Build)?;
+ compile_opts.export_dir = args.value_of_path("out-dir", config);
ops::compile(&ws, &compile_opts)?;
Ok(())
}
..BuildConfig::default()
},
profiles,
+ None,
&units,
)?;
/// The specified target will be compiled with all the available arguments,
/// note that this only accounts for the *final* invocation of rustc
pub target_rustc_args: Option<Vec<String>>,
+ /// The directory to copy final artifacts to. Note that even if `out_dir` is
+ /// set, a copy of artifacts still could be found a `target/(debug\release)`
+ /// as usual.
+ // Note that, although the cmd-line flag name is `out-dir`, in code we use
+ // `export_dir`, to avoid confusion with out dir at `target/debug/deps`.
+ pub export_dir: Option<PathBuf>,
}
impl<'a> CompileOptions<'a> {
message_format: MessageFormat::Human,
target_rustdoc_args: None,
target_rustc_args: None,
+ export_dir: None,
}
}
}
ref filter,
ref target_rustdoc_args,
ref target_rustc_args,
+ ref export_dir,
} = *options;
let target = match target {
package_targets.push((to_build, vec![(target, profile)]));
}
}
-
let mut ret = {
let _p = profile::start("compiling");
let mut build_config = scrape_build_config(config, jobs, target)?;
config,
build_config,
profiles,
+ export_dir.clone(),
&exec,
)?
};
mode: ops::CompileMode::Build,
target_rustdoc_args: None,
target_rustc_args: None,
+ export_dir: None,
},
Arc::new(DefaultExecutor),
)?;
pub(super) host: Layout,
/// The target directory layout for the target (if different from then host)
pub(super) target: Option<Layout>,
+ export_dir: Option<(PathBuf, Vec<Unit<'a>>)>,
ws: &'a Workspace<'cfg>,
metas: HashMap<Unit<'a>, Option<Metadata>>,
/// For each Unit, a list all files produced.
roots: &[Unit<'a>],
host: Layout,
target: Option<Layout>,
+ export_dir: Option<PathBuf>,
ws: &'a Workspace<'cfg>,
cx: &Context<'a, 'cfg>,
) -> CompilationFiles<'a, 'cfg> {
ws,
host,
target,
+ export_dir: export_dir.map(|dir| (dir, roots.to_vec())),
metas,
outputs,
}
}
}
+ pub fn export_dir(&self, unit: &Unit<'a>) -> Option<PathBuf> {
+ let &(ref dir, ref roots) = self.export_dir.as_ref()?;
+ if roots.contains(unit) {
+ Some(dir.clone())
+ } else {
+ None
+ }
+ }
+
pub fn pkg_dir(&self, unit: &Unit<'a>) -> String {
let name = unit.pkg.package_id().name();
match self.metas[unit] {
use std::collections::{HashMap, HashSet};
use std::env;
-use std::path::Path;
+use std::path::{Path, PathBuf};
use std::str::{self, FromStr};
use std::sync::Arc;
config: &'cfg Config,
build_config: BuildConfig,
profiles: &'a Profiles,
+ export_dir: Option<PathBuf>,
units: &[Unit<'a>],
) -> CargoResult<Context<'a, 'cfg>> {
let dest = if build_config.release {
cx.probe_target_info()?;
let deps = build_unit_dependencies(units, &cx)?;
cx.unit_dependencies = deps;
- let files = CompilationFiles::new(units, host_layout, target_layout, ws, &cx);
+ let files = CompilationFiles::new(units, host_layout, target_layout, export_dir, ws, &cx);
cx.files = Some(files);
Ok(cx)
}
use std::ffi::{OsStr, OsString};
use std::fs;
use std::io::{self, Write};
-use std::path::{self, PathBuf};
+use std::path::{self, Path, PathBuf};
use std::sync::Arc;
use same_file::is_same_file;
config: &'cfg Config,
build_config: BuildConfig,
profiles: &'a Profiles,
+ export_dir: Option<PathBuf>,
exec: &Arc<Executor>,
) -> CargoResult<Compilation<'cfg>> {
let units = pkg_targets
config,
build_config,
profiles,
+ export_dir,
&units,
)?;
fresh: bool,
) -> CargoResult<Work> {
let outputs = cx.outputs(unit)?;
+ let export_dir = cx.files().export_dir(unit);
let package_id = unit.pkg.package_id().clone();
let target = unit.target.clone();
let profile = unit.profile.clone();
}
};
destinations.push(dst.display().to_string());
+ hardlink_or_copy(&src, &dst)?;
+ if let Some(ref path) = export_dir {
+ //TODO: check if dir
+ if !path.exists() {
+ fs::create_dir(path)?;
+ }
- debug!("linking {} to {}", src.display(), dst.display());
- if is_same_file(src, dst).unwrap_or(false) {
- continue;
- }
- if dst.exists() {
- paths::remove_file(&dst)?;
+ hardlink_or_copy(&src, &path.join(dst.file_name().unwrap()))?;
}
-
- let link_result = if src.is_dir() {
- #[cfg(unix)]
- use std::os::unix::fs::symlink;
- #[cfg(target_os = "redox")]
- use std::os::redox::fs::symlink;
- #[cfg(windows)]
- use std::os::windows::fs::symlink_dir as symlink;
-
- let dst_dir = dst.parent().unwrap();
- assert!(src.starts_with(dst_dir));
- symlink(src.strip_prefix(dst_dir).unwrap(), dst)
- } else {
- fs::hard_link(src, dst)
- };
- link_result
- .or_else(|err| {
- debug!("link failed {}. falling back to fs::copy", err);
- fs::copy(src, dst).map(|_| ())
- })
- .chain_err(|| {
- format!(
- "failed to link or copy `{}` to `{}`",
- src.display(),
- dst.display()
- )
- })?;
}
if json_messages {
}))
}
+fn hardlink_or_copy(src: &Path, dst: &Path) -> CargoResult<()> {
+ debug!("linking {} to {}", src.display(), dst.display());
+ if is_same_file(src, dst).unwrap_or(false) {
+ return Ok(());
+ }
+ if dst.exists() {
+ paths::remove_file(&dst)?;
+ }
+
+ let link_result = if src.is_dir() {
+ #[cfg(unix)]
+ use std::os::unix::fs::symlink;
+ #[cfg(target_os = "redox")]
+ use std::os::redox::fs::symlink;
+ #[cfg(windows)]
+ use std::os::windows::fs::symlink_dir as symlink;
+
+ let dst_dir = dst.parent().unwrap();
+ assert!(src.starts_with(dst_dir));
+ symlink(src.strip_prefix(dst_dir).unwrap(), dst)
+ } else {
+ fs::hard_link(src, dst)
+ };
+ link_result
+ .or_else(|err| {
+ debug!("link failed {}. falling back to fs::copy", err);
+ fs::copy(src, dst).map(|_| ())
+ })
+ .chain_err(|| {
+ format!(
+ "failed to link or copy `{}` to `{}`",
+ src.display(),
+ dst.display()
+ )
+ })?;
+ Ok(())
+}
+
fn load_build_deps(cx: &Context, unit: &Unit) -> Option<Arc<BuildScripts>> {
cx.build_scripts.get(unit).cloned()
}
use core::{GitReference, PackageIdSpec, Profiles, SourceId, WorkspaceConfig, WorkspaceRootConfig};
use core::{Dependency, Manifest, PackageId, Summary, Target};
-use core::{EitherManifest, Edition, Feature, Features, VirtualManifest};
+use core::{Edition, EitherManifest, Feature, Features, VirtualManifest};
use core::dependency::{Kind, Platform};
use core::manifest::{LibKind, Lto, ManifestMetadata, Profile};
use sources::CRATES_IO;
mod metadata;
mod net_config;
mod new;
+mod out_dir;
mod overrides;
mod package;
mod patch;
--- /dev/null
+use cargotest::support::{execs, project};
+use hamcrest::assert_that;
+use std::path::Path;
+use std::fs;
+
+#[test]
+fn binary_with_debug() {
+ 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, World!") }"#)
+ .build();
+
+ assert_that(p.cargo("build --out-dir out"), execs().with_status(0));
+ check_dir_contents(
+ &p.root().join("out"),
+ &["foo"],
+ &["foo"],
+ &["foo.exe", "foo.pdb"],
+ );
+}
+
+#[test]
+fn static_library_with_debug() {
+ let p = project("foo")
+ .file(
+ "Cargo.toml",
+ r#"
+ [project]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [lib]
+ crate-type = ["staticlib"]
+ "#,
+ )
+ .file(
+ "src/lib.rs",
+ r#"
+ #[no_mangle]
+ pub extern "C" fn foo() { println!("Hello, World!") }
+ "#,
+ )
+ .build();
+
+ assert_that(p.cargo("build --out-dir out"), execs().with_status(0));
+ check_dir_contents(
+ &p.root().join("out"),
+ &["libfoo.a"],
+ &["libfoo.a"],
+ &["foo.lib"],
+ );
+}
+
+#[test]
+fn dynamic_library_with_debug() {
+ let p = project("foo")
+ .file(
+ "Cargo.toml",
+ r#"
+ [project]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [lib]
+ crate-type = ["cdylib"]
+ "#,
+ )
+ .file(
+ "src/lib.rs",
+ r#"
+ #[no_mangle]
+ pub extern "C" fn foo() { println!("Hello, World!") }
+ "#,
+ )
+ .build();
+
+ assert_that(p.cargo("build --out-dir out"), execs().with_status(0));
+ check_dir_contents(
+ &p.root().join("out"),
+ &["libfoo.so"],
+ &["libfoo.so"],
+ &["foo.dll", "foo.dll.lib"],
+ );
+}
+
+#[test]
+fn rlib_with_debug() {
+ let p = project("foo")
+ .file(
+ "Cargo.toml",
+ r#"
+ [project]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [lib]
+ crate-type = ["rlib"]
+ "#,
+ )
+ .file(
+ "src/lib.rs",
+ r#"
+ pub fn foo() { println!("Hello, World!") }
+ "#,
+ )
+ .build();
+
+ assert_that(p.cargo("build --out-dir out"), execs().with_status(0));
+ check_dir_contents(
+ &p.root().join("out"),
+ &["libfoo.rlib"],
+ &["libfoo.rlib"],
+ &["libfoo.rlib"],
+ );
+}
+
+#[test]
+fn include_only_the_binary_from_the_current_package() {
+ let p = project("foo")
+ .file(
+ "Cargo.toml",
+ r#"
+ [project]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [workspace]
+
+ [dependencies]
+ utils = { path = "./utils" }
+ "#,
+ )
+ .file("src/lib.rs", "extern crate utils;")
+ .file(
+ "src/main.rs",
+ r#"
+ extern crate foo;
+ extern crate utils;
+ fn main() {
+ println!("Hello, World!")
+ }
+ "#,
+ )
+ .file(
+ "utils/Cargo.toml",
+ r#"
+ [project]
+ name = "utils"
+ version = "0.0.1"
+ authors = []
+ "#,
+ )
+ .file("utils/src/lib.rs", "")
+ .build();
+
+ assert_that(
+ p.cargo("build --bin foo --out-dir out"),
+ execs().with_status(0),
+ );
+ check_dir_contents(
+ &p.root().join("out"),
+ &["foo"],
+ &["foo"],
+ &["foo.exe", "foo.pdb"],
+ );
+}
+
+fn check_dir_contents(
+ out_dir: &Path,
+ expected_linux: &[&str],
+ expected_mac: &[&str],
+ expected_win: &[&str],
+) {
+ let expected = if cfg!(target_os = "windows") {
+ expected_win
+ } else if cfg!(target_os = "macos") {
+ expected_mac
+ } else {
+ expected_linux
+ };
+
+ let actual = list_dir(out_dir);
+ let mut expected = expected.iter().map(|s| s.to_string()).collect::<Vec<_>>();
+ expected.sort();
+ assert_eq!(actual, expected);
+}
+
+fn list_dir(dir: &Path) -> Vec<String> {
+ let mut res = Vec::new();
+ for entry in fs::read_dir(dir).unwrap() {
+ let entry = entry.unwrap();
+ res.push(entry.file_name().into_string().unwrap());
+ }
+ res.sort();
+ res
+}