Skip to content

Conversation

@gridbugs
Copy link
Collaborator

Allow the dune build command to be run concurrently with an RPC server (in either passive or eager watch mode). This works by having the second instance of dune send RPC build command to the RPC server, and the RPC server then sends diagnostic messages back to the first instance of dune.

Currently this is only implemented for the dune build command, and promoting changes into source files is not supported. There are also some subtle differences between the output of dune build is performing the build itself and when it's acting as an RPC client, such as file paths being absolute rather than relative and the underline under code errors losing its color.

@gridbugs gridbugs force-pushed the allow-concurrent-build-with-rpc-server branch from 4b4630c to e7751bb Compare April 30, 2025 05:25
Copy link
Collaborator

@Leonidas-from-XIV Leonidas-from-XIV left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's a very nice feature. I know it has some limitations (e.g. can't build in a different profile) but I was glad to see that the target can be specified and I think that covers 80% of all usecases.

It would be nice to have tests that show this mechanism working.

bin/build_cmd.ml Outdated
Printf.printf
"Error: %s\n%!"
(Dyn.to_string (Dune_rpc_private.Response.Error.to_dyn error))
| Ok Success -> print_endline "Success"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we just print_endline? In case of success I mostly expect Dune to stay silent.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should actually be using Dune_console. Otherwise it would break the TUI

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've changed it to use Dune_console for now, but I don't feel strongly about changing it to not output anything on success either.

bin/build_cmd.ml Outdated
Console.print_user_message main);
User_error.raise
[ (match List.length errors with
| 0 -> Pp.textf "Build failed."
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this a Code_error?

bin/build_cmd.ml Outdated
run_build_command ~common ~config ~request
match Dune_rpc_client.Where.get () with
| Some where ->
Scheduler.go_without_rpc_server ~common ~config (fun () -> build_rpc where targets)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems weird to call a function go_without_rpc_server that calls build_rpc. Maybe build_rpc should be build_remote to clarify that the build is happening on the far end of the RPC connection?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed to build_via_rpc_server and added some clarifying comments.

bin/build_cmd.ml Outdated
Target.interpret_targets (Common.root common) config setup targets
in
run_build_command ~common ~config ~request
match Dune_rpc_client.Where.get () with
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this how we determine whether the build is happening locally or remotely?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep. Clarified with a comment.

;;

let go ~common ~config f =
let f =
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe rename this to with_rpc to emphasize that this wrapper function will actually enable RPC?

"Error: %s\n%!"
(Dyn.to_string (Dune_rpc_private.Response.Error.to_dyn error))
| Ok Success -> print_endline "Success"
| Ok (Failure _) -> print_endline "Failure"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Failures should presumably not be printed to stdout?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe but I'm not so sure. This code was just refactored in this PR, not added. dune rpc build is not a very user-friendly command (e.g. it needs to be passed sexps on the command-line) so my assumption is that it's intended to be called from scripts. As such it's not unreasonable to expect that scripts would expect the status of the build to be printed to stdout for ease of parsing.

bin/rpc/build.ml Outdated
let+ response = build_sexp_string_targets ~wait ~targets where in
match response with
| Error (error : Dune_rpc_private.Response.Error.t) ->
Printf.printf
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should probably go to stderr or even use User_error or something?

(** Sends a command to an RPC server to build the specified targets and wait
for the build to complete or fail. *)
val build
: wait:bool
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the wait argument to wait for the result or to wait for the connection?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the connection. I've added that info to the doc comment.

let open Conv in
let from { main; related } = main, related in
let to_ (main, related) = make ~main ~related in
let main = field "main" (required User_message.sexp_without_annots) in
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why sexp_without_annots here? What are annots?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clarified with a doc comment on the sexp_without_annots function.

@gridbugs gridbugs force-pushed the allow-concurrent-build-with-rpc-server branch 2 times, most recently from 4b01bdc to 4fe5dbf Compare May 6, 2025 02:20
@gridbugs
Copy link
Collaborator Author

gridbugs commented May 6, 2025

It would be nice to have tests that show this mechanism working.

Added a test.

@gridbugs
Copy link
Collaborator Author

gridbugs commented May 6, 2025

Looks like this change has made some of our rpc tests flaky. Investigating...

@gridbugs gridbugs force-pushed the allow-concurrent-build-with-rpc-server branch from 4f51f49 to 5753bdf Compare May 6, 2025 06:58
@gridbugs
Copy link
Collaborator Author

gridbugs commented May 6, 2025

The problem was that I moved Dune_rpc_impl.Where.get () outside of the retry loop so waiting for the server to start wasn't working properly. Should be fixed now.

@gridbugs
Copy link
Collaborator Author

gridbugs commented May 6, 2025

Ah looks like something else isn't working now.

@gridbugs
Copy link
Collaborator Author

gridbugs commented May 6, 2025

There's a non-deterministic crash with this error:

+|  Internal error, please report upstream including the contents of _build/log.
+|  Description:
+|    ("Memo error handler raised an exception",
+|     { exns =
+|         [ { exn =
+|               "File \"src/dune_engine/build_system.ml\", line 67, characters 11-17: Assertion failed"
+|           ; backtrace =
+|               "Raised at Dune_engine__Build_system.State.update_build_progress_exn in file \"src/dune_engine/build_system.ml\", line 67, characters 11-23\n\
+|                Called from Dune_engine__Build_system.State.add_errors in file \"src/dune_engine/build_system.ml\", lines 86-87, characters 6-73\n\
+|                Called from Dune_engine__Build_system.report_early_exn in file \"src/dune_engine/build_system.ml\", line 1110, characters 14-37\n\
+|                Called from Fiber__Scheduler.exec in file \"vendor/fiber/src/scheduler.ml\", line 76, characters 8-11\n\
+|                "
+|           }
+|         ]
+|     })
+|  Raised at Stdune__Code_error.raise in file
+|    "otherlibs/stdune/src/code_error.ml", line 10, characters 30-62
+|  Called from Fiber__Core.map.(fun) in file "vendor/fiber/src/core.ml", line
+|    68, characters 31-36
+|  Called from Fiber__Scheduler.exec in file "vendor/fiber/src/scheduler.ml",
+|    line 76, characters 8-11
+|
+|    I must not crash.  Uncertainty is the mind-killer. Exceptions are the
+|  little-death that brings total obliteration.  I will fully express my cases.
+|  Execution will pass over me and through me.  And when it has gone past, I
+|  will unwind the stack along its path.  Where the cases are handled there will
+|  be nothing.  Only I will remain.

The loc it points to is:

  let update_build_progress_exn ~f =
    let current = Svar.read t in
    match current with
    | Building current -> Svar.write t @@ Building (f current)
    | _ -> assert false   (* <------ *)
  ;;

I wonder if this is a race between a rebuild by the eager watch server in response to a file change and a build due to an rpc build request. Will investigate more tomorrow.

@maiste maiste mentioned this pull request May 6, 2025
gridbugs added 4 commits May 7, 2025 11:54
Allow the `dune build` command to be run concurrently with an RPC
server (in either passive or eager watch mode). This works by having the
second instance of dune send RPC build command to the RPC server, and
the RPC server then sends diagnostic messages back to the first instance
of dune.

Currently this is only implemented for the `dune build` command, and
promoting changes into source files is not supported. There are also
some subtle differences between the output of `dune build` is performing
the build itself and when it's acting as an RPC client, such as file
paths being absolute rather than relative and the underline under code
errors losing its color.

Signed-off-by: Stephen Sherratt <stephen@sherra.tt>
This function evaluates a fiber concurrently with an RPC server, so
renaming to clarify this fact.

Signed-off-by: Stephen Sherratt <stephen@sherra.tt>
Signed-off-by: Stephen Sherratt <stephen@sherra.tt>
Signed-off-by: Stephen Sherratt <stephen@sherra.tt>
@gridbugs gridbugs force-pushed the allow-concurrent-build-with-rpc-server branch from 5753bdf to a59bb63 Compare May 7, 2025 01:55
@gridbugs
Copy link
Collaborator Author

gridbugs commented May 7, 2025

I wonder if this is a race between a rebuild by the eager watch server in response to a file change and a build due to an rpc build request.

This turned out to be the issue. I've fixed this but now it looks like it's getting stuck running tests. I can't reproduce locally but I'll try setting up a VM with a single core in case that makes the difference.

@gridbugs gridbugs force-pushed the allow-concurrent-build-with-rpc-server branch from c11e179 to a59bb63 Compare May 7, 2025 05:39
@gridbugs
Copy link
Collaborator Author

gridbugs commented May 7, 2025

looks like it's getting stuck running tests

This issue seems to have gone away on its own? Maybe I needed to be more patient...

@gridbugs gridbugs merged commit e88e045 into ocaml:main May 7, 2025
41 of 42 checks passed
maiste added a commit to maiste/opam-repository that referenced this pull request May 20, 2025
CHANGES:

### Fixed

- Fixed a bug that was causing cram tests attached to multiple aliases to be run multiple
  times. (ocaml/dune#11547, @Alizter)

- Fix: pass pkg-config (extra) args in all pkgconfig invocations. A missing --personality
  flag would result in pkgconf not finding libraries in some contexts. (ocaml/dune#11619, @MisterDA)

- Fix: Evaluate `enabled_if` when computing the stubs for stanzas such as
  `foreign_library` (ocaml/dune#11707, @Alizter, @rgrinberg)

- Fix $ dune describe pp for libraries in the presence of `(include_subdirs
  unqualified)` (ocaml/dune#11729, fixes ocaml/dune#10999, @rgrinberg)

- Fix `$ dune subst` in sub directories of a git repository (ocaml/dune#11760, fixes
  ocaml/dune#11045, @Richard-Degenne)

- Fix a crash involving `Path.drop_prefix` when using Melange on Windows
  (ocaml/dune#11767, @nojb)

### Added

- Added detection and warning for common typos in package dependency
  constraints (ocaml/dune#11600, fixes ocaml/dune#11575, @kemsguy7)

- Added `(extra_objects)` field to `(foreign_library)` stanza with `(:include)` support.
  (ocaml/dune#11683, @Alizter)

### Changed

- Allow build RPC messages to be handled by dune's RPC server in eager watch
  mode (ocaml/dune#11622, @gridbugs)

- Allow concurrent build with RPC server (ocaml/dune#11712, @gridbugs)
maiste added a commit to maiste/opam-repository that referenced this pull request May 21, 2025
CHANGES:

### Fixed

- Fixed a bug that was causing cram tests attached to multiple aliases to be run multiple
  times. (ocaml/dune#11547, @Alizter)

- Fix: pass pkg-config (extra) args in all pkgconfig invocations. A missing --personality
  flag would result in pkgconf not finding libraries in some contexts. (ocaml/dune#11619, @MisterDA)

- Fix: Evaluate `enabled_if` when computing the stubs for stanzas such as
  `foreign_library` (ocaml/dune#11707, @Alizter, @rgrinberg)

- Fix $ dune describe pp for libraries in the presence of `(include_subdirs
  unqualified)` (ocaml/dune#11729, fixes ocaml/dune#10999, @rgrinberg)

- Fix `$ dune subst` in sub directories of a git repository (ocaml/dune#11760, fixes
  ocaml/dune#11045, @Richard-Degenne)

- Fix a crash involving `Path.drop_prefix` when using Melange on Windows
  (ocaml/dune#11767, @nojb)

### Added

- Added detection and warning for common typos in package dependency
  constraints (ocaml/dune#11600, fixes ocaml/dune#11575, @kemsguy7)

- Added `(extra_objects)` field to `(foreign_library)` stanza with `(:include)` support.
  (ocaml/dune#11683, @Alizter)

### Changed

- Allow build RPC messages to be handled by dune's RPC server in eager watch
  mode (ocaml/dune#11622, @gridbugs)

- Allow concurrent build with RPC server (ocaml/dune#11712, @gridbugs)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants