Skip to content

Commit

Permalink
Cargo raze can now be run from anywhere within a cargo workspace (#330)
Browse files Browse the repository at this point in the history
* Cargo raze can now be run from anywhere within a cargo workspace

* Addressed PR feedback
  • Loading branch information
UebelAndre committed Dec 29, 2020
1 parent e763292 commit 8c96a39
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 37 deletions.
153 changes: 126 additions & 27 deletions impl/src/bin/cargo-raze.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
};

Expand Down Expand Up @@ -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(
Expand All @@ -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<Metadata> {
// 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<RazeMetadata> {
let metadata_fetcher: RazeMetadataFetcher = match options.flag_cargo_bin_path {
Some(ref cargo_bin_path) => RazeMetadataFetcher::new(
cargo_bin_path,
Expand All @@ -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<PlannedBuild> {
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<FileOutputs>)> {
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,
};
Expand All @@ -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<FileOutputs>,
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() {
Expand All @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion impl/src/planning.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
31 changes: 22 additions & 9 deletions impl/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -712,7 +712,7 @@ fn parse_raze_settings_legacy(metadata: &Metadata) -> Result<RazeSettings> {
}

/** Parses raze settings from the contents of a `Cargo.toml` file */
fn parse_raze_settings(metadata: Metadata) -> Result<RazeSettings> {
fn parse_raze_settings(metadata: &Metadata) -> Result<RazeSettings> {
// Workspace takes precedence
let workspace_level_settigns = metadata.workspace_metadata.get("raze");
if let Some(value) = workspace_level_settigns {
Expand All @@ -727,7 +727,7 @@ fn parse_raze_settings(metadata: Metadata) -> Result<RazeSettings> {
}

// 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."
Expand All @@ -740,10 +740,18 @@ fn parse_raze_settings(metadata: Metadata) -> Result<RazeSettings> {
}

/** 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<Metadata> {
// This fetch does not require network access.
Expand All @@ -763,8 +771,8 @@ impl MetadataFetcher for SettingsMetadataFetcher {
}
}

/** Load settings used to configure the functionality of Cargo Raze */
pub fn load_settings<T: AsRef<Path>>(
/** Load settings from a given Cargo manifest */
pub fn load_settings_from_manifest<T: AsRef<Path>>(
cargo_toml_path: T,
cargo_bin_path: Option<String>,
) -> Result<RazeSettings, RazeError> {
Expand Down Expand Up @@ -792,6 +800,11 @@ pub fn load_settings<T: AsRef<Path>>(
result.unwrap()
};

load_settings(&metadata)
}

/** Load settings used to configure the functionality of Cargo Raze */
pub fn load_settings(metadata: &Metadata) -> Result<RazeSettings, RazeError> {
let mut settings = {
let result = parse_raze_settings(metadata);
if result.is_err() {
Expand All @@ -801,7 +814,7 @@ pub fn load_settings<T: AsRef<Path>>(
result.unwrap()
};

validate_settings(&mut settings, cargo_toml_dir)?;
validate_settings(&mut settings, &metadata.workspace_root)?;

Ok(settings)
}
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit 8c96a39

Please sign in to comment.