The b0 action manual

This manual shows how to deal with b0 actions and how to create your own.

TODO. Not super clear yet.

Basics

Build artefacts are meant to be executed, displayed, tested, benchmarked, deployed etc. As such b0 treats building as a mere side effect and associates to each build unit an action. The action of a unit u can simply be run with:

b0 -- UNIT ARG…

This builds the unit named UNIT like b0 -u UNIT would do and execute UNIT's action with arguments ARG….

Note that an action:

A lot of domain specific build unit constructors will define a default action for you. Here are a few examples:

Making your own action

XXX Needs more words.

An action has to be part of a unit, that's the way you can operate on it. However that unit may be empty build-wise. The B0_unit.of_action constructors do this.

The following action takes a sript located in scripts/hello.sh in the B0.ml scope directory and runs it with the cwd set to the scope directory.

let hello =
  B0_unit.of_action' "hello" ~doc:"Run hello script" @@ fun env _ ~args ->
  let cwd = B0_env.scope_dir env in
  let hello = B0_env.in_scope_dir env ~/"scripts/hello.sh" in
  Ok (Os.Exit.execv ~cwd Cmd.(path hello %% args))

Even though you should rewrite all these scripts as OCaml actions a direct shortcut for the above boilerplate is provided by B0_unit.Action.scope_exec:

let hello =
  B0_unit.of_action' "hello" ~doc:"Run hello script" @@
  B0_unit.Action.scope_exec (Cmd.path ~/"scripts/hello.sh")

The two last examples actually perform Os.Exit.execv so the b0 invocation becomes these executables. You can also simply run cmds without becoming them, so rather than shell scripting around you can write OCaml scripts.

let hello =
  B0_unit.of_action "hello" @@ fun env _ ~args ->
  let* () = Os.Cmd.run Cmd.(tool "echo" % "hey" ) in
  let* () = Os.Cmd.run Cmd.(tool "echo" % "ho" ) in
  Ok ()

Howto

Run a built executable in (another) action

For those units that build an executable and mention it in their metadata as B0_unit.exe_file and are in your scope the following shows how to run it in another unit. The key step is to look them up with B0_env.unit_exe_file_cmd and make sure that unit is actually built by specifying it in ~units of B0_unit.of_action.

let tool = … (* This unit should have the B0_unit.exe_file set. *)
let action
  let doc = "Do stuff with tool" in
  B0_unit.of_action "my-exec" ~units:[tool] ~doc @@ fun env _ ~args ->
  let* cmd = B0_env.unit_exe_file_cmd env download_data in
  Os.Cmd.run Cmd.(cmd % "--help")

Fetch an URL and write it in the scope

let download_test_data =
  let doc = "Download test data" in
  B0_unit.of_action "download-test-data" ~doc @@ fun env _ ~args:_ ->
  let file = "data.json" in
  let url = Fmt.str "https://example.org/%s" file in
  let dst = Fmt.str "test/%s" file in
  B0_action_kit.fetch_url env url (B0_env.in_scope_dir env ~/dst)