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
|
### 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
|
- `ArtifactOutput::write_extras` now takes the `Artifacts` directly
|
||||||
[#1491](https://github.com/gakonst/ethers-rs/pull/1491)
|
[#1491](https://github.com/gakonst/ethers-rs/pull/1491)
|
||||||
- Make `ethers-solc` optional dependency of `ethers`, needs `ethers-solc` feature to activate
|
- Make `ethers-solc` optional dependency of `ethers`, needs `ethers-solc` feature to activate
|
||||||
|
|
|
@ -42,6 +42,22 @@ pub struct ArtifactId {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl 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
|
/// Returns a <filename>:<name> slug that identifies an artifact
|
||||||
///
|
///
|
||||||
/// Note: This identifier is not necessarily unique. If two contracts have the same name, they
|
/// 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> {
|
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> {
|
pub fn into_inner(self) -> ArtifactsMap<T> {
|
||||||
self.0
|
self.0
|
||||||
}
|
}
|
||||||
|
@ -250,7 +278,8 @@ impl<T> Artifacts<T> {
|
||||||
name,
|
name,
|
||||||
source: source.clone(),
|
source: source.clone(),
|
||||||
version: artifact.version,
|
version: artifact.version,
|
||||||
},
|
}
|
||||||
|
.with_slashed_paths(),
|
||||||
artifact.artifact,
|
artifact.artifact,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
|
@ -15,6 +15,17 @@ use std::{collections::BTreeMap, ops::Deref, path::Path};
|
||||||
pub struct VersionedContracts(pub FileToContractsMap<Vec<VersionedContract>>);
|
pub struct VersionedContracts(pub FileToContractsMap<Vec<VersionedContract>>);
|
||||||
|
|
||||||
impl VersionedContracts {
|
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 {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.0.is_empty()
|
self.0.is_empty()
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,19 @@ pub struct ProjectCompileOutput<T: ArtifactOutput = ConfigurableArtifacts> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ArtifactOutput> ProjectCompileOutput<T> {
|
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>`
|
/// 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
|
/// This returns a chained iterator of both cached and recompiled contract artifacts
|
||||||
|
@ -386,6 +399,12 @@ pub struct AggregatedCompilerOutput {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl 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
|
/// Whether the output contains a compiler error
|
||||||
pub fn has_error(&self) -> bool {
|
pub fn has_error(&self) -> bool {
|
||||||
self.errors.iter().any(|err| err.severity.is_error())
|
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>>);
|
pub struct VersionedSourceFiles(pub BTreeMap<String, Vec<VersionedSourceFile>>);
|
||||||
|
|
||||||
impl VersionedSourceFiles {
|
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 {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.0.is_empty()
|
self.0.is_empty()
|
||||||
}
|
}
|
||||||
|
|
|
@ -207,15 +207,28 @@ impl<'a, T: ArtifactOutput> ProjectCompiler<'a, T> {
|
||||||
/// let output = project.compile().unwrap();
|
/// let output = project.compile().unwrap();
|
||||||
/// ```
|
/// ```
|
||||||
pub fn compile(self) -> Result<ProjectCompileOutput<T>> {
|
pub fn compile(self) -> Result<ProjectCompileOutput<T>> {
|
||||||
|
let slash_paths = self.project.slash_paths;
|
||||||
|
|
||||||
// drive the compiler statemachine to completion
|
// 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
|
/// Does basic preprocessing
|
||||||
/// - sets proper source unit names
|
/// - sets proper source unit names
|
||||||
/// - check cache
|
/// - check cache
|
||||||
fn preprocess(self) -> Result<PreprocessedState<'a, T>> {
|
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)?;
|
let mut cache = ArtifactsCache::new(project, edges)?;
|
||||||
// retain and compile only dirty sources and all their imports
|
// retain and compile only dirty sources and all their imports
|
||||||
|
@ -344,6 +357,32 @@ enum CompilerSources {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl 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`]
|
/// 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<T: ArtifactOutput>(self, cache: &mut ArtifactsCache<T>) -> FilteredCompilerSources {
|
||||||
fn filtered_sources<T: ArtifactOutput>(
|
fn filtered_sources<T: ArtifactOutput>(
|
||||||
|
|
|
@ -8,6 +8,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::artifacts::output_selection::ContractOutputSelection;
|
use crate::artifacts::output_selection::ContractOutputSelection;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{
|
use std::{
|
||||||
collections::{BTreeSet, HashSet},
|
collections::{BTreeSet, HashSet},
|
||||||
|
@ -145,6 +146,28 @@ impl ProjectPathsConfig {
|
||||||
Ok(Source::read_all_files(self.input_files())?)
|
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.
|
/// Attempts to resolve an `import` from the given working directory.
|
||||||
///
|
///
|
||||||
/// The `cwd` path is the parent dir of the file that includes the `import`
|
/// 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,
|
solc_jobs: usize,
|
||||||
/// Offline mode, if set, network access (download solc) is disallowed
|
/// Offline mode, if set, network access (download solc) is disallowed
|
||||||
pub offline: bool,
|
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 {
|
impl Project {
|
||||||
|
@ -531,6 +535,8 @@ pub struct ProjectBuilder<T: ArtifactOutput = ConfigurableArtifacts> {
|
||||||
auto_detect: bool,
|
auto_detect: bool,
|
||||||
/// Use offline mode
|
/// Use offline mode
|
||||||
offline: bool,
|
offline: bool,
|
||||||
|
/// Whether to slash paths of the `ProjectCompilerOutput`
|
||||||
|
slash_paths: bool,
|
||||||
/// handles all artifacts related tasks
|
/// handles all artifacts related tasks
|
||||||
artifacts: T,
|
artifacts: T,
|
||||||
/// Which error codes to ignore
|
/// Which error codes to ignore
|
||||||
|
@ -552,6 +558,7 @@ impl<T: ArtifactOutput> ProjectBuilder<T> {
|
||||||
no_artifacts: false,
|
no_artifacts: false,
|
||||||
auto_detect: true,
|
auto_detect: true,
|
||||||
offline: false,
|
offline: false,
|
||||||
|
slash_paths: true,
|
||||||
artifacts,
|
artifacts,
|
||||||
ignored_error_codes: Vec::new(),
|
ignored_error_codes: Vec::new(),
|
||||||
allowed_paths: vec![],
|
allowed_paths: vec![],
|
||||||
|
@ -626,6 +633,15 @@ impl<T: ArtifactOutput> ProjectBuilder<T> {
|
||||||
self
|
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
|
/// Disables writing artifacts to disk
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn no_artifacts(self) -> Self {
|
pub fn no_artifacts(self) -> Self {
|
||||||
|
@ -684,6 +700,7 @@ impl<T: ArtifactOutput> ProjectBuilder<T> {
|
||||||
solc_jobs,
|
solc_jobs,
|
||||||
offline,
|
offline,
|
||||||
build_info,
|
build_info,
|
||||||
|
slash_paths,
|
||||||
..
|
..
|
||||||
} = self;
|
} = self;
|
||||||
ProjectBuilder {
|
ProjectBuilder {
|
||||||
|
@ -694,6 +711,7 @@ impl<T: ArtifactOutput> ProjectBuilder<T> {
|
||||||
no_artifacts,
|
no_artifacts,
|
||||||
auto_detect,
|
auto_detect,
|
||||||
offline,
|
offline,
|
||||||
|
slash_paths,
|
||||||
artifacts,
|
artifacts,
|
||||||
ignored_error_codes,
|
ignored_error_codes,
|
||||||
allowed_paths,
|
allowed_paths,
|
||||||
|
@ -736,9 +754,15 @@ impl<T: ArtifactOutput> ProjectBuilder<T> {
|
||||||
solc_jobs,
|
solc_jobs,
|
||||||
offline,
|
offline,
|
||||||
build_info,
|
build_info,
|
||||||
|
slash_paths,
|
||||||
} = self;
|
} = 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 = solc.unwrap_or_default();
|
||||||
let solc_config = solc_config.unwrap_or_else(|| SolcConfig::builder().build());
|
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(),
|
allowed_lib_paths: allowed_paths.into(),
|
||||||
solc_jobs: solc_jobs.unwrap_or_else(::num_cpus::get),
|
solc_jobs: solc_jobs.unwrap_or_else(::num_cpus::get),
|
||||||
offline,
|
offline,
|
||||||
|
slash_paths,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::utils;
|
use crate::utils;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{
|
use std::{
|
||||||
collections::{hash_map::Entry, HashMap},
|
collections::{hash_map::Entry, HashMap},
|
||||||
|
@ -224,6 +225,15 @@ impl Remapping {
|
||||||
.map(|(name, path)| Remapping { name, path: format!("{}/", path.display()) })
|
.map(|(name, path)| Remapping { name, path: format!("{}/", path.display()) })
|
||||||
.collect()
|
.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
|
/// A relative [`Remapping`] that's aware of the current location
|
||||||
|
@ -263,7 +273,7 @@ impl RelativeRemapping {
|
||||||
impl fmt::Display for RelativeRemapping {
|
impl fmt::Display for RelativeRemapping {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
let mut s = {
|
let mut s = {
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(windows)]
|
||||||
{
|
{
|
||||||
// ensure we have `/` slashes on windows
|
// ensure we have `/` slashes on windows
|
||||||
use path_slash::PathExt;
|
use path_slash::PathExt;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
//! Utility functions
|
//! Utility functions
|
||||||
|
|
||||||
|
use cfg_if::cfg_if;
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashSet,
|
collections::HashSet,
|
||||||
ops::Range,
|
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
|
/// 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> {
|
pub fn canonicalize(path: impl AsRef<Path>) -> Result<PathBuf, SolcIoError> {
|
||||||
let path = path.as_ref();
|
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.
|
/// Returns the same path config but with canonicalized paths.
|
||||||
|
|
Loading…
Reference in New Issue