this repo has no description
at main 517 lines 21 kB view raw
1{0 How to Drive [odoc]} 2 3[odoc] is a CLI tool to create API and documentation for OCaml 4projects. However, it operates at a rather low level, taking individual files 5through several distinct phases until the HTML output is generated. 6 7For this reason, just like for building any multifiles OCaml project, [odoc] 8needs to be driven by a higher level tool. The driver will take care of calling 9the [odoc] command with the right arguments throughout the different 10phases. The {{!/odoc-driver/page-index}odoc-driver} package contains a "reference driver", that is kept up-to-date 11with the latest development of [odoc], 12 13Several drivers for [odoc] exist, such as: 14{{:https://dune.readthedocs.io/en/stable/documentation.html}dune} and 15{{:https://erratique.ch/software/odig/doc/}odig}. 16 17This document explains how to drive [odoc], as of version 3. It is not needed to 18know any of this to {e use} [odoc], it is targeted at driver authors, tools that 19interact with [odoc], or any curious passerby. This includes several subjects: 20 21- A big picture view of the doc generation model, 22- A unified explanation for the various command line flags, 23- An explanation of the [odoc] pipeline, 24- A convention for building docs for [opam]-installed packages. 25 26In addition to the documentation, the reference driver is a good tool to 27understand how to build [odoc] projects. It can be useful to look at the 28implementation code, but it can also help to simply look at all invocations of 29[odoc] during a run of the driver. 30 31{1:units Trees of documentation} 32 33In its third major version, [odoc] has been improved so that the same 34documentation can work on multiple scenarios, from local switches to big 35monorepos, or the {{:https://ocaml.org/packages}ocaml.org} hub of documentation for all packages, without 36anything breaking, especially references. 37 38The idea is that we have named groups of documentation, that we'll call {e trees} 39here. We have two kinds of trees: page trees, and modules trees. Inside the 40trees, the hierarchy is managed by [odoc]. The driver is free to "root" them 41however they like in the overall hierarchy. So [odoc] is responsible for the 42hierarchy below the trees root, and the driver is responsible for the one 43outside of the trees. In order to reference another tree, a documentation author 44can use the name of the tree in the reference. 45 46Different situations will give different meanings to the trees. In the case of 47[opam] packages, though, there is a natural meaning to give to those trees 48(you'll find more details in the {{!section-conv}convention for opam-installed packages}). Any 49opam package will have an associated "documentation tree", named with the name 50of the package. Any of its libraries will have an associated "module tree", 51named with the name of the library. Another package can thus refer to the doc 52using the package name, or to any of its library using the library name, no 53matter where the package is located in the hierarchy. 54 55{1 The doc generation pipeline} 56 57Just like when compiling OCaml modules, generating docs for these modules need 58to be run in a specific order, as some information for generating docs for a 59file might reside in another one. However, [odoc] actually allows a particular 60file to reference a module that depends on it, seemingly creating a circular 61dependency. 62 63This circular dependency problem is one of the reasons we have several phases in 64[odoc]. Let's review them: 65 66- The [compile] phase, which is used to create the [.odoc] artifacts from 67 [.cm{i;t;ti}] and [.mld] files. This is where [odoc] does similar work to 68 that of the OCaml compiler, computing expansions for each module types. The dependencies 69 between are the same as the ones for the [.cm{i;t;ti}] input. 70 71- The [link] phase transforms the [.odoc] artifacts to [.odocl]. The main result 72 of this phase is to resolve odoc references, which also has an effect on 73 canonical modules. 74 75- The [indexing] phase generates [.odoc-index] files from sets of [.odocl] 76 files. These index files will be used both for generating a global sidebar, 77 and for generating a search index. 78 79- The [generation] phase takes the [.odocl] and [.odoc-index] files and turns 80 them into either HTML, man pages or Latex files. 81 82{2 The compile phase} 83 84The compile phase takes as input a set of [.cm{i;t;ti}] as well as [.mld] files, 85and builds a directory hierarchy of [.odoc] files. 86 87There are distinct commands for this phase: [odoc compile] for interfaces and 88pages, [odoc compile-impl] for implementations, and [odoc compile-asset] for 89assets. 90 91{3 Compiling interfaces} 92 93Let's have a look at a generic invocation of [odoc] during the compile phase: 94 95{@shell[ 96 $ odoc compile --output-dir <od> --parent-id <pid> -I <dir1> -I <dir2> <input-file>.<ext> 97]} 98 99- [<input-file>.<ext>] is the input file, either a [.cm{i;t;ti}] file or an [.mld] 100 file. Prefer [.cmti] files over the other formats! 101 102- [--output-dir <od>] allows to specify the directory that will contain all the 103 [.odoc] files. This directory has to be fully managed by [odoc] and should not 104 be modified by another tool! The output file depends on the [--parent-id] 105 option. 106 107- [--parent-id <pid>] allows to place the output [.odoc] file in the 108 documentation hierarchy. This consists in a [/] separated sequence of non 109 empty strings (used as directory name). This "path" determines where the 110 [.odoc] file will be located below the [<od>] output dir. The name of the 111 output file is [<input-file>.odoc] for modules, and [page-<input-file>.odoc] 112 for pages. Documentation artifacts that will be in the same {{!units}unit of 113 documentation} need to hare a common root in their parent id. 114 115- [-I <dir>] corresponds to the search path for other [.odoc] files. Multiple 116 directory can be added to the search path, so every required [.odoc] file is 117 in the search path. The required [.odoc] files are the one generated from a 118 [.cm{i;t;ti}] file listed when calling [odoc compile-deps] on the input 119 file. 120 121A concrete example for such command would be: 122 123{@shell[ 124 $ odoc compile 125 ~/.opam/5.2.0/lib/ppxlib/ppxlib__Extension.cmti 126 --output-dir _odoc/ 127 -I _odoc/ocaml-base-compiler/compiler-libs.common 128 -I _odoc/ocaml-base-compiler/stdlib 129 -I _odoc/ocaml-compiler-libs/ocaml-compiler-libs.common 130 -I _odoc/ppxlib/ppxlib 131 -I _odoc/ppxlib/ppxlib.ast 132 -I _odoc/ppxlib/ppxlib.astlib 133 -I _odoc/ppxlib/ppxlib.stdppx 134 -I _odoc/ppxlib/ppxlib.traverse_builtins 135 -I _odoc/sexplib0/sexplib0 136 --parent-id ppxlib/ppxlib 137]} 138 139{3 Compiling implementations} 140 141A [compile-impl] command is pretty similar: 142 143{@shell[ 144 $ odoc compile-impl --output-dir <od> --source-id <sid> --parent-id <pid> -I <dir1> -I <dir2> <input-file>.<ext> 145]} 146 147- [<input-file>.cmt] is the input file, it has to be a [.cmt] file. 148 149- [--output-dir <od>] has the same meaning as for [odoc compile]. 150 151- [--parent-id <pid>] also has the same meaning as for [odoc compile]. However, 152 the name of the output file is [impl-<input-file>.odoc]. Implementations need 153 to be available through the [-I] search path, so it is very likely that one 154 wants the implementation and interface [.odoc] files to share the same parent 155 id. 156 157- [-I <dir>] also corresponds to the search path for other [.odoc] files. 158 159- [source-id <sid>] is a new argument specific to [compile-impl]. This corresponds to the location of the rendering of the source, which is required to generate links to it. 160 161A concrete example for such command would be: 162 163{@shell[ 164 $ odoc compile-impl 165 ~/.opam/5.2.0/lib/ppxlib/ppxlib__Spellcheck.cmt 166 --output-dir _odoc/ 167 -I _odoc/ocaml-base-compiler/compiler-libs.common 168 -I _odoc/ocaml-base-compiler/stdlib 169 -I _odoc/ocaml-compiler-libs/ocaml-compiler-libs.common 170 -I _odoc/ppxlib/ppxlib 171 -I _odoc/ppxlib/ppxlib.ast 172 -I _odoc/ppxlib/ppxlib.astlib 173 -I _odoc/ppxlib/ppxlib.stdppx 174 -I _odoc/sexplib0/sexplib0 175 --enable-missing-root-warning 176 --parent-id ppxlib/ppxlib 177 --source-id ppxlib/src/ppxlib/spellcheck.ml 178]} 179 180{3 Compiling assets} 181 182Assets are given during the generation phase. But we still need to create an 183[.odoc] file, for [odoc]'s resolution mechanism. 184 185{@shell[ 186 $ odoc compile-asset --output-dir <od> --parent-id <pid> --name <assetname> 187]} 188 189- [--output-dir] and [--parent-id] are identical to the [compile] and 190 [compile-impl] commands, 191 192- [--name <assetname>] gives the asset name. 193 194- The output file name is computed from the previous values as 195 [<output-dir>/<parent-id>/asset-<assetname>.odoc]. 196 197{2 The link phase} 198 199The link phase requires the directory of the [compile] phase to generate its set 200of [.odocl] files. This phase resolves references and canonicals. 201 202A generic link command is: 203 204{@shell[ 205 $ odoc link 206 -I <dir1> -I <dir2> 207 -P <pname1>:<pdir1> -P <pname2>:<pdir2> 208 -L <lname1>:<ldir1> -L <lname2>:<ldir2> 209 <path/to/file.odoc> 210]} 211 212- [<path/to/file.odoc] is the input [.odoc] file. The result of this command is 213 [path/to/file.odocl]. This path was determined by [--output-dir] and 214 [--parent-id] from the link phase, and it is important for the indexing phase 215 that it stays in the same location. 216 217- [-P <name>:<dir>] are used to list the "page trees", used to resolve 218 references such as [{!/ocamlfind/index}]. 219 220- [-L <name>:<dir>] are used to list the "module trees", used to resolve 221 references such as [{!/findlib.dynload/Fl_dynload}]. This also adds [<dir>] to 222 the search path. 223 224- [-I <dir>] adds [<dir>] to the search path. The search path is used to resolve 225 references that do not use the "named tree" mechanism, such as [{!Module}] and 226 [{!page-pagename}]. 227 228{2 The indexing phase} 229 230The indexing phase refers to the "crunching" of information split in several 231[.odocl] files. Currently, there are two use-cases for this phase: 232 233- Generating a search index. This requires all information from linked 234 interfaces and pages, but also form linked implementations in order to sort 235 results (by number of occurrences). 236 237- Generating a global sidebar. 238 239{3 Counting occurrences} 240 241This step counts the number of occurrences of each value/type/... in the 242implementation, and stores them in a table. A generic invocation is: 243 244{@shell[ 245 $ odoc count-occurrences <dir1> <dir2> -o <path/to/name.odoc-occurrences> 246]} 247 248An example of such command: 249 250{@shell[ 251 $ odoc count-occurrences _odoc/ -o _odoc/occurrences-all.odoc-occurrences 252]} 253 254{3 Indexing entries} 255 256The [odoc compile-index] produces an [.odoc-index] file, from [.odocl] files, 257other [.odoc-index] files, and possibly some [.odoc-occurrences] files. 258 259To create an index for the page and documentation units, we use the [-P] and 260[-L] arguments. 261 262{@shell[ 263 $ odoc compile-index 264 -o path/to/<indexname>.odoc-index 265 -P <pname1>:<ppath1> 266 -P <pname2>:<ppath2> 267 -L <lname1>:<lpath1> 268 -L <lname2>:<lpath2> 269 --occurrences <path/to/name.odoc-occurrences> 270]} 271 272An example of such command: 273 274{@shell[ 275 $ odoc compile-index 276 -o _odoc/ppxlib/index.odoc-index 277 -P ppxlib:_odoc/ppxlib 278 -L ppxlib:_odoc/ppxlib/ppxlib 279 -L ppxlib.ast:_odoc/ppxlib/ppxlib.ast 280 -L ppxlib.astlib:_odoc/ppxlib/ppxlib.astlib 281 -L ppxlib.metaquot:_odoc/ppxlib/ppxlib.metaquot 282 -L ppxlib.metaquot_lifters:_odoc/ppxlib/ppxlib.metaquot_lifters 283 -L ppxlib.print_diff:_odoc/ppxlib/ppxlib.print_diff 284 -L ppxlib.runner:_odoc/ppxlib/ppxlib.runner 285 -L ppxlib.runner_as_ppx:_odoc/ppxlib/ppxlib.runner_as_ppx 286 -L ppxlib.stdppx:_odoc/ppxlib/ppxlib.stdppx 287 -L ppxlib.traverse:_odoc/ppxlib/ppxlib.traverse 288 -L ppxlib.traverse_builtins:_odoc/ppxlib/ppxlib.traverse_builtins 289 --occurrences _odoc/occurrences-all.odoc-occurrences 290]} 291 292{2 The generation phase} 293 294The generation phase is the phase that takes all information computed in 295previous files, and actually generates the documentation. It can take the form 296of HTML, Latex and manpages, although currently HTML is the [odoc] backend that 297supports the most functionalities (such as images, videos, ...). 298 299In this manual, we describe the HTML generation usecase. Usually, 300generating for other backend boils down to replacing [html-generate] by 301[latex-generate] or [man-generate], refer to the manpage to see the diverging 302options. 303 304Given an [.odocl] file, [odoc] might generate a single [.html] file, or a 305complete directory of [.html] files. The [--output-dir] option specifies the 306root for generating those outputs. 307 308{3 A JavaScript file for search requests} 309 310[odoc] provides a way to plugin a JavaScript file, containing the code to answer 311user's queries. In order to never block the UI, this file will be loaded in a 312web worker to perform searches: 313 314- The search query will be sent as a plain string to the web worker, using the 315 standard mechanism of message passing. 316 317- The web worker has to send back the result as a message to the main thread, 318 containing the results. The format for the result message is a string that can 319 be parsed as a list of JSON objects. Each object contain two keys-value pairs: 320 a key ["url"] which contains a relative URL from [--ouput-dir]; and a key 321 ["html"] which contain the html showing the search entry. 322 323- The JavaScript file must be manually put in the [--output-dir] values, the driver can 324 decide where. 325 326{3 Interfaces and pages} 327 328A generic [html-generate] command for interfaces has the following form: 329 330{@shell[ 331 $ odoc html-generate 332 --output-dir <odir> 333 --index <path/to/file.odoc-index> 334 --search-uri <relative/to/output-dir/file.js> 335 --search-uri <relative/to/output-dir/file2.js> 336 <path/to/file.odocl> 337]} 338 339- [--output-dir <odir>] is used to specify the root output for the generated 340 [.html]. 341 342- [--index <path/to/file.odoc-index>] is given to [odoc] for sidebar generation. 343 344- [--search-uri <relative/to/output-dir/file.js>] tells [odoc] which file(s) to 345 load in a web worker. 346 347The output directory or file can be computed from this command's [--output-dir], 348the initial [--parent-id] given when creating the [.odoc] file, as well as the 349unit name. In the case of a module, the output is a directory named with the 350name of the module. In the case of a page, the output is a file with the name of 351the page and the [.html] extension. 352 353An example of such command is: 354 355{@shell[ 356 $ odoc html-generate 357 _odoc/ppxlib/page-index.odocl 358 --index _odoc/ppxlib/index.odoc-index 359 --search-uri ppxlib/sherlodoc_db.js 360 --search-uri sherlodoc.js 361 -o _html/ 362]} 363 364{3 Source code} 365 366{@shell[ 367 $ odoc html-generate-source --output-dir <odir> --impl <path/to/impl-file.odocl> <path/to/source/file.ml> 368]} 369 370- [--output-dir <odir>] has been covered already 371 372- [--impl <path/to/impl-file.odocl>] allows to give the implementation file. 373 374- [<path/to/source/file.ml>] is the source file. 375 376The output file can be computed from this command's [--output-dir], and the 377initial [--source-id] and [--name] given when creating the [impl-*.odoc] file. 378 379An example of such command is: 380 381{@shell[ 382 $ odoc html-generate-source 383 --impl _odoc/ppxlib/ppxlib/impl-ppxlib__Reconcile.odocl 384 /home/panglesd/.opam/5.2.0/lib/ppxlib/reconcile.ml 385 -o _html/ 386]} 387 388{3 Generating docs for assets} 389 390This is the phase where we pass the actual asset. We pass it as a positional 391argument, and give the asset unit using [--asset-unit]. 392 393{@shell[ 394 $ odoc html-generate-asset --output-dir <odir> --asset-unit <path/to/asset-file.odocl> <path/to/asset/file.ext> 395]} 396 397{1:conv Convention for installed packages} 398 399In order to build the documentation for installed packages, the driver needs to 400give a meaning to the various concepts above. In particular, it needs to 401define the pages and libraries trees, know where to find the pages and assets, 402what id to give them, when linking it needs to know to which trees the artifact 403may be linking... 404 405So that the different drivers and installed packages play well together, we 406define here a convention for building installed packages. If both the package 407and the driver follow it, building the docs should go well! 408 409{2 The [-P] and [-L] trees, and their root ids} 410 411Each package defines a set of trees, each of them having a root id. These roots 412will be used in [--parent-id] and in [-P] and [-L]. 413 414The driver can decide any set of mutually disjoint set of roots, without posing 415problem to the reference resolution. For instance, both [-P 416pkg:<output_dir>/pkg] and [-P pkg:<output_dir>/pkg/version] are 417acceptable versions. However, we define here "canonical" roots: 418 419Each installed package [<p>] defines a single page root id: [<p>]. 420 421For each package [<p>], each library [<l>] defines a library root id: 422[<p>/<l>]. 423 424For instance, a package [foo] with two libraries: [foo] and [foo.bar] will 425define three trees: 426 427- A documentation tree named [foo], with root id [foo]. When referred 428 from other trees, a [-P foo:<odoc_dir>/foo] argument needs to be added 429 at the link phase. 430 431- A module tree named [foo], with root id [foo/foo]. When referred from 432 other trees, a [-L foo:<odoc_dir>/foo/foo] argument needs to be added 433 at the link phase. 434 435- A module tree named [foo.bar], with root id [foo/foo.bar]. When referred from 436 other trees, a [-L foo.bar:<odoc_dir>/foo/foo.bar] argument needs to be 437 added at the link phase. 438 439{2 Link-time dependencies} 440 441Installed OPAM packages need to specify which trees they may be referencing 442during the link phase, so that the proper [-P] and [-L] arguments are 443added. (Note that these dependencies can be circular, as they 444happen during the link phase and only require the artifact from the compile 445phase.) 446 447An installed package [<p>] specifies its tree dependencies in a file at 448[<opam root>/doc/<p>/odoc-config.sexp]. This file contains s-expressions. 449 450Stanzas of the form [(packages p1 p2 ...)] specifies that page trees [p1], 451[p2], ..., should be added using the [-P] argument: with the canonical roots, it 452would be [-P p1:<output_dir>/p1 -P p2:<output_dir>/p2 -P ...]. 453 454Stanzas of the form [(libraries l1 l2 ...)] specifies that module trees [l1], 455[l2], ..., should be added using the [-L] argument: with the canonical roots, it 456would be [-L l1:<output_dir>/p1/l1 -L l2<output_dir>/p2/l2 -L ...], 457where [p1] is the package [l1] is in, etc. 458 459{2 The units} 460 461The module units of a package [p] are all files installed by [p] that can be 462found in [<opam root>/lib/p/] or a subdirectory. 463 464The page units are those files that can be found in [<opam 465root>/doc/odoc-pages/] or a subdirectory, and that have an [.mld] extension. 466 467The asset units are those files that can be found in [<opam 468root>/doc/odoc-pages/] or a subdirectory, but that do not have an [.mld] 469extension. Additionally, they are all files found in [<opam 470root>/doc/odoc-assets/]. 471 472{2 The [--parent-id] arguments} 473 474Interface and implementation units have as parent id the root of the library 475tree they belong to: with "canonical" roots, [<pkgname>/<libname>]. 476 477Page units that are found in [<opam 478root>/doc/<pkgname>/odoc-pages/<relpath>/<name>.mld] have the parent id from 479their page tree, followed by [<relpath>]. So, with canonical roots, 480[<pkgname>/<relpath>]. 481 482Asset units that are found in [<opam 483root>/doc/<pkgname>/odoc-pages/<relpath>/<name>.<ext>] have the parent id from 484their page tree, followed by [<relpath>]. With canonical roots, 485[<pkgname>/<relpath>]. 486 487Asset units that are found in [<opam root>/doc/<pkgname>/odoc-assets/<filename>] 488have the parent id from their page tree, followed by [_asset/<filename>] 489[<p>/_assets/<filename>]. 490 491{2 The [--source-id] arguments} 492 493The driver could choose the source id without breaking references. However, 494following the canonical roots convention, implementation units must have as 495source id: [<pkgname>/src/<libraryname>/<filename>.ml]. 496 497{2 Ordering the generated pages} 498 499The canonical hierarchy introduces directories (one per library) that may not be 500ordered by the author, either by omitting it in the [@children_order] tag or by 501not specifying any [@children_order] tag. In this case, [odoc] needs to come up 502with a reasonable default order, which may not be easy without some help from 503the driver. 504 505In auto-generated pages (either [index.mld] in a directory, or [page.mld]), 506[odoc] supports the [@order_category <category_name>] tag, to help sorting the 507pages, if it is not sorted by the parent's [@children_order]. The resulting 508order is: 509 510- First, pages in the order given in their parent's [@children_order], 511- Then, pages ordered lexicographically by their [@order_category]. An undefined 512 category comes before a defined one. 513- Inside a category, pages are ordered lexicographically by their first title, 514- Two pages with the same name will be ordered using their file name! 515 516Note that [@order_category] is not suitable for author use, as it may change in 517the future. Use this tag only in the driver's autogenerated pages!