Monorepo management for opam overlays
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

perf: optimize Merlin backend sharing and pipeline caching

- Share Merlin backend across files in merlint and prune analysis
- Add pipeline caching for outline/dump/enclosing queries (local-only)
- Use fresh pipelines for occurrences queries (cross-file index)
- Add --no-build/-B flag to merlint to skip automatic dune build
- Fix docs unit test for bad function format detection
- Add Eio-based sequential test runner to monopam with verbose output
- Update merlint cram tests to use -B flag for faster test runs

+44 -21
+44 -21
bin/cmd_test.ml
··· 10 10 status : [ `Ok | `Slow | `Fail | `Timeout ]; 11 11 } 12 12 13 - let run_test ~timeout dir = 13 + let status_to_string = function 14 + | `Ok -> "OK" 15 + | `Slow -> "SLOW" 16 + | `Fail -> "FAIL" 17 + | `Timeout -> "TIMEOUT" 18 + 19 + let run_test_eio ~process_mgr ~timeout dir = 20 + let cmd = Printf.sprintf "timeout %ds dune test %s 2>&1" timeout dir in 21 + Log.info (fun m -> m "Running: %s" cmd); 14 22 let start = Unix.gettimeofday () in 15 - let cmd = 16 - Printf.sprintf "timeout %ds opam exec -- dune test %s 2>&1" timeout dir 17 - in 18 - let ic = Unix.open_process_in cmd in 19 - let output = In_channel.input_all ic in 20 - let exit_status = Unix.close_process_in ic in 23 + let args = [ "sh"; "-c"; cmd ] in 24 + Eio.Switch.run @@ fun sw -> 25 + let proc = Eio.Process.spawn ~sw process_mgr args in 26 + let status = Eio.Process.await proc in 21 27 let duration = Unix.gettimeofday () -. start in 22 28 let status = 23 - match exit_status with 24 - | Unix.WEXITED 0 -> if duration > 2.0 then `Slow else `Ok 25 - | Unix.WEXITED 124 -> `Timeout 29 + match status with 30 + | `Exited 0 -> if duration > 2.0 then `Slow else `Ok 31 + | `Exited 124 -> `Timeout 26 32 | _ -> `Fail 27 33 in 28 - Log.debug (fun m -> m "%s: %s" dir output); 34 + Log.info (fun m -> 35 + m " %s: %s (%.2fs)" dir (status_to_string status) duration); 29 36 { name = dir; duration; status } 30 37 31 38 let find_test_dirs () = ··· 44 51 | _ -> None) 45 52 |> List.sort_uniq String.compare 46 53 47 - let status_to_string = function 48 - | `Ok -> "OK" 49 - | `Slow -> "SLOW" 50 - | `Fail -> "FAIL" 51 - | `Timeout -> "TIMEOUT" 52 - 53 54 let status_style = function 54 55 | `Ok -> Tty.Style.(fg (Tty.Color.ansi `Green)) 55 56 | `Slow -> Tty.Style.(fg (Tty.Color.ansi `Yellow)) ··· 58 59 59 60 let styled style s = Fmt.str "%a" (Tty.Style.styled style Fmt.string) s 60 61 62 + let prebuild ~process_mgr dirs = 63 + (* Pre-build all test directories to warm up dune cache *) 64 + let targets = String.concat " " dirs in 65 + let cmd = Printf.sprintf "dune build %s 2>/dev/null" targets in 66 + Log.info (fun m -> m "Running: %s" cmd); 67 + let args = [ "sh"; "-c"; cmd ] in 68 + Eio.Switch.run @@ fun sw -> 69 + let proc = Eio.Process.spawn ~sw process_mgr args in 70 + let _ = Eio.Process.await proc in 71 + () 72 + 73 + let run_tests_sequential ~process_mgr ~timeout dirs = 74 + (* Run tests sequentially - they share the dune build lock *) 75 + List.map (run_test_eio ~process_mgr ~timeout) dirs 76 + 61 77 let run timeout filter () = 78 + Eio_main.run @@ fun env -> 79 + let process_mgr = Eio.Stdenv.process_mgr env in 62 80 let dirs = find_test_dirs () in 63 81 let dirs = 64 82 match filter with ··· 66 84 | fs -> List.filter (fun d -> List.mem d fs) dirs 67 85 in 68 86 Log.info (fun m -> m "Testing %d directories" (List.length dirs)); 69 - let results = List.map (run_test ~timeout) dirs in 87 + List.iter (fun d -> Log.info (fun m -> m " - %s" d)) dirs; 88 + Log.info (fun m -> m "Pre-building test targets..."); 89 + prebuild ~process_mgr dirs; 90 + Log.info (fun m -> m "Running tests sequentially (timeout: %ds)..." timeout); 91 + let results = run_tests_sequential ~process_mgr ~timeout dirs in 70 92 let pp_result r = 71 93 let status_str = 72 94 styled (status_style r.status) (status_to_string r.status) ··· 100 122 `S Manpage.s_description; 101 123 `P 102 124 "Runs $(b,dune test) on each directory in the monorepo and reports \ 103 - which pass, fail, or are slow (>2s)."; 125 + which pass, fail, or are slow (>2s). Tests are prebuilt then run \ 126 + sequentially."; 104 127 `S "EXAMPLES"; 105 128 `Pre "monopam test"; 106 129 `Pre "monopam test ocaml-qemu ocaml-bloom"; 107 - `Pre "monopam test --timeout 60"; 130 + `Pre "monopam test --timeout 120"; 108 131 ] 109 132 in 110 133 let info = Cmd.info "test" ~doc ~man in 111 134 let timeout_arg = 112 135 let doc = "Timeout in seconds per test directory." in 113 - Arg.(value & opt int 30 & info [ "timeout"; "t" ] ~docv:"SECONDS" ~doc) 136 + Arg.(value & opt int 60 & info [ "timeout"; "t" ] ~docv:"SECONDS" ~doc) 114 137 in 115 138 let filter_arg = 116 139 let doc = "Only test specific directories." in