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

feat: support native dependencies #2386

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
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
3 changes: 3 additions & 0 deletions Cargo.lock

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

3 changes: 3 additions & 0 deletions config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ pub use error::SolidityErrorCode;
// helpers for fixing configuration warnings
pub mod fix;

/// Contains the `foundry.toml` manifest representation
pub mod manifest;

// reexport so cli types can implement `figment::Provider` to easily merge compiler arguments
pub use figment;

Expand Down
71 changes: 71 additions & 0 deletions config/src/manifest.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
use ethers_solc::remappings::RelativeRemapping;
use semver::Version;
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;

/// Contains all the information about a foundry project, as loaded from a `foundry.toml`.
///
/// Compared to the core `Config` type, this type represents the `foundry.toml` as is where `Config`
/// represents a single profile and all resolved settings, this includes settings from global
/// foundry.toml, env vars
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Manifest {
/// All dependencies declared in the `foundry.toml`
pub dependencies: Option<BTreeMap<String, FoundryDependency>>,
/// All declared profiles
pub profiles: Option<FoundryProfiles>,
// TODO add standalone entries, like rpc_endpoints
}

/// Represents a dependency entry
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(untagged)]
pub enum FoundryDependency {
/// `dependency = "org/name@tag"`
Simple(String),
/// Provides additional settings, such as path
Detailed(DetailedFoundryDependency),
}

/// Represents detailed settings for a dependency
#[derive(Deserialize, Serialize, Clone, Debug, Default)]
pub struct DetailedFoundryDependency {
/// The version of the dependency
pub version: Option<Version>,
/// The _relative_ path to a dependency.
pub path: Option<String>,
/// URL where this dependency can be found
pub git: Option<String>,
/// branch of the `git` repository
pub branch: Option<String>,
/// tag of the `git` repository
pub tag: Option<String>,
/// commit of the `git` repository
pub rev: Option<String>,
/// The remappings to use for this repository
#[serde(alias = "remappings")]
pub remappings: Option<TomlRemappings>,
}

/// Represents the remappings a dependency provides
#[derive(Deserialize, Serialize, Clone, Debug, Eq, PartialEq)]
#[serde(untagged)]
pub enum TomlRemappings {
/// A single remapping for the dependency
Single(RelativeRemapping),
/// Multiple remappings, to account for nested submodules
Multiple(Vec<RelativeRemapping>),
}

/// Represents a set of profiles in a `foundry.toml`
#[derive(Deserialize, Serialize, Clone, Debug, Default)]
pub struct FoundryProfiles(BTreeMap<String, FoundryProfile>);

/// A single profile in a `foundry.toml`
///
/// This is essentially an excerpt of `crate::Config`
#[derive(Deserialize, Serialize, Clone, Debug, Default, Eq, PartialEq)]
#[serde(default, rename_all = "kebab-case")]
pub struct FoundryProfile {
// TODO basically an excerpt of `Config` but everything optional
}
3 changes: 3 additions & 0 deletions forge/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ rayon = "1.5"
rlp = "0.5.1"
once_cell = "1.13"
comfy-table = "6.0.0"
fs2 = "0.4.3"
termcolor = "1.1.3"
atty = "0.2.14"

[dev-dependencies]
ethers = { git = "https://github.com/gakonst/ethers-rs", default-features = false, features = ["solc-full", "solc-tests"] }
Expand Down
2 changes: 2 additions & 0 deletions forge/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
/// Gas reports
pub mod gas_report;

pub mod registry;

/// Coverage reports
pub mod coverage;

Expand Down
25 changes: 25 additions & 0 deletions forge/src/registry/files.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//! OS-specific file access

use crate::registry::RegistryConfig;
use foundry_common::fs;
use std::path::{Path, PathBuf};

#[derive(Debug)]
pub struct FileLock {}

/// The `FileSystem` is a shareable abstraction for accessing an underlying path that supports
/// locking and concurrent access.
#[derive(Clone, Debug)]
pub struct FileSystem {
root: PathBuf,
}

// === impl FileSystem ===

impl FileSystem {}

/// Acquires a file lock and provides a status update if the lock can't be acquired immediately.
///
/// This is useful because there can be a long-running, conflicting forge action that's currently
/// locking the file and we want to relay this state.
fn acquire(config: &RegistryConfig, msg: &str, path: &Path) {}
18 changes: 18 additions & 0 deletions forge/src/registry/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//! forge's registry support

use crate::registry::{files::FileSystem, shell::Shell};
use std::cell::RefCell;

mod files;
mod package;
mod shell;

/// Forge registry related config.
#[derive(Debug)]
pub struct RegistryConfig {
/// The location of the foundry home directory
foundry_home: FileSystem,
/// Holds the output shell used for emitting messages
// This is a `RefCell` so we can access all `mut` output functions
shell: RefCell<Shell>,
}
34 changes: 34 additions & 0 deletions forge/src/registry/package.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use foundry_config::manifest::Manifest;
use std::{path::PathBuf, rc::Rc};

/// Represents a single dependency
#[derive(Clone)]
pub struct Package {
inner: Rc<PackageInner>,
}

// === impl Package ===

impl Package {
/// Creates a new `Package` with the given path and manifest
pub fn new(manifest: Option<Manifest>, path: impl into<PathBuf>) -> Package {
Package {
inner: Rc::new(PackageInner {
manifest,
path: path.into(),
// TODO
has_submodules: false,
}),
}
}
}

#[derive(Clone)]
struct PackageInner {
/// The manifest of package if it contains a `foundry.toml`
manifest: Option<Manifest>,
/// Where this package is stored
path: PathBuf,
/// Whether this dependency has additional git submodules
has_submodules: bool,
}
90 changes: 90 additions & 0 deletions forge/src/registry/shell.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
//! shell abstraction used to interact with stdout

use std::io::Write;

use termcolor::{
self, Color,
Color::{Cyan, Green, Red, Yellow},
ColorChoice, ColorSpec, StandardStream, WriteColor,
};

/// Provides a configurable abstraction for how messages should be emitted, like verbosity and color
#[derive(Default)]
pub struct Shell {
output: Output,
verbosity: Verbosity,
needs_clear: bool,
}

// === impl Shell ===

impl Shell {
/// Creates a shell that will always write into the given `out`
pub fn plain(out: Box<dyn Write>) -> Shell {
Shell { output: Output::Plain(out), verbosity: Verbosity::Verbose, needs_clear: false }
}

/// Returns access to stdout
fn stdout(&mut self) -> &mut dyn Write {
match self {
Output::TermColored(term) => &mut term.stdout,
Output::Plain(ref mut w) => w,
}
}

/// Returns access to `io::Write`.
fn stderr(&mut self) -> &mut dyn Write {
match self {
Output::TermColored(term) => &mut term.stderr,
Output::Plain(ref mut w) => w,
}
}
}

/// The `Write`able output abstract
enum Output {
/// Writes messages as they come into the `Write`
///
/// Main purpose for this variant is testing
Plain(Box<dyn Write>),
/// Uses `termcolor` for writing colored text to stdout/stdwerr
TermColored(TermConfig),
}

/// An advanced, configured `Output` wrapper
struct TermConfig {
stdout: StandardStream,
stderr: StandardStream,
stderr_tty: bool,
}

impl Default for TermConfig {
fn default() -> Self {
fn choice(stream: atty::Stream) -> ColorChoice {
if atty::is(stream) {
ColorChoice::Auto
} else {
ColorChoice::Never
}
}
Self {
stdout: StandardStream::stdout(choice(atty::Stream::Stdout)),
stderr: StandardStream::stderr(choice(atty::Stream::Stderr)),
stderr_tty: atty::is(atty::Stream::Stderr),
}
}
}

/// Verbosity level of the output
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Verbosity {
Verbose,
Normal,
Silent,
}

impl Default for Verbosity {
fn default() -> Self {
Verbosity::Verbose
}
}