|  |  | @ -6,146 +6,10 @@ | 
			
		
	
		
		
			
				
					
					|  |  |  |  *) |  |  |  |  *) | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | open! IStd |  |  |  | open! IStd | 
			
		
	
		
		
			
				
					
					|  |  |  | open PolyVariantEqual |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | module L = Logging |  |  |  | module L = Logging | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | (** Module to merge the results of capture for different buck targets. *) |  |  |  | (** Module to merge the results of capture for different buck targets. *) | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | (** Flag to control whether the timestamp of symbolic links |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     is used to determine whether a captured directory needs to be merged. *) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | let check_timestamp_of_symlinks = true |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | let buck_out () = Filename.concat Config.project_root "buck-out" |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | let modified_targets = ref String.Set.empty |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | let record_modified_targets_from_file file = |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   match Utils.read_file file with |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   | Ok targets -> |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |       modified_targets := List.fold ~f:String.Set.add ~init:String.Set.empty targets |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   | Error error -> |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |       L.user_error "Failed to read modified targets file '%s': %s@." file error ; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |       () |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | type stats = {mutable files_linked: int; mutable targets_merged: int} |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | let empty_stats () = {files_linked= 0; targets_merged= 0} |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | let link_exists s = |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   try |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     ignore (Unix.lstat s) ; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     true |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   with Unix.Unix_error _ -> false |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | let create_link ~stats src dst = |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   if link_exists dst then Unix.unlink dst ; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   Unix.symlink ~src ~dst ; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   (* Set the accessed and modified time of the original file slightly in the past.  Due to |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |      the coarse precision of the timestamps, it is possible for the source and destination of a |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |      link to have the same modification time. When this happens, the files will be considered to |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |      need re-analysis every time, indefinitely. *) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   let near_past = Unix.gettimeofday () -. 1. in |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   Unix.utimes src ~access:near_past ~modif:near_past ; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   stats.files_linked <- stats.files_linked + 1 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | (** Create symbolic links recursively from the destination to the source. |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     Replicate the structure of the source directory in the destination, |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     with files replaced by links to the source. *) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | let rec slink ~stats ~skiplevels src dst = |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   L.(debug MergeCapture Verbose) "slink src:%s dst:%s skiplevels:%d@." src dst skiplevels ; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   if Sys.is_directory src = `Yes then ( |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     if Sys.file_exists dst <> `Yes then Unix.mkdir dst ~perm:0o700 ; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     let items = Sys.readdir src in |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     Array.iter |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |       ~f:(fun item -> |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         slink ~stats ~skiplevels:(skiplevels - 1) (Filename.concat src item) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |           (Filename.concat dst item) ) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |       items ) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   else if skiplevels > 0 then () |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   else create_link ~stats src dst |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | (** Determine if the destination should link to the source. |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     To check if it was linked before, check if all the captured source files |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     from the source are also in the destination. |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     And for each of the files inside (.cfg, .cg, etc), check that the destinations |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     of symbolic links were not modified after the links themselves. *) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | let should_link ~target ~target_results_dir ~stats infer_out_src infer_out_dst = |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   let num_captured_files = ref 0 in |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   let symlink_up_to_date file = |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     let filename = DB.filename_from_string file in |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     let time_orig = DB.file_modified_time ~symlink:false filename in |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     let time_link = DB.file_modified_time ~symlink:true filename in |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     L.(debug MergeCapture Verbose) "file:%s time_orig:%f time_link:%f@." file time_orig time_link ; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     time_link > time_orig |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   in |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   let symlinks_up_to_date captured_file = |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     if Sys.is_directory captured_file = `Yes then |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |       let contents = Array.to_list (Sys.readdir captured_file) in |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |       List.for_all |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         ~f:(fun file -> |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |           let file_path = Filename.concat captured_file file in |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |           Sys.file_exists file_path = `Yes |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |           && ((not check_timestamp_of_symlinks) || symlink_up_to_date file_path) ) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         contents |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     else true |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   in |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   let check_file captured_file = |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     Sys.file_exists captured_file = `Yes && symlinks_up_to_date captured_file |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   in |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   let was_copied () = |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     let captured_src = Filename.concat infer_out_src Config.captured_dir_name in |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     let captured_dst = Filename.concat infer_out_dst Config.captured_dir_name in |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     if Sys.file_exists captured_src = `Yes && Sys.is_directory captured_src = `Yes then ( |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |       let captured_files = Array.to_list (Sys.readdir captured_src) in |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |       num_captured_files := List.length captured_files ; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |       List.for_all ~f:(fun file -> check_file (Filename.concat captured_dst file)) captured_files ) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     else true |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   in |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   let was_modified () = String.Set.mem !modified_targets target in |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   let r = was_modified () || not (was_copied ()) in |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   if r then stats.targets_merged <- stats.targets_merged + 1 ; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   L.(debug MergeCapture Verbose) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     "lnk:%s:%d %s@." |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     (if r then "T" else "F") |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     !num_captured_files target_results_dir ; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   if r then L.(debug MergeCapture Medium) "%s@." target_results_dir ; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   r |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | (** should_link needs to know whether the source file has changed, |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     and  to determine whether the destination has never been copied. |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     In both cases, perform the link. *) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | let process_merge_file deps_file = |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   let infer_out_dst = Config.results_dir in |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   let stats = empty_stats () in |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   let process_line line = |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     match Str.split_delim (Str.regexp (Str.quote "\t")) line with |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     | target :: _ :: target_results_dir :: _ -> |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         let infer_out_src = |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |           if Filename.is_relative target_results_dir then |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             Filename.dirname (buck_out ()) ^/ target_results_dir |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |           else target_results_dir |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         in |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         let skiplevels = 2 in |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         (* Don't link toplevel files *) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         if should_link ~target ~target_results_dir ~stats infer_out_src infer_out_dst then |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |           slink ~stats ~skiplevels infer_out_src infer_out_dst |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     | _ -> |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         () |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   in |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   ( match Utils.read_file deps_file with |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   | Ok lines -> |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |       List.iter ~f:process_line lines |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   | Error error -> |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |       L.internal_error "Couldn't read deps file '%s': %s" deps_file error ) ; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   L.progress "Targets merged: %d@\n" stats.targets_merged ; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   L.progress "Files linked: %d@\n" stats.files_linked |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | let merge_global_tenvs infer_deps_file = |  |  |  | let merge_global_tenvs infer_deps_file = | 
			
		
	
		
		
			
				
					
					|  |  |  |   let global_tenv = Tenv.create () in |  |  |  |   let global_tenv = Tenv.create () in | 
			
		
	
		
		
			
				
					
					|  |  |  |   let merge infer_out_src = |  |  |  |   let merge infer_out_src = | 
			
		
	
	
		
		
			
				
					|  |  | @ -165,7 +29,6 @@ let merge_captured_targets () = | 
			
		
	
		
		
			
				
					
					|  |  |  |   let infer_deps_file = Config.(results_dir ^/ buck_infer_deps_file_name) in |  |  |  |   let infer_deps_file = Config.(results_dir ^/ buck_infer_deps_file_name) in | 
			
		
	
		
		
			
				
					
					|  |  |  |   MergeResults.merge_buck_flavors_results infer_deps_file ; |  |  |  |   MergeResults.merge_buck_flavors_results infer_deps_file ; | 
			
		
	
		
		
			
				
					
					|  |  |  |   if Config.genrule_master_mode then merge_global_tenvs infer_deps_file ; |  |  |  |   if Config.genrule_master_mode then merge_global_tenvs infer_deps_file ; | 
			
		
	
		
		
			
				
					
					|  |  |  |   process_merge_file infer_deps_file ; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   L.progress "Merging captured Buck targets took %a@\n%!" Mtime.Span.pp (Mtime_clock.count time0) |  |  |  |   L.progress "Merging captured Buck targets took %a@\n%!" Mtime.Span.pp (Mtime_clock.count time0) | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
	
		
		
			
				
					|  |  | 
 |