# How can my code know my program's version?

**URL:** <https://discuss.ocaml.org/t/how-can-my-code-know-my-programs-version/17980>\
**Category:** Learning\
**Tags:** dune\
**Created:** [April 12, 2026, 5:09pm UTC](https://discuss.ocaml.org/t/how-can-my-code-know-my-programs-version/17980 "2026-04-12T17:09:02Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![user1](https://avatars.discourse-cdn.com/v4/letter/u/5daacb/32.png) [@user1](https://discuss.ocaml.org/u/user1)\
**Post date:** [April 12, 2026, 5:09pm UTC](https://discuss.ocaml.org/t/how-can-my-code-know-my-programs-version/17980/1 "2026-04-12T17:09:02Z")

</div>

In an example in the Cmdliner tutorial [tutorial (cmdliner.tutorial)](https://erratique.ch/software/cmdliner/doc/tutorial.html) I see the promising line

```ocaml
  Cmd.make (Cmd.info "TODO" ~version:"%%VERSION%%") @@
  ...

```

so I guess there should be some kind of preprocessing to replace that `%%VERSION%%` string with the correct value, taken from a “single source of truth”, but I cannot find either. What/where should be that single source of truth, and what should be that expansion mechanism?

One candidate for the source of truth is probably the `version` stanza in `dune-project`?

As for the mechanism, I saw something a bit different in some online projects, with dune files with

```dune
(rule
 (targets version.ml)
 (action
  (with-stdout-to %{targets}
   (echo "let version = \"%{version:my_project}%\""))))

```

which would allow for instance to write in my code

```ocaml
  Cmd.make (Cmd.info "TODO" ~version:Version.version) @@
  ...

```

But unfortunately, that does not display the version in the `version` stanza, but another string that I cannot make sense of, like `2b8df27-dirty%`.

Is there a “canonical” or at least a preferred way to obtain a version number in an OCaml project?

Thank you.

---

<div class="post-metadata">

**Author:** ![dbuenzli](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/dbuenzli/32/18_2.png) [@dbuenzli](https://discuss.ocaml.org/u/dbuenzli)\
**Post date:** [April 12, 2026, 5:58pm UTC](https://discuss.ocaml.org/t/how-can-my-code-know-my-programs-version/17980/2 "2026-04-12T17:58:06Z")

</div>

> [@user1](#):
>
> But unfortunately, that does not display the version in the `version` stanza, but another string that I cannot make sense of, like `2b8df27-dirty%`.

That looks like the result of `git describe --dirty --always`. This means you are at commit 2b8df27 and your checkout is dirty.

The day you `git tag -a` a commit, the command will output the tag and thus the version if your tag is a version.

---

<div class="post-metadata">

**Author:** ![yawaramin](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/yawaramin/32/3384_2.png) [@yawaramin](https://discuss.ocaml.org/u/yawaramin)\
**Post date:** [April 12, 2026, 6:19pm UTC](https://discuss.ocaml.org/t/how-can-my-code-know-my-programs-version/17980/3 "2026-04-12T18:19:00Z")

</div>

See [How to make dune include version number in a library module - #3 by Juloo](https://discuss.ocaml.org/t/how-to-make-dune-include-version-number-in-a-library-module/8864/3)

---

<div class="post-metadata">

**Author:** ![lindig](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/lindig/32/532_2.png) [@lindig](https://discuss.ocaml.org/u/lindig)\
**Post date:** [April 12, 2026, 7:03pm UTC](https://discuss.ocaml.org/t/how-can-my-code-know-my-programs-version/17980/4 "2026-04-12T19:03:17Z")

</div>

I don’t like patching of binary files and use something simpler:

```bash
#! /bin/sh
# build.sh
cat <<EOF
let git_revision = "$(git describe --tags --always --dirty)"
let build_time = "$(date)"
EOF

```

```auto
(rule
 (target build.ml)
 (deps (universe) build.sh)
 (action
  (with-stdout-to
   %{target}
   (bash "./build.sh"))))

```

This creates a file `build.ml` which gets compiled. Now I can use:

```auto
let main_cmd =
  let version = Build.git_revision in
  ...

```

```auto
C.Cmd.eval_result main_cmd

```

This only works on macOS and Linux.

---

<div class="post-metadata">

**Author:** ![yawaramin](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/yawaramin/32/3384_2.png) [@yawaramin](https://discuss.ocaml.org/u/yawaramin)\
**Post date:** [April 12, 2026, 8:15pm UTC](https://discuss.ocaml.org/t/how-can-my-code-know-my-programs-version/17980/5 "2026-04-12T20:15:26Z")

</div>

Fairly sure you can get it to work on any OCaml supported OS by replacing the shell script with an OCaml file that dune will compile and run for you.

---

<div class="post-metadata">

**Author:** ![dbuenzli](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/dbuenzli/32/18_2.png) [@dbuenzli](https://discuss.ocaml.org/u/dbuenzli)\
**Post date:** [April 12, 2026, 8:29pm UTC](https://discuss.ocaml.org/t/how-can-my-code-know-my-programs-version/17980/6 "2026-04-12T20:29:19Z")

</div>

I think that’s what the OP did (in pure dune) !

---

<div class="post-metadata">

**Author:** ![user1](https://avatars.discourse-cdn.com/v4/letter/u/5daacb/32.png) [@user1](https://discuss.ocaml.org/u/user1)\
**Post date:** [April 12, 2026, 11:22pm UTC](https://discuss.ocaml.org/t/how-can-my-code-know-my-programs-version/17980/7 "2026-04-12T23:22:08Z")

</div>

Thank you, I understand a bit better the mechanism. It seems that there are mainly two methods: the `dune subst` command and a `rule` stanza that generates a `.ml` file with the data of `\"%{version:my_project}%\"`.

It seems that both end up using a version (or tag) set by git (and `dune subst` looks to be usable only when one releases a package). However, there is another solution to the question linked by @yawaramin, which proposes to use **`dune-buid-info`** (new links: [Dune Libraries - Dune documentation](https://dune.readthedocs.io/en/stable/dune-libs.html#build-info) and [dune-build-info 3.22.0 · OCaml Package](https://ocaml.org/p/dune-build-info/3.22.0/doc/index.html)). It uses the version indicated in the `version` stanza of `dune-project` and looks like the preferred way.

---

<div class="post-metadata">

**Author:** ![hannes](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/hannes/32/2302_2.png) [@hannes](https://discuss.ocaml.org/u/hannes)\
**Post date:** [April 13, 2026, 9:01am UTC](https://discuss.ocaml.org/t/how-can-my-code-know-my-programs-version/17980/8 "2026-04-13T09:01:30Z")

</div>

I recommend to not embed the build time - it in my opinion doesn’t include any useful information (which the git describe lacks); and it destroys reproducibility (see [https://reproducible-builds.org/](https://reproducible-builds.org/)) – also see [do not embed version and timestamp information by hannesm · Pull Request #90 · backtracking/ocamlgraph · GitHub](https://github.com/backtracking/ocamlgraph/pull/90) as an example).

In respect to how to do this with dune, I am aware of [patch/bin/dune at main · hannesm/patch · GitHub](https://github.com/hannesm/patch/blob/main/bin/dune#L8-L9) (which I don’t know how it works, or how dune determines the version, but it seems to work nicely).

---

<div class="post-metadata">

**Author:** ![lindig](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/lindig/32/532_2.png) [@lindig](https://discuss.ocaml.org/u/lindig)\
**Post date:** [April 13, 2026, 9:56am UTC](https://discuss.ocaml.org/t/how-can-my-code-know-my-programs-version/17980/9 "2026-04-13T09:56:52Z")

</div>

Reproducibility depends on the entire state of Opam, which is not captured by Git if we only look at the code of the application that we are building. In any case, this is just a demonstration how to easily include information from the environment at build time.

---

<div class="post-metadata">

**Author:** ![hannes](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/hannes/32/2302_2.png) [@hannes](https://discuss.ocaml.org/u/hannes)\
**Post date:** [April 13, 2026, 11:18am UTC](https://discuss.ocaml.org/t/how-can-my-code-know-my-programs-version/17980/10 "2026-04-13T11:18:06Z")

</div>

> [@lindig](#):
>
> Reproducibility depends on the entire state of Opam, which is not captured by Git

I never claimed that “not including the date will provide you with reproducibility”.

I codeveloped several years ago tooling to capture what influences reproducibility, take a look at orb [GitHub - robur-coop/orb: check opam package reproductibility · GitHub](https://github.com/robur-coop/orb) if you’re interested. It includes environment variables, the `opam swtich export --full --freeze`, and system packages.

All I intended to say is that a timestamp doesn’t really give you any further information: it neither tells you which opam-repository commit you were at, or which OS / last package update; but actively harms reproducibility (see the previously linked PR to ocamlgraph) – thus I suggest to not include it, not even in “demonstration how to easily include information from the environment”.
