feat(solc): yul compilation (#994)
* added yul compilation * fix doctest * fixes * changelog * cleanup * fmt * test update * lint * ci fix Co-authored-by: Georgios Konstantopoulos <me@gakonst.com>
This commit is contained in:
parent
37c25df5a5
commit
e1d66b8cd3
|
@ -47,6 +47,7 @@
|
||||||
- Add a getter to `ProjectCompileOutput` that returns a mapping of compiler
|
- Add a getter to `ProjectCompileOutput` that returns a mapping of compiler
|
||||||
versions to a vector of name + contract struct tuples
|
versions to a vector of name + contract struct tuples
|
||||||
[#908](https://github.com/gakonst/ethers-rs/pull/908)
|
[#908](https://github.com/gakonst/ethers-rs/pull/908)
|
||||||
|
- Add Yul compilation [994](https://github.com/gakonst/ethers-rs/pull/994)
|
||||||
- Enforce commutativity of ENS reverse resolution
|
- Enforce commutativity of ENS reverse resolution
|
||||||
[#996](https://github.com/gakonst/ethers-rs/pull/996)
|
[#996](https://github.com/gakonst/ethers-rs/pull/996)
|
||||||
|
|
||||||
|
|
|
@ -52,13 +52,37 @@ pub struct CompilerInput {
|
||||||
|
|
||||||
impl CompilerInput {
|
impl CompilerInput {
|
||||||
/// Reads all contracts found under the path
|
/// Reads all contracts found under the path
|
||||||
pub fn new(path: impl AsRef<Path>) -> Result<Self, SolcIoError> {
|
pub fn new(path: impl AsRef<Path>) -> Result<Vec<Self>, SolcIoError> {
|
||||||
Source::read_all_from(path.as_ref()).map(Self::with_sources)
|
Source::read_all_from(path.as_ref()).map(Self::with_sources)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new Compiler input with default settings and the given sources
|
/// Creates a new Compiler input with default settings and the given sources
|
||||||
pub fn with_sources(sources: Sources) -> Self {
|
pub fn with_sources(sources: Sources) -> Vec<Self> {
|
||||||
Self { language: "Solidity".to_string(), sources, settings: Default::default() }
|
let mut solidity_sources = BTreeMap::new();
|
||||||
|
let mut yul_sources = BTreeMap::new();
|
||||||
|
for (path, source) in sources {
|
||||||
|
if path.extension() == Some(std::ffi::OsStr::new("yul")) {
|
||||||
|
yul_sources.insert(path, source);
|
||||||
|
} else {
|
||||||
|
solidity_sources.insert(path, source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut res = Vec::new();
|
||||||
|
if !solidity_sources.is_empty() {
|
||||||
|
res.push(Self {
|
||||||
|
language: "Solidity".to_string(),
|
||||||
|
sources: solidity_sources,
|
||||||
|
settings: Default::default(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if !yul_sources.is_empty() {
|
||||||
|
res.push(Self {
|
||||||
|
language: "Yul".to_string(),
|
||||||
|
sources: yul_sources,
|
||||||
|
settings: Default::default(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the settings for compilation
|
/// Sets the settings for compilation
|
||||||
|
@ -99,12 +123,6 @@ impl CompilerInput {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for CompilerInput {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::with_sources(Default::default())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
|
@ -805,6 +823,12 @@ impl CompilerOutput {
|
||||||
err.source_location.as_ref().map(|s| files.contains(s.file.as_str())).unwrap_or(true)
|
err.source_location.as_ref().map(|s| files.contains(s.file.as_str())).unwrap_or(true)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn merge(&mut self, other: CompilerOutput) {
|
||||||
|
self.errors.extend(other.errors);
|
||||||
|
self.contracts.extend(other.contracts);
|
||||||
|
self.sources.extend(other.sources);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A wrapper helper type for the `Contracts` type alias
|
/// A wrapper helper type for the `Contracts` type alias
|
||||||
|
|
|
@ -410,7 +410,12 @@ impl Solc {
|
||||||
/// Convenience function for compiling all sources under the given path
|
/// Convenience function for compiling all sources under the given path
|
||||||
pub fn compile_source(&self, path: impl AsRef<Path>) -> Result<CompilerOutput> {
|
pub fn compile_source(&self, path: impl AsRef<Path>) -> Result<CompilerOutput> {
|
||||||
let path = path.as_ref();
|
let path = path.as_ref();
|
||||||
self.compile(&CompilerInput::new(path)?)
|
let mut res: CompilerOutput = Default::default();
|
||||||
|
for input in CompilerInput::new(path)? {
|
||||||
|
let output = self.compile(&input)?;
|
||||||
|
res.merge(output)
|
||||||
|
}
|
||||||
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Same as [`Self::compile()`], but only returns those files which are included in the
|
/// Same as [`Self::compile()`], but only returns those files which are included in the
|
||||||
|
@ -425,7 +430,7 @@ impl Solc {
|
||||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
/// use ethers_solc::{CompilerInput, Solc};
|
/// use ethers_solc::{CompilerInput, Solc};
|
||||||
/// let solc = Solc::default();
|
/// let solc = Solc::default();
|
||||||
/// let input = CompilerInput::new("./contracts")?;
|
/// let input = CompilerInput::new("./contracts")?[0].clone();
|
||||||
/// let output = solc.compile_exact(&input)?;
|
/// let output = solc.compile_exact(&input)?;
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
|
@ -571,8 +576,8 @@ impl Solc {
|
||||||
/// use ethers_solc::{CompilerInput, Solc};
|
/// use ethers_solc::{CompilerInput, Solc};
|
||||||
/// let solc1 = Solc::default();
|
/// let solc1 = Solc::default();
|
||||||
/// let solc2 = Solc::default();
|
/// let solc2 = Solc::default();
|
||||||
/// let input1 = CompilerInput::new("contracts").unwrap();
|
/// let input1 = CompilerInput::new("contracts").unwrap()[0].clone();
|
||||||
/// let input2 = CompilerInput::new("src").unwrap();
|
/// let input2 = CompilerInput::new("src").unwrap()[0].clone();
|
||||||
///
|
///
|
||||||
/// let outputs = Solc::compile_many([(solc1, input1), (solc2, input2)], 2).await.flattened().unwrap();
|
/// let outputs = Solc::compile_many([(solc1, input1), (solc2, input2)], 2).await.flattened().unwrap();
|
||||||
/// # }
|
/// # }
|
||||||
|
|
|
@ -341,24 +341,23 @@ fn compile_sequential(
|
||||||
solc.args
|
solc.args
|
||||||
);
|
);
|
||||||
|
|
||||||
let input = CompilerInput::with_sources(sources)
|
for input in CompilerInput::with_sources(sources) {
|
||||||
|
let input = input
|
||||||
.settings(settings.clone())
|
.settings(settings.clone())
|
||||||
.normalize_evm_version(&version)
|
.normalize_evm_version(&version)
|
||||||
.with_remappings(paths.remappings.clone());
|
.with_remappings(paths.remappings.clone());
|
||||||
|
|
||||||
tracing::trace!(
|
tracing::trace!(
|
||||||
"calling solc `{}` with {} sources {:?}",
|
"calling solc `{}` with {} sources {:?}",
|
||||||
version,
|
version,
|
||||||
input.sources.len(),
|
input.sources.len(),
|
||||||
input.sources.keys()
|
input.sources.keys()
|
||||||
);
|
);
|
||||||
|
|
||||||
report::solc_spawn(&solc, &version, &input);
|
report::solc_spawn(&solc, &version, &input);
|
||||||
let output = solc.compile_exact(&input)?;
|
let output = solc.compile_exact(&input)?;
|
||||||
report::solc_success(&solc, &version, &output);
|
report::solc_success(&solc, &version, &output);
|
||||||
tracing::trace!("compiled input, output has error: {}", output.has_error());
|
tracing::trace!("compiled input, output has error: {}", output.has_error());
|
||||||
|
aggregated.extend(version.clone(), output);
|
||||||
aggregated.extend(version, output);
|
}
|
||||||
}
|
}
|
||||||
Ok(aggregated)
|
Ok(aggregated)
|
||||||
}
|
}
|
||||||
|
@ -383,13 +382,14 @@ fn compile_parallel(
|
||||||
// nothing to compile
|
// nothing to compile
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
for input in CompilerInput::with_sources(sources) {
|
||||||
let job = CompilerInput::with_sources(sources)
|
let job = input
|
||||||
.settings(settings.clone())
|
.settings(settings.clone())
|
||||||
.normalize_evm_version(&version)
|
.normalize_evm_version(&version)
|
||||||
.with_remappings(paths.remappings.clone());
|
.with_remappings(paths.remappings.clone());
|
||||||
|
|
||||||
jobs.push((solc, version, job))
|
jobs.push((solc.clone(), version.clone(), job))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// start a rayon threadpool that will execute all `Solc::compile()` processes
|
// start a rayon threadpool that will execute all `Solc::compile()` processes
|
||||||
|
|
|
@ -67,7 +67,9 @@ pub fn source_files(root: impl AsRef<Path>) -> Vec<PathBuf> {
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(Result::ok)
|
.filter_map(Result::ok)
|
||||||
.filter(|e| e.file_type().is_file())
|
.filter(|e| e.file_type().is_file())
|
||||||
.filter(|e| e.path().extension().map(|ext| ext == "sol").unwrap_or_default())
|
.filter(|e| {
|
||||||
|
e.path().extension().map(|ext| (ext == "sol") || (ext == "yul")).unwrap_or_default()
|
||||||
|
})
|
||||||
.map(|e| e.path().into())
|
.map(|e| e.path().into())
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
pragma solidity >=0.6.6;
|
||||||
|
|
||||||
|
contract Dapp {
|
||||||
|
|
||||||
|
function modified() public {}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
object "SimpleStore" {
|
||||||
|
code {
|
||||||
|
datacopy(0, dataoffset("Runtime"), datasize("Runtime"))
|
||||||
|
return(0, datasize("Runtime"))
|
||||||
|
}
|
||||||
|
object "Runtime" {
|
||||||
|
code {
|
||||||
|
calldatacopy(0, 0, 36) // write calldata to memory
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -92,6 +92,36 @@ fn can_compile_dapp_sample() {
|
||||||
assert_eq!(cache, updated_cache);
|
assert_eq!(cache, updated_cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn can_compile_yul_sample() {
|
||||||
|
let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("test-data/yul-sample");
|
||||||
|
let paths = ProjectPathsConfig::builder().sources(root);
|
||||||
|
let project = TempProject::<ConfigurableArtifacts>::new(paths).unwrap();
|
||||||
|
|
||||||
|
let compiled = project.compile().unwrap();
|
||||||
|
assert!(compiled.find("Dapp").is_some());
|
||||||
|
assert!(compiled.find("SimpleStore").is_some());
|
||||||
|
assert!(!compiled.has_compiler_errors());
|
||||||
|
|
||||||
|
// nothing to compile
|
||||||
|
let compiled = project.compile().unwrap();
|
||||||
|
assert!(compiled.find("Dapp").is_some());
|
||||||
|
assert!(compiled.find("SimpleStore").is_some());
|
||||||
|
assert!(compiled.is_unchanged());
|
||||||
|
|
||||||
|
let cache = SolFilesCache::read(project.cache_path()).unwrap();
|
||||||
|
|
||||||
|
// delete artifacts
|
||||||
|
std::fs::remove_dir_all(&project.paths().artifacts).unwrap();
|
||||||
|
let compiled = project.compile().unwrap();
|
||||||
|
assert!(compiled.find("Dapp").is_some());
|
||||||
|
assert!(compiled.find("SimpleStore").is_some());
|
||||||
|
assert!(!compiled.is_unchanged());
|
||||||
|
|
||||||
|
let updated_cache = SolFilesCache::read(project.cache_path()).unwrap();
|
||||||
|
assert_eq!(cache, updated_cache);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn can_compile_configured() {
|
fn can_compile_configured() {
|
||||||
let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("test-data/dapp-sample");
|
let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("test-data/dapp-sample");
|
||||||
|
|
Loading…
Reference in New Issue