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

Rewrite relative path for dev-dependency #760

Merged
merged 3 commits into from
Sep 30, 2022
Merged
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
234 changes: 126 additions & 108 deletions crates/cargo-contract/src/workspace/manifest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ use super::{
use crate::OptimizationPasses;

use std::{
collections::HashSet,
convert::TryFrom,
fs,
path::{
Expand Down Expand Up @@ -326,114 +325,13 @@ impl Manifest {
/// - `[dependencies]`
///
/// Dependencies with package names specified in `exclude_deps` will not be rewritten.
pub(super) fn rewrite_relative_paths<I, S>(
&mut self,
exclude_deps: I,
) -> Result<&mut Self>
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
let abs_path = self.path.as_ref().canonicalize()?;
let abs_dir = abs_path
.parent()
.expect("The manifest path is a file path so has a parent; qed");

let to_absolute = |value_id: String,
existing_path: &mut value::Value|
-> Result<()> {
let path_str = existing_path
.as_str()
.ok_or_else(|| anyhow::anyhow!("{} should be a string", value_id))?;
#[cfg(windows)]
// On Windows path separators are `\`, hence we need to replace the `/` in
// e.g. `src/lib.rs`.
let path_str = &path_str.replace("/", "\\");
let path = PathBuf::from(path_str);
if path.is_relative() {
let lib_abs = abs_dir.join(path);
tracing::debug!("Rewriting {} to '{}'", value_id, lib_abs.display());
*existing_path = value::Value::String(lib_abs.to_string_lossy().into())
}
Ok(())
pub fn rewrite_relative_paths(&mut self, exclude_deps: &[String]) -> Result<()> {
let manifest_dir = self.path.absolute_directory()?;
let path_rewrite = PathRewrite {
exclude_deps,
manifest_dir,
};

let rewrite_path =
|table_value: &mut value::Value, table_section: &str, default: &str| {
let table = table_value.as_table_mut().ok_or_else(|| {
anyhow::anyhow!("'[{}]' section should be a table", table_section)
})?;

match table.get_mut("path") {
Some(existing_path) => {
to_absolute(format!("[{}]/path", table_section), existing_path)
}
None => {
let default_path = PathBuf::from(default);
if !default_path.exists() {
anyhow::bail!(
"No path specified, and the default `{}` was not found",
default
)
}
let path = abs_dir.join(default_path);
tracing::debug!("Adding default path '{}'", path.display());
table.insert(
"path".into(),
value::Value::String(path.to_string_lossy().into()),
);
Ok(())
}
}
};

// Rewrite `[lib] path = /path/to/lib.rs`
if let Some(lib) = self.toml.get_mut("lib") {
rewrite_path(lib, "lib", "src/lib.rs")?;
}

// Rewrite `[[bin]] path = /path/to/main.rs`
if let Some(bin) = self.toml.get_mut("bin") {
let bins = bin.as_array_mut().ok_or_else(|| {
anyhow::anyhow!("'[[bin]]' section should be a table array")
})?;

// Rewrite `[[bin]] path =` value to an absolute path.
for bin in bins {
rewrite_path(bin, "[bin]", "src/main.rs")?;
}
}

// Rewrite any dependency relative paths
if let Some(dependencies) = self.toml.get_mut("dependencies") {
let exclude = exclude_deps
.into_iter()
.map(|s| s.as_ref().to_string())
.collect::<HashSet<_>>();
let table = dependencies
.as_table_mut()
.ok_or_else(|| anyhow::anyhow!("dependencies should be a table"))?;
for (name, value) in table {
let package_name = {
let package = value.get("package");
let package_name = package.and_then(|p| p.as_str()).unwrap_or(name);
package_name.to_string()
};

if !exclude.contains(&package_name) {
if let Some(dependency) = value.as_table_mut() {
if let Some(dep_path) = dependency.get_mut("path") {
to_absolute(
format!("dependency {}", package_name),
dep_path,
)?;
}
}
}
}
}

Ok(self)
path_rewrite.rewrite_relative_paths(&mut self.toml)
}

/// Writes the amended manifest to the given path.
Expand Down Expand Up @@ -484,6 +382,126 @@ impl Manifest {
}
}

/// Replace relative paths with absolute paths with the working directory.
struct PathRewrite<'a> {
exclude_deps: &'a [String],
manifest_dir: PathBuf,
}

impl<'a> PathRewrite<'a> {
/// Replace relative paths with absolute paths with the working directory.
fn rewrite_relative_paths(&self, toml: &mut value::Table) -> Result<()> {
// Rewrite `[lib] path = /path/to/lib.rs`
if let Some(lib) = toml.get_mut("lib") {
self.rewrite_path(lib, "lib", "src/lib.rs")?;
}

// Rewrite `[[bin]] path = /path/to/main.rs`
if let Some(bin) = toml.get_mut("bin") {
let bins = bin.as_array_mut().ok_or_else(|| {
anyhow::anyhow!("'[[bin]]' section should be a table array")
})?;

// Rewrite `[[bin]] path =` value to an absolute path.
for bin in bins {
self.rewrite_path(bin, "[bin]", "src/main.rs")?;
}
}

self.rewrite_dependencies_relative_paths(toml, "dependencies")?;
self.rewrite_dependencies_relative_paths(toml, "dev-dependencies")?;

Ok(())
}

fn rewrite_path(
&self,
table_value: &mut value::Value,
table_section: &str,
default: &str,
) -> Result<()> {
let table = table_value.as_table_mut().ok_or_else(|| {
anyhow::anyhow!("'[{}]' section should be a table", table_section)
})?;

match table.get_mut("path") {
Some(existing_path) => {
self.to_absolute_path(format!("[{}]/path", table_section), existing_path)
}
None => {
let default_path = PathBuf::from(default);
if !default_path.exists() {
anyhow::bail!(
"No path specified, and the default `{}` was not found",
default
)
}
let path = self.manifest_dir.join(default_path);
tracing::debug!("Adding default path '{}'", path.display());
table.insert(
"path".into(),
value::Value::String(path.to_string_lossy().into()),
);
Ok(())
}
}
}

/// Expand a relative path to an absolute path.
fn to_absolute_path(
&self,
value_id: String,
existing_path: &mut value::Value,
) -> Result<()> {
let path_str = existing_path
.as_str()
.ok_or_else(|| anyhow::anyhow!("{} should be a string", value_id))?;
#[cfg(windows)]
// On Windows path separators are `\`, hence we need to replace the `/` in
// e.g. `src/lib.rs`.
let path_str = &path_str.replace("/", "\\");
let path = PathBuf::from(path_str);
if path.is_relative() {
let lib_abs = self.manifest_dir.join(path);
tracing::debug!("Rewriting {} to '{}'", value_id, lib_abs.display());
*existing_path = value::Value::String(lib_abs.to_string_lossy().into())
}
Ok(())
}

/// Rewrite the relative paths of dependencies.
fn rewrite_dependencies_relative_paths(
&self,
toml: &mut value::Table,
section_name: &str,
) -> Result<()> {
if let Some(dependencies) = toml.get_mut(section_name) {
let table = dependencies
.as_table_mut()
.ok_or_else(|| anyhow::anyhow!("dependencies should be a table"))?;
for (name, value) in table {
let package_name = {
let package = value.get("package");
let package_name = package.and_then(|p| p.as_str()).unwrap_or(name);
package_name.to_string()
};

if !self.exclude_deps.contains(&package_name) {
if let Some(dependency) = value.as_table_mut() {
if let Some(dep_path) = dependency.get_mut("path") {
self.to_absolute_path(
format!("dependency {}", package_name),
dep_path,
)?;
}
}
}
}
}
Ok(())
}
}

fn crate_type_exists(crate_type: &str, crate_types: &[value::Value]) -> bool {
crate_types
.iter()
Expand Down