Project Description =================== Configuration files used by :code:`drom` are in `TOML syntax `__. See also `Learn TOML in Y minutes `__ for a very quick introduction. A project is composed of the *project* itself, and a set of *packages* (libraries and executables), among which the main package shares the same name as the project. An :code:`opam` package is generated for each package in the project. The :code:`drom.toml` Project File ---------------------------------- To get a feeling of what is in this file, we can look at the one used for :code:`drom` itself:: [project] drom-version = "0.1.0" [project] drom-version = "0.1.0" [project] authors = ["Fabrice Le Fessant ", "Léo Andrès "] copyright = "OCamlPro SAS & Origin Labs SAS" edition = "4.10.0" github-organization = "ocamlpro" license = "LGPL2" min-edition = "4.07.0" mode = "binary" name = "drom" synopsis = "The drom tool is a wrapper over opam/dune in an attempt to provide a cargo-like user experience" version = "0.2.0" windows-ci = true # keys that you could also define: # build-profile = "...build-profile..." # odoc-target = "...odoc-target..." # sphinx-target = "...sphinx-target..." # archive = "...archive..." # dev-repo = "...dev-repo..." # bug-reports = "...bug-reports..." # doc-api = "...doc-api..." # doc-gen = "...doc-gen..." # homepage = "...homepage..." [project] description = """ The drom tool is a wrapper over opam/dune in an attempt to provide a cargo-like user experience. It can be used to create full OCaml projects with sphinx and odoc documentation. It has specific knowledge of Github and will generate files for Github Actions CI and Github pages. """ [project] skip = "sphinx/about.rst src/drom_lib/main.ml sphinx/index.rst CHANGES.md test/expect-tests/test.ml ocamlformat" [dependencies] [tools.ocamlformat] for-test = true [tools.odoc] for-doc = true [tools.ppx_expect] for-test = true [tools.ppx_inline_test] for-test = true [profile.dev] ocaml-flags = "-w +a-4-40-41-42-44" [profile.release] ocaml-flags = "-w -a" [project] generators = ["ocamllex", "menhir"] pack-modules = true skip-dirs = ["drom-test"] [[package]] dir = "src/drom" [[package]] dir = "src/drom_lib" Notice that every package contains only the directory where the sources are located, in which :code:`drom` expects to find a file \ :code:`package.toml` describing the package itself. The :code:`[project]` table ~~~~~~~~~~~~~~~~~~~~~~~~~~~ This table describes the main project. For multi-package projects, these fields are used as default if the corresponding fields are not specified in sub-packages. Here is a short description of the fields: * :code:`name`: the name of the project and of its main package. * :code:`version`: the current version of the project * :code:`authors`: the list of authors of the project, to display in opam files and in the documentation * :code:`copyright`: the name of the copyright holder for the project * :code:`edition`: the version of OCaml to be used by default, when building locally or on the CI * :code:`github-organization`: the organization owning the project on Github. This field is used to compute the URLs for the documentation. * :code:`kind`: specify whether packages should be programs (:code:`"program"`) or libraries (:code:`"library"`) by default. * :code:`license`: the identifier of the license. See the section about licenses. :code:`drom` knows a few license and may automatically generate the corresponding files. * :code:`min-edition`: the minimal version of OCaml with which the project can be built. The CI will start at least one job with this version. * :code:`mode`: the target of the compilation. It is either :code:`"binary"` for an executable, or :code:`javascript` for a file generated by :code:`js_of_ocaml`. * :code:`synopsis`: a very short description of the project. :code:`opam` requires this field to start with an uppercase letter and not end with a dot. * :code:`description`: a long description of the project. * :code:`pack-modules`: a bool indicating whether modules should be packed. :code:`true` by default. It corresponds to the :code:`wrapped` field of :code:`dune`. Some fields are computed automatically if :code:`github-organization` is specified, but can be overriden: * :code:`archive`: the URL from where the sources can be downloaded. Can contain :code:`${version}` instead of the version. * :code:`dev-repo`: the location of the VCS for the project. They are often specified with :code:`git+https://`. * :code:`bug-reports`: the URL where issues can be created. * :code:`doc-api`: the URL where the documentation of the API can be found. * :code:`doc-gen`: the URL where the general documentation can be found. * :code:`homepage`: the URL of the project main homepage Another important field is the :code:`skip` field: * :code:`skip` contains a space-separated list of files, that should not be modified anymore by :code:`drom`. By default, :code:`drom` will generate any missing file, and upgrade any file that was previously generated by :code:`drom` and not modified since then. :code:`drom` will print a warning everytime it finds a modified file. This option can be used to avoid this warning, or to prevent :code:`drom` from recreating an unwanted file. :code:`drom` generates a file :code:`_drom/maximum-skip-field.txt` showing all the values that can appear in this field. There is an exception for :code:`drom.toml` and :code:`package.toml` files, that are only modified when the user configuration has been changed, a changing option passed to :code:`drom project`, or the :code:`--upgrade` option to benefit from a more recent version of :code:`drom`. :code:`skip` can also contains *tags*: such tags can be used to reference several files, related to the tag itself. For example, current skeletons use the following tags: :code:`code` (source files), :code:`docs` (documentation), :code:`sphinx` (sphinx documentation), :code:`test` (test files), :code:`github` (github pages and workflows). More tags may be defined in the future. To detect modifications of its files, :code:`drom` generates a file `.drom` in the project. This file should be committed in the repository with the files, so that :code:`drom` can always use it to detect changes. Every time :code:`drom` skips a file because it has been modified, it saves the new version in the :code:`_drom/skipped/` directory. For example, if :code:`drom` outputs:: [...] Skipping modified file dune-project [...] You can use the command:: $ diff dune-project _drom/skipped/dune-project to see the differences. if the differences are meaningless, you may then decide to promote the new file by removing your file and restarting :code:`drom`. The :code:`[dependencies]` table ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This section contains dependencies with their versions. These dependencies are for libraries only, as they will be added in the generated :code:`dune` files. For example:: ez_config = "0.1.0" means that the project should be linked with :code:`ez_config` with a version compatible with :code:`0.1.0`. :code:`drom` uses semantic versioning, so it means :code:`version >= 0.1.0` and :code:`version < 1.0.0`. If the version is not formatted as :code:`X.Y.Z`, :code:`drom` will only generate a constraint :code:`version >= V`. Versions can also be specified with some other formats: * Following :code:`opam` conventions : :code:`">0.1.0"` for example * No constraint specified, with the empty string :code:`""` * Using the string :code:`"version"` meaning that the version of the dependency should be the same one as the package It is possible to specify more information than the version, in which case the dependency object should be seen as a record containing the fields: * :code:`version` for the version of the dependency * :code:`libname` for the name of the dependency that should be used as a dependency in :code:`dune` files * :code:`for-test` for a boolean specifying if the dependency is only needed for tests (:code:`with-test` in :code:`opam` files) * :code:`for-doc` for a boolean specifying if the dependency is only needed for tests (:code:`with-doc` in :code:`opam` files) For example:: [dependencies] ez-config = { version = "0.1.0", libname = "ez_config" } or equivalently:: [dependencies.ez-config] version = "0.1.0" libanem = "ez_config" meaning that the project depends on the :code:`opam` package :code:`ez-config`, and that it should be linked with the corresponding library :code:`ez_config`. The :code:`[tools]` table ~~~~~~~~~~~~~~~~~~~~~~~~~ This section contains dependencies that will appear in the :code:`opam` files, but not as libraries in the :code:`dune` files. The :code:`[package]` table ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The :code:`package` table has one entry per package (library or program) included in the project. One of the packages must have the same name as the project. The most important field of the package description in the :code:`drom.toml` file is the :code:`dir` field. :code:`drom` expects to find a :code:`package.toml` file in that directory with more information on the packages. Yet, it is possible to inline these fields also in the :code:`drom.toml` file, but :code:`drom.toml` will move them to their :code:`package.toml` file if you ask it to upgrade the file (using :code:`drom project --upgrade`). You can check the list of available fields in the documentation of :code:`package.toml`. The :code:`[fields]` table ~~~~~~~~~~~~~~~~~~~~~~~~~~ The :code:`fields` table contains fields that will be added to the the :code:`dune-project` file. There is two fields may be used : * :code:`dune-project-header` * :code:`dune-project-trailer` The first one add verbatim content to the :code:`dune-project` file, right after the :code:`(lang dune X.Y)` line. The second one add verbatim content at the end of file. Using those fields may be required when using very specific :code:`dune` build feature that are not handled by :code:`drom`. For example, using the parser generator :code:`menhir` for non trivial cases will likely need such tuning. The :code:`package.toml` Files ------------------------------ Every package source directory (as specified by the :code:`dir` field of each :code:`[[package]]` entry in the :code:`drom.toml` file) can contain a :code:`package.toml` file. For example:: $ less src/drom_lib/package.toml gen-version = "version.ml" kind = "library" name = "drom_lib" pack-modules = true [dependencies] directories = "0.2" ez_cmdliner = "0.1.0" ez_config = "0.1.0" ez_file = "0.2.0" opam-file-format = "2.1.1" toml = "5.0.0" [fields] dune-libraries = "bigarray" The following fields are allowed in a package definition: * :code:`dir`: the directory of the package sources (defaults to :code:`src/${name}/` * :code:`dependencies`: the :code:`opam` *library* dependencies of this package * :code:`generators`: the list of program for which specific :code:`dune` targets should be generated. Defaults to an empty list, but the project can define its own list (:code:`[ "ocamllex" ; "ocamlyacc" ]` by default). The generator :code:`menhir` is also available. * :code:`gen-version`: whether a :code:`version.ml` file should be created * :code:`kind`: the kind of the package (:code:`library` or :code:`program`) * :code:`name`: the name of the package (under which it will be published in :code:`opam` * :code:`pack`: if modules should be wrapped, the name of the wrapper module (defaults to the capitalized name of the library otherwise) * :code:`skeleton`: the skeleton to be used to generate the files of this project (defaults to :code:`"program"`). See the section on skeletons for more info. * :code:`tools`: the :code:`opam` *tools* dependencies of this package The following fields are also allowed, and default to the project ones if not specified: * :code:`authors`: the authors of the package * :code:`description`: the full description of the package * :code:`mode`: the mode of the package (:code:`binary` or :code:`javascript`) * :code:`pack-modules`: whether modules should be wrapped or not * :code:`synopsis`: the one-line description of the package * :code:`version`: the version of this package For :code:`menhir` usage, you will find an optional :code:`[menhir]` talbe, with the following fields: * :code:`version`: the version of the menhir language configuration * :code:`parser`: a table with the following fields: * :code:`modules`: an array of string with all your menhir modules * :code:`merge-into`: an optional string for the name of the merged module, by default the last value in the :code:`modules` array if not provided * :code:`tokens`: an optional to add the :code:`--external-tokens` flag * :code:`flags`: an optional string array with menhir flags * :code:`infer`: an optional boolean which adds the :code:`(infer )` rule to the :code:`dune` file * :code:`tokens`: an optional table that will add the `--only-tokens` flag to the :code:`menhir` rule with the following fiels: * :code:`modules`: a string array with all the modules to add to the rule * :code:`flags`: an optional string array with all the flags to add to menhir Finally, there is a table :code:`[fields]` within a package. Currently, the following fields are used by skeletons: * :code:`dune-libraries`: extra libraries that can be added in the :code:`libraries` field of :code:`dune`. Typically for internal libraries. * :code:`dune-stanzas`: extra :code:`dune` stanzas in the description * :code:`dune-trailer`: a string that is added at the end of the :code:`dune` file. * :code:`opam-trailer`: a string that is added at the end of the :code:`opam` file for the package. In skeletons, *fields* are referenced using parens (for example :code:`!(dune-stanzas)`). Skeletons --------- :code:`drom` generates most of the files using template files stored in *skeletons* on which substitutions are applied. There are two kinds of skeletons: * *Project skeletons* are used to create and update most of the files of the project, excluding package specific files. * *Package skeletons* are used to create and update package specific files, mostly :code:`dune` and sources files When :code:`drom` is called in a project, it generates a file :code:`_drom/known-skeletons.txt` listing all the skeletons it knows about. Beware that, if you create a new skeleton, you will have to share it for other users to be able to use :code:`drom` on the project to update generated files. Project Skeletons ~~~~~~~~~~~~~~~~~ Default project skeletons are defined in the source tree in: `https://github.com/OCamlPro/drom/tree/master/src/drom_lib/skeletons/projects `__ The following project skeletons are available by default: * The :code:`virtual` skeleton is for a "meta" project, i.e. a project containing other packages, but whose main package (with the name of the project) does not define a program or library. This skeleton can be seen as the root of the inheritance tree between project skeletons. Files:: $ drom new VIRTUAL --skeleton virtual └── VIRTUAL/ ├── .drom (drom state, do not edit) ├── .github/ │ └── workflows/ │ ├── doc-deploy.yml │ └── workflow.yml ├── .gitignore ├── .ocamlformat ├── .ocamlformat-ignore ├── .ocp-indent ├── CHANGES.md ├── LICENSE.md ├── Makefile ├── README.md ├── docs/ │ ├── README.txt │ ├── doc/ │ │ └── index.html │ ├── favicon.png │ ├── index.html │ ├── sphinx/ │ │ └── index.html │ └── style.css ├── drom.toml <────────── project config EDIT ! ├── dune ├── dune-project ├── sphinx/ │ ├── _static/ │ │ └── css/ │ │ └── fixes.css │ ├── about.rst │ ├── conf.py │ ├── index.rst │ ├── install.rst │ └── license.rst ├── src/ │ └── VIRTUAL/ │ └── package.toml <────────── package config EDIT ! └── test/ ├── expect-tests/ │ ├── dune │ └── test.ml ├── inline-tests/ │ ├── dune │ └── test.ml └── output-tests/ ├── dune ├── test1.expected ├── test2.expected └── test2.ml Notice that there are no :code:`.opam` file generated by default in the :code:`virtual` skeleton, until you add other packages using :code:`drom package XXX --new SKELETON` * The :code:`library` skeleton contains only a library package. It inherits from the :code:`virtual` skeleton. Files:: $ drom new LIBRARY --skeleton library └── LIBRARY/ │ . (same as virtual ) . ├── LIBRARY.opam ├── src/ │ └── LIBRARY/ │ ├── dune │ ├── index.mld │ ├── main.ml │ ├── package.toml <────────── package config EDIT ! │ └── version.mlt └── test/ . . (same as virtual ) * The :code:`program` skeleton contains both a library and a driver packages. It inherits from the :code:`virtual` skeleton. It is the default project skeleton used when nothing is specified. Files:: $ drom new PROGRAM --skeleton program └── PROGRAM/ │ . (same as virtual ) . ├── PROGRAM.opam ├── PROGRAM_lib.opam ├── src/ │ ├── PROGRAM/ │ │ ├── dune │ │ ├── index.mld │ │ ├── main.ml │ │ └── package.toml <────────── package config EDIT ! │ └── PROGRAM_lib/ │ ├── dune │ ├── index.mld │ ├── main.ml │ ├── package.toml <────────── package config EDIT ! │ └── version.mlt └── test/ . . (same as virtual ) * The :code:`mini-lib` skeleton contains a library package, with a minimal set of files (no test, no docs, etc.) (since version 0.2.2) Files:: $ drom new MINI-LIB --skeleton mini-lib └── MINI-LIB/ ├── .drom (drom state, do not edit) ├── .gitignore ├── CHANGES.md ├── LICENSE.md ├── MINI-LIB.opam ├── Makefile ├── README.md ├── drom.toml <────────── project config EDIT ! ├── dune ├── dune-project └── src/ └── MINI-LIB/ ├── dune ├── package.toml <────────── package config EDIT ! └── version.mlt * The :code:`mini-prg` skeleton contains a program package, with a minimal set of files (no test, no docs, etc.) (since version 0.2.2) Files:: $ drom new MINI-PRG --skeleton mini-prg └── MINI-PRG/ ├── .drom (drom state, do not edit) ├── .gitignore ├── CHANGES.md ├── LICENSE.md ├── MINI-PRG.opam ├── MINI-PRG_lib.opam ├── Makefile ├── README.md ├── drom.toml <────────── project config EDIT ! ├── dune ├── dune-project └── src/ ├── MINI-PRG/ │ ├── dune │ └── package.toml <────────── package config EDIT ! └── MINI-PRG_lib/ ├── dune ├── package.toml <────────── package config EDIT ! └── version.mlt * The :code:`ppx_deriver` skeleton contains a library package that defines a *ppx_deriver*, with a minimal example of code. (since version 0.2.2) Files:: $ drom new PPX_DERIVER --skeleton ppx_deriver └── PPX_DERIVER/ │ . (same as virtual ) . ├── PPX_DERIVER.opam ├── src/ │ └── PPX_DERIVER/ │ ├── dune │ ├── index.mld │ ├── main.ml │ ├── package.toml <────────── package config EDIT ! │ └── version.mlt └── test/ . . (same as virtual ) Package Skeletons ~~~~~~~~~~~~~~~~~ Default package skeletons are defined in the source tree in: `https://github.com/OCamlPro/drom/tree/master/src/drom_lib/skeletons/packages `__ The following package skeletons are available by default: * The :code:`virtual` skeleton is an empty skeleton. * The :code:`library` skeleton contains a simple library. * The :code:`program` skeleton contains a simple program. * The :code:`driver` skeleton contains a simple program that only calls the main entry point of an associated library. * The :code:`ppx_deriver` skeleton contains a simple library that defines a *ppx_deriver*. Note that the project :code:`program` skeleton combines a :code:`library` package skeleton with a :code:`driver` package skeleton. User-specified skeletons ~~~~~~~~~~~~~~~~~~~~~~~~ Users can create their own skeletons. Check the documentation on :ref:`Contributing skeletons`