Skip to content

Commit

Permalink
Get features from all targets - refactor (#2600)
Browse files Browse the repository at this point in the history
<!-- Reference any GitHub issues resolved by this PR -->

Closes #

## Introduced changes

<!-- A brief description of the changes -->

Part of stack

Base: #2570

## Checklist

<!-- Make sure all of these are complete -->

- [x] Linked relevant issue
- [x] Updated relevant documentation
- [x] Added relevant tests
- [x] Performed self-review of the code
- [x] Added changes to `CHANGELOG.md`
  • Loading branch information
cptartur authored Oct 23, 2024
1 parent 6e3094d commit 426d245
Show file tree
Hide file tree
Showing 7 changed files with 242 additions and 180 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/scarb-api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ which.workspace = true
semver.workspace = true
regex.workspace = true
rayon.workspace = true
itertools.workspace = true
universal-sierra-compiler-api = { path = "../universal-sierra-compiler-api" }

[dev-dependencies]
Expand Down
4 changes: 4 additions & 0 deletions crates/scarb-api/src/artifacts.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#[allow(clippy::module_inception)]
pub mod artifacts;
pub mod deserialized;
pub mod representation;
89 changes: 89 additions & 0 deletions crates/scarb-api/src/artifacts/artifacts.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use anyhow::Result;

use crate::artifacts::representation::StarknetArtifactsRepresentation;
use camino::{Utf8Path, Utf8PathBuf};
use itertools::Itertools;
use rayon::iter::{IntoParallelIterator, ParallelIterator};
use std::collections::HashMap;
use std::fs;
use universal_sierra_compiler_api::{compile_sierra_at_path, SierraType};

/// Contains compiled Starknet artifacts
#[derive(Debug, PartialEq, Clone)]
pub struct StarknetContractArtifacts {
/// Compiled sierra code
pub sierra: String,
/// Compiled casm code
pub casm: String,
}

#[derive(PartialEq, Debug)]
pub(crate) struct StarknetArtifactsFiles {
base_file: Utf8PathBuf,
other_files: Vec<Utf8PathBuf>,
}

impl StarknetArtifactsFiles {
pub(crate) fn new(base_file: Utf8PathBuf, other_files: Vec<Utf8PathBuf>) -> Self {
Self {
base_file,
other_files,
}
}

pub(crate) fn load_contracts_artifacts(
self,
) -> Result<HashMap<String, (StarknetContractArtifacts, Utf8PathBuf)>> {
let mut base_artifacts: HashMap<String, (StarknetContractArtifacts, Utf8PathBuf)> =
compile_artifacts(
StarknetArtifactsRepresentation::try_from_path(self.base_file.as_path())?
.artifacts(),
)?;

let other_artifact_representations: Vec<StarknetArtifactsRepresentation> = self
.other_files
.iter()
.map(|path| StarknetArtifactsRepresentation::try_from_path(path.as_path()))
.collect::<Result<_>>()?;

let other_artifacts: Vec<(String, Utf8PathBuf)> =
unique_artifacts(other_artifact_representations, &base_artifacts);

let compiled_artifacts = compile_artifacts(other_artifacts)?;

base_artifacts.extend(compiled_artifacts);

Ok(base_artifacts)
}
}

fn unique_artifacts(
artifact_representations: Vec<StarknetArtifactsRepresentation>,
current_artifacts: &HashMap<String, (StarknetContractArtifacts, Utf8PathBuf)>,
) -> Vec<(String, Utf8PathBuf)> {
artifact_representations
.into_iter()
.flat_map(StarknetArtifactsRepresentation::artifacts)
.unique()
.filter(|(name, _)| !current_artifacts.contains_key(name))
.collect()
}

fn compile_artifacts(
artifacts: Vec<(String, Utf8PathBuf)>,
) -> Result<HashMap<String, (StarknetContractArtifacts, Utf8PathBuf)>> {
artifacts
.into_par_iter()
.map(|(name, path)| {
compile_artifact_at_path(&path).map(|artifact| (name.to_string(), (artifact, path)))
})
.collect::<Result<_>>()
}

fn compile_artifact_at_path(path: &Utf8Path) -> Result<StarknetContractArtifacts> {
let sierra = fs::read_to_string(path)?;

let casm = compile_sierra_at_path(path.as_str(), None, &SierraType::Contract)?;

Ok(StarknetContractArtifacts { sierra, casm })
}
39 changes: 39 additions & 0 deletions crates/scarb-api/src/artifacts/deserialized.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use anyhow::{Context, Result};
use camino::{Utf8Path, Utf8PathBuf};
use serde::Deserialize;
use std::fs;

#[derive(Deserialize, Debug, PartialEq, Clone)]
pub(super) struct StarknetArtifacts {
pub version: u32,
pub contracts: Vec<StarknetContract>,
}

#[allow(dead_code)]
#[derive(Deserialize, Debug, PartialEq, Clone)]
pub(super) struct StarknetContract {
pub id: String,
pub package_name: String,
pub contract_name: String,
pub artifacts: StarknetContractArtifactPaths,
}

#[allow(dead_code)]
#[derive(Deserialize, Debug, PartialEq, Clone)]
pub(super) struct StarknetContractArtifactPaths {
pub sierra: Utf8PathBuf,
}

/// Get deserialized contents of `starknet_artifacts.json` file generated by Scarb
///
/// # Arguments
///
/// * `path` - A path to `starknet_artifacts.json` file.
pub(super) fn artifacts_for_package(path: &Utf8Path) -> Result<StarknetArtifacts> {
let starknet_artifacts =
fs::read_to_string(path).with_context(|| format!("Failed to read {path:?} contents"))?;
let starknet_artifacts: StarknetArtifacts =
serde_json::from_str(starknet_artifacts.as_str())
.with_context(|| format!("Failed to parse {path:?} contents. Make sure you have enabled sierra code generation in Scarb.toml"))?;
Ok(starknet_artifacts)
}
78 changes: 78 additions & 0 deletions crates/scarb-api/src/artifacts/representation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
use crate::artifacts::deserialized::{artifacts_for_package, StarknetArtifacts};
use anyhow::anyhow;
use camino::{Utf8Path, Utf8PathBuf};

pub(super) struct StarknetArtifactsRepresentation {
path: Utf8PathBuf,
artifacts: StarknetArtifacts,
}

impl StarknetArtifactsRepresentation {
pub(super) fn try_from_path(path: &Utf8Path) -> anyhow::Result<Self> {
let artifacts = artifacts_for_package(path)?;
let path = path
.parent()
.ok_or_else(|| anyhow!("Failed to get parent for path = {}", &path))?
.to_path_buf();

Ok(Self { path, artifacts })
}

pub(super) fn artifacts(self) -> Vec<(String, Utf8PathBuf)> {
self.artifacts
.contracts
.into_iter()
.map(|contract| {
(
contract.contract_name,
self.path.join(contract.artifacts.sierra.as_path()),
)
})
.collect()
}
}

#[cfg(test)]
mod tests {
use crate::ScarbCommand;
use assert_fs::fixture::{FileTouch, FileWriteStr, PathChild, PathCopy};
use assert_fs::TempDir;
use camino::Utf8PathBuf;

use super::*;

#[test]
fn parsing_starknet_artifacts() {
let temp = crate::tests::setup_package("basic_package");

ScarbCommand::new_with_stdio()
.current_dir(temp.path())
.arg("build")
.run()
.unwrap();

let artifacts_path = temp
.path()
.join("target/dev/basic_package.starknet_artifacts.json");
let artifacts_path = Utf8PathBuf::from_path_buf(artifacts_path).unwrap();

let artifacts = artifacts_for_package(&artifacts_path).unwrap();

assert!(!artifacts.contracts.is_empty());
}

#[test]
fn parsing_starknet_artifacts_on_invalid_file() {
let temp = TempDir::new().unwrap();
temp.copy_from("../../", &[".tool-versions"]).unwrap();
let path = temp.child("wrong.json");
path.touch().unwrap();
path.write_str("\"aa\": {}").unwrap();
let artifacts_path = Utf8PathBuf::from_path_buf(path.to_path_buf()).unwrap();

let result = artifacts_for_package(&artifacts_path);
let err = result.unwrap_err();

assert!(err.to_string().contains(&format!("Failed to parse {artifacts_path:?} contents. Make sure you have enabled sierra code generation in Scarb.toml")));
}
}
Loading

0 comments on commit 426d245

Please sign in to comment.