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

[WIP, do not commit] set Cargo.toml configuration from the command line #4380

Closed
wants to merge 2 commits 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
56 changes: 29 additions & 27 deletions src/bin/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub struct Options {
flag_frozen: bool,
flag_all: bool,
flag_exclude: Vec<String>,
flag_config: Vec<String>,
}

pub const USAGE: &'static str = "
Expand All @@ -41,32 +42,33 @@ Usage:
cargo build [options]

Options:
-h, --help Print this message
-p SPEC, --package SPEC ... Package to build
--all Build all packages in the workspace
--exclude SPEC ... Exclude packages from the build
-j N, --jobs N Number of parallel jobs, defaults to # of CPUs
--lib Build only this package's library
--bin NAME Build only the specified binary
--bins Build all binaries
--example NAME Build only the specified example
--examples Build all examples
--test NAME Build only the specified test target
--tests Build all tests
--bench NAME Build only the specified bench target
--benches Build all benches
--release Build artifacts in release mode, with optimizations
--features FEATURES Space-separated list of features to also build
--all-features Build all available features
--no-default-features Do not build the `default` feature
--target TRIPLE Build for the target triple
--manifest-path PATH Path to the manifest to compile
-v, --verbose ... Use verbose output (-vv very verbose/build.rs output)
-q, --quiet No output printed to stdout
--color WHEN Coloring: auto, always, never
--message-format FMT Error format: human, json [default: human]
--frozen Require Cargo.lock and cache are up to date
--locked Require Cargo.lock is up to date
-h, --help Print this message
-p SPEC, --package SPEC ... Package to build
--all Build all packages in the workspace
--exclude SPEC ... Exclude packages from the build
-j N, --jobs N Number of parallel jobs, defaults to # of CPUs
--lib Build only this package's library
--bin NAME Build only the specified binary
--bins Build all binaries
--example NAME Build only the specified example
--examples Build all examples
--test NAME Build only the specified test target
--tests Build all tests
--bench NAME Build only the specified bench target
--benches Build all benches
--release Build artifacts in release mode, with optimizations
--features FEATURES Space-separated list of features to also build
--all-features Build all available features
--no-default-features Do not build the `default` feature
--target TRIPLE Build for the target triple
--manifest-path PATH Path to the manifest to compile
-c CONFIG, --config CONFIG ... Set/override values specified in Cargo.toml
-v, --verbose ... Use verbose output (-vv very verbose/build.rs output)
-q, --quiet No output printed to stdout
--color WHEN Coloring: auto, always, never
--message-format FMT Error format: human, json [default: human]
--frozen Require Cargo.lock and cache are up to date
--locked Require Cargo.lock is up to date

If the --package argument is given, then SPEC is a package id specification
which indicates which package should be built. If it is not given, then the
Expand All @@ -92,7 +94,7 @@ pub fn execute(options: Options, config: &Config) -> CliResult {
options.flag_locked)?;

let root = find_root_manifest_for_wd(options.flag_manifest_path, config.cwd())?;
let ws = Workspace::new(&root, config)?;
let ws = Workspace::new_with_overrides(&root, config, &options.flag_config)?;

let spec = Packages::from_flags(ws.is_virtual(),
options.flag_all,
Expand Down
31 changes: 20 additions & 11 deletions src/cargo/core/workspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,12 @@ impl<'cfg> Workspace<'cfg> {
/// before returning it, so `Ok` is only returned for valid workspaces.
pub fn new(manifest_path: &Path, config: &'cfg Config)
-> CargoResult<Workspace<'cfg>> {
let overrides = vec!();
Workspace::new_with_overrides(manifest_path, config, &overrides)
}

pub fn new_with_overrides(manifest_path: &Path, config: &'cfg Config,
overrides: &Vec<String>) -> CargoResult<Workspace<'cfg>> {
let target_dir = config.target_dir()?;

let mut ws = Workspace {
Expand All @@ -116,7 +122,7 @@ impl<'cfg> Workspace<'cfg> {
is_ephemeral: false,
require_optional_deps: true,
};
ws.root_manifest = ws.find_root(manifest_path)?;
ws.root_manifest = ws.find_root(manifest_path, &overrides)?;
ws.find_members()?;
ws.validate()?;
Ok(ws)
Expand Down Expand Up @@ -272,7 +278,7 @@ impl<'cfg> Workspace<'cfg> {
///
/// Returns an error if `manifest_path` isn't actually a valid manifest or
/// if some other transient error happens.
fn find_root(&mut self, manifest_path: &Path)
fn find_root(&mut self, manifest_path: &Path, overrides: &Vec<String>)
-> CargoResult<Option<PathBuf>> {
fn read_root_pointer(member_manifest: &Path, root_link: &str) -> CargoResult<PathBuf> {
let path = member_manifest.parent().unwrap()
Expand All @@ -283,7 +289,7 @@ impl<'cfg> Workspace<'cfg> {
};

{
let current = self.packages.load(&manifest_path)?;
let current = self.packages.load(&manifest_path, &overrides)?;
match *current.workspace_config() {
WorkspaceConfig::Root { .. } => {
debug!("find_root - is root {}", manifest_path.display());
Expand All @@ -300,7 +306,7 @@ impl<'cfg> Workspace<'cfg> {
let manifest = path.join("Cargo.toml");
debug!("find_root - trying {}", manifest.display());
if manifest.exists() {
match *self.packages.load(&manifest)?.workspace_config() {
match *self.packages.load(&manifest, &overrides)?.workspace_config() {
WorkspaceConfig::Root { ref exclude, ref members } => {
debug!("find_root - found a root checking exclusion");
if !is_excluded(members, exclude, path, manifest_path) {
Expand Down Expand Up @@ -337,7 +343,8 @@ impl<'cfg> Workspace<'cfg> {
}
};
let members = {
let root = self.packages.load(&root_manifest)?;
let unused = vec!();
let root = self.packages.load(&root_manifest, &unused)?;
match *root.workspace_config() {
WorkspaceConfig::Root { ref members, .. } => members.clone(),
_ => bail!("root of a workspace inferred but wasn't a root: {}",
Expand Down Expand Up @@ -379,16 +386,17 @@ impl<'cfg> Workspace<'cfg> {
if self.members.iter().any(|p| p == &manifest_path) {
return Ok(())
}
let unused = vec!();
if is_path_dep
&& !manifest_path.parent().unwrap().starts_with(self.root())
&& self.find_root(&manifest_path)? != self.root_manifest {
&& self.find_root(&manifest_path, &unused)? != self.root_manifest {
// If `manifest_path` is a path dependency outside of the workspace,
// don't add it, or any of its dependencies, as a members.
return Ok(())
}

let root = root_manifest.parent().unwrap();
match *self.packages.load(root_manifest)?.workspace_config() {
match *self.packages.load(root_manifest, &unused)?.workspace_config() {
WorkspaceConfig::Root { ref members, ref exclude } => {
if is_excluded(members, exclude, root, &manifest_path) {
return Ok(())
Expand All @@ -401,7 +409,7 @@ impl<'cfg> Workspace<'cfg> {
self.members.push(manifest_path.clone());

let candidates = {
let pkg = match *self.packages.load(&manifest_path)? {
let pkg = match *self.packages.load(&manifest_path, &unused)? {
MaybePackage::Package(ref p) => p,
MaybePackage::Virtual(_) => return Ok(()),
};
Expand Down Expand Up @@ -471,8 +479,9 @@ impl<'cfg> Workspace<'cfg> {
}
}

let unused = vec!();
for member in self.members.clone() {
let root = self.find_root(&member)?;
let root = self.find_root(&member, &unused)?;
if root == self.root_manifest {
continue
}
Expand Down Expand Up @@ -613,14 +622,14 @@ impl<'cfg> Packages<'cfg> {
&self.packages[manifest_path.parent().unwrap()]
}

fn load(&mut self, manifest_path: &Path) -> CargoResult<&MaybePackage> {
fn load(&mut self, manifest_path: &Path, overrides: &Vec<String>) -> CargoResult<&MaybePackage> {
let key = manifest_path.parent().unwrap();
match self.packages.entry(key.to_path_buf()) {
Entry::Occupied(e) => Ok(e.into_mut()),
Entry::Vacant(v) => {
let source_id = SourceId::for_path(key)?;
let (manifest, _nested_paths) =
read_manifest(&manifest_path, &source_id, self.config)?;
read_manifest(&manifest_path, &source_id, &overrides, self.config)?;
Ok(v.insert(match manifest {
EitherManifest::Real(manifest) => {
MaybePackage::Package(Package::new(manifest,
Expand Down
6 changes: 4 additions & 2 deletions src/cargo/ops/cargo_read_manifest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ use util::toml::read_manifest;
pub fn read_package(path: &Path, source_id: &SourceId, config: &Config)
-> CargoResult<(Package, Vec<PathBuf>)> {
trace!("read_package; path={}; source-id={}", path.display(), source_id);
let (manifest, nested) = read_manifest(path, source_id, config)?;
let unused = vec!();
let (manifest, nested) = read_manifest(path, source_id, &unused, config)?;
let manifest = match manifest {
EitherManifest::Real(manifest) => manifest,
EitherManifest::Virtual(..) => {
Expand Down Expand Up @@ -114,7 +115,8 @@ fn read_nested_packages(path: &Path,

let manifest_path = find_project_manifest_exact(path, "Cargo.toml")?;

let (manifest, nested) = match read_manifest(&manifest_path, source_id, config) {
let unused = vec!();
let (manifest, nested) = match read_manifest(&manifest_path, source_id, &unused, config) {
Err(err) => {
// Ignore malformed manifests found on git repositories
//
Expand Down
62 changes: 59 additions & 3 deletions src/cargo/util/toml/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,27 +25,83 @@ use util::errors::{CargoError, CargoResult, CargoResultExt};
mod targets;
use self::targets::targets;

pub fn read_manifest(path: &Path, source_id: &SourceId, config: &Config)
pub fn read_manifest(path: &Path, source_id: &SourceId, overrides: &Vec<String>,
config: &Config)
-> CargoResult<(EitherManifest, Vec<PathBuf>)> {
trace!("read_manifest; path={}; source-id={}", path.display(), source_id);
let contents = paths::read(path)?;

do_read_manifest(&contents, path, source_id, config).chain_err(|| {
do_read_manifest(&contents, path, source_id, &overrides, config).chain_err(|| {
format!("failed to parse manifest at `{}`", path.display())
})
}

fn merge_toml(orig: &toml::Value, overrides: &toml::Value) -> CargoResult<toml::Value> {
match (orig, overrides) {
// We've descended down some TOML path and found identically-typed scalar
// values; the override is the correct one to pick.
(&toml::Value::String(_), &toml::Value::String(_)) |
(&toml::Value::Integer(_), &toml::Value::Integer(_)) |
(&toml::Value::Float(_), &toml::Value::Float(_)) |
(&toml::Value::Boolean(_), &toml::Value::Boolean(_)) |
(&toml::Value::Datetime(_), &toml::Value::Datetime(_)) => Ok(overrides.clone()),

// We have two tables to merge. We want to recursively merge all of the
// values in the override table into the first one.
(&toml::Value::Table(ref orig), &toml::Value::Table(ref overrides)) => {
let mut new_table = orig.clone();
for (key, value) in overrides {
// XXX This should not be so difficult!
match orig.get(key) {
Some(ref v) => {
let merged = merge_toml(v, &value)?;
*(new_table.get_mut(key).unwrap()) = merged;
}
None => {
new_table.insert(key.clone(), value.clone());
}
};
}
Ok(toml::Value::Table(new_table))
},

// XXX arrays are a peculiar case. Do we just accept the override array,
// or do we attempt to merge recursively? What does merging recursively even
// *mean* if the arrays don't have the same length?
//
// Anything else is just something we're not equipped to handle.
(_, _) => bail!("Can't incorporate command-line manifest modifications."),
}
}

fn do_read_manifest(contents: &str,
manifest_file: &Path,
source_id: &SourceId,
overrides: &Vec<String>,
config: &Config)
-> CargoResult<(EitherManifest, Vec<PathBuf>)> {
let package_root = manifest_file.parent().unwrap();

let toml = {
let pretty_filename =
util::without_prefix(manifest_file, config.cwd()).unwrap_or(manifest_file);
parse(contents, pretty_filename, config)?
let candidate = parse(contents, pretty_filename, config)?;

if overrides.is_empty() {
candidate
} else {
let command_line_filename = "<command-line>".to_owned();
let command_line_filename = Path::new(&command_line_filename);
// XXX need some better error handling
let toml_overrides = parse(&overrides[0], &command_line_filename, config);
let toml_overrides = overrides[1..].iter().fold(toml_overrides, |acc, ref o| {
acc.and_then(|v| {
parse(&o, &command_line_filename, config).and_then(|v2| merge_toml(&v, &v2))
})
})?;

merge_toml(&candidate, &toml_overrides)?
}
};

let mut unused = BTreeSet::new();
Expand Down