Skip to content

manifest: Tezos-inspired manifest system for dune generation#18425

Draft
dannywillems wants to merge 28 commits intocompatiblefrom
soc/manifest-system
Draft

manifest: Tezos-inspired manifest system for dune generation#18425
dannywillems wants to merge 28 commits intocompatiblefrom
soc/manifest-system

Conversation

@dannywillems
Copy link
Member

Summary

  • Adds a Tezos-inspired manifest system (manifest/) that centrally declares all library and executable targets, then generates dune files from those declarations
  • Structured into 15 domain-based layer files (ppx, base, concurrency, test, tooling, node, snarky, crypto, storage, infra, domain, ledger, transaction, protocol, network) and 27 product files (one per src/app directory)
  • Manages 324 dune files with 0 structural differences verified by s-expression comparison (field-order and dep-order insensitive)
  • Generated dune files have formatting-only changes: vertical layout, sorted deps, header comment - no semantic build changes

Test plan

  • Verify dune build manifest/main.exe succeeds
  • Verify dune exec manifest/main.exe -- --check shows 0 differences
  • Verify dune exec manifest/test/test_manifest.exe passes 22 tests
  • Verify dune build succeeds (no build regressions)
  • Review generated dune file diffs for formatting-only changes

Add a manifest system in manifest/ that centralizes build metadata
in OCaml and generates dune files programmatically. Includes:

- Dune s-expression parser, pretty-printer, and structural comparator
- DSL for declaring libraries and executables with PPX presets
- Check mode (--check) for CI verification
- Opam file generation support
- 22 Alcotest tests covering parsing, comparison, and DSL
- 10 proof-of-concept libraries + 3 executables declared

The manifest only depends on str and unix from OCaml stdlib.
Run with: dune exec manifest/main.exe
Wave 0: Add manifest features needed for full migration:
- private_library/private_executable for targets without public_name
- kind field (ppx_deriver)
- ppx_runtime_libraries
- flags for libraries
- link_flags for executables
- enabled_if for libraries/executables
- js_of_ocaml field
- file_stanzas for top-level stanzas (vendored_dirs, include_subdirs)
- Fix structural comparison for atom vs single-element list

Wave 1: Migrate 36 leaf libraries + PPX ecosystem:
allocation_functor, codable, comptime, error_json,
integers_stubs_js, key_value_database, linked_tree,
logproc_lib, interpolator_lib, mina_wire_types, one_or_two,
otp_lib, participating_state, perf_histograms, proof_cache_tag,
rosetta_coding, rosetta_models, sgn_type, structured_log_events,
sync_status, unsigned_extended, visualization, webkit_trace_event,
webkit_trace_event.binary, graphql_basic_scalars, graphql_wrapper,
mina_compile_config, ppx_annot, ppx_register_event, ppx_version,
ppx_version.runtime, ppx_mina, ppx_to_enum, ppx_representatives,
ppx_representatives.runtime, storage + 7 ppx_mina test libraries
Migrate 36 libraries from Wave 2:
- mina_stdlib, blake2, bignum_bigint, string_sign
- snark_keys_header, plonkish_prelude, random_oracle_input
- outside_hash_image, hash_prefixes, sgn, timeout_lib
- quickcheck_lib, test_util, disk_cache.intf
- run_in_thread (virtual + native/fake), interruptible
- promise (virtual + native/js/js_helpers)
- internal_tracing + context_call
- mina_metrics (virtual + none/prometheus)
- cache_dir (virtual + native/fake)
- node_config (intf + main + for_unit_tests + profiled +
  unconfigurable_constants + version)

Also fix flags type from string list to Dune_s_expr.t list
to support nested s-expression flags like
(:standard -w a -warn-error +a).

87 dune files checked, 0 differences. 22 tests pass.
Migrate 22 libraries from Wave 3:
- logger (virtual + context_logger/fake/file_system/native)
- o1trace, o1trace_webkit_event
- mina_stdlib_unix, mina_numbers
- cache_lib, trust_system, parallel
- mina_version.dummy, mina_version.runtime
- mina_signature_kind (virtual + type/config/testnet/mainnet)
- multi_key_file_storage

104 dune files checked, 0 differences. 22 tests pass.
Add test_integration.exe that registers all product_mina
declarations and verifies each generated dune file is
structurally identical to the original using s-expr comparison.

- Add Manifest.check() returning structured results per file
- Add Manifest.reset() to clear registered targets
- Extract target_path before check for proper ordering
- Split product_mina into manifest_product library so it can
  be shared between main.exe and test_integration.exe
Adds declarations for the crypto layer: kimchi bindings (js/node/web
backends, pasta poseidon), kimchi backend (common, pasta, basic,
constraint system, gadgets), crypto params, pickles (base, types,
composition types, plonk checks, pseudo, limb vector, backend, main),
snark params/bits, random oracle (+ permutation virtual/impls),
non_zero_curve_point, signature_lib, secrets, key_gen,
bowe_gabizon_hash.

143 dune files pass structural comparison, 0 differences.
Adds test stanza support and forbidden_libraries to the manifest DSL.

Migrates: hash_prefix_states (+ create virtual/native/js),
data_hash_lib, sparse_ledger_lib, block_time, proof_carrying_data,
protocol_version, genesis_constants, network_peer,
node_addrs_and_ports, user_command_input, fields_derivers (+ json,
graphql, zkapps), rocksdb, key_cache (+ sync, async, native),
parallel_scan, merkle_address, merkle_list_prover/verifier,
lmdb_storage, disk_cache (virtual + filesystem, identity, lmdb,
test_lib, test, utils), dummy_values (+ gen_values exe),
mina_base.test_helpers.

180 dune files pass structural comparison, 0 differences.
Adds test stanza support (with package), inline_tests_deps,
inline_tests_bare, forbidden_libraries for executables, and
name-implied-by-public_name equivalence in the comparator.

Migrates: staged_ledger_diff, mina_transaction, mina_transaction_logic
(+tests), transaction_witness, merkle_ledger (+tests), merkle_mask,
mina_ledger (+test_helpers), transaction_protocol_state,
transaction_snark (+20 test subdirs), transaction_snark_work,
transaction_snark_scan_state, zkapp_command_builder,
zkapp_vk_cache_tag.

218 dune files pass structural comparison, 0 differences.
Add declarations for blockchain_snark, mina_state, mina_block,
consensus (with modules_exclude for shared-dir executables),
staged_ledger, snark_work_lib, snark_worker, precomputed_values,
genesis_ledger, genesis_proof, genesis_ledger_helper, vrf_lib,
vrf_evaluator, snark_profiler_lib, rosetta_lib,
generated_graphql_queries, ledger_proof, prover, verifier,
mina_incremental, and mina_plugins.

New manifest features:
- preprocessor_deps for executables
- modules_exclude for (:standard \ ...) syntax
- file_deps for test stanzas (file dependencies)

247 files pass structural comparison, 0 differences.
Split the monolithic product_mina.ml into domain-based layer files
(ppx, base, concurrency, test, tooling, node, snarky, crypto, storage,
infra, domain, ledger, transaction, protocol, network) and individual
product files (one per src/app directory).

Add manifest DSL features: bisect_sigterm, modes, enabled_if for tests,
and symlink .ocamlformat for consistent formatting.

324 dune files managed, 0 structural differences, 22 tests passing.
Auto-generated dune files from the manifest system. Changes are
formatting-only (vertical layout, sorted deps, header comment).
No semantic changes to build targets.
@dannywillems dannywillems requested review from a team as code owners February 8, 2026 15:10
@georgeee
Copy link
Member

georgeee commented Feb 8, 2026

@dannywillems what's the benefit of using this approach vs. plain dune files?

To me they seem more or less the same thing, with this approach being less familiar to an Ocaml developer. So there would be some benefits in doing the transition, to justify the work?

Organize manifest files into a cleaner structure:
- manifest/layers/  — 15 domain layer declarations
- manifest/products/ — 27 product (executable) declarations
- manifest/test/    — unit and integration tests

Uses (include_subdirs unqualified) so dune finds modules in subdirs.
Define each opam dependency once in externals.ml (114 bindings)
and reference them by name in all layer/product files, replacing
1,977 raw opam string calls with typed values. Gives compile-time
typo detection and single point of truth for external dep names.
Add manifest-build, manifest-generate, manifest-check, and
manifest-test Makefile targets. Add GitHub Actions workflow that
runs on PRs touching manifest/ or src/**/dune to verify generated
dune files stay in sync with manifest declarations.
@dannywillems dannywillems requested a review from a team as a code owner February 8, 2026 15:30
@glyh
Copy link
Member

glyh commented Feb 8, 2026

Have you talked with someone from the ocaml team? If so could you provide a thead so I can look at?
I tend to reject this PR naturally:

  • It's huge for review without clear benefit in doing so
  • We have experience on using niche tech product(e.g. dhall, and arguably ocaml) and they become a barrier for maintaining because it's not well supported. Using this dune generation means we have to maintain it, which is extra burden in an already complicated build environment

Create layer_consensus, layer_snark_worker, layer_service, and
layer_base_types to break circular module dependencies between
the 15 existing layers.

Move ~30 library definitions to their proper layers:
- consensus/state/vrf libs from network to layer_consensus
- snark work/scan state libs from network+transaction to
  layer_snark_worker
- verifier/prover from network to layer_service

Replace backward cross-layer references with local "xxx" calls
to break OCaml compilation cycles while preserving identical
dep values in generated dune files.

Fix forward references caused by converting from function-body
style (let register () = ...) to top-level let bindings.

Remove dead register() dispatch pattern from product_mina.ml.

Verified: builds cleanly, --check shows 0 differences,
22/22 tests pass.
Move proof_cache_tag from layer_base to layer_crypto where it
belongs since it depends on pickles.

Remove 361 decorative section comments (tier headers, dashed
separators) across all manifest files.
Move libraries out of layer_infra, layer_base, and layer_domain
into the layers that match their domain:

- pipe_lib, parallel → layer_concurrency
- network_peer, trust_system → layer_network
- mina_stdlib_unix, mina_numbers, mina_version_dummy,
  mina_version_runtime → layer_base
- cache_lib, multi_key_file_storage → layer_storage
- mina_signature_kind (+ type, config, testnet, mainnet)
  → layer_protocol
The infra layer now only contains logging-related libraries
(logger, logger_file_system, logger_context_logger, logger_fake,
o1trace, o1trace_webkit_event). Rename to reflect its actual scope.
Extract kimchi/plonk bindings and backends into layer_kimchi (17 libs)
and pickles proof composition into layer_pickles (10 libs). Also move
snarky_taylor into layer_snarky.

layer_crypto retains general crypto: blake2, bignum_bigint, snark_params,
random_oracle, signature_lib, secrets, key_gen, and related utilities.

Compilation order: layer_kimchi -> layer_crypto -> layer_pickles.
Split the single manifest_product library into tiered libraries:
manifest_foundation -> layer_kimchi -> layer_crypto -> layer_pickles
-> manifest_upper -> manifest_products.

The compiler now rejects cross-layer violations, e.g. layer_crypto
cannot reference Layer_pickles, and layer_kimchi cannot reference
Layer_crypto.
Reorder definitions in layer_ppx.ml so that ppx_version_runtime,
ppx_to_enum, ppx_representatives, and ppx_representatives_runtime
are defined before their use, replacing local "..." with direct
OCaml value references.
@cjjdespres
Copy link
Member

I would prefer to wait on this until after the hard fork, and to discuss why it's necessary to do this. I'd also rather keep the ocaml build system fairly vanilla, unless we can't avoid extending it for whatever reason.

Add a third dep_kind (Submodule) to distinguish git submodule
dependencies from both opam and local manifest-managed libraries.
Define all 12 snarky submodule libraries as typed constants in a
Snarky_lib module and replace 148 local "..." string references
across 16 layer/product files.
Move externals into manifest_lib and give layer_snarky its own
dune library depending only on manifest_lib, with no dependency
on any other layer.
Add snarky_blake2, snarky_curves, snarky_field_extensions,
snarky_group_map, snarky_log, and snark_keys_header to
layer_snarky. Move user_command_input from layer_domain to
layer_transaction.
Move libraries to their semantically correct layers:
- structured_log_events: layer_base -> layer_logging
- perf_histograms: layer_base -> layer_tooling
- child_processes: layer_base -> layer_concurrency
- rosetta_coding, rosetta_models: layer_base -> new layer_rosetta
- rosetta_lib: layer_network -> new layer_rosetta

Create layer_rosetta for Rosetta API support code.
Update cross-layer references across 12 files.
Organize all ~deps: lists in 21 layer files and 23 product files
into consistent groups: opam deps first, cross-layer refs second,
local refs third — each group sorted alphabetically.
@dannywillems dannywillems marked this pull request as draft February 8, 2026 20:57
(** Dune S-expression AST, pretty-printing, parsing,
and structural comparison. *)

type t = Atom of string | List of t list | Comment of string
Copy link
Member Author

Choose a reason for hiding this comment

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

There is already a sexpr library. Claude doesn't know about it...

@@ -0,0 +1,9 @@
(** Mina base types layer: core types that sit above crypto primitives.
Copy link
Member Author

Choose a reason for hiding this comment

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

Should not exist.

@dannywillems
Copy link
Member Author

OPAM files generation to support.

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.

4 participants