Skip to content

Commit 9482297

Browse files
authored
Fix dependency analysis and simplify compilation code (#12)
* Fix dependency analysis * Abstract the function * rename * Fix bug in exec_compile * Renaming * Simplify * Simplify * Simplify * fmt
1 parent b444971 commit 9482297

File tree

1 file changed

+131
-75
lines changed

1 file changed

+131
-75
lines changed

src/verus.rs

Lines changed: 131 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ impl VerusTarget {
278278
};
279279

280280
// Check if all recursive dependencies have their verification files and are not newer than us
281-
let deps = get_local_dependency_recursive(self);
281+
let deps = get_local_dependency(self);
282282
for dep in deps.values() {
283283
if !dep.library_proof().exists() {
284284
return false;
@@ -536,7 +536,7 @@ pub fn find_target(t: &str) -> Result<VerusTarget, String> {
536536
Ok(target)
537537
}
538538

539-
pub fn get_local_dependency(target: &VerusTarget) -> IndexMap<String, VerusTarget> {
539+
fn get_local_dependency_direct(target: &VerusTarget) -> IndexMap<String, VerusTarget> {
540540
let all = verus_targets();
541541
let mut deps = IndexMap::new();
542542

@@ -560,11 +560,11 @@ pub fn get_local_dependency(target: &VerusTarget) -> IndexMap<String, VerusTarge
560560
deps
561561
}
562562

563-
pub fn get_local_dependency_recursive(target: &VerusTarget) -> IndexMap<String, VerusTarget> {
563+
pub fn get_local_dependency(target: &VerusTarget) -> IndexMap<String, VerusTarget> {
564564
let mut result = IndexMap::new();
565565
let mut visited = std::collections::HashSet::new();
566566

567-
fn collect_deps_recursive(
567+
fn collect_deps_recursively(
568568
target: &VerusTarget,
569569
result: &mut IndexMap<String, VerusTarget>,
570570
visited: &mut std::collections::HashSet<String>,
@@ -579,7 +579,7 @@ pub fn get_local_dependency_recursive(target: &VerusTarget) -> IndexMap<String,
579579
visited.insert(target_name.clone());
580580

581581
// Get direct dependencies
582-
let direct_deps = get_local_dependency(target);
582+
let direct_deps = get_local_dependency_direct(target);
583583

584584
// Add direct dependencies to result (unless it's the root target)
585585
for (dep_name, dep_target) in direct_deps.iter() {
@@ -588,16 +588,16 @@ pub fn get_local_dependency_recursive(target: &VerusTarget) -> IndexMap<String,
588588
result.insert(dep_key, dep_target.clone());
589589
}
590590
// Recursively collect dependencies of this dependency
591-
collect_deps_recursive(dep_target, result, visited, false);
591+
collect_deps_recursively(dep_target, result, visited, false);
592592
}
593593
}
594594

595-
collect_deps_recursive(target, &mut result, &mut visited, true);
595+
collect_deps_recursively(target, &mut result, &mut visited, true);
596596
result
597597
}
598598

599599
pub fn get_dependent_targets(target: &VerusTarget, release: bool) -> IndexMap<String, VerusTarget> {
600-
let mut deps = get_local_dependency_recursive(target);
600+
let mut deps = get_local_dependency(target);
601601
let order = resolve_deps_cached(target, release).full_externs;
602602
deps.sort_by(|a, _, b, _| {
603603
let x = order.get_index_of(a).unwrap_or(usize::MAX);
@@ -613,7 +613,7 @@ pub fn get_dependent_targets_batch(
613613
) -> IndexMap<String, VerusTarget> {
614614
let mut deps = IndexMap::new();
615615
for target in targets.iter() {
616-
deps.extend(get_local_dependency_recursive(target));
616+
deps.extend(get_local_dependency(target));
617617
}
618618
let order = resolve_deps_cached(targets.first().unwrap(), release).full_externs;
619619
deps.sort_by(|a, _, b, _| {
@@ -659,7 +659,7 @@ pub fn cmd_push_import(cmd: &mut Command, imports: &[&VerusTarget]) {
659659
}
660660
}
661661

662-
pub fn check_import(imports: &[&VerusTarget]) -> Result<(), DynError> {
662+
pub fn check_imports_compiled(imports: &[&VerusTarget]) -> Result<(), DynError> {
663663
for imp in imports.iter() {
664664
if !imp.library_proof().exists() {
665665
return Err(format!(
@@ -681,49 +681,6 @@ pub fn check_import(imports: &[&VerusTarget]) -> Result<(), DynError> {
681681
Ok(())
682682
}
683683

684-
pub fn check_import_with_auto_compile(
685-
imports: &[&VerusTarget],
686-
options: &ExtraOptions,
687-
) -> Result<(), DynError> {
688-
let mut missing_targets = Vec::new();
689-
690-
for imp in imports.iter() {
691-
if !imp.library_proof().exists() || !imp.library_path().exists() {
692-
missing_targets.push((*imp).clone());
693-
}
694-
}
695-
696-
if !missing_targets.is_empty() {
697-
info!(
698-
"Missing verification files for dependencies: {:?}",
699-
missing_targets.iter().map(|t| &t.name).collect::<Vec<_>>()
700-
);
701-
info!("Automatically compiling missing dependencies...");
702-
703-
// Get all dependencies of missing targets to ensure proper compilation order
704-
let extended_targets = get_dependent_targets_batch(&missing_targets, options.release);
705-
for target in extended_targets.values() {
706-
if !target.library_proof().exists() {
707-
compile_target(target, &vec![], options).map_err(|e| {
708-
format!("Failed to auto-compile dependency `{}`: {}", target.name, e)
709-
})?;
710-
}
711-
}
712-
713-
// Compile the missing targets themselves
714-
for target in &missing_targets {
715-
if !target.library_proof().exists() {
716-
compile_target(target, &vec![], options).map_err(|e| {
717-
format!("Failed to auto-compile dependency `{}`: {}", target.name, e)
718-
})?;
719-
}
720-
}
721-
}
722-
723-
// Final check
724-
check_import(imports)
725-
}
726-
727684
pub fn check_externs(externs: &IndexMap<String, String>) -> Result<(), DynError> {
728685
for (name, path) in externs.iter() {
729686
if !Path::new(path).exists() {
@@ -835,7 +792,20 @@ fn get_build_dir(release: bool) -> &'static str {
835792
}
836793
}
837794

838-
pub fn compile_target(
795+
/// Compile a single target using the Verus verifier.
796+
///
797+
/// This function directly invokes the Verus compiler on a single target.
798+
/// It handles dependency setup, external crate linking, and compilation options.
799+
/// It does NOT recursively compile dependencies, an error will occur if dependencies
800+
/// are missing. To compile a target along with its dependencies,
801+
/// - use `compile_target_with_dependencies` for that.
802+
///
803+
/// # Arguments
804+
///
805+
/// * `target` - The target to compile
806+
/// * `imports` - Additional targets to import (not auto-discovered)
807+
/// * `options` - Compilation options (log, trace, release, max_errors, disasm, pass_through)
808+
pub fn compile_single_target(
839809
target: &VerusTarget,
840810
imports: &[VerusTarget],
841811
options: &ExtraOptions,
@@ -859,7 +829,7 @@ pub fn compile_target(
859829

860830
prepare(target, options.release);
861831

862-
let mut deps = get_local_dependency_recursive(target);
832+
let mut deps = get_local_dependency(target);
863833
let dep_rebuilt = deps.values().into_iter().any(|t| t.rebuilt == true);
864834

865835
if !dep_rebuilt && target.is_fresh(&verus_targets()) {
@@ -893,9 +863,7 @@ pub fn compile_target(
893863
// imported dependencies
894864
deps.extend(extra_imports.clone());
895865
let all_imports = deps.values().collect::<Vec<_>>();
896-
check_import_with_auto_compile(all_imports.as_slice(), options).unwrap_or_else(|e| {
897-
error!("Error during verification: {}", e);
898-
});
866+
check_imports_compiled(all_imports.as_slice())?;
899867
cmd_push_import(cmd, all_imports.as_slice());
900868

901869
// import external crates
@@ -983,6 +951,60 @@ pub fn compile_target(
983951
Err(format!("Error during compilation: `{}`", target.name,).into())
984952
}
985953

954+
/// Recursively compile a target and all its dependencies in the correct order.
955+
///
956+
/// This function handles the recursive compilation of a target and its dependencies,
957+
/// ensuring proper topological ordering. It ensures that all dependencies of a target
958+
/// are compiled before the target itself. It also maintains a set of already-compiled
959+
/// targets to avoid redundant compilation.
960+
///
961+
/// # Arguments
962+
///
963+
/// * `target` - The target to compile along with its dependencies
964+
/// * `compiled` - Set tracking already-compiled target names (modified in-place)
965+
/// * `scope_targets` - Map of targets allowed to be compiled (acts as a scope limiter).
966+
/// Only targets in this map will actually be compiled, even if they're in dependencies.
967+
/// * `options` - Extra compilation options
968+
///
969+
/// # Behavior
970+
///
971+
/// 1. Returns early if the target is already in the `compiled` set
972+
/// 2. Recursively compiles all dependencies that are in `extended_targets`
973+
/// 3. Compiles the target itself if it's in `extended_targets`
974+
/// 4. Marks the target as compiled to prevent duplicate work
975+
pub fn compile_target_with_dependencies(
976+
target: &VerusTarget,
977+
compiled: &mut std::collections::HashSet<String>,
978+
scope_targets: &IndexMap<String, VerusTarget>,
979+
options: &ExtraOptions,
980+
) {
981+
let all_targets = verus_targets();
982+
983+
if compiled.contains(&target.name) {
984+
return;
985+
}
986+
987+
// First compile all dependencies that exist in scope
988+
for dep in &target.dependencies {
989+
if scope_targets.contains_key(&dep.name) {
990+
if let Some(dep_target) = all_targets.get(&dep.name) {
991+
compile_target_with_dependencies(dep_target, compiled, scope_targets, options);
992+
}
993+
}
994+
}
995+
996+
// Then compile this target if it's in scope
997+
if scope_targets.contains_key(&target.name) {
998+
compile_single_target(target, &vec![], options).unwrap_or_else(|e| {
999+
error!(
1000+
"Unable to build the dependent proof: `{}` ({})",
1001+
target.name, e
1002+
);
1003+
});
1004+
compiled.insert(target.name.clone());
1005+
}
1006+
}
1007+
9861008
pub fn exec_verify(
9871009
targets: &[VerusTarget],
9881010
imports: &[VerusTarget],
@@ -998,13 +1020,15 @@ pub fn exec_verify(
9981020
let deps_dir = out_dir.join(get_build_dir(options.release)).join("deps");
9991021

10001022
let extended_targets = get_dependent_targets_batch(targets, options.release);
1001-
for target in extended_targets.values() {
1002-
compile_target(target, &vec![], options).unwrap_or_else(|e| {
1003-
error!(
1004-
"Unable to build the dependent proof: `{}` ({})",
1005-
target.name, e
1006-
);
1007-
});
1023+
1024+
let mut compiled = std::collections::HashSet::new();
1025+
let all_targets = verus_targets();
1026+
1027+
// Process each dependency in extended_targets
1028+
for target_name in extended_targets.keys() {
1029+
if let Some(target) = all_targets.get(target_name) {
1030+
compile_target_with_dependencies(target, &mut compiled, &extended_targets, options);
1031+
}
10081032
}
10091033

10101034
let ts_start = Instant::now();
@@ -1037,11 +1061,42 @@ pub fn exec_verify(
10371061
}
10381062

10391063
// imported dependencies
1040-
let deps = &mut get_local_dependency_recursive(target);
1064+
let deps = &mut get_local_dependency(target);
10411065
deps.extend(extra_imports.clone());
10421066
let all_imports = deps.values().collect::<Vec<_>>();
10431067

1044-
check_import_with_auto_compile(all_imports.as_slice(), options).unwrap_or_else(|e| {
1068+
// Check and compile missing imports
1069+
let mut missing_targets = Vec::new();
1070+
for imp in all_imports.iter() {
1071+
if !imp.library_proof().exists() || !imp.library_path().exists() {
1072+
missing_targets.push((*imp).clone());
1073+
}
1074+
}
1075+
1076+
if !missing_targets.is_empty() {
1077+
info!(
1078+
"Missing verification files for dependencies: {:?}",
1079+
missing_targets.iter().map(|t| &t.name).collect::<Vec<_>>()
1080+
);
1081+
info!("Automatically compiling missing dependencies...");
1082+
1083+
let mut compiled = std::collections::HashSet::new();
1084+
let missing_targets_map: IndexMap<String, VerusTarget> = missing_targets
1085+
.iter()
1086+
.map(|t| (t.name.clone(), t.clone()))
1087+
.collect();
1088+
1089+
for target_item in &missing_targets {
1090+
compile_target_with_dependencies(
1091+
target_item,
1092+
&mut compiled,
1093+
&missing_targets_map,
1094+
options,
1095+
);
1096+
}
1097+
}
1098+
1099+
check_imports_compiled(all_imports.as_slice()).unwrap_or_else(|e| {
10451100
error!("Error during verification: {}", e);
10461101
});
10471102
cmd_push_import(cmd, all_imports.as_slice());
@@ -1165,13 +1220,14 @@ pub fn exec_compile(
11651220
options: &ExtraOptions,
11661221
) -> Result<(), DynError> {
11671222
let extended_targets = get_dependent_targets_batch(targets, options.release);
1168-
for target in extended_targets.values() {
1169-
compile_target(target, &[], options).unwrap_or_else(|e| {
1170-
error!(
1171-
"Unable to build the dependent proof: `{}` ({})",
1172-
target.name, e
1173-
);
1174-
});
1223+
let mut compiled = std::collections::HashSet::new();
1224+
let all_targets = verus_targets();
1225+
1226+
// Process each dependency in extended_targets
1227+
for target_name in extended_targets.keys() {
1228+
if let Some(target) = all_targets.get(target_name) {
1229+
compile_target_with_dependencies(target, &mut compiled, &extended_targets, options);
1230+
}
11751231
}
11761232

11771233
// remove the targets that has been compiled
@@ -1184,7 +1240,7 @@ pub fn exec_compile(
11841240
.collect::<Vec<_>>();
11851241

11861242
for target in remaining_targets.iter() {
1187-
compile_target(target, imports, options)?;
1243+
compile_single_target(target, imports, options)?;
11881244
}
11891245

11901246
Ok(())

0 commit comments

Comments
 (0)