Skip to content

Commit 740e676

Browse files
authored
Merge pull request #1722 from cryspen/cargo-hax-haxmeta-verb
feat(cli): support `haxmeta` explicit file manipulation
2 parents f88735a + e44ddf0 commit 740e676

File tree

7 files changed

+123
-32
lines changed

7 files changed

+123
-32
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ Changes to the frontend:
3535

3636
Change to cargo-hax:
3737
- Improve the caching of rustc when using `cargo hax` commands (#1719)
38+
- Add hidden commands and flags to explicitly manipulate `haxmeta` files (#1722)
3839

3940
Changes to hax-lib:
4041
- New behavior for `hax_lib::include`: it now forces inclusion when in contradiction with `-i` flag.

cli/driver/src/exporter.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -152,11 +152,12 @@ impl Callbacks for ExtractionCallbacks {
152152
let manifest_dir = std::path::Path::new(&manifest_dir);
153153

154154
let data = hax_types::driver_api::EmitHaxMetaMessage {
155-
manifest_dir: manifest_dir.to_path_buf(),
156-
working_dir: opts
157-
.working_dir
158-
.to_path(rustc_span::FileNameDisplayPreference::Local)
159-
.to_path_buf(),
155+
manifest_dir: Some(manifest_dir.to_path_buf()),
156+
working_dir: Some(
157+
opts.working_dir
158+
.to_path(rustc_span::FileNameDisplayPreference::Local)
159+
.to_path_buf(),
160+
),
160161
path: haxmeta_path,
161162
};
162163
eprintln!(

cli/subcommands/src/cargo_hax.rs

Lines changed: 57 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -169,9 +169,12 @@ impl HaxMessage {
169169
} => {
170170
let mut _rctx = None;
171171
let rctx = rctx.unwrap_or_else(|| _rctx.get_or_insert(ReportCtx::default()));
172-
diagnostic.with_message(rctx, &working_dir, Level::Error, |msg| {
173-
eprintln!("{}", renderer.render(msg))
174-
});
172+
diagnostic.with_message(
173+
rctx,
174+
working_dir.as_ref().map(PathBuf::as_path),
175+
Level::Error,
176+
|msg| eprintln!("{}", renderer.render(msg)),
177+
);
175178
}
176179
Self::EngineNotFound {
177180
is_opam_setup_correctly,
@@ -258,8 +261,8 @@ impl HaxMessage {
258261
fn run_engine(
259262
haxmeta: HaxMeta<hax_frontend_exporter::ThirBody>,
260263
id_table: id_table::Table,
261-
working_dir: PathBuf,
262-
manifest_dir: PathBuf,
264+
working_dir: Option<PathBuf>,
265+
manifest_dir: Option<PathBuf>,
263266
backend: &BackendOptions<()>,
264267
message_format: MessageFormat,
265268
) -> bool {
@@ -326,7 +329,9 @@ fn run_engine(
326329
]
327330
.iter()
328331
.collect();
329-
manifest_dir.join(&relative_path)
332+
manifest_dir
333+
.map(|manifest_dir| manifest_dir.join(&relative_path))
334+
.unwrap_or(relative_path)
330335
});
331336

332337
let stdout = std::io::BufReader::new(engine_subprocess.stdout.take().unwrap());
@@ -371,10 +376,12 @@ fn run_engine(
371376
.iter()
372377
.map(PathBuf::from)
373378
.map(|path| {
374-
if path.is_absolute() {
375-
path
376-
} else {
379+
if let Some(working_dir) = working_dir.as_ref()
380+
&& path.is_relative()
381+
{
377382
working_dir.join(path).to_path_buf()
383+
} else {
384+
path
378385
}
379386
})
380387
.map(|path| fs::read_to_string(path).ok())
@@ -640,9 +647,29 @@ fn run_command(options: &Options, haxmeta_files: Vec<EmitHaxMetaMessage>) -> boo
640647
path,
641648
} in haxmeta_files
642649
{
643-
let (haxmeta, id_table): (HaxMeta<Body>, _) =
650+
let (mut haxmeta, id_table): (HaxMeta<Body>, _) =
644651
HaxMeta::read(fs::File::open(&path).unwrap());
645652

653+
if let Some(root_module) = &backend.prune_haxmeta {
654+
use hax_frontend_exporter::{DefPathItem, DisambiguatedDefPathItem, IsBody};
655+
656+
/// Remove every item from an `HaxMeta` whose path is not `*::<root_module>::**`, where `root_module` is a string.
657+
fn prune_haxmeta<B: IsBody>(haxmeta: &mut HaxMeta<B>, root_module: &str) {
658+
haxmeta.items.retain(|item| match &item.owner_id.path[..] {
659+
[] => true,
660+
[
661+
DisambiguatedDefPathItem {
662+
data: DefPathItem::TypeNs(s),
663+
disambiguator: 0,
664+
},
665+
..,
666+
] => s == root_module,
667+
_ => false,
668+
})
669+
}
670+
prune_haxmeta(&mut haxmeta, root_module.as_str())
671+
}
672+
646673
error = error
647674
|| run_engine(
648675
haxmeta,
@@ -655,6 +682,12 @@ fn run_command(options: &Options, haxmeta_files: Vec<EmitHaxMetaMessage>) -> boo
655682
}
656683
error
657684
}
685+
Command::Serialize { .. } => {
686+
for EmitHaxMetaMessage { path, .. } in haxmeta_files {
687+
HaxMessage::ProducedFile { path, wrote: true }.report(options.message_format, None);
688+
}
689+
false
690+
}
658691
}
659692
}
660693

@@ -679,7 +712,20 @@ fn main() {
679712
};
680713
options.normalize_paths();
681714

682-
let (haxmeta_files, exit_code) = compute_haxmeta_files(&options);
715+
let (haxmeta_files, exit_code) = options
716+
.haxmeta
717+
.clone()
718+
.map(|path| {
719+
(
720+
vec![EmitHaxMetaMessage {
721+
working_dir: None,
722+
manifest_dir: None,
723+
path,
724+
}],
725+
0,
726+
)
727+
})
728+
.unwrap_or_else(|| compute_haxmeta_files(&options));
683729
let error = run_command(&options, haxmeta_files);
684730

685731
std::process::exit(if exit_code == 0 && error {

hax-types/src/cli_options/mod.rs

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,14 @@ pub struct BackendOptions<E: Extension> {
341341
#[arg(long)]
342342
pub profile: bool,
343343

344+
/// Prune Rust items that are not under the provided top-level module name.
345+
/// This will effectively remove all items that don't match `*::<prune_haxmetadata>::**`.
346+
/// This prunning occurs directly on the `haxmeta` file, in the frontend.
347+
/// This is independent from any engine options.
348+
#[arg(long)]
349+
#[clap(hide = true)]
350+
pub prune_haxmeta: Option<String>,
351+
344352
/// Enable engine debugging: dumps the AST at each phase.
345353
///
346354
/// The value of `<DEBUG_ENGINE>` can be either:
@@ -423,6 +431,29 @@ pub enum Command<E: Extension> {
423431
include_extra: bool,
424432
},
425433

434+
/// Serialize to a `haxmeta` file, the internal binary format used by hax to
435+
/// store the ASTs produced by the hax exporter.
436+
#[clap(hide = true)]
437+
Serialize {
438+
/// Whether the bodies are exported as THIR, built MIR, const
439+
/// MIR, or a combination. Repeat this option to extract a
440+
/// combination (e.g. `-k thir -k mir-built`). Pass `--kind`
441+
/// alone with no value to disable body extraction.
442+
#[arg(
443+
value_enum,
444+
short,
445+
long = "kind",
446+
num_args = 0..=3,
447+
default_values_t = [ExportBodyKind::Thir]
448+
)]
449+
kind: Vec<ExportBodyKind>,
450+
451+
/// When extracting to a given backend, the exporter is called with different `cfg` options.
452+
/// This option allows to set the same flags as `cargo hax into` would pick.
453+
#[arg(short)]
454+
backend: Option<BackendName>,
455+
},
456+
426457
#[command(flatten)]
427458
CliExtension(E::Command),
428459
}
@@ -431,7 +462,16 @@ impl<E: Extension> Command<E> {
431462
pub fn body_kinds(&self) -> Vec<ExportBodyKind> {
432463
match self {
433464
Command::JSON { kind, .. } => kind.clone(),
434-
_ => vec![ExportBodyKind::Thir],
465+
Command::Serialize { kind, .. } => kind.clone(),
466+
Command::Backend { .. } | Command::CliExtension { .. } => vec![ExportBodyKind::Thir],
467+
}
468+
}
469+
pub fn backend_name(&self) -> Option<BackendName> {
470+
match self {
471+
Command::Backend(backend_options) => Some((&backend_options.backend).into()),
472+
Command::JSON { .. } => None,
473+
Command::Serialize { backend, .. } => backend.clone(),
474+
Command::CliExtension(_) => None,
435475
}
436476
}
437477
}
@@ -475,6 +515,12 @@ pub struct ExtensibleOptions<E: Extension> {
475515
#[arg(long = "deps")]
476516
pub deps: bool,
477517

518+
/// Provide a precomputed haxmeta file explicitly.
519+
/// Setting this option bypasses rustc and the exporter altogether.
520+
#[arg(long)]
521+
#[clap(hide = true)]
522+
pub haxmeta: Option<PathBuf>,
523+
478524
/// By default, hax uses `$CARGO_TARGET_DIR/hax` as target folder,
479525
/// to avoid recompilation when working both with `cargo hax` and
480526
/// `cargo build` (or, e.g. `rust-analyzer`). This option disables
@@ -540,7 +586,7 @@ pub struct ExporterOptions {
540586
}
541587

542588
#[derive_group(Serializers)]
543-
#[derive(JsonSchema, Debug, Clone, Copy)]
589+
#[derive(JsonSchema, ValueEnum, Debug, Clone, Copy)]
544590
pub enum BackendName {
545591
Fstar,
546592
Coq,
@@ -570,22 +616,17 @@ impl fmt::Display for BackendName {
570616

571617
impl From<&Options> for ExporterOptions {
572618
fn from(options: &Options) -> Self {
573-
let backend = match &options.command {
574-
Command::Backend(backend_options) => Some((&backend_options.backend).into()),
575-
_ => None,
576-
};
577-
let body_kinds = options.command.body_kinds();
578619
ExporterOptions {
579620
deps: options.deps,
580621
force_cargo_build: options.force_cargo_build.clone(),
581-
backend,
582-
body_kinds,
622+
backend: options.command.backend_name(),
623+
body_kinds: options.command.body_kinds(),
583624
}
584625
}
585626
}
586627

587-
impl From<&Backend<()>> for BackendName {
588-
fn from(backend: &Backend<()>) -> Self {
628+
impl<E: Extension> From<&Backend<E>> for BackendName {
629+
fn from(backend: &Backend<E>) -> Self {
589630
match backend {
590631
Backend::Fstar { .. } => BackendName::Fstar,
591632
Backend::Coq { .. } => BackendName::Coq,

hax-types/src/diagnostics/message.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::prelude::*;
77
pub enum HaxMessage {
88
Diagnostic {
99
diagnostic: super::Diagnostics,
10-
working_dir: PathBuf,
10+
working_dir: Option<PathBuf>,
1111
} = 254,
1212
EngineNotFound {
1313
is_opam_setup_correctly: bool,

hax-types/src/diagnostics/report.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ impl Diagnostics {
4242
pub fn with_message<R, F: for<'a> FnMut(Message<'a>) -> R>(
4343
&self,
4444
report_ctx: &mut ReportCtx,
45-
working_dir: &Path,
45+
working_dir: Option<&Path>,
4646
level: Level,
4747
mut then: F,
4848
) -> R {
@@ -52,7 +52,9 @@ impl Diagnostics {
5252
if let Some(path) = span.filename.to_path() {
5353
let source = {
5454
let mut path = path.to_path_buf();
55-
if path.is_relative() {
55+
if let Some(working_dir) = working_dir
56+
&& path.is_relative()
57+
{
5658
path = working_dir.join(&path);
5759
};
5860
report_ctx.file_contents(path)

hax-types/src/driver_api.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ pub const HAX_DRIVER_STDERR_PREFIX: &str = "::hax-driver::";
55
#[derive_group(Serializers)]
66
#[derive(Debug, Clone)]
77
pub struct EmitHaxMetaMessage {
8-
pub working_dir: PathBuf,
9-
pub manifest_dir: PathBuf,
8+
pub working_dir: Option<PathBuf>,
9+
pub manifest_dir: Option<PathBuf>,
1010
pub path: PathBuf,
1111
}
1212
#[derive_group(Serializers)]

0 commit comments

Comments
 (0)