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: create solar-cli and meta crate solar #71

Merged
merged 1 commit into from
Oct 19, 2024
Merged
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
34 changes: 25 additions & 9 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 Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ inherits = "profiling"
[workspace.dependencies]
# compiler crates
solar-ast = { version = "0.0.0", path = "crates/ast" }
solar-cli = { version = "0.0.0", path = "crates/cli" }
solar-config = { version = "0.0.0", path = "crates/config" }
solar-data-structures = { version = "0.0.0", path = "crates/data-structures" }
solar-interface = { version = "0.0.0", path = "crates/interface" }
Expand Down
58 changes: 58 additions & 0 deletions crates/cli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
[package]
name = "solar-cli"
description = "Solidity compiler CLI implementation"
homepage = "https://github.com/ithacaxyz/solar/"

version.workspace = true
authors.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
repository.workspace = true
keywords.workspace = true
categories.workspace = true

[lints]
workspace = true

[build-dependencies]
vergen = { workspace = true, features = ["build", "git", "gitcl"] }

[dependencies]
solar-config = { workspace = true, features = ["clap"] }
solar-interface = { workspace = true, features = ["json"] }
solar-sema.workspace = true

alloy-primitives.workspace = true
cfg-if.workspace = true
clap = { workspace = true, features = ["derive"] }
rayon.workspace = true
tracing.workspace = true
tracing-subscriber = { workspace = true, features = ["registry", "env-filter"] }

tracing-chrome = { version = "0.7", optional = true }
tracing-tracy = { version = "0.11", optional = true, features = ["demangle"] }

[target.'cfg(unix)'.dependencies]
libc.workspace = true

tikv-jemallocator = { workspace = true, optional = true }

[features]
default = ["jemalloc", "tracing-off"]
# Nightly-only features for faster/smaller builds.
nightly = [
"solar-config/nightly",
"solar-interface/nightly",
"solar-sema/nightly",
]
# Faster but less portable algorithm implementations, such as Keccak-256.
asm = ["alloy-primitives/asm-keccak"]
# Faster but less portable allocator.
jemalloc = ["dep:tikv-jemallocator"]

# Debugging and profiling.
tracing-off = ["tracing/release_max_level_off"]
tracing-chrome = ["dep:tracing-chrome"]
tracy = ["dep:tracing-tracy"]
tracy-allocator = ["tracy"]
File renamed without changes.
6 changes: 5 additions & 1 deletion crates/solar/src/cli.rs → crates/cli/src/cli.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! Solar CLI arguments.

use clap::{ColorChoice, Parser, ValueHint};
use solar_config::{CompilerOutput, CompilerStage, Dump, EvmVersion, Language};
use std::path::PathBuf;
Expand Down Expand Up @@ -79,7 +81,8 @@ pub struct Args {
}

impl Args {
pub(crate) fn populate_unstable(&mut self) -> Result<(), clap::Error> {
/// Parses the `-Z` arguments into the `unstable` field.
pub fn populate_unstable(&mut self) -> Result<(), clap::Error> {
if !self._unstable.is_empty() {
let hack = self._unstable.iter().map(|s| format!("--{s}"));
self.unstable =
Expand All @@ -99,6 +102,7 @@ pub enum ErrorFormat {
RichJson,
}

/// A single import map, AKA remapping: `map=path`.
#[derive(Clone, Debug)]
pub struct ImportMap {
pub map: PathBuf,
Expand Down
167 changes: 167 additions & 0 deletions crates/cli/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
//! The main entry point for the Solar compiler.

#![doc(
html_logo_url = "https://raw.githubusercontent.com/ithacaxyz/solar/main/assets/logo.jpg",
html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256"
)]
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]

use clap::Parser as _;
use cli::Args;
use solar_interface::{
diagnostics::{DiagCtxt, DynEmitter, HumanEmitter, JsonEmitter},
Result, Session, SessionGlobals, SourceMap,
};
use std::{collections::BTreeSet, num::NonZeroUsize, path::Path, sync::Arc};

pub mod cli;
pub mod utils;

#[cfg(all(unix, any(target_env = "gnu", target_os = "macos")))]
pub mod sigsegv_handler;

/// Signal handler to extract a backtrace from stack overflow.
///
/// This is a no-op because this platform doesn't support our signal handler's requirements.
#[cfg(not(all(unix, any(target_env = "gnu", target_os = "macos"))))]
pub mod sigsegv_handler {
/// No-op function.
pub fn install() {}
}

// `asm` feature.
use alloy_primitives as _;

use tracing as _;

pub fn parse_args<I, T>(itr: I) -> Result<Args, clap::Error>
where
I: IntoIterator<Item = T>,
T: Into<std::ffi::OsString> + Clone,
{
let mut args = Args::try_parse_from(itr)?;
args.populate_unstable()?;
Ok(args)
}

pub fn run_compiler_args(args: Args) -> Result<()> {
run_compiler_with(args, Compiler::run_default)
}

pub struct Compiler {
pub sess: Session,
pub args: Args,
}

impl Compiler {
pub fn run_default(&self) -> Result<()> {
let Self { sess, args } = self;

if sess.language.is_yul() && !args.unstable.parse_yul {
return Err(sess.dcx.err("Yul is not supported yet").emit());
}

// Partition arguments into three categories:
// - `stdin`: `-`, occurrences after the first are ignored
// - remappings: `path=mapped`
// - paths: everything else
let stdin = args.input.iter().any(|arg| *arg == Path::new("-"));
let non_stdin_args = args.input.iter().filter(|arg| *arg != Path::new("-"));
let arg_remappings = non_stdin_args
.clone()
.filter_map(|arg| arg.to_str().unwrap_or("").parse::<cli::ImportMap>().ok());
let paths =
non_stdin_args.filter(|arg| !arg.as_os_str().as_encoded_bytes().contains(&b'='));

let mut pcx = solar_sema::ParsingContext::new(sess);
let remappings = arg_remappings.chain(args.import_map.iter().cloned());
for map in remappings {
pcx.file_resolver.add_import_map(map.map, map.path);
}
for path in &args.import_path {
let new = pcx.file_resolver.add_import_path(path.clone());
if !new {
let msg = format!("import path {} already specified", path.display());
return Err(sess.dcx.err(msg).emit());
}
}

if stdin {
pcx.load_stdin()?;
}
pcx.load_files(paths)?;

pcx.parse_and_resolve()?;

Ok(())
}

fn finish_diagnostics(&self) -> Result {
self.sess.dcx.print_error_count()
}
}

fn run_compiler_with(args: Args, f: impl FnOnce(&Compiler) -> Result + Send) -> Result {
utils::run_in_thread_pool_with_globals(args.threads, |jobs| {
let ui_testing = args.unstable.ui_testing;
let source_map = Arc::new(SourceMap::empty());
let emitter: Box<DynEmitter> = match args.error_format {
cli::ErrorFormat::Human => {
let color = match args.color {
clap::ColorChoice::Always => solar_interface::ColorChoice::Always,
clap::ColorChoice::Auto => solar_interface::ColorChoice::Auto,
clap::ColorChoice::Never => solar_interface::ColorChoice::Never,
};
let human = HumanEmitter::stderr(color)
.source_map(Some(source_map.clone()))
.ui_testing(ui_testing);
Box::new(human)
}
cli::ErrorFormat::Json | cli::ErrorFormat::RichJson => {
let writer = Box::new(std::io::BufWriter::new(std::io::stderr()));
let json = JsonEmitter::new(writer, source_map.clone())
.pretty(args.pretty_json_err)
.rustc_like(matches!(args.error_format, cli::ErrorFormat::RichJson))
.ui_testing(ui_testing);
Box::new(json)
}
};
let dcx = DiagCtxt::new(emitter).set_flags(|flags| {
flags.deduplicate_diagnostics &= !ui_testing;
flags.track_diagnostics &= !ui_testing;
flags.track_diagnostics |= args.unstable.track_diagnostics;
});

let mut sess = Session::new(dcx, source_map);
sess.evm_version = args.evm_version;
sess.language = args.language;
sess.stop_after = args.stop_after;
sess.dump = args.unstable.dump.clone();
sess.jobs = NonZeroUsize::new(jobs).unwrap();
if !args.input.is_empty()
&& args.input.iter().all(|arg| arg.extension() == Some("yul".as_ref()))
{
sess.language = solar_config::Language::Yul;
}
sess.emit = {
let mut set = BTreeSet::default();
for &emit in &args.emit {
if !set.insert(emit) {
let msg = format!("cannot specify `--emit {emit}` twice");
return Err(sess.dcx.err(msg).emit());
}
}
set
};
sess.out_dir = args.out_dir.clone();
sess.pretty_json = args.pretty_json;

let compiler = Compiler { sess, args };

SessionGlobals::with_source_map(compiler.sess.clone_source_map(), move || {
let mut r = f(&compiler);
r = compiler.finish_diagnostics().and(r);
r
})
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ impl fmt::Write for RawStderr {
/// We don't really care how many bytes we actually get out. SIGSEGV comes for our head.
/// Splash stderr with letters of our own blood to warn our friends about the monster.
macro_rules! raw_errln {
($tokens:tt) => {
let _ = ::core::fmt::Write::write_fmt(&mut RawStderr(()), format_args!($tokens));
($($tokens:tt)*) => {
let _ = ::core::fmt::Write::write_fmt(&mut RawStderr(()), format_args!($($tokens)*));
let _ = ::core::fmt::Write::write_char(&mut RawStderr(()), '\n');
};
}
Expand Down
Loading