Skip to content

Commit 1185221

Browse files
committed
[hermes] Add Charon step
gherrit-pr-id: Ge1bd84b5d857e18e3668e809844c6d73b808a020
1 parent 0fe1018 commit 1185221

File tree

208 files changed

+127435
-37
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

208 files changed

+127435
-37
lines changed

tools/Cargo.lock

Lines changed: 57 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tools/hermes/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ cargo_metadata = "0.23.1"
1212
clap = { version = "4.5.57", features = ["derive"] }
1313
clap-cargo = { version = "0.18.3", features = ["cargo_metadata"] }
1414
dashmap = "6.1.0"
15+
env_logger = "0.11.8"
1516
log = "0.4.29"
1617
miette = { version = "7.6.0", features = ["derive", "fancy"] }
1718
proc-macro2 = { version = "1.0.105", features = ["span-locations"] }

tools/hermes/src/charon.rs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
use std::process::Command;
2+
3+
use anyhow::{bail, Context as _, Result};
4+
5+
use crate::{
6+
resolve::{Args, Roots},
7+
shadow::HermesTargetPackage,
8+
};
9+
10+
pub fn run_charon(args: &Args, roots: &Roots, packages: &[HermesTargetPackage]) -> Result<()> {
11+
let charon_root = roots.charon_root();
12+
13+
std::fs::create_dir_all(&charon_root).context("Failed to create charon output directory")?;
14+
15+
for pkg in packages {
16+
if pkg.start_from.is_empty() {
17+
continue;
18+
}
19+
20+
log::info!("Invoking Charon on package '{}'...", pkg.name);
21+
22+
let mut cmd = Command::new("charon");
23+
cmd.arg("cargo");
24+
25+
// Output artifacts to target/hermes/<hash>/charon
26+
let llbc_path = charon_root.join(pkg.llbc_file_name());
27+
log::debug!("Writing .llbc file to {}", llbc_path.display());
28+
cmd.arg("--dest-file").arg(llbc_path);
29+
30+
// Fail fast on errors
31+
cmd.arg("--abort-on-error");
32+
33+
// Start translation from specific entry points
34+
cmd.arg("--start-from").arg(pkg.start_from.join(","));
35+
36+
// Separator for the underlying cargo command
37+
cmd.arg("--");
38+
39+
cmd.arg("--manifest-path").arg(&pkg.shadow_manifest_path);
40+
41+
// NOTE: We do not forward --workspace, -p, or --exclude, as we are
42+
// manually iterating over the selected packages.
43+
if args.lib {
44+
cmd.arg("--lib");
45+
}
46+
if args.bins {
47+
cmd.arg("--bins");
48+
}
49+
for bin in &args.bin {
50+
cmd.arg("--bin").arg(bin);
51+
}
52+
if args.examples {
53+
cmd.arg("--examples");
54+
}
55+
for example in &args.example {
56+
cmd.arg("--example").arg(example);
57+
}
58+
if args.tests {
59+
cmd.arg("--tests");
60+
}
61+
for test in &args.test {
62+
cmd.arg("--test").arg(test);
63+
}
64+
65+
// Forward all feature-related flags.
66+
if args.features.all_features {
67+
cmd.arg("--all-features");
68+
}
69+
if args.features.no_default_features {
70+
cmd.arg("--no-default-features");
71+
}
72+
for feature in &args.features.features {
73+
cmd.arg("--features").arg(feature);
74+
}
75+
76+
// Reuse the main target directory for dependencies to save time.
77+
cmd.env("CARGO_TARGET_DIR", &roots.cargo_target_dir);
78+
79+
log::debug!("Command: {:?}", cmd);
80+
81+
let status = cmd.status().context("Failed to execute charon")?;
82+
83+
if !status.success() {
84+
bail!("Charon failed with status: {}", status);
85+
}
86+
}
87+
88+
Ok(())
89+
}

tools/hermes/src/main.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
mod charon;
12
mod errors;
23
mod parse;
34
mod resolve;
@@ -22,6 +23,8 @@ enum Commands {
2223
}
2324

2425
fn main() -> anyhow::Result<()> {
26+
env_logger::init();
27+
2528
if std::env::var("HERMES_UI_TEST_MODE").is_ok() {
2629
ui_test_shim::run();
2730
return Ok(());
@@ -31,7 +34,17 @@ fn main() -> anyhow::Result<()> {
3134
match args.command {
3235
Commands::Verify(resolve_args) => {
3336
let roots = resolve::resolve_roots(&resolve_args)?;
34-
shadow::build_shadow_crate(&roots)
37+
let entry_points = shadow::build_shadow_crate(&roots)?;
38+
if entry_points.is_empty() {
39+
anyhow::bail!("No Hermes annotations (/// ```lean ...) found in the selected targets. Nothing to verify.");
40+
}
41+
charon::run_charon(&resolve_args, &roots, &entry_points)
3542
}
3643
}
3744
}
45+
46+
/// A no-op function with a Hermes annotation so we can test Hermes on itself.
47+
///
48+
/// ```lean
49+
/// ```
50+
fn _foo() {}

tools/hermes/src/parse.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,17 @@ pub enum ParsedItem {
4545
}
4646

4747
impl ParsedItem {
48+
pub fn name(&self) -> Option<String> {
49+
match self {
50+
Self::Fn(item) => Some(item.sig.ident.to_string()),
51+
Self::Struct(item) => Some(item.ident.to_string()),
52+
Self::Enum(item) => Some(item.ident.to_string()),
53+
Self::Union(item) => Some(item.ident.to_string()),
54+
Self::Trait(item) => Some(item.ident.to_string()),
55+
Self::Impl(_) => None,
56+
}
57+
}
58+
4859
/// Returns the attributes on this item.
4960
fn attrs(&self) -> &[Attribute] {
5061
match self {
@@ -62,7 +73,7 @@ impl ParsedItem {
6273
#[derive(Debug)]
6374
pub struct ParsedLeanItem {
6475
pub item: ParsedItem,
65-
module_path: Vec<String>,
76+
pub module_path: Vec<String>,
6677
lean_block: String,
6778
source_file: Option<PathBuf>,
6879
}

tools/hermes/src/resolve.rs

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,41 +11,41 @@ use clap::Parser;
1111
#[derive(Parser, Debug)]
1212
pub struct Args {
1313
#[command(flatten)]
14-
manifest: clap_cargo::Manifest,
14+
pub manifest: clap_cargo::Manifest,
1515

1616
#[command(flatten)]
17-
workspace: clap_cargo::Workspace,
17+
pub workspace: clap_cargo::Workspace,
1818

1919
#[command(flatten)]
20-
features: clap_cargo::Features,
20+
pub features: clap_cargo::Features,
2121

2222
/// Verify the library target
2323
#[arg(long)]
24-
lib: bool,
24+
pub lib: bool,
2525

2626
/// Verify specific binary targets
2727
#[arg(long)]
28-
bin: Vec<String>,
28+
pub bin: Vec<String>,
2929

3030
/// Verify all binary targets
3131
#[arg(long)]
32-
bins: bool,
32+
pub bins: bool,
3333

3434
/// Verify specific example targets
3535
#[arg(long)]
36-
example: Vec<String>,
36+
pub example: Vec<String>,
3737

3838
/// Verify all example targets
3939
#[arg(long)]
40-
examples: bool,
40+
pub examples: bool,
4141

4242
/// Verify specific test targets
4343
#[arg(long)]
44-
test: Vec<String>,
44+
pub test: Vec<String>,
4545

4646
/// Verify all test targets
4747
#[arg(long)]
48-
tests: bool,
48+
pub tests: bool,
4949
}
5050

5151
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -111,6 +111,10 @@ impl Roots {
111111
pub fn shadow_root(&self) -> PathBuf {
112112
self.hermes_run_root.join("shadow")
113113
}
114+
115+
pub fn charon_root(&self) -> PathBuf {
116+
self.hermes_run_root.join("charon")
117+
}
114118
}
115119

116120
/// Resolves all verification roots.

0 commit comments

Comments
 (0)