diff --git a/src/cargo.rs b/src/cargo.rs index 478b1ecd..158a4b55 100644 --- a/src/cargo.rs +++ b/src/cargo.rs @@ -120,7 +120,7 @@ pub(crate) fn run_cargo( let mut cargo_command = if let Some(cargo_path) = args.cargo_path.as_ref() { Command::new(cargo_path) } else if let Some(bin) = config.cargo.cargo.as_ref() { - Command::new(config.config_path.join(bin)) + Command::new(config.config_dir.join(bin)) } else { Command::new("cargo") }; @@ -143,7 +143,7 @@ pub(crate) fn run_cargo( } } } else if let Some(bin) = config.cargo.rustc.as_ref() { - cargo_command.env("RUSTC", config.config_path.join(bin)); + cargo_command.env("RUSTC", config.config_dir.join(bin)); } if let Some(cargo_home) = cargo_home { diff --git a/src/config.rs b/src/config.rs index 106b1253..9a85b05c 100644 --- a/src/config.rs +++ b/src/config.rs @@ -31,6 +31,7 @@ use serde::de::MapAccess; use serde::de::Visitor; use serde::de::value::MapAccessDeserializer; +use crate::Args; use crate::platform::PlatformConfig; use crate::platform::PlatformName; use crate::universe::UniverseConfig; @@ -41,7 +42,15 @@ use crate::universe::UniverseName; pub struct Config { /// Path the config was read from #[serde(skip)] - pub config_path: PathBuf, + pub config_dir: PathBuf, + + /// Path to the Cargo.toml we are buckifying + #[serde(default)] + pub manifest_path: PathBuf, + + /// Where to write the output. + #[serde(default)] + pub third_party_dir: PathBuf, /// Try to compute a precise list of sources rather than using globbing #[serde(default)] @@ -325,11 +334,21 @@ where deserializer.deserialize_any(VendorConfigVisitor) } -pub fn read_config(dir: &Path) -> anyhow::Result { - let reindeer_toml = dir.join("reindeer.toml"); +pub fn read_config(args: &Args, reindeer_toml: &Path) -> anyhow::Result { + let dir = reindeer_toml + .parent() + .context("Invalid path to reindeer.toml")?; let mut config = try_read_config(&reindeer_toml)?; - config.config_path = dir.to_path_buf(); + config.config_dir = dir.to_path_buf(); + + // Apply overrides from CLI args + if let Some(manifest_path) = args.manifest_path.clone() { + config.manifest_path = manifest_path; + } + if let Some(third_party_dir) = args.third_party_dir.clone() { + config.third_party_dir = third_party_dir; + } if config.buck.buckfile_imports.is_default { // Fill in some prelude imports so Reindeer generates working targets diff --git a/src/main.rs b/src/main.rs index 1e8a693a..125d2b62 100644 --- a/src/main.rs +++ b/src/main.rs @@ -20,8 +20,10 @@ //! //! (TBD - rest of it) +use std::path::Path; use std::path::PathBuf; +use anyhow::Context; use clap::Parser; use clap::Subcommand; @@ -56,14 +58,23 @@ pub struct Args { /// Extra cargo options #[arg(long, value_name = "ARGUMENT")] cargo_options: Vec, - /// Path to third-party dir - #[arg(long, default_value = ".", value_name = "PATH")] - third_party_dir: PathBuf, + /// Path to third-party dir. Overrides configuration from `reindeer.toml`. + /// + /// Path is relative to current dir, whereas the one in `reindeer.toml` is relative to the + /// parent directory of the `reindeer.toml` file. + #[arg(long, value_name = "PATH")] + third_party_dir: Option, #[command(subcommand)] subcommand: SubCommand, - /// Path to the `Cargo.toml to generate from + /// Path to the `Cargo.toml to generate from. Overrides configuration from `reindeer.toml`. + /// + /// Path is relative to current dir, whereas the one in `reindeer.toml` is relative to the + /// parent directory of the `reindeer.toml` file. #[arg(long, value_name = "PATH")] manifest_path: Option, + /// Path to a `reindeer.toml` file to read configuration from. + #[arg(short = 'c', long, value_name = "PATH")] + config: Option, } #[derive(Debug, Subcommand)] @@ -110,19 +121,65 @@ pub struct Paths { fn try_main() -> anyhow::Result<()> { let args = Args::parse(); - let third_party_dir = dunce::canonicalize(&args.third_party_dir)?; - let mut config = config::read_config(&third_party_dir)?; - - let manifest_path = args - .manifest_path - .clone() - .unwrap_or_else(|| third_party_dir.join("Cargo.toml")); - let paths = Paths { - lockfile_path: manifest_path.with_file_name("Cargo.lock"), - manifest_path, - cargo_home: third_party_dir.join(".cargo"), - third_party_dir, - }; + let paths; + let mut config; + + if let Some(from_args) = args.third_party_dir.as_deref() { + let third_party_dir = dunce::canonicalize(&from_args)?; + config = config::read_config(&args, &third_party_dir.join("reindeer.toml"))?; + + let manifest_path = args + .manifest_path + .clone() + .unwrap_or_else(|| third_party_dir.join("Cargo.toml")); + + paths = Paths { + lockfile_path: manifest_path.with_file_name("Cargo.lock"), + manifest_path, + cargo_home: third_party_dir.join(".cargo"), + third_party_dir, + }; + } else { + let config_path = args.config.as_deref().unwrap_or(Path::new("reindeer.toml")); + config = config::read_config(&args, config_path)?; + + let third_party_dir = args + .third_party_dir + .clone() + .unwrap_or_else(|| config.config_dir.join(&config.third_party_dir)); + let third_party_dir = dunce::canonicalize(&third_party_dir)?; + + let manifest_path = args + .manifest_path + .clone() + .unwrap_or_else(|| config.config_dir.join(&config.manifest_path)); + + let lockfile_path = manifest_path.with_file_name("Cargo.lock"); + + paths = Paths { + lockfile_path, + manifest_path, + cargo_home: third_party_dir.join(".cargo"), + third_party_dir, + }; + } + + if !paths.manifest_path.is_file() { + return Err(anyhow::anyhow!( + "Path {} is not a file. It should be a Cargo.toml", + paths.manifest_path.display() + )); + } + + if !paths.third_party_dir.exists() { + std::fs::create_dir_all(&paths.third_party_dir) + .with_context(|| format!("Creating directory {}", paths.third_party_dir.display()))?; + } else if !paths.third_party_dir.is_dir() { + return Err(anyhow::anyhow!( + "Path {} must be a directory", + paths.manifest_path.display() + )); + } log::debug!("Args = {:#?}, paths {:#?}", args, paths);