|
| 1 | +How to make a Homebrew Package with Dune |
| 2 | +======================================== |
| 3 | + |
| 4 | +This guide will show you how to make a Homebrew package for an application |
| 5 | +using Dune package management. The only dependency of the Homebrew package will |
| 6 | +be Dune, and all OCaml dependencies (including the OCaml compiler) will be |
| 7 | +installed by Dune while building the Homebrew package. |
| 8 | + |
| 9 | +To use Dune package management to build a project as a Homebrew package, the |
| 10 | +project must have a source archive hosted online somewhere (e.g. a gzipped |
| 11 | +tarball on the project's Github release page). |
| 12 | + |
| 13 | +Before making a Homebrew package, it's a good idea to familiarize yourself with |
| 14 | +Homebrew's terminology and packaging conventions `here |
| 15 | +<https://docs.brew.sh/Adding-Software-to-Homebrew>`__. |
| 16 | + |
| 17 | +Homebrew packages are recommended to be source-based, and for the source code |
| 18 | +to be explicitly versioned, so for this example assume ``my_app`` has a |
| 19 | +versioned archive hosted on Github with version ``0.1.0``. |
| 20 | + |
| 21 | +Homebrew can generate a starting point for a formula if you point it at a |
| 22 | +source archive hosted on Github: |
| 23 | + |
| 24 | +.. code:: console |
| 25 | +
|
| 26 | + $ brew create https://github.com/me/my_app/archive/refs/tags/0.1.0.tar.gz |
| 27 | +
|
| 28 | +A source archive like the one in the above command is generated when you |
| 29 | +release a project on Github. The above command will generate a file named |
| 30 | +``my_app.rb`` in your current tap. All the project metadata will be filled in |
| 31 | +automatically based on the project on Github. All we need to do now is to |
| 32 | +specify dependencies and the commands ``brew`` should run when installing the |
| 33 | +package. |
| 34 | + |
| 35 | +Here's the complete formula for ``my_app``. Note that the ``test`` section is |
| 36 | +intended to be a sanity check of the core functionality of the package, not a |
| 37 | +complete integration test suite. Read more about Homebrew package tests `here |
| 38 | +<https://docs.brew.sh/Formula-Cookbook#add-a-test-to-the-formula>`__. Note |
| 39 | +however that tests run in an environment without access to build dependencies |
| 40 | +such as Dune, so ``dune runtest`` can't be used to test Homebrew packages. |
| 41 | + |
| 42 | +.. code:: ruby |
| 43 | +
|
| 44 | + class MyApp < Formula |
| 45 | + desc "My awesome app" |
| 46 | + homepage "https://github.com/me/my_app" |
| 47 | + url "https://github.com/me/my_app/releases/download/0.1.0/0.1.0.tar.gz" |
| 48 | + sha256 "eb8705de406441675747a639351d0d59bffe7b9f5b05ec9b6e11b4c4c9d7a6ee" |
| 49 | + license "MIT" |
| 50 | +
|
| 51 | + depends_on "dune" => :build |
| 52 | +
|
| 53 | + def install |
| 54 | + system "dune", "pkg", "lock" |
| 55 | + system "dune", "build", "@install", "--release", "--only-packages", "my_app" |
| 56 | + system "dune", "install", "--prefix=#{prefix}", "my_app" |
| 57 | + end |
| 58 | +
|
| 59 | + test do |
| 60 | + # Test your application here! |
| 61 | + system "my_app", "--version" |
| 62 | + end |
| 63 | + end |
| 64 | +
|
| 65 | +This assumes that the name of the package in the source archive is ``my_app``. |
| 66 | +That is, the archive contains a ``dune-project`` file defining a package named |
| 67 | +``my_app``, or that the archive contains a ``my_app.opam``. The archive may |
| 68 | +contain multiple packages provided that ``my_app`` is one of them. |
| 69 | + |
| 70 | +Note the first install command: ``dune pkg lock``. This is only necessary if |
| 71 | +the source archive doesn't contain a lockdir (a ``dune.lock`` directory |
| 72 | +containing the transitive dependency closure of the package). Running ``dune |
| 73 | +pkg lock`` invokes Dune's dependency solver resolving the dependencies in |
| 74 | +``dune-project`` or ``my_app.opam`` against the current tip of the `Opam |
| 75 | +Repository <https://github.com/ocaml/opam-repository>`_. Solving dependencies |
| 76 | +while installing a Homebrew package means that the exact versions of |
| 77 | +dependencies may be different each time the Homebrew package is installed. For |
| 78 | +this reason it's advised to use source archives which already contain a |
| 79 | +lockdir, and omit the ``dune pkg lock`` command when packaging a Dune project |
| 80 | +for Homebrew. However in some situations solving dependencies at install time |
| 81 | +is unavoidable, such as when packaging a project whose source archive is |
| 82 | +already released and lacks a lockdir. |
| 83 | + |
| 84 | +If there are any packages with external dependencies (i.e. ``depexts``) in |
| 85 | +``my_app``'s transitive dependency closure, their corresponding Homebrew |
| 86 | +package must be added as a dependency of ``my_app``'s Homebrew packages by |
| 87 | +adding a ``depends_on`` entry for each. List all the external dependencies |
| 88 | +among ``my_app``'s transitive dependency closure by running: |
| 89 | + |
| 90 | +.. code:: console |
| 91 | +
|
| 92 | + $ dune show depexts |
| 93 | +
|
| 94 | +External dependencies can be platform-specific, so if you're planning to make the |
| 95 | +Homebrew package available for macOS, be sure to run the above command on a Mac |
| 96 | +to determine which external dependencies need to be added to the formula. |
0 commit comments