fix(solc): convert source paths on windows (#1540)
* fix(solc): convert source paths on windows * refactor: slash all paths * fix: use correct import * feat: slash compiler output * add util function * slash by default * slash artifact id * typo * updat cfg * unify cfg * update changelog
This commit is contained in:
parent
e817073f8e
commit
e62c84d650
|
@ -115,6 +115,8 @@
|
|||
|
||||
### Unreleased
|
||||
|
||||
- On windows all paths in the `ProjectCompilerOutput` are now slashed by default
|
||||
[#1540](https://github.com/gakonst/ethers-rs/pull/1540)
|
||||
- `ArtifactOutput::write_extras` now takes the `Artifacts` directly
|
||||
[#1491](https://github.com/gakonst/ethers-rs/pull/1491)
|
||||
- Make `ethers-solc` optional dependency of `ethers`, needs `ethers-solc` feature to activate
|
||||
|
|
|
@ -42,6 +42,22 @@ pub struct ArtifactId {
|
|||
}
|
||||
|
||||
impl ArtifactId {
|
||||
/// Converts any `\\` separators in the `path` to `/`
|
||||
pub fn slash_paths(&mut self) {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use path_slash::PathBufExt;
|
||||
self.path = self.path.to_slash_lossy().as_ref().into();
|
||||
self.source = self.source.to_slash_lossy().as_ref().into();
|
||||
}
|
||||
}
|
||||
|
||||
/// Convenience function fo [`Self::slash_paths()`]
|
||||
pub fn with_slashed_paths(mut self) -> Self {
|
||||
self.slash_paths();
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns a <filename>:<name> slug that identifies an artifact
|
||||
///
|
||||
/// Note: This identifier is not necessarily unique. If two contracts have the same name, they
|
||||
|
@ -178,6 +194,18 @@ impl<T: Serialize> Artifacts<T> {
|
|||
}
|
||||
|
||||
impl<T> Artifacts<T> {
|
||||
/// Converts all `\\` separators in _all_ paths to `/`
|
||||
pub fn slash_paths(&mut self) {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use path_slash::PathExt;
|
||||
self.0 = std::mem::take(&mut self.0)
|
||||
.into_iter()
|
||||
.map(|(path, files)| (Path::new(&path).to_slash_lossy().to_string(), files))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_inner(self) -> ArtifactsMap<T> {
|
||||
self.0
|
||||
}
|
||||
|
@ -250,7 +278,8 @@ impl<T> Artifacts<T> {
|
|||
name,
|
||||
source: source.clone(),
|
||||
version: artifact.version,
|
||||
},
|
||||
}
|
||||
.with_slashed_paths(),
|
||||
artifact.artifact,
|
||||
)
|
||||
})
|
||||
|
|
|
@ -15,6 +15,17 @@ use std::{collections::BTreeMap, ops::Deref, path::Path};
|
|||
pub struct VersionedContracts(pub FileToContractsMap<Vec<VersionedContract>>);
|
||||
|
||||
impl VersionedContracts {
|
||||
/// Converts all `\\` separators in _all_ paths to `/`
|
||||
pub fn slash_paths(&mut self) {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use path_slash::PathExt;
|
||||
self.0 = std::mem::take(&mut self.0)
|
||||
.into_iter()
|
||||
.map(|(path, files)| (Path::new(&path).to_slash_lossy().to_string(), files))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0.is_empty()
|
||||
}
|
||||
|
|
|
@ -34,6 +34,19 @@ pub struct ProjectCompileOutput<T: ArtifactOutput = ConfigurableArtifacts> {
|
|||
}
|
||||
|
||||
impl<T: ArtifactOutput> ProjectCompileOutput<T> {
|
||||
/// Converts all `\\` separators in _all_ paths to `/`
|
||||
pub fn slash_paths(&mut self) {
|
||||
self.compiler_output.slash_paths();
|
||||
self.compiled_artifacts.slash_paths();
|
||||
self.cached_artifacts.slash_paths();
|
||||
}
|
||||
|
||||
/// Convenience function fo [`Self::slash_paths()`]
|
||||
pub fn with_slashed_paths(mut self) -> Self {
|
||||
self.slash_paths();
|
||||
self
|
||||
}
|
||||
|
||||
/// All artifacts together with their contract file name and name `<file name>:<name>`
|
||||
///
|
||||
/// This returns a chained iterator of both cached and recompiled contract artifacts
|
||||
|
@ -386,6 +399,12 @@ pub struct AggregatedCompilerOutput {
|
|||
}
|
||||
|
||||
impl AggregatedCompilerOutput {
|
||||
/// Converts all `\\` separators in _all_ paths to `/`
|
||||
pub fn slash_paths(&mut self) {
|
||||
self.sources.slash_paths();
|
||||
self.contracts.slash_paths();
|
||||
}
|
||||
|
||||
/// Whether the output contains a compiler error
|
||||
pub fn has_error(&self) -> bool {
|
||||
self.errors.iter().any(|err| err.severity.is_error())
|
||||
|
|
|
@ -9,6 +9,18 @@ use std::{collections::BTreeMap, path::Path};
|
|||
pub struct VersionedSourceFiles(pub BTreeMap<String, Vec<VersionedSourceFile>>);
|
||||
|
||||
impl VersionedSourceFiles {
|
||||
/// Converts all `\\` separators in _all_ paths to `/`
|
||||
pub fn slash_paths(&mut self) {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use path_slash::PathExt;
|
||||
self.0 = std::mem::take(&mut self.0)
|
||||
.into_iter()
|
||||
.map(|(path, files)| (Path::new(&path).to_slash_lossy().to_string(), files))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0.is_empty()
|
||||
}
|
||||
|
|
|
@ -207,15 +207,28 @@ impl<'a, T: ArtifactOutput> ProjectCompiler<'a, T> {
|
|||
/// let output = project.compile().unwrap();
|
||||
/// ```
|
||||
pub fn compile(self) -> Result<ProjectCompileOutput<T>> {
|
||||
let slash_paths = self.project.slash_paths;
|
||||
|
||||
// drive the compiler statemachine to completion
|
||||
self.preprocess()?.compile()?.write_artifacts()?.write_cache()
|
||||
let mut output = self.preprocess()?.compile()?.write_artifacts()?.write_cache()?;
|
||||
|
||||
if slash_paths {
|
||||
// ensures we always use `/` paths
|
||||
output.slash_paths();
|
||||
}
|
||||
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
/// Does basic preprocessing
|
||||
/// - sets proper source unit names
|
||||
/// - check cache
|
||||
fn preprocess(self) -> Result<PreprocessedState<'a, T>> {
|
||||
let Self { edges, project, sources, sparse_output } = self;
|
||||
let Self { edges, project, mut sources, sparse_output } = self;
|
||||
|
||||
// convert paths on windows to ensure consistency with the `CompilerOutput` `solc` emits,
|
||||
// which is unix style `/`
|
||||
sources.slash_paths();
|
||||
|
||||
let mut cache = ArtifactsCache::new(project, edges)?;
|
||||
// retain and compile only dirty sources and all their imports
|
||||
|
@ -344,6 +357,32 @@ enum CompilerSources {
|
|||
}
|
||||
|
||||
impl CompilerSources {
|
||||
/// Converts all `\\` separators to `/`
|
||||
///
|
||||
/// This effectively ensures that `solc` can find imported files like `/src/Cheats.sol` in the
|
||||
/// VFS (the `CompilerInput` as json) under `src/Cheats.sol`.
|
||||
fn slash_paths(&mut self) {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use path_slash::PathBufExt;
|
||||
|
||||
fn slash_versioned_sources(v: &mut VersionedSources) {
|
||||
for (_, (_, sources)) in v {
|
||||
*sources = std::mem::take(sources)
|
||||
.into_iter()
|
||||
.map(|(path, source)| {
|
||||
(PathBuf::from(path.to_slash_lossy().as_ref()), source)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
match self {
|
||||
CompilerSources::Sequential(v) => slash_versioned_sources(v),
|
||||
CompilerSources::Parallel(v, _) => slash_versioned_sources(v),
|
||||
};
|
||||
}
|
||||
}
|
||||
/// Filters out all sources that don't need to be compiled, see [`ArtifactsCache::filter`]
|
||||
fn filtered<T: ArtifactOutput>(self, cache: &mut ArtifactsCache<T>) -> FilteredCompilerSources {
|
||||
fn filtered_sources<T: ArtifactOutput>(
|
||||
|
|
|
@ -8,6 +8,7 @@ use crate::{
|
|||
};
|
||||
|
||||
use crate::artifacts::output_selection::ContractOutputSelection;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
collections::{BTreeSet, HashSet},
|
||||
|
@ -145,6 +146,28 @@ impl ProjectPathsConfig {
|
|||
Ok(Source::read_all_files(self.input_files())?)
|
||||
}
|
||||
|
||||
/// Converts all `\\` separators in _all_ paths to `/`
|
||||
pub fn slash_paths(&mut self) {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use path_slash::PathBufExt;
|
||||
|
||||
let slashed = |p: &mut PathBuf| {
|
||||
*p = p.to_slash_lossy().as_ref().into();
|
||||
};
|
||||
slashed(&mut self.root);
|
||||
slashed(&mut self.cache);
|
||||
slashed(&mut self.artifacts);
|
||||
slashed(&mut self.build_infos);
|
||||
slashed(&mut self.sources);
|
||||
slashed(&mut self.tests);
|
||||
slashed(&mut self.scripts);
|
||||
|
||||
self.libraries.iter_mut().for_each(slashed);
|
||||
self.remappings.iter_mut().for_each(Remapping::slash_path);
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to resolve an `import` from the given working directory.
|
||||
///
|
||||
/// The `cwd` path is the parent dir of the file that includes the `import`
|
||||
|
|
|
@ -78,6 +78,10 @@ pub struct Project<T: ArtifactOutput = ConfigurableArtifacts> {
|
|||
solc_jobs: usize,
|
||||
/// Offline mode, if set, network access (download solc) is disallowed
|
||||
pub offline: bool,
|
||||
/// Windows only config value to ensure the all paths use `/` instead of `\\`, same as `solc`
|
||||
///
|
||||
/// This is a noop on other platforms
|
||||
pub slash_paths: bool,
|
||||
}
|
||||
|
||||
impl Project {
|
||||
|
@ -531,6 +535,8 @@ pub struct ProjectBuilder<T: ArtifactOutput = ConfigurableArtifacts> {
|
|||
auto_detect: bool,
|
||||
/// Use offline mode
|
||||
offline: bool,
|
||||
/// Whether to slash paths of the `ProjectCompilerOutput`
|
||||
slash_paths: bool,
|
||||
/// handles all artifacts related tasks
|
||||
artifacts: T,
|
||||
/// Which error codes to ignore
|
||||
|
@ -552,6 +558,7 @@ impl<T: ArtifactOutput> ProjectBuilder<T> {
|
|||
no_artifacts: false,
|
||||
auto_detect: true,
|
||||
offline: false,
|
||||
slash_paths: true,
|
||||
artifacts,
|
||||
ignored_error_codes: Vec::new(),
|
||||
allowed_paths: vec![],
|
||||
|
@ -626,6 +633,15 @@ impl<T: ArtifactOutput> ProjectBuilder<T> {
|
|||
self
|
||||
}
|
||||
|
||||
/// Sets whether to slash all paths on windows
|
||||
///
|
||||
/// If set to `true` all `\\` separators are replaced with `/`, same as solc
|
||||
#[must_use]
|
||||
pub fn set_slashed_paths(mut self, slashed_paths: bool) -> Self {
|
||||
self.slash_paths = slashed_paths;
|
||||
self
|
||||
}
|
||||
|
||||
/// Disables writing artifacts to disk
|
||||
#[must_use]
|
||||
pub fn no_artifacts(self) -> Self {
|
||||
|
@ -684,6 +700,7 @@ impl<T: ArtifactOutput> ProjectBuilder<T> {
|
|||
solc_jobs,
|
||||
offline,
|
||||
build_info,
|
||||
slash_paths,
|
||||
..
|
||||
} = self;
|
||||
ProjectBuilder {
|
||||
|
@ -694,6 +711,7 @@ impl<T: ArtifactOutput> ProjectBuilder<T> {
|
|||
no_artifacts,
|
||||
auto_detect,
|
||||
offline,
|
||||
slash_paths,
|
||||
artifacts,
|
||||
ignored_error_codes,
|
||||
allowed_paths,
|
||||
|
@ -736,9 +754,15 @@ impl<T: ArtifactOutput> ProjectBuilder<T> {
|
|||
solc_jobs,
|
||||
offline,
|
||||
build_info,
|
||||
slash_paths,
|
||||
} = self;
|
||||
|
||||
let paths = paths.map(Ok).unwrap_or_else(ProjectPathsConfig::current_hardhat)?;
|
||||
let mut paths = paths.map(Ok).unwrap_or_else(ProjectPathsConfig::current_hardhat)?;
|
||||
|
||||
if slash_paths {
|
||||
// ensures we always use `/` paths
|
||||
paths.slash_paths();
|
||||
}
|
||||
|
||||
let solc = solc.unwrap_or_default();
|
||||
let solc_config = solc_config.unwrap_or_else(|| SolcConfig::builder().build());
|
||||
|
@ -761,6 +785,7 @@ impl<T: ArtifactOutput> ProjectBuilder<T> {
|
|||
allowed_lib_paths: allowed_paths.into(),
|
||||
solc_jobs: solc_jobs.unwrap_or_else(::num_cpus::get),
|
||||
offline,
|
||||
slash_paths,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use crate::utils;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
collections::{hash_map::Entry, HashMap},
|
||||
|
@ -224,6 +225,15 @@ impl Remapping {
|
|||
.map(|(name, path)| Remapping { name, path: format!("{}/", path.display()) })
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Converts any `\\` separators in the `path` to `/`
|
||||
pub fn slash_path(&mut self) {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use path_slash::PathExt;
|
||||
self.path = Path::new(&self.path).to_slash_lossy().to_string();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A relative [`Remapping`] that's aware of the current location
|
||||
|
@ -263,7 +273,7 @@ impl RelativeRemapping {
|
|||
impl fmt::Display for RelativeRemapping {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut s = {
|
||||
#[cfg(target_os = "windows")]
|
||||
#[cfg(windows)]
|
||||
{
|
||||
// ensure we have `/` slashes on windows
|
||||
use path_slash::PathExt;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
//! Utility functions
|
||||
|
||||
use cfg_if::cfg_if;
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
ops::Range,
|
||||
|
@ -157,9 +158,22 @@ pub fn is_local_source_name(libs: &[impl AsRef<Path>], source: impl AsRef<Path>)
|
|||
}
|
||||
|
||||
/// Canonicalize the path, platform-agnostic
|
||||
///
|
||||
/// On windows this will ensure the path only consists of `/` separators
|
||||
pub fn canonicalize(path: impl AsRef<Path>) -> Result<PathBuf, SolcIoError> {
|
||||
let path = path.as_ref();
|
||||
dunce::canonicalize(&path).map_err(|err| SolcIoError::new(err, path))
|
||||
cfg_if! {
|
||||
if #[cfg(windows)] {
|
||||
let res = dunce::canonicalize(path).map(|p| {
|
||||
use path_slash::PathBufExt;
|
||||
PathBuf::from(p.to_slash_lossy().as_ref())
|
||||
});
|
||||
} else {
|
||||
let res = dunce::canonicalize(path);
|
||||
}
|
||||
};
|
||||
|
||||
res.map_err(|err| SolcIoError::new(err, path))
|
||||
}
|
||||
|
||||
/// Returns the same path config but with canonicalized paths.
|
||||
|
|
Loading…
Reference in New Issue