From 8c96a399e8c37400808e902f726df85c75385329 Mon Sep 17 00:00:00 2001 From: UebelAndre Date: Mon, 28 Dec 2020 22:01:10 -0800 Subject: [PATCH] Cargo raze can now be run from anywhere within a cargo workspace (#330) * Cargo raze can now be run from anywhere within a cargo workspace * Addressed PR feedback --- impl/src/bin/cargo-raze.rs | 153 ++++++++++++++++++++++++++++++------- impl/src/planning.rs | 2 +- impl/src/settings.rs | 31 +++++--- 3 files changed, 149 insertions(+), 37 deletions(-) diff --git a/impl/src/bin/cargo-raze.rs b/impl/src/bin/cargo-raze.rs index 3e9ae2593..414959bdc 100644 --- a/impl/src/bin/cargo-raze.rs +++ b/impl/src/bin/cargo-raze.rs @@ -14,22 +14,25 @@ use std::{ collections::HashMap, + env, fs::{self, File}, io::Write, path::{Path, PathBuf}, }; -use anyhow::Result; +use anyhow::{anyhow, Context, Result}; +use cargo_metadata::Metadata; use docopt::Docopt; use cargo_raze::{ checks, - metadata::{CargoWorkspaceFiles, RazeMetadataFetcher}, - planning::{BuildPlanner, BuildPlannerImpl}, + metadata::{CargoWorkspaceFiles, MetadataFetcher, RazeMetadata, RazeMetadataFetcher}, + planning::{BuildPlanner, BuildPlannerImpl, PlannedBuild}, + rendering::FileOutputs, rendering::{bazel::BazelRenderer, BuildRenderer, RenderDetails}, settings::RazeSettings, - settings::{load_settings, GenMode}, + settings::{load_settings, GenMode, SettingsMetadataFetcher}, util::{find_bazel_workspace_root, PlatformDetails}, }; @@ -72,6 +75,28 @@ Options: fn main() -> Result<()> { // Parse options + let options = parse_options(); + + // Load settings + let (local_metadata, settings) = load_raze_settings(&options)?; + + // Fetch metadata + let raze_metadata = fetch_raze_metadata(&options, &settings, &local_metadata)?; + + // Do Planning + let planned_build = do_planning(&settings, &raze_metadata)?; + + // Render BUILD files + let (render_details, bazel_file_outputs) = + render_files(&settings, &raze_metadata, &planned_build, &local_metadata)?; + + // Write BUILD files + write_files(&bazel_file_outputs, &render_details, &settings, &options)?; + + Ok(()) +} + +fn parse_options() -> Options { let options: Options = Docopt::new(USAGE) .map(|d| { d.version(Some( @@ -81,16 +106,61 @@ fn main() -> Result<()> { .and_then(|d| d.deserialize()) .unwrap_or_else(|e| e.exit()); - // Load settings - let manifest_path = PathBuf::from(options - .flag_manifest_path - .unwrap_or("./Cargo.toml".to_owned())).canonicalize()?; - let settings = load_settings(&manifest_path, options.flag_cargo_bin_path.clone())?; + options +} + +fn load_raze_settings(options: &Options) -> Result<(Metadata, RazeSettings)> { + let metadata = fetch_local_metadata(options)?; + + // Parse settings with that metadata + let settings = match load_settings(&metadata) { + Ok(settings) => settings, + Err(err) => return Err(anyhow!(err.to_string())), + }; + if options.flag_verbose.unwrap_or(false) { println!("Loaded override settings: {:#?}", settings); } - // Fetch metadata + Ok((metadata, settings)) +} + +fn fetch_local_metadata(options: &Options) -> Result { + // Gather basic, offline metadata to parse settings from + let fetcher = if let Some(cargo_bin_path) = &options.flag_cargo_bin_path { + SettingsMetadataFetcher { + cargo_bin_path: PathBuf::from(cargo_bin_path), + } + } else { + SettingsMetadataFetcher::default() + }; + + let working_directory = if let Some(manifest_path) = &options.flag_manifest_path { + let manifest_path = PathBuf::from(manifest_path).canonicalize()?; + if !manifest_path.is_file() { + return Err(anyhow!("manifest path `{}` is not a file.", manifest_path.display())); + } + // UNWRAP: Unwrap safe due to check above. + PathBuf::from(manifest_path.parent().unwrap()) + } else { + env::current_dir()? + }; + + fetcher + .fetch_metadata(&working_directory, false) + .with_context(|| { + format!( + "Failed to fetch metadata for {}", + working_directory.display() + ) + }) +} + +fn fetch_raze_metadata( + options: &Options, + settings: &RazeSettings, + local_metadata: &Metadata, +) -> Result { let metadata_fetcher: RazeMetadataFetcher = match options.flag_cargo_bin_path { Some(ref cargo_bin_path) => RazeMetadataFetcher::new( cargo_bin_path, @@ -99,41 +169,61 @@ fn main() -> Result<()> { ), None => RazeMetadataFetcher::default(), }; + let cargo_raze_working_dir = - find_bazel_workspace_root(&manifest_path).unwrap_or(std::env::current_dir()?); - let lock_path_opt = if let Some(parent) = manifest_path.parent() { - let lock_path = parent.join("Cargo.lock"); + find_bazel_workspace_root(&local_metadata.workspace_root).unwrap_or(env::current_dir()?); + + let toml_path = local_metadata.workspace_root.join("Cargo.toml"); + let lock_path_opt = { + let lock_path = local_metadata.workspace_root.join("Cargo.lock"); fs::metadata(&lock_path).ok().map(|_| lock_path) - } else { - None }; + let files = CargoWorkspaceFiles { - toml_path: manifest_path, + toml_path, lock_path_opt, }; + let remote_genmode_inputs = gather_remote_genmode_inputs(&cargo_raze_working_dir, &settings); - let metadata = metadata_fetcher.fetch_metadata( + + let raze_metadata = metadata_fetcher.fetch_metadata( &files, remote_genmode_inputs.binary_deps, remote_genmode_inputs.override_lockfile, )?; - checks::check_metadata(&metadata.metadata, &settings, &cargo_raze_working_dir)?; - // Do Planning + checks::check_metadata(&raze_metadata.metadata, &settings, &cargo_raze_working_dir)?; + Ok(raze_metadata) +} + +fn do_planning( + settings: &RazeSettings, + metadata: &RazeMetadata, +) -> Result { let platform_details = match &settings.target { Some(target) => Some(PlatformDetails::new_using_rustc(target)?), None => None, }; - let planned_build = - BuildPlannerImpl::new(metadata.clone(), settings.clone()).plan_build(platform_details)?; - // Render BUILD files + BuildPlannerImpl::new(metadata.clone(), settings.clone()) + .plan_build(platform_details) +} + +fn render_files( + settings: &RazeSettings, + metadata: &RazeMetadata, + planned_build: &PlannedBuild, + local_metadata: &Metadata, +) -> Result<(RenderDetails, Vec)> { + let cargo_raze_working_dir = + find_bazel_workspace_root(&local_metadata.workspace_root).unwrap_or(env::current_dir()?); + let mut bazel_renderer = BazelRenderer::new(); let render_details = RenderDetails { - cargo_root: metadata.cargo_workspace_root, + cargo_root: metadata.cargo_workspace_root.clone(), path_prefix: PathBuf::from(&settings.workspace_path.trim_start_matches("/")), - package_aliases_dir: settings.package_aliases_dir, - vendored_buildfile_name: settings.output_buildfile_suffix, + package_aliases_dir: settings.package_aliases_dir.clone(), + vendored_buildfile_name: settings.output_buildfile_suffix.clone(), bazel_root: cargo_raze_working_dir, experimental_api: settings.experimental_api, }; @@ -146,11 +236,19 @@ fn main() -> Result<()> { GenMode::Unspecified => Vec::new(), }; - // Write BUILD files + Ok((render_details, bazel_file_outputs)) +} + +fn write_files( + bazel_file_outputs: &Vec, + render_details: &RenderDetails, + settings: &RazeSettings, + options: &Options, +) -> Result<()> { if &settings.genmode == &GenMode::Remote { let remote_dir = render_details .bazel_root - .join(render_details.path_prefix) + .join(&render_details.path_prefix) .join("remote"); // Clean out the "remote" directory so users can easily see what build files are relevant if remote_dir.exists() { @@ -162,6 +260,7 @@ fn main() -> Result<()> { } } } + for output in bazel_file_outputs.iter() { if options.flag_dryrun.unwrap_or(false) { println!("{}:\n{}", output.path.display(), output.contents); diff --git a/impl/src/planning.rs b/impl/src/planning.rs index f66932614..f0067e159 100644 --- a/impl/src/planning.rs +++ b/impl/src/planning.rs @@ -590,7 +590,7 @@ mod tests { let settings = { let (_temp_dir, files) = make_workspace(toml_file, None); - crate::settings::load_settings(&files.toml_path, None).unwrap() + crate::settings::load_settings_from_manifest(&files.toml_path, None).unwrap() }; let planner = BuildPlannerImpl::new( diff --git a/impl/src/settings.rs b/impl/src/settings.rs index 44066f7e0..4849a951e 100644 --- a/impl/src/settings.rs +++ b/impl/src/settings.rs @@ -712,7 +712,7 @@ fn parse_raze_settings_legacy(metadata: &Metadata) -> Result { } /** Parses raze settings from the contents of a `Cargo.toml` file */ -fn parse_raze_settings(metadata: Metadata) -> Result { +fn parse_raze_settings(metadata: &Metadata) -> Result { // Workspace takes precedence let workspace_level_settigns = metadata.workspace_metadata.get("raze"); if let Some(value) = workspace_level_settigns { @@ -727,7 +727,7 @@ fn parse_raze_settings(metadata: Metadata) -> Result { } // Attempt to load legacy settings but do not allow failures to propogate - if let Ok(settings) = parse_raze_settings_legacy(&metadata) { + if let Ok(settings) = parse_raze_settings_legacy(metadata) { eprintln!( "WARNING: The top-level `[raze]` key is deprecated. Please set `[workspace.metadata.raze]` \ or `[package.metadata.raze]` instead." @@ -740,10 +740,18 @@ fn parse_raze_settings(metadata: Metadata) -> Result { } /** A cargo command wrapper for gathering cargo metadata used to parse [RazeSettings](crate::settings::RazeSettings) */ -struct SettingsMetadataFetcher { +pub struct SettingsMetadataFetcher { pub cargo_bin_path: PathBuf, } +impl Default for SettingsMetadataFetcher { + fn default() -> SettingsMetadataFetcher { + SettingsMetadataFetcher { + cargo_bin_path: SYSTEM_CARGO_BIN_PATH.into(), + } + } +} + impl MetadataFetcher for SettingsMetadataFetcher { fn fetch_metadata(&self, working_dir: &Path, _include_deps: bool) -> Result { // This fetch does not require network access. @@ -763,8 +771,8 @@ impl MetadataFetcher for SettingsMetadataFetcher { } } -/** Load settings used to configure the functionality of Cargo Raze */ -pub fn load_settings>( +/** Load settings from a given Cargo manifest */ +pub fn load_settings_from_manifest>( cargo_toml_path: T, cargo_bin_path: Option, ) -> Result { @@ -792,6 +800,11 @@ pub fn load_settings>( result.unwrap() }; + load_settings(&metadata) +} + +/** Load settings used to configure the functionality of Cargo Raze */ +pub fn load_settings(metadata: &Metadata) -> Result { let mut settings = { let result = parse_raze_settings(metadata); if result.is_err() { @@ -801,7 +814,7 @@ pub fn load_settings>( result.unwrap() }; - validate_settings(&mut settings, cargo_toml_dir)?; + validate_settings(&mut settings, &metadata.workspace_root)?; Ok(settings) } @@ -866,7 +879,7 @@ pub mod tests { let cargo_toml_path = temp_workspace_dir.path().join("Cargo.toml"); std::fs::write(&cargo_toml_path, &toml_contents).unwrap(); - let settings = load_settings(cargo_toml_path, None).unwrap(); + let settings = load_settings_from_manifest(cargo_toml_path, None).unwrap(); assert!(settings.binary_deps.len() > 0); } @@ -902,7 +915,7 @@ pub mod tests { let cargo_toml_path = temp_workspace_dir.path().join("Cargo.toml"); std::fs::write(&cargo_toml_path, &toml_contents).unwrap(); - let settings = load_settings(cargo_toml_path, /*cargo_bin_path=*/ None).unwrap(); + let settings = load_settings_from_manifest(cargo_toml_path, /*cargo_bin_path=*/ None).unwrap(); assert!(settings.binary_deps.len() > 0); } @@ -935,7 +948,7 @@ pub mod tests { std::fs::write(crate_toml, toml_contents).unwrap(); } - let settings = load_settings(files.toml_path, None).unwrap(); + let settings = load_settings_from_manifest(files.toml_path, None).unwrap(); assert_eq!(&settings.workspace_path, "//workspace_path/raze"); assert_eq!(settings.genmode, GenMode::Remote); assert_eq!(settings.crates.len(), 2);