Skip to content

Commit 3434d56

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

File tree

208 files changed

+127479
-45
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

+127479
-45
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: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
use std::process::Command;
2+
3+
use anyhow::{bail, Context as _, Result};
4+
5+
use crate::{
6+
resolve::{Args, HermesTargetKind, Roots},
7+
shadow::HermesArtifact,
8+
};
9+
10+
pub fn run_charon(args: &Args, roots: &Roots, packages: &[HermesArtifact]) -> 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 artifact in packages {
16+
if artifact.start_from.is_empty() {
17+
continue;
18+
}
19+
20+
log::info!("Invoking Charon on package '{}'...", artifact.name.package_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(artifact.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(artifact.start_from.join(","));
35+
36+
// Separator for the underlying cargo command
37+
cmd.arg("--");
38+
39+
cmd.arg("--manifest-path").arg(&artifact.shadow_manifest_path);
40+
41+
match artifact.target_kind {
42+
HermesTargetKind::Lib
43+
| HermesTargetKind::RLib
44+
| HermesTargetKind::ProcMacro
45+
| HermesTargetKind::CDyLib
46+
| HermesTargetKind::DyLib
47+
| HermesTargetKind::StaticLib => {
48+
cmd.arg("--lib");
49+
}
50+
HermesTargetKind::Bin => {
51+
cmd.arg("--bin").arg(&artifact.name.target_name);
52+
}
53+
HermesTargetKind::Example => {
54+
cmd.arg("--example").arg(&artifact.name.target_name);
55+
}
56+
HermesTargetKind::Test => {
57+
cmd.arg("--test").arg(&artifact.name.target_name);
58+
}
59+
}
60+
61+
// Forward all feature-related flags.
62+
if args.features.all_features {
63+
cmd.arg("--all-features");
64+
}
65+
if args.features.no_default_features {
66+
cmd.arg("--no-default-features");
67+
}
68+
for feature in &args.features.features {
69+
cmd.arg("--features").arg(feature);
70+
}
71+
72+
// Reuse the main target directory for dependencies to save time.
73+
cmd.env("CARGO_TARGET_DIR", &roots.cargo_target_dir);
74+
75+
log::debug!("Command: {:?}", cmd);
76+
77+
let status = cmd.status().context("Failed to execute charon")?;
78+
79+
if !status.success() {
80+
bail!("Charon failed with status: {}", status);
81+
}
82+
}
83+
84+
Ok(())
85+
}

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: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,44 +11,44 @@ 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

51-
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
51+
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
5252
pub enum HermesTargetKind {
5353
/// A library target (generic).
5454
Lib,
@@ -98,19 +98,37 @@ impl TryFrom<&TargetKind> for HermesTargetKind {
9898
}
9999
}
100100

101+
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
102+
pub struct HermesTargetName {
103+
pub package_name: PackageName,
104+
pub target_name: String,
105+
}
106+
107+
#[derive(Debug)]
108+
pub struct HermesTarget {
109+
pub name: HermesTargetName,
110+
pub kind: HermesTargetKind,
111+
/// Path to the main source file for this target.
112+
pub src_path: PathBuf,
113+
}
114+
101115
#[derive(Debug)]
102116
pub struct Roots {
103117
pub workspace: PathBuf,
104118
pub cargo_target_dir: PathBuf,
105119
// E.g., `target/hermes/<hash>`.
106120
hermes_run_root: PathBuf,
107-
pub roots: Vec<(PackageName, HermesTargetKind, PathBuf)>,
121+
pub roots: Vec<HermesTarget>,
108122
}
109123

110124
impl Roots {
111125
pub fn shadow_root(&self) -> PathBuf {
112126
self.hermes_run_root.join("shadow")
113127
}
128+
129+
pub fn charon_root(&self) -> PathBuf {
130+
self.hermes_run_root.join("charon")
131+
}
114132
}
115133

116134
/// Resolves all verification roots.
@@ -151,11 +169,14 @@ pub fn resolve_roots(args: &Args) -> Result<Roots> {
151169
}
152170

153171
for (target, kind) in targets {
154-
roots.roots.push((
155-
package.name.clone(),
156-
kind.clone(),
157-
target.src_path.as_std_path().to_owned(),
158-
));
172+
roots.roots.push(HermesTarget {
173+
name: HermesTargetName {
174+
package_name: package.name.clone(),
175+
target_name: target.name.clone(),
176+
},
177+
kind,
178+
src_path: target.src_path.as_std_path().to_owned(),
179+
});
159180
}
160181
}
161182

0 commit comments

Comments
 (0)