@@ -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
599599pub 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-
727684pub 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+
9861008pub 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