The package previously declared a phantom `conf-zap` opam dep whose only
action was `zap --version`; with no `zap` binary on PATH that made
`opam install zap --with-test` and `dune build` fail outright, and there
was no CI.
- Drop the `conf-zap` virtual package and its dependency. The runtime
library, the `zap.unix` library and the `zapc-ocaml` codegen plugin all
build with no external tool; the `zap` schema compiler (from cpp-core)
is only needed to *generate* code, run the conformance tests and the
benchmarks. `opam install zap` / `dune build @install` are now hermetic.
- Gate the codegen + test + example + benchmark stanzas on a ZAP_COMPILER
env var (dune lang bumped 2.8 -> 3.15 for `%{env:..}` in `enabled_if`).
Unset -> build just the libraries + plugin; set (with `zap` on PATH) ->
run the full suite. The gate cannot fake-pass: with ZAP_COMPILER set and
no compiler, the build fails rather than silently skipping tests.
- Fix a warning-as-error in the plugin (`unused value syntax_error`).
- Add .github/workflows/ci.yml (push to main + PR + workflow_dispatch):
a hermetic `build` job (ocaml 5.1 and 4.08, ubuntu + macos,
`dune build @install`) and a `test` job that builds the `zap` compiler
from zap-proto/cpp-core, exports ZAP_COMPILER and runs `dune test`.
- README/Makefile document the single build (@install) and test
(ZAP_COMPILER=1 ... @runtest) paths. License stays BSD-2-Clause
(upstream capnp-ocaml); ignore the local _opam switch.
3.4 KiB
zap-proto/ocaml
Docs: ZAP OCaml SDK · part of the ZAP Protocol
OCaml code generator and runtime for ZAP (Zero-copy App Proto). The compiler
plugin (zapc-ocaml) turns .zap schemas into idiomatic OCaml modules; the
zap runtime library provides the zero-copy reader/builder, codecs and packing.
The generated code uses the serialized wire format as its in-memory
representation, so there is no parse/decode step — accessors read straight out
of the buffer. As a consequence the generated types are not OCaml records:
each field is read with <field>_get and written with <field>_set (or
<field>_set_exn for bounded integers). See README.adoc for
the full type mapping and generated-module structure.
Install
opam install zap
This installs the zap runtime library, the zap.unix library and the
zapc-ocaml code-generation plugin — none of which need an external tool to
build.
To actually generate OCaml from a .zap schema you also need the zap schema
compiler front-end on your PATH. It is built from
zap-proto/cpp-core and is not yet
packaged by opam, so install it separately (see that repo's build
instructions). The compiler is only required for code generation, the
conformance tests and the benchmarks — not to build or install this package.
Generate code
zap compile reads the schema and pipes a code-generation request to the
OCaml plugin. With dune, codegen is a rule:
(rule
(targets shape.ml shape.mli)
(action (run zap compile -o %{bin:zapc-ocaml} %{dep:shape.zap})))
Or directly:
zap compile -o zapc-ocaml shape.zap
Encode / decode
Given a foo.zap declaring struct Foo { num @0 :Int32; }, the plugin emits a
functor Foo.Make parameterised over the message backend (bytes or
Bigarray):
module Foo = Foo.Make(Zap.BytesMessage)
(* build a message and serialize it *)
let encode (n : int32) : string =
let b = Foo.Builder.Foo.init_root () in
Foo.Builder.Foo.num_set b n;
Zap.Codecs.serialize ~compression:`None (Foo.Builder.Foo.to_message b)
(* read a field back, zero-copy, out of the wire bytes *)
let decode_exn (s : string) : int32 =
let stream = Zap.Codecs.FramedStream.of_string ~compression:`None s in
match Zap.Codecs.FramedStream.get_next_frame stream with
| Ok message -> Foo.Reader.Foo.num_get (Foo.Reader.Foo.of_message message)
| Error _ -> failwith "incomplete frame"
let () = Printf.printf "%ld\n" (decode_exn (encode 3l))
Runnable end-to-end examples live in src/examples/.
Build & test
# Build the runtime libraries and the codegen plugin (no compiler needed):
make build # == dune build @install
# Run the unit + conformance tests and the examples/benchmarks. These link
# code generated from the .zap schemas, so the `zap` compiler must be on PATH.
# The codegen/test/example/benchmark stanzas are gated on the ZAP_COMPILER env
# var; set it to any non-empty value once the compiler is installed:
make test # == ZAP_COMPILER=1 dune build @install @runtest ...
CI mirrors this: a build job compiles the libraries + plugin across the
supported OCaml versions, and a separate test job builds the zap compiler
from cpp-core, exports ZAP_COMPILER, and runs dune test.
License
BSD-2-Clause. See LICENSE.md.