diff --git a/bin/arg.ml b/bin/arg.ml index 7cffc60593a..b00a7e14c6a 100644 --- a/bin/arg.ml +++ b/bin/arg.ml @@ -41,6 +41,7 @@ module Dep = struct type t = Dep_conf.t + let equal = Dep_conf.equal let file s = Dep_conf.File (String_with_vars.make_text Loc.none s) let make_alias_sw ~dir s = diff --git a/bin/arg.mli b/bin/arg.mli index c1f7b3f5a23..ca272582701 100644 --- a/bin/arg.mli +++ b/bin/arg.mli @@ -21,6 +21,7 @@ end module Dep : sig type t = Dune_lang.Dep_conf.t + val equal : t -> t -> bool val file : string -> t val alias : dir:Stdune.Path.Local.t -> Dune_engine.Alias.Name.t -> t val alias_rec : dir:Stdune.Path.Local.t -> Dune_engine.Alias.Name.t -> t diff --git a/bin/build_cmd.ml b/bin/build_cmd.ml index f90d2c17806..5ba33fa6964 100644 --- a/bin/build_cmd.ml +++ b/bin/build_cmd.ml @@ -205,7 +205,7 @@ let build = state of the lock could otherwise change between checking it and taking it. *) match Dune_util.Global_lock.lock ~timeout:None with - | Error () -> + | Error lock_held_by -> (* This case is reached if dune detects that another instance of dune is already running. Rather than performing the build itself, the current instance of dune will instruct the already-running instance to @@ -215,6 +215,16 @@ let build = perform the RPC call. *) Scheduler.go_without_rpc_server ~common ~config (fun () -> + if not (Common.Builder.equal builder Common.Builder.default) + then + User_warning.emit + [ Pp.textf + "Your build request is being forwarded to a running Dune instance%s so \ + most command-line arguments will be ignored." + (match (lock_held_by : Dune_util.Global_lock.Lock_held_by.t) with + | Unknown -> "" + | Pid_from_lockfile pid -> sprintf " (pid: %d)" pid) + ]; build_via_rpc_server ~print_on_success:true ~targets) | Ok () -> let request setup = diff --git a/bin/common.ml b/bin/common.ml index dd8cd606f12..2b09eb2dae2 100644 --- a/bin/common.ml +++ b/bin/common.ml @@ -22,7 +22,38 @@ module Workspace = Source.Workspace open struct open Cmdliner module Cmd = Cmd - module Term = Term + + module Term = struct + include Term + + (* Evaluate a parser, passing it no command-line arguments, in an + environment with no variables set. It's only valid to pass a parser + which can be evaluated with no arguments, otherwise a code error will + be raised. Returns the result of the parser. This is intended to be + used to extract the default value for a type implied by the behaviour + of its parser when no command-line arguments are passed to it. *) + let eval_no_args_empty_env t = + let raise_code_error data = + Code_error.raise "Unexpected result evaluating term with no args" data + in + (* Cmdliner doesn't allow argv to be empty. *) + let argv = [| "dune" |] in + let env _ = None in + match Cmd.eval_value ~argv ~env (Cmd.v (Cmd.info "dune") t) with + | Ok (`Ok x) -> x + | Ok `Help -> raise_code_error [ "ok", Dyn.string "help" ] + | Ok `Version -> raise_code_error [ "ok", Dyn.string "version" ] + | Error e -> + let error_string = + match e with + | `Parse -> "parse" + | `Term -> "term" + | `Exn -> "exn" + in + raise_code_error [ "error", Dyn.string error_string ] + ;; + end + module Manpage = Manpage end @@ -1025,6 +1056,94 @@ module Builder = struct ; log_file = Default } ;; + + let default = Term.eval_no_args_empty_env term + + let equal + t + { debug_dep_path + ; debug_backtraces + ; debug_artifact_substitution + ; debug_load_dir + ; debug_digests + ; debug_package_logs + ; wait_for_filesystem_clock + ; only_packages + ; capture_outputs + ; diff_command + ; promote + ; ignore_promoted_rules + ; force + ; no_print_directory + ; ignore_lock_dir + ; store_orig_src_dir + ; default_target + ; watch + ; print_metrics + ; dump_memo_graph_file + ; dump_memo_graph_format + ; dump_memo_graph_with_timing + ; dump_gc_stats + ; always_show_command_line + ; promote_install_files + ; file_watcher + ; workspace_config + ; cache_debug_flags + ; report_errors_config + ; separate_error_messages + ; stop_on_first_error + ; require_dune_project_file + ; watch_exclusions + ; build_dir + ; root + ; stats_trace_file + ; stats_trace_extended + ; allow_builds + ; default_root_is_cwd + ; log_file + } + = + Bool.equal t.debug_dep_path debug_dep_path + && Bool.equal t.debug_backtraces debug_backtraces + && Bool.equal t.debug_artifact_substitution debug_artifact_substitution + && Bool.equal t.debug_load_dir debug_load_dir + && Bool.equal t.debug_digests debug_digests + && Bool.equal t.debug_package_logs debug_package_logs + && Bool.equal t.wait_for_filesystem_clock wait_for_filesystem_clock + && Only_packages.Clflags.equal t.only_packages only_packages + && Bool.equal t.capture_outputs capture_outputs + && Option.equal String.equal t.diff_command diff_command + && Option.equal Dune_engine.Clflags.Promote.equal t.promote promote + && Bool.equal t.ignore_promoted_rules ignore_promoted_rules + && Bool.equal t.force force + && Bool.equal t.no_print_directory no_print_directory + && Bool.equal t.ignore_lock_dir ignore_lock_dir + && Bool.equal t.store_orig_src_dir store_orig_src_dir + && Arg.Dep.equal t.default_target default_target + && Dune_rpc_impl.Watch_mode_config.equal t.watch watch + && Bool.equal t.print_metrics print_metrics + && Option.equal Path.External.equal t.dump_memo_graph_file dump_memo_graph_file + && Graph.File_format.equal t.dump_memo_graph_format dump_memo_graph_format + && Bool.equal t.dump_memo_graph_with_timing dump_memo_graph_with_timing + && Option.equal Path.External.equal t.dump_gc_stats dump_gc_stats + && Bool.equal t.always_show_command_line always_show_command_line + && Bool.equal t.promote_install_files promote_install_files + && Dune_engine.Scheduler.Run.file_watcher_equal t.file_watcher file_watcher + && Source.Workspace.Clflags.equal t.workspace_config workspace_config + && Dune_engine.Cache_debug_flags.equal t.cache_debug_flags cache_debug_flags + && Dune_engine.Report_errors_config.equal t.report_errors_config report_errors_config + && Bool.equal t.separate_error_messages separate_error_messages + && Bool.equal t.stop_on_first_error stop_on_first_error + && Bool.equal t.require_dune_project_file require_dune_project_file + && List.equal String.equal t.watch_exclusions watch_exclusions + && String.equal t.build_dir build_dir + && Option.equal String.equal t.root root + && Option.equal String.equal t.stats_trace_file stats_trace_file + && Bool.equal t.stats_trace_extended stats_trace_extended + && Bool.equal t.allow_builds allow_builds + && Bool.equal t.default_root_is_cwd default_root_is_cwd + && Log.File.equal t.log_file log_file + ;; end type t = diff --git a/bin/common.mli b/bin/common.mli index 39b1cf52439..1ed5e618d6b 100644 --- a/bin/common.mli +++ b/bin/common.mli @@ -28,6 +28,7 @@ val prefix_target : t -> string -> string module Builder : sig type t + val equal : t -> t -> bool val root : t -> string option val set_root : t -> string -> t val forbid_builds : t -> t @@ -38,6 +39,7 @@ module Builder : sig val set_promote : t -> Dune_engine.Clflags.Promote.t -> t val default_target : t -> Arg.Dep.t val term : t Cmdliner.Term.t + val default : t end (** [init] creates a [Common.t] by executing a sequence of side-effecting actions to diff --git a/bin/tools/tools_common.ml b/bin/tools/tools_common.ml index 0d722304a0e..924ba768581 100644 --- a/bin/tools/tools_common.ml +++ b/bin/tools/tools_common.ml @@ -29,7 +29,7 @@ let build_dev_tool_via_rpc dev_tool = let lock_and_build_dev_tool ~common ~config dev_tool = let open Fiber.O in match Dune_util.Global_lock.lock ~timeout:None with - | Error () -> + | Error _lock_held_by -> Scheduler.go_without_rpc_server ~common ~config (fun () -> let* () = Lock_dev_tool.lock_dev_tool dev_tool |> Memo.run in build_dev_tool_via_rpc dev_tool) diff --git a/src/dune_cache/config.ml b/src/dune_cache/config.ml index e14ab5f7ae4..df66c9825ae 100644 --- a/src/dune_cache/config.ml +++ b/src/dune_cache/config.ml @@ -8,6 +8,13 @@ module Reproducibility_check = struct | Check_with_probability of float | Check + let equal a b = + match a, b with + | Skip, Skip | Check, Check -> true + | Check_with_probability a, Check_with_probability b -> a = b + | _, _ -> false + ;; + let sample = function | Skip -> false | Check_with_probability p -> Random.float 1. < p diff --git a/src/dune_cache/config.mli b/src/dune_cache/config.mli index e7461c5baa3..0168f00c9b3 100644 --- a/src/dune_cache/config.mli +++ b/src/dune_cache/config.mli @@ -16,6 +16,8 @@ module Reproducibility_check : sig | Check_with_probability of float (** [0 < p < 1] *) | Check + val equal : t -> t -> bool + (** Should we check the current build rule for reproducibility? - If [t = Skip], return [false]. diff --git a/src/dune_cache_storage/mode.ml b/src/dune_cache_storage/mode.ml index f75110f4c76..dbc1ed7555d 100644 --- a/src/dune_cache_storage/mode.ml +++ b/src/dune_cache_storage/mode.ml @@ -4,6 +4,12 @@ type t = | Hardlink | Copy +let equal a b = + match a, b with + | Hardlink, Hardlink | Copy, Copy -> true + | _, _ -> false +;; + let all = [ "hardlink", Hardlink; "copy", Copy ] let of_string s = diff --git a/src/dune_cache_storage/mode.mli b/src/dune_cache_storage/mode.mli index 6af6d4217fd..811455f3e3e 100644 --- a/src/dune_cache_storage/mode.mli +++ b/src/dune_cache_storage/mode.mli @@ -24,6 +24,7 @@ type t = | Hardlink | Copy +val equal : t -> t -> bool val all : (string * t) list val to_string : t -> string val of_string : string -> (t, string) result diff --git a/src/dune_config_file/display.ml b/src/dune_config_file/display.ml index 9b89c81546a..bc284a53ae0 100644 --- a/src/dune_config_file/display.ml +++ b/src/dune_config_file/display.ml @@ -8,6 +8,15 @@ type t = } | Tui +let equal a b = + match a, b with + | ( Simple { status_line = a_status_line; verbosity = a_verbosity } + , Simple { status_line = b_status_line; verbosity = b_verbosity } ) -> + Bool.equal a_status_line b_status_line && Display.equal a_verbosity b_verbosity + | Tui, Tui -> true + | _, _ -> false +;; + let progress = Simple { status_line = true; verbosity = Quiet } let verbose = Simple { status_line = true; verbosity = Verbose } let short = Simple { status_line = true; verbosity = Short } diff --git a/src/dune_config_file/display.mli b/src/dune_config_file/display.mli index 7dd164c79e9..2f788291329 100644 --- a/src/dune_config_file/display.mli +++ b/src/dune_config_file/display.mli @@ -9,6 +9,8 @@ type t = } | Tui +val equal : t -> t -> bool + (** All the supported display modes for setting from the command line. *) val all : (string * t) list diff --git a/src/dune_config_file/dune_config_file.ml b/src/dune_config_file/dune_config_file.ml index 46c094ce653..b45a09e132e 100644 --- a/src/dune_config_file/dune_config_file.ml +++ b/src/dune_config_file/dune_config_file.ml @@ -25,6 +25,13 @@ module Dune_config = struct ; license : string list option } + let equal t { authors; maintainers; maintenance_intent; license } = + Option.equal (List.equal String.equal) t.authors authors + && Option.equal (List.equal String.equal) t.authors maintainers + && Option.equal (List.equal String.equal) t.authors maintenance_intent + && Option.equal (List.equal String.equal) t.authors license + ;; + let decode = fields (let+ authors = field_o "authors" (repeat string) @@ -59,6 +66,14 @@ module Dune_config = struct ] ;; + let equal a b = + match a, b with + | Preserve, Preserve + | Clear_on_rebuild, Clear_on_rebuild + | Clear_on_rebuild_and_flush_history, Clear_on_rebuild_and_flush_history -> true + | _, _ -> false + ;; + let to_dyn = function | Preserve -> Dyn.Variant ("Preserve", []) | Clear_on_rebuild -> Dyn.Variant ("Clear_on_rebuild", []) @@ -74,6 +89,13 @@ module Dune_config = struct | Fixed of int | Auto + let equal a b = + match a, b with + | Fixed a, Fixed b -> Int.equal a b + | Auto, Auto -> true + | _, _ -> false + ;; + let error = Error "invalid concurrency value, must be 'auto' or a positive number" let of_string = function @@ -105,6 +127,8 @@ module Dune_config = struct module Sandboxing_preference = struct type t = Sandbox_mode.t list + let equal = List.equal Sandbox_mode.equal + let decode : Sandbox_mode.t Dune_sexp.Decoder.t = let open Dune_sexp.Decoder in enum @@ -125,6 +149,14 @@ module Dune_config = struct | Enabled_except_user_rules | Enabled + let equal a b = + match a, b with + | Disabled, Disabled + | Enabled_except_user_rules, Enabled_except_user_rules + | Enabled, Enabled -> true + | _, _ -> false + ;; + let to_string = function | Disabled -> "disabled" | Enabled_except_user_rules -> "enabled-except-user-rules" @@ -166,6 +198,8 @@ module Dune_config = struct module Storage_mode = struct type t = Dune_cache_storage.Mode.t option + let equal = Option.equal Dune_cache_storage.Mode.equal + let all = ("auto", None) :: List.map ~f:(fun (name, mode) -> name, Some mode) Dune_cache_storage.Mode.all @@ -206,6 +240,56 @@ module Dune_config = struct } end + module Make_equal + (M : S) + (Equal : sig + val field : ('a -> 'a -> bool) -> 'a M.field -> 'a M.field -> bool + end) = + struct + let field = Equal.field + + let equal + (t : M.t) + { M.display + ; concurrency + ; terminal_persistence + ; sandboxing_preference + ; cache_enabled + ; cache_reproducibility_check + ; cache_storage_mode + ; action_stdout_on_success + ; action_stderr_on_success + ; project_defaults + ; experimental + } + = + field Display.equal t.display display + && field Concurrency.equal t.concurrency concurrency + && field Terminal_persistence.equal t.terminal_persistence terminal_persistence + && field Sandboxing_preference.equal t.sandboxing_preference sandboxing_preference + && field Cache.Toggle.equal t.cache_enabled cache_enabled + && field + Dune_cache.Config.Reproducibility_check.equal + t.cache_reproducibility_check + cache_reproducibility_check + && field Cache.Storage_mode.equal t.cache_storage_mode cache_storage_mode + && field + Action_output_on_success.equal + t.action_stdout_on_success + action_stdout_on_success + && field + Action_output_on_success.equal + t.action_stderr_on_success + action_stderr_on_success + && field Project_defaults.equal t.project_defaults project_defaults + && field + (List.equal + (Tuple.T2.equal String.equal (Tuple.T2.equal Loc.equal String.equal))) + t.experimental + experimental + ;; + end + module Make_superpose (A : S) (B : S) @@ -318,6 +402,13 @@ module Dune_config = struct (struct let field f = Dyn.option f end) + + include + Make_equal + (M) + (struct + let field f = Option.equal f + end) end include @@ -333,6 +424,13 @@ module Dune_config = struct let field f = f end) + include + Make_equal + (M) + (struct + let field f = f + end) + let standard_watch_exclusions = [ {|^_opam|} ; {|/_opam|} @@ -352,7 +450,6 @@ module Dune_config = struct ;; let hash = Poly.hash - let equal a b = Poly.equal a b let default = { display = Simple { verbosity = Quiet; status_line = not Execution_env.inside_dune } diff --git a/src/dune_config_file/dune_config_file.mli b/src/dune_config_file/dune_config_file.mli index 743ada5e59f..7c1638a8da6 100644 --- a/src/dune_config_file/dune_config_file.mli +++ b/src/dune_config_file/dune_config_file.mli @@ -20,6 +20,7 @@ module Dune_config : sig | Fixed of int | Auto + val equal : t -> t -> bool val of_string : string -> (t, string) result val to_string : t -> string end @@ -94,6 +95,7 @@ module Dune_config : sig val empty : t val superpose : t -> t -> t val to_dyn : t -> Dyn.t + val equal : t -> t -> bool end (** A standard list of watch exclusions *) diff --git a/src/dune_engine/cache_debug_flags.ml b/src/dune_engine/cache_debug_flags.ml index fa536b2eabb..9caa7ba56c5 100644 --- a/src/dune_engine/cache_debug_flags.ml +++ b/src/dune_engine/cache_debug_flags.ml @@ -7,3 +7,9 @@ type t = ; workspace_local_cache : bool ; fs_cache : bool } + +let equal t { shared_cache; workspace_local_cache; fs_cache } = + Bool.equal t.shared_cache shared_cache + && Bool.equal t.workspace_local_cache workspace_local_cache + && Bool.equal t.fs_cache fs_cache +;; diff --git a/src/dune_engine/clflags.ml b/src/dune_engine/clflags.ml index 7bb186f8190..de5346bb6ec 100644 --- a/src/dune_engine/clflags.ml +++ b/src/dune_engine/clflags.ml @@ -3,6 +3,12 @@ module Promote = struct | Automatically | Never + let equal a b = + match a, b with + | Automatically, Automatically | Never, Never -> true + | _, _ -> false + ;; + let to_dyn = function | Automatically -> Dyn.variant "Automatically" [] | Never -> Dyn.variant "Never" [] diff --git a/src/dune_engine/clflags.mli b/src/dune_engine/clflags.mli index 3b7c2f2bcbf..c5f2bb87356 100644 --- a/src/dune_engine/clflags.mli +++ b/src/dune_engine/clflags.mli @@ -22,6 +22,7 @@ module Promote : sig | Automatically | Never + val equal : t -> t -> bool val to_dyn : t -> Dyn.t end diff --git a/src/dune_engine/display.ml b/src/dune_engine/display.ml index c2f91998cc0..3a79ca67507 100644 --- a/src/dune_engine/display.ml +++ b/src/dune_engine/display.ml @@ -3,6 +3,12 @@ type t = | Short | Verbose +let equal a b = + match a, b with + | Quiet, Quiet | Short, Short | Verbose, Verbose -> true + | _, _ -> false +;; + let to_dyn : t -> Dyn.t = function | Quiet -> Variant ("Quiet", []) | Short -> Variant ("Short", []) diff --git a/src/dune_engine/display.mli b/src/dune_engine/display.mli index 64e1b8a1d0c..eb4ec024bb2 100644 --- a/src/dune_engine/display.mli +++ b/src/dune_engine/display.mli @@ -10,4 +10,5 @@ type t = | Short | Verbose +val equal : t -> t -> bool val to_dyn : t -> Dyn.t diff --git a/src/dune_engine/report_errors_config.ml b/src/dune_engine/report_errors_config.ml index 4d7119f9fba..d8c845fca3a 100644 --- a/src/dune_engine/report_errors_config.ml +++ b/src/dune_engine/report_errors_config.ml @@ -15,6 +15,12 @@ type t = scheduling. *) | Twice +let equal a b = + match a, b with + | Early, Early | Deterministic, Deterministic | Twice, Twice -> true + | _, _ -> false +;; + let default : t = match Dune_util.Execution_env.inside_dune with | true -> Deterministic diff --git a/src/dune_engine/scheduler.ml b/src/dune_engine/scheduler.ml index 09b2d37b516..5b64f909658 100644 --- a/src/dune_engine/scheduler.ml +++ b/src/dune_engine/scheduler.ml @@ -1109,6 +1109,12 @@ module Run = struct | Automatic | No_watcher + let file_watcher_equal a b = + match a, b with + | Automatic, Automatic | No_watcher, No_watcher -> true + | _, _ -> false + ;; + module Build_outcome = Build_outcome module Event_queue = Event.Queue module Event = Handler.Event diff --git a/src/dune_engine/scheduler.mli b/src/dune_engine/scheduler.mli index 85c10d8bc4e..8d90199f6dd 100644 --- a/src/dune_engine/scheduler.mli +++ b/src/dune_engine/scheduler.mli @@ -30,6 +30,8 @@ module Run : sig | Automatic | No_watcher + val file_watcher_equal : file_watcher -> file_watcher -> bool + module Shutdown : sig module Reason : sig type t = diff --git a/src/dune_graph/graph.ml b/src/dune_graph/graph.ml index d4484dcd6f1..b159661b33e 100644 --- a/src/dune_graph/graph.ml +++ b/src/dune_graph/graph.ml @@ -6,6 +6,12 @@ module File_format = struct | Dot | Summary + let equal a b = + match a, b with + | Gexf, Gexf | Dot, Dot | Summary, Summary -> true + | _, _ -> false + ;; + let conv = ( (function | "gexf" -> Ok Gexf diff --git a/src/dune_graph/graph.mli b/src/dune_graph/graph.mli index cdc26504ad9..649b161e8f3 100644 --- a/src/dune_graph/graph.mli +++ b/src/dune_graph/graph.mli @@ -6,6 +6,7 @@ module File_format : sig | Dot | Summary + val equal : t -> t -> bool val conv : (string -> (t, [> `Msg of string ]) result) * (Format.formatter -> t -> unit) end diff --git a/src/dune_lang/dep_conf.ml b/src/dune_lang/dep_conf.ml index d8866d8c3ad..c417b8e1da0 100644 --- a/src/dune_lang/dep_conf.ml +++ b/src/dune_lang/dep_conf.ml @@ -7,6 +7,10 @@ module Glob_files = struct ; recursive : bool } + let equal t { glob; recursive } = + String_with_vars.equal t.glob glob && Bool.equal t.recursive recursive + ;; + let to_dyn { glob; recursive } = Dyn.record [ "glob", String_with_vars.to_dyn glob; "recursive", Dyn.bool recursive ] ;; @@ -15,6 +19,16 @@ end module Sandbox_config = struct type t = Loc.t * [ `None | `Always | `Preserve_file_kind ] list + let equal = + Tuple.T2.equal + Loc.equal + (List.equal (fun a b -> + match a, b with + | `None, `None | `Always, `Always | `Preserve_file_kind, `Preserve_file_kind -> + true + | _, _ -> false)) + ;; + let all = [ "none", `None; "always", `Always; "preserve_file_kind", `Preserve_file_kind ] ;; @@ -50,6 +64,21 @@ type t = | Sandbox_config of Sandbox_config.t | Include of string +let equal a b = + match a, b with + | File a, File b + | Alias a, Alias b + | Alias_rec a, Alias_rec b + | Source_tree a, Source_tree b + | Package a, Package b + | Env_var a, Env_var b -> String_with_vars.equal a b + | Glob_files a, Glob_files b -> Glob_files.equal a b + | Universe, Universe -> true + | Sandbox_config a, Sandbox_config b -> Sandbox_config.equal a b + | Include a, Include b -> String.equal a b + | _, _ -> false +;; + let remove_locs = function | File sw -> File (String_with_vars.remove_locs sw) | Alias sw -> Alias (String_with_vars.remove_locs sw) diff --git a/src/dune_lang/dep_conf.mli b/src/dune_lang/dep_conf.mli index a2798be623a..cee2a4db4d0 100644 --- a/src/dune_lang/dep_conf.mli +++ b/src/dune_lang/dep_conf.mli @@ -42,6 +42,7 @@ type t = | Sandbox_config of Sandbox_config.t | Include of string +val equal : t -> t -> bool val remove_locs : t -> t include Conv.S with type t := t diff --git a/src/dune_rpc_impl/watch_mode_config.ml b/src/dune_rpc_impl/watch_mode_config.ml index 517659e1910..c4c8f1ac1b5 100644 --- a/src/dune_rpc_impl/watch_mode_config.ml +++ b/src/dune_rpc_impl/watch_mode_config.ml @@ -8,6 +8,12 @@ type t = | No | Yes of rebuild_trigger +let equal a b = + match a, b with + | No, No | Yes Eager, Yes Eager | Yes Passive, Yes Passive -> true + | _, _ -> false +;; + let all = [ No; Yes Eager; Yes Passive ] let to_string = function diff --git a/src/dune_rpc_impl/watch_mode_config.mli b/src/dune_rpc_impl/watch_mode_config.mli index 38d553d3c0e..83aff53318f 100644 --- a/src/dune_rpc_impl/watch_mode_config.mli +++ b/src/dune_rpc_impl/watch_mode_config.mli @@ -6,5 +6,6 @@ type t = | No | Yes of rebuild_trigger +val equal : t -> t -> bool val to_string : t -> string val of_string : string -> (t, string) Result.t diff --git a/src/dune_util/global_lock.ml b/src/dune_util/global_lock.ml index 0ae0a729b67..ab34631affb 100644 --- a/src/dune_util/global_lock.ml +++ b/src/dune_util/global_lock.ml @@ -66,6 +66,30 @@ end let locked = ref false +module Lock_held_by = struct + type t = + | Pid_from_lockfile of int + | Unknown + + let read_lock_file () = + match Io.read_file (Path.build lock_file) with + | exception _ -> Unknown + | pid -> + (match int_of_string_opt pid with + | Some pid -> Pid_from_lockfile pid + | None -> + User_error.raise + [ Pp.textf + "Unexpected contents of build directory global lock file (%s). Expected \ + an integer PID. Found: %s" + (Path.Build.to_string_maybe_quoted lock_file) + pid + ] + ~hints: + [ Pp.textf "Try deleting %s" (Path.Build.to_string_maybe_quoted lock_file) ]) + ;; +end + let lock ~timeout = match Config.(get global_lock) with | `Disabled -> Ok () @@ -90,20 +114,22 @@ let lock ~timeout = | `Success -> locked := true; Ok () - | `Failure -> Error ()) + | `Failure -> + let lock_held_by = Lock_held_by.read_lock_file () in + Error lock_held_by) ;; let lock_exn ~timeout = match lock ~timeout with | Ok () -> () - | Error () -> + | Error lock_held_by -> User_error.raise [ Pp.textf "A running dune%s instance has locked the build directory. If this is not the \ - case, please delete %s" - (match Io.read_file (Path.build lock_file) with - | exception _ -> "" - | pid -> sprintf " (pid: %s)" pid) + case, please delete %S." + (match lock_held_by with + | Unknown -> "" + | Pid_from_lockfile pid -> sprintf " (pid: %d)" pid) (Path.Build.to_string_maybe_quoted lock_file) ] ;; diff --git a/src/dune_util/global_lock.mli b/src/dune_util/global_lock.mli index da66837b4df..1b5eef0209b 100644 --- a/src/dune_util/global_lock.mli +++ b/src/dune_util/global_lock.mli @@ -2,10 +2,16 @@ Before starting rpc, writing to the build dir, this lock should be locked. *) -(** attempt to acquire a lock. once a lock is locked, subsequent locks always +module Lock_held_by : sig + type t = + | Pid_from_lockfile of int + | Unknown +end + +(** Attempt to acquire a lock. once a lock is locked, subsequent locks always succeed. Returns [Ok ()] if the lock is acquired within [timeout] seconds, and [Error ()] otherwise. *) -val lock : timeout:float option -> (unit, unit) result +val lock : timeout:float option -> (unit, Lock_held_by.t) result val lock_exn : timeout:float option -> unit diff --git a/src/dune_util/log.ml b/src/dune_util/log.ml index 15d66becbd1..3600067d483 100644 --- a/src/dune_util/log.ml +++ b/src/dune_util/log.ml @@ -6,7 +6,14 @@ module File = struct | Default | No_log_file | This of Path.t - | Out_channel of out_channel + | Stderr + + let equal a b = + match a, b with + | Default, Default | No_log_file, No_log_file | Stderr, Stderr -> true + | This a, This b -> Path.equal a b + | _, _ -> false + ;; end type real = { oc : out_channel option } @@ -18,7 +25,7 @@ let init ?(file = File.Default) () = let oc = match file with | No_log_file -> None - | Out_channel s -> Some s + | Stderr -> Some stderr | This path -> Path.mkdir_p (Path.parent_exn path); Some (Io.open_out path) diff --git a/src/dune_util/log.mli b/src/dune_util/log.mli index f4fc2468113..dc350ec8671 100644 --- a/src/dune_util/log.mli +++ b/src/dune_util/log.mli @@ -6,7 +6,9 @@ module File : sig | Default | No_log_file | This of Path.t - | Out_channel of out_channel + | Stderr + + val equal : t -> t -> bool end (** Initialise the log file *) diff --git a/src/source/only_packages.ml b/src/source/only_packages.ml index cefb4654c03..fd438d3dcdd 100644 --- a/src/source/only_packages.ml +++ b/src/source/only_packages.ml @@ -8,6 +8,16 @@ module Clflags = struct ; command_line_option : string } + let equal a b = + match a, b with + | No_restriction, No_restriction -> true + | ( Restrict { names = a_names; command_line_option = a_command_line_options } + , Restrict { names = b_names; command_line_option = b_command_line_options } ) -> + Package.Name.Set.equal a_names b_names + && String.equal a_command_line_options b_command_line_options + | _, _ -> false + ;; + let to_dyn = function | No_restriction -> Dyn.Variant ("No_restriction", []) | Restrict { names; command_line_option } -> diff --git a/src/source/only_packages.mli b/src/source/only_packages.mli index 01ee36ced6a..87b26521e80 100644 --- a/src/source/only_packages.mli +++ b/src/source/only_packages.mli @@ -11,6 +11,8 @@ module Clflags : sig (** Which of [-p], [--only-packages], ... was passed *) } + val equal : t -> t -> bool + (** This must be called exactly once *) val set : t -> unit end diff --git a/src/source/workspace.ml b/src/source/workspace.ml index e079e07ba8b..00db72a6243 100644 --- a/src/source/workspace.ml +++ b/src/source/workspace.ml @@ -749,6 +749,24 @@ module Clflags = struct ; config_from_config_file : Dune_config.Partial.t } + let equal + t + { x + ; profile + ; instrument_with + ; workspace_file + ; config_from_command_line + ; config_from_config_file + } + = + Option.equal Context_name.equal t.x x + && Option.equal Profile.equal t.profile profile + && Option.equal (List.equal Lib_name.equal) t.instrument_with instrument_with + && Option.equal Path.Outside_build_dir.equal t.workspace_file workspace_file + && Dune_config.Partial.equal t.config_from_command_line config_from_command_line + && Dune_config.Partial.equal t.config_from_config_file config_from_config_file + ;; + let to_dyn { x ; profile diff --git a/src/source/workspace.mli b/src/source/workspace.mli index e43020d958e..c6d5c201bd7 100644 --- a/src/source/workspace.mli +++ b/src/source/workspace.mli @@ -143,6 +143,8 @@ module Clflags : sig ; config_from_config_file : Dune_config.Partial.t } + val equal : t -> t -> bool + (** This must be called exactly once *) val set : t -> unit end diff --git a/test/blackbox-tests/test-cases/watching/watching-eager-concurrent-build-command.t b/test/blackbox-tests/test-cases/watching/watching-eager-concurrent-build-command.t index f226d336979..0285d45db1f 100644 --- a/test/blackbox-tests/test-cases/watching/watching-eager-concurrent-build-command.t +++ b/test/blackbox-tests/test-cases/watching/watching-eager-concurrent-build-command.t @@ -9,6 +9,7 @@ Build the project once before starting the watch server so the watch server star $ dune build --watch & Success, waiting for filesystem changes... Success, waiting for filesystem changes... + Success, waiting for filesystem changes... File "foo.ml", line 1, characters 9-21: 1 | let () = print_endlin "Hello, World!" ^^^^^^^^^^^^ @@ -26,6 +27,13 @@ Demonstrate that we can run "dune build" while the watch server is running. $ dune build Success +Demonstrate that a warning is displayed when extra arguments are passed to +"dune build", since those arguments will be ignored. + $ dune build --auto-promote 2>&1 | sed 's/pid: [0-9]*/pid: PID/g' + Warning: Your build request is being forwarded to a running Dune instance + (pid: PID) so most command-line arguments will be ignored. + Success + Demonstrate that error messages are still printed by "dune build" when it's acting as an RPC client while running concurrently with an RPC server. $ echo 'let () = print_endlin "Hello, World!"' > foo.ml diff --git a/test/expect-tests/dune_rpc_e2e/dune_rpc_e2e.ml b/test/expect-tests/dune_rpc_e2e/dune_rpc_e2e.ml index 117f01eb352..8cda8362173 100644 --- a/test/expect-tests/dune_rpc_e2e/dune_rpc_e2e.ml +++ b/test/expect-tests/dune_rpc_e2e/dune_rpc_e2e.ml @@ -9,7 +9,7 @@ module Session = Csexp_rpc.Session (* enable to debug process stdout/stderr *) let debug = false -let () = if debug then Dune_util.Log.init ~file:(Out_channel stderr) () +let () = if debug then Dune_util.Log.init ~file:Stderr () let dune_prog = lazy