···11+v1.1.1 2025-11-03 Zagreb
22+-------------------------
33+44+- Support for large package builds. On build, generate a `pkg.itarget`
55+ at the root with the files to build and invoke `ocamlbuild` with
66+ `pkg.otarget` rather than the explicit list of files. The
77+ `pkg.itarget` file can be ignored by your VCS, it is generated on
88+ the fly on each build. As a side effect, improves error reporting on
99+ build failures.
1010+1111+v1.1.0 2025-07-23 Zagreb
1212+------------------------
1313+1414+- Change the default of `Topkg.Conf.debugger_support` from `false` to
1515+ `true`. This enables the install of `.ml` and `.cmt` files by
1616+ default which is expected for a good experience on `ocaml.org`. The
1717+ previous behaviour can be recovered by setting
1818+ `TOPKG_CONF_DEBUGGER_SUPPORT=false` in your environment (#143).
1919+2020+- Fix install of hidden library modules when `Topkg.Conf.debugger_support` is
2121+ `true`:
2222+ 1. When the hidden module has an `.mli`, the `.cmi` files was being installed
2323+ instead of the `.cmti` and the `.mli` was missing (#143).
2424+ 2. When the hidden module had no `.mli` the installation would fail
2525+ by requiring one.
2626+2727+v1.0.8 2025-03-10 La Forclaz (VS)
2828+---------------------------------
2929+3030+- `topkg-care`: handle `fmt` and `cmdliner` deprecations.
3131+- Require OCaml >= 4.08
3232+3333+v1.0.7 2022-01-20 La Forclaz (VS)
3434+---------------------------------
3535+3636+- Fix install of C stubs in byte code only installations.
3737+ Thanks to @kit-ty-kate for the investigation (#140).
3838+3939+v1.0.6 2022-11-04 Zagreb
4040+------------------------
4141+4242+- Fix native dynlink detection on OCaml 5.0 and thus `cmxs` file
4343+ installation. Thanks to @kit-ty-kate for the report and the patch.
4444+4545+v1.0.5 2022-01-28 La Forclaz (VS)
4646+---------------------------------
4747+4848+- `Topkg.String.parse_version`. Support for the new OCaml
4949+ version string format (https://github.com/ocaml/ocaml/pull/9712)
5050+- Switch from `opam config var` to `opam var`.
5151+- Fix compilation for next version of `cmdliner`.
5252+5353+v1.0.4 2021-10-04 Zagreb
5454+-------------------------
5555+5656+- Remove mentions of `Result.result` in the code base. We got the
5757+ dependency indirectly through `bos` and the latest version of the
5858+ latter no longer depends on it.
5959+6060+v1.0.3 2020-09-19 Zagreb
6161+------------------------
6262+6363+- Fix for OCaml 4.12. The M.() notation is evil.
6464+ Thanks to Florian Angeletti for the fix.
6565+6666+v1.0.2 2020-07-30 Zagreb
6767+------------------------
6868+6969+- Support OCaml configurations without shared library support
7070+ (#136). Thanks to Antonio Nuno Monteiro for the help.
7171+7272+v1.0.1 2019-07-19 Zagreb
7373+------------------------
7474+7575+- Require OCaml 4.03 and handle various stdlib deprecations.
7676+- Drop `result` dependency.
7777+- Fix an upcoming `Fmt` incompability.
7878+- Addition of `synopsis` and `description` fields to the
7979+ opam file generated for the tarball.
8080+8181+v1.0.0 2018-10-14 Zagreb
8282+------------------------
8383+8484+- Support (only) for opam-publish v2.0.0. The `--pkg-opam-dir` option
8585+ of `topkg opam` to indicate the old-style opam package directory to
8686+ submit is renamed to `--opam-publish-file` to indicate the opam file
8787+ to submit.
8888+- Add `topkg opam publish` command, alias of `topkg opam submit`.
8989+- Toy github delegate: make curl follow redirects. Before obscure
9090+ failures would result when repos got moved around (#120). Thanks
9191+ to Richard Mortier for the report.
9292+- Fix infinite loop in `Topkg.OS.File.write_subst`. This could result
9393+ in `topkg distrib` never finishing (#128). Thanks to Christophe
9494+ Troestler for reporting and Jérémie Dimino for the patch.
9595+- Add `.ps` and `.eps` files to default watermarking excludes.
9696+ Thanks Christophe Troestler for the suggestion (#128).
9797+- Use `command -v` rather than `type` to check for tool existence.
9898+ Thanks to Hannes Mehnert for the patch.
9999+100100+v0.9.1 2017-10-16 Zagreb
101101+------------------------
102102+103103+- Make `topkg build` return a non-zero exit code when the build
104104+ fails. Thanks to Etienne Millon for the patch.
105105+- Improve `topkg doc` for `jbuilder` users. Thanks to Thomas
106106+ Gazagnaire for the patch.
107107+- `ocamlbuild` users: default to parallel builds. This can
108108+ be controlled via the `--jobs` command line argument. Thanks
109109+ to Edwin Török for the patch.
110110+111111+v0.9.0 La Forclaz (VS)
112112+----------------------
113113+114114+- Deprecate `--pinned` in favor of `--dev-pkg` in the `build` command.
115115+ The semantics is the same and with opam < 2.0 it should still be set
116116+ to `"%{pinned}%"`. With opam >= 2.0 it should be set to `"%{dev}%"`
117117+ this allows to infer the correct build context for (non-pinned) VCS
118118+ packages (#79).
119119+- Improve `ocamlbuild` cross-compilation support. Adds the
120120+ `Conf.toolchain` configuration key. If specified on the command line
121121+ or via the `TOPKG_CONF_TOOLCHAIN` environment variable, its value is
122122+ used with the `-toolchain` option introduced in `ocamlbuild` 0.11.0
123123+ in default build command `Pkg.build_cmd`. If unspecified the default
124124+ build command is left unchanged. Thanks to whitequark for the patch.
125125+- Add the `--raw ARG` repeteable option to the `build` command. Allows
126126+ to skip package build instructions and opam install file generation
127127+ to simply invoke the package build command with the `ARG` argument.
128128+- `topkg doc` (ocamlbuild specific). Build the documentation using
129129+ the package `build` command and `--raw` arguments. Avoids problems
130130+ encountered by packages that use ocamlbuild plugins (#80).
131131+- Add the `cma`, `cmxa` and `cmxs` optional arguments to `Pkg.mllib`.
132132+ These allow to precisely specify what you (don't) want to build. They
133133+ all default to `true`. Thanks to Stephen Dolan for the suggestion.
134134+- Add `Pkg.lib_root` and `Pkg.libexec_root` install fields. Warning
135135+ these are opam 2.0 only fields.
136136+- Change `test` command for multi-opam packages by mirroring the way
137137+ the `build` command works. The `--pkg-name` or `-n` option specifies
138138+ the package's test to run or list. If unspecified the default
139139+ package is tested, before `pkg/pkg.ml test` would list and run
140140+ the last built package. This means that if you have `pkg/pkg.ml
141141+ build -n PKG && pkg/pkg test` invocations you need to turn them into
142142+ `pkg/pkg.ml build -n PKG && pkg/pkg test -n PKG`.
143143+- Fix `topkg run`, do not run `.so` and `.cmxs` files.
144144+- Fix changelog parsing. Subsections of an entry were not being properly
145145+ parsed (#103).
146146+- Fix `topkg opam pkg`'s `url` file generation for github users which
147147+ have `dev-repo:` with opam "version control bound" uris (#106).
148148+- Depends at least on `cmdliner.1.0.0` and `opam-format` (`opam-lib`
149149+ is out).
150150+151151+v0.8.1 2016-11-02 Zagreb
152152+------------------------
153153+154154+- Add `Pkg.{nothing,flatten}`. Thanks to David Kaloper Meršinjak for the
155155+ suggestion and the patch.
156156+157157+158158+v0.8.0 2016-10-31 Zagreb
159159+------------------------
160160+161161+- Add `Conf.debugger_support` a configuration key to inform to the
162162+ build system it should build and install build artefacts for
163163+ debuggers. Packages using `Pkg.{mlib,clib}` descriptions will handle
164164+ this automatically. The key can be set globally in a switch via the
165165+ `TOPKG_CONF_DEBUGGER_SUPPORT` environment variable (#77).
166166+- Add `Pkg.{ocb_tag,ocb_bool_tag,ocb_bool_tags}` to easily extend
167167+ `ocamlbuild` invocations according to the build configuration (#78). Thanks
168168+ to David Kaloper Meršinjak for the idea and the patch.
169169+- Add `Exts.interface` for installing `mli` only compilation units (#74).
170170+- `Pkg.mllib` description. Correct support for mllib which have subpaths (#75).
171171+- Documentation generation. Fix support in the presence of `ocamlbuild`
172172+ plugins (#80). Thanks to David Kaloper Meršinjak for the report
173173+ and the patch.
174174+- Documentation generation. If there is no `doc/style.css` but `odig` is
175175+ installed, use its `ocamldoc` stylesheet. This allows to see how it will
176176+ be rendered by `odig` and avoids maintaining stylesheets in repos.
177177+178178+v0.7.9 2016-09-21 Zagreb
179179+------------------------
180180+181181+- Better package parsing in `ocamlbuild` _tags files. Thanks
182182+ to Thomas Gazagnaire for the report.
183183+- Remove references to internal names in the API.
184184+185185+v0.7.8 2016-08-09 Zagreb
186186+------------------------
187187+188188+- Add a `--profile` configuration key. Thanks to David Kaloper
189189+ Meršinjak for the patch.
190190+- Add `OS.File.write_subst`. Allows clients to substitute watermark
191191+ like variables in hooks.
192192+- Add `Pkg.{build,clean}_cmd`. Allows clients to extend the default
193193+ build system invocation.
194194+- Be more quiet on package builds (log the `.install` file
195195+ written message at info level).
196196+- Remove Topkg_care.Browser. Depend on the `webbrowser` package
197197+ instead.
198198+199199+v0.7.7 2016-07-13 Cambridge (UK)
200200+--------------------------------
201201+202202+- Test description, allow to specify a working directory
203203+ for the test via the `?dir` optional argument. Thanks
204204+ to Thomas Gazagnaire for suggesting.
205205+- Fix behaviour of pinned distribution watermarking when
206206+ git repo is not at the root directory of the package and
207207+ `--vcs true` is forced.
208208+- Fix pkg/pkg.ml's main's ignoring `TOPKG_VERBOSITY`'s value.
209209+- Toy GitHub delegate: fix log verbosity propagation.
210210+- Fix `Vcs.is_dirty` to detect untracked files. Thanks to Hannes Mehnert.
211211+- Pager, do not try to discover if `TERM` variable is undefined.
212212+- Add `topkg run` to easily run built executables.
213213+214214+v0.7.6 2016-07-01 Cambridge (UK)
215215+--------------------------------
216216+217217+- Add `pkg/pkg.ml clean` command. Removes the opam install file
218218+ and performs an effect that can be specified via `clean` in the
219219+ `Pkg.build` description. The `topkg clean` command now simply forwards
220220+ to this command.
221221+- Change the signature of the build command `cmd` in the `Pkg.build`
222222+ specification. This is an API breaking change but does not affect any
223223+ published package. See #53 for details.
224224+- Add `Conf.OCaml.word_size`. Reports the bit word size for
225225+ the programs that are produced by a given compiler.
226226+- Build configuration key parsing. Fail hard on any error instead
227227+ of warn and continue (#56). Thanks to Thomas Gazagnaire for suggesting
228228+ the previous idea was a terrible one.
229229+- Add a `--debug` configuration key. Defaults to `true` or the value
230230+ of the `TOPKG_CONF_DEBUG` environment variable. The default build
231231+ system invocation is changed to enable save of debugging information
232232+ in build artefacts if they key is `true`. The key is generally not
233233+ meant to be specified by packagers so that the policy can changed in
234234+ bulk over topkg packages (#54).
235235+- `Pkg.files_to_watermark` default function. Make sure only files are
236236+ returned (#58). Fixes problems with symlinks to directories in git
237237+ checkout. Thanks to Thomas Gazagnaire for reporting.
238238+- Improve error message of some `Topkg.OS` functions (#57).
239239+- Remove deprecated `--installer` configuration key.
240240+- `topkg lint` fix regression in error opam lint report.
241241+242242+v0.7.5 2016-06-22 Cambridge (UK)
243243+--------------------------------
244244+245245+- `topkg doc` add short option `-d` for `--dev`.
246246+- Fix `Pkg.mllib`, module list was lowercased rather than uncapitalized.
247247+248248+v0.7.4 2016-06-17 Cambridge (UK)
249249+--------------------------------
250250+251251+- Add test description and run support. New `topkg test` command.
252252+- Add distribution publication description support. Allows to define the
253253+ set of default publication artefacts in the package description. The cli
254254+ syntax of `topkg publish` for alternative artefacts changes from
255255+ `alt KIND` to `alt-KIND`.
256256+- Distributed (and thus installed) opam files are now properly
257257+ versioned via the `version:` field.
258258+- Improve tarball reproducibility across systems by not relying on the
259259+ VCS checkout state for determining the read and write rights (#43).
260260+- opam package submission: use the `opam-publish` submit message
261261+ to append the release notes to the submission.
262262+- Toy GitHub delegate: improve user authentication by trying to steal
263263+ an existing opam-publish token.
264264+- Toy GitHub delegate: improve package documentation publication. Thanks
265265+ to Thomas Gazagnaire for the patches.
266266+- Error message and IPC logging level propagation improvements. Thanks to
267267+ Thomas Gazagnaire for the help.
268268+269269+v0.7.3 2016-06-12 Cambridge (UK)
270270+--------------------------------
271271+272272+- Change pin build detection (#44). This changes opam build
273273+ instruction for packages. Substitute `"--installer" "true"` by
274274+ `"--pinned" "%{pinned}%"` in build instructions. The
275275+ `--installer` option is deprecated and has no effect.
276276+277277+v0.7.2 2016-06-02 Cambridge (UK)
278278+--------------------------------
279279+280280+- `Pkg.describe`, allow multiple readme, change log and license files.
281281+ The optional arguments `readme`, `change_log` and `license` become
282282+ `readmes`, `change_logs`, `licenses` with the same default. When
283283+ topkg needs to act on a change log (e.g. `topkg log`) or readme
284284+ (e.g. `topkg opam descr`), it acts on the first element of
285285+ `change_log` and/or `readmes`.
286286+287287+- Fix `Conf.vcs` discovery to only look for a git
288288+ directory in the build directory (#42).
289289+290290+v0.7.1 2016-05-26 Arbaz (VS)
291291+----------------------------
292292+293293+- Improve Windows support. Thanks to Andreas Hauptmann for the help.
294294+295295+v0.7.0 2016-05-22 La Forclaz (VS)
296296+---------------------------------
297297+298298+First release.
+39
vendor/opam/topkg/DEVEL.md
···11+Package structure
22+-----------------
33+44+This repo is the base of two opam packages:
55+66+- `topkg`, with opam file [topkg.opam](topkg.opam)
77+- `topkg-care` with opam file [topkg-care.opam](topkg-care.opam)
88+99+Both share the same [pkg/pkg.ml](pkg/pkg.ml) file. `topkg` simply
1010+builds the `Topkg` library and `topkg-care` builds the `Topkg_care`
1111+library and the `topkg` command line tool. The distinction is made in
1212+the `pkg.ml` file based on the package name passed to the build
1313+instructions.
1414+1515+The reason for this structure is that while both `Topkg` and `Topkg_care`
1616+could be distributed together and `Topkg_care` be simply build
1717+depending on optional dependencies being present, this would have a
1818+fatal flaw: `topkg` cannot be used for any of `Topkg_care`'s
1919+dependencies. Since we want to use `topkg` for these dependencies
2020+aswell, this structure allows to cut the dependency cycle that would
2121+occur.
2222+2323+So if you want to develop `topkg` you should do a:
2424+2525+```
2626+opam pin add -kgit topkg topkg#master
2727+opam pin add -kgit topkg-care topkg#master
2828+```
2929+3030+Changing the Topkg API
3131+----------------------
3232+3333+Is a *very* delicate thing to do as it could break packages. Here's an
3434+invocation that can be used to reinstall packages that build depend on
3535+`topkg`:
3636+3737+```
3838+opam reinstall $(opam list -s --installed --depends-on topkg)
3939+```
+13
vendor/opam/topkg/LICENSE.md
···11+Copyright (c) 2016 Daniel C. Bünzli
22+33+Permission to use, copy, modify, and/or distribute this software for any
44+purpose with or without fee is hereby granted, provided that the above
55+copyright notice and this permission notice appear in all copies.
66+77+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
88+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
99+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1010+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1111+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1212+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1313+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+55
vendor/opam/topkg/README.md
···11+Topkg — The transitory OCaml software packager
22+==============================================
33+44+**Warning** Topkg is in maintenance mode and should not longer be used.
55+66+Topkg is a packager for distributing OCaml software. It provides an
77+API to describe the files a package installs in a given build
88+configuration and to specify information about the package's
99+distribution, creation and publication procedures.
1010+1111+The optional topkg-care package provides the `topkg` command line tool
1212+which helps with various aspects of a package's life cycle: creating
1313+and linting a distribution, releasing it on the WWW, publish its
1414+documentation, add it to the OCaml opam repository, etc.
1515+1616+Topkg is distributed under the ISC license and has **no**
1717+dependencies. This is what your packages will need as a *build*
1818+dependency.
1919+2020+Topkg-care is distributed under the ISC license it depends on
2121+[fmt][fmt], [logs][logs], [bos][bos], [cmdliner][cmdliner],
2222+[webbrowser][webbrowser] and `opam-format`.
2323+2424+[fmt]: http://erratique.ch/software/fmt
2525+[logs]: http://erratique.ch/software/logs
2626+[bos]: http://erratique.ch/software/bos
2727+[cmdliner]: http://erratique.ch/software/cmdliner
2828+[webbrowser]: http://erratique.ch/software/webbrowser
2929+3030+Home page: <http://erratique.ch/software/topkg>
3131+3232+## Installation
3333+3434+Topkg and topkg-care can be installed with `opam`:
3535+3636+ opam install topkg # All you need for your packages
3737+ opam install topkg-care # topkg binary, takes care of your packages
3838+3939+If you don't use `opam` consult the [`topkg.opam`](topkg.opam) and
4040+[`topkg-care.opam`](topkg-care.opam) files for build instructions.
4141+4242+## Documentation
4343+4444+A basic introduction and API reference is automatically generated by
4545+`ocamldoc` from the interfaces. It can be consulted [online][doc].
4646+4747+The `topkg` command line tool is extensively documented in man pages
4848+available through it's help system. Type:
4949+5050+```
5151+topkg help release # for help about releasing your package
5252+topkg help # for more help
5353+```
5454+5555+[doc]: http://erratique.ch/software/topkg/doc
+11
vendor/opam/topkg/_tags
···11+true : bin_annot, safe_string
22+33+<_b0> : -traverse
44+<src> : include
55+66+<src-{care,bin}> : include
77+<src-{care,bin}/*> : package(opam-format cmdliner fmt.cli logs.cli bos.setup), \
88+ package(webbrowser webbrowser.cli)
99+1010+<test> : include
1111+<support> : include
+8
vendor/opam/topkg/doc/index-topkg-care.mld
···11+{0 Topkg {%html: <span class="version">%%VERSION%%</span>%}}
22+33+{b Warning.} Topkg is in maintenance mode and should not longer be
44+used.
55+66+{1:library_topkg_care Library [topkg.care]}
77+88+{!modules: Topkg_care }
+8
vendor/opam/topkg/doc/index-topkg.mld
···11+{0 Topkg {%html: <span class="version">%%VERSION%%</span>%}}
22+33+{b Warning.} Topkg is in maintenance mode and should not longer be
44+used.
55+66+{1:library_topkg Library [topkg]}
77+88+{!modules: Topkg}
+12
vendor/opam/topkg/doc/index.mld
···11+{0 Topkg {%html: <span class="version">%%VERSION%%</span>%}}
22+33+{b Warning.} Topkg is in maintenance mode and should not longer be
44+used.
55+66+{1:library_topkg Library [topkg]}
77+88+{!modules: Topkg}
99+1010+{1:library_topkg_care Library [topkg.care]}
1111+1212+{!modules: Topkg_care }
···11+#!/usr/bin/env ocaml
22+#use "topfind"
33+44+(* Bootstrap from source, note #mod_use is 4.01 *)
55+#directory "src"
66+#mod_use "topkg_result.ml"
77+#mod_use "topkg_string.ml"
88+#mod_use "topkg_log.ml"
99+#mod_use "topkg_fpath.ml"
1010+#mod_use "topkg_cmd.ml"
1111+#mod_use "topkg_os.ml"
1212+#mod_use "topkg_vcs.ml"
1313+#mod_use "topkg_codec.ml"
1414+#mod_use "topkg_conf.ml"
1515+#mod_use "topkg_fexts.ml"
1616+#mod_use "topkg_opam.ml"
1717+#mod_use "topkg_test.ml"
1818+#mod_use "topkg_install.ml"
1919+#mod_use "topkg_build.ml"
2020+#mod_use "topkg_distrib.ml"
2121+#mod_use "topkg_publish.ml"
2222+#mod_use "topkg_pkg.ml"
2323+#mod_use "topkg_ipc.ml"
2424+#mod_use "topkg_main.ml"
2525+#mod_use "topkg.ml"
2626+2727+open Topkg
2828+2929+let () =
3030+ let metas = [ Pkg.meta_file ~install:false "pkg/META" ] in
3131+ let opams =
3232+ let install = false in
3333+ let not_topkg_deps =
3434+ Some ["fmt"; "logs"; "bos"; "cmdliner"; "webbrowser"; "opam-format"]
3535+ in
3636+ [ Pkg.opam_file ~install "topkg.opam" ~lint_deps_excluding:not_topkg_deps;
3737+ Pkg.opam_file ~install "topkg-care.opam" ]
3838+ in
3939+ Pkg.describe ~metas ~opams "topkg" @@ fun c ->
4040+ match (* bootstrap, Conf doesn't work, eqs *) Topkg_conf.pkg_name c with
4141+ | "topkg" ->
4242+ Ok [ Pkg.lib "pkg/META";
4343+ Pkg.lib "topkg.opam" ~dst:"opam";
4444+ Pkg.mllib ~api:["Topkg"] "src/topkg.mllib";
4545+ Pkg.doc "doc/index-topkg.mld" ~dst:"odoc-pages/index.mld";
4646+ Pkg.test "test/test"; ]
4747+ | "topkg-care" ->
4848+ Ok [ Pkg.lib "topkg-care.opam" ~dst:"opam";
4949+ Pkg.mllib ~api:["Topkg_care"] "src-care/topkg_care.mllib";
5050+ Pkg.bin "src-bin/topkg_bin" ~dst:"topkg";
5151+ Pkg.bin "src-bin/toy_github_delegate"
5252+ ~dst:"toy-github-topkg-delegate";
5353+ Pkg.doc "doc/index-topkg-care.mld" ~dst:"odoc-pages/index.mld";
5454+ Pkg.doc "test/unsupportive-delegate";
5555+ Pkg.doc "test/echo-delegate" ]
5656+ | other ->
5757+ R.error_msgf "unknown package name: %s" other
+40
vendor/opam/topkg/src-bin/bistro.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+open Bos_setup
77+88+let bistro () =
99+ begin
1010+ let verb = Cmd.(v "--verbosity" % Logs.(level_to_string (level ()))) in
1111+ let topkg = Cmd.(v "topkg") in
1212+ OS.Cmd.run Cmd.(topkg % "distrib" %% verb)
1313+ >>= fun () -> OS.Cmd.run Cmd.(topkg % "publish" %% verb)
1414+ >>= fun () -> OS.Cmd.run Cmd.(topkg % "opam" %% verb % "pkg")
1515+ >>= fun () -> OS.Cmd.run Cmd.(topkg % "opam" %% verb % "submit")
1616+ >>= fun () -> Ok 0
1717+ end
1818+ |> Cli.handle_error
1919+2020+(* Command line interface *)
2121+2222+open Cmdliner
2323+2424+let doc = "For when you are in a hurry or need to go for a drink"
2525+let sdocs = Manpage.s_common_options
2626+let exits = Cli.exits
2727+let man_xrefs = [ `Main; `Cmd "distrib"; `Cmd "publish"; `Cmd "opam" ]
2828+let man =
2929+ [ `S Manpage.s_description;
3030+ `P "The $(tname) command (quick in Russian) is equivalent to invoke:";
3131+ `Pre "\
3232+topkg distrib # Create the distribution archive
3333+topkg publish # Publish it on the WWW with its documentation
3434+topkg opam pkg # Create an opam package
3535+topkg opam submit # Submit it to OCaml's opam repository";
3636+ `P "See topkg-release(7) for more information."; ]
3737+3838+let cmd =
3939+ Cmd.v (Cmd.info "bistro" ~doc ~sdocs ~exits ~man ~man_xrefs) @@
4040+ Term.(const bistro $ Cli.setup)
+8
vendor/opam/topkg/src-bin/bistro.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** The [bistro] command. *)
77+88+val cmd : int Cmdliner.Cmd.t
+127
vendor/opam/topkg/src-bin/browse.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+open Bos_setup
77+88+(* Targets *)
99+1010+let opam_doc_field =
1111+ "doc", `Opam "doc", "doc opam file field"
1212+1313+let opam_homepage_field =
1414+ "homepage", `Opam "homepage", "homepage opam file field"
1515+1616+let opam_issues_field =
1717+ "issues", `Opam "bug-reports", "bug-reports opam file field"
1818+1919+let opam_repo_field =
2020+ "repo", `Opam "dev-repo", "dev-repo opam file field"
2121+2222+let topkg_api =
2323+ "topkg-api", `Uri "%%PKG_DOC%%", "topkg's API docs"
2424+2525+let ocaml_man =
2626+ "ocaml-man", `Uri "http://caml.inria.fr/pub/docs/manual-ocaml/",
2727+ "OCaml manual"
2828+2929+let ocaml_issues =
3030+ "ocaml-issues", `Uri "http://caml.inria.fr/mantis/", "OCaml issue tracker"
3131+3232+let ocamlbuild_man =
3333+ "ocamlbuild-man",
3434+ `Uri "https://github.com/ocaml/ocamlbuild/blob/master/manual/manual.adoc",
3535+ "OCamlbuild manual"
3636+3737+let opam_man =
3838+ "opam-man", `Uri "http://opam.ocaml.org/doc/Manual.html", "opam manual"
3939+4040+let packages =
4141+ "packages", `Uri "http://opam.ocaml.org/packages/", "OCaml opam repository"
4242+4343+let planet =
4444+ "planet", `Uri "https://ocaml.org/community/planet/", "OCaml Planet"
4545+4646+let temptation =
4747+ "temptation",
4848+ `Uri "https://www.\x2568\x2561\x2573\x256B\x2565\x256C\x256C.org", ""
4949+5050+let caml_list =
5151+ "caml-list", `Uri "http://news.gmane.org/gmane.comp.lang.caml.inria",
5252+ "Main OCaml mailing list"
5353+5454+let weekly_news =
5555+ "weekly-news", `Uri "http://alan.petitepomme.net/cwn/", "OCaml Weekly News"
5656+5757+let targets =
5858+ [ opam_doc_field; opam_homepage_field; opam_issues_field; opam_repo_field;
5959+ topkg_api; ocaml_man; ocaml_issues; ocamlbuild_man; opam_man; packages;
6060+ planet; temptation; caml_list; weekly_news; ]
6161+6262+let parse_target, max_target_len =
6363+ let add (acc, len) (t, v, _) = (t, v) :: acc, max len (String.length t) in
6464+ let index, max = List.fold_left add ([], 0) targets in
6565+ (* This gives us trie lookup *)
6666+ Cmdliner.Arg.conv_parser (Cmdliner.Arg.enum index), max
6767+6868+(* opam field uris *)
6969+7070+let opam_field_uri opam field =
7171+ Topkg_care.Opam.File.fields opam
7272+ >>= fun fields -> match String.Map.find field fields with
7373+ | Some (uri :: _) -> Ok uri
7474+ | Some [] -> R.error_msgf "%a: field %s is empty" Fpath.pp opam field
7575+ | None -> R.error_msgf "%a: field %s is undefined" Fpath.pp opam field
7676+7777+(* Browse command *)
7878+7979+let browse () pkg_file opam browser prefix background target =
8080+ begin
8181+ let uri = match parse_target target with
8282+ | Ok (`Uri uri) -> Ok uri
8383+ | Ok (`Opam field) ->
8484+ let pkg = Topkg_care.Pkg.v ?opam pkg_file in
8585+ Topkg_care.Pkg.opam pkg >>= fun opam -> opam_field_uri opam field
8686+ | Error msg ->
8787+ let uri_prefixes = ["http://"; "https://"; "file://"] in
8888+ if List.exists (fun p -> String.is_prefix ~affix:p target) uri_prefixes
8989+ then Ok target
9090+ else Error msg
9191+ in
9292+ uri
9393+ >>= fun uri -> Webbrowser.reload ~background ~prefix ?browser uri
9494+ >>= fun () -> Ok 0
9595+ end
9696+ |> Cli.handle_error
9797+9898+(* Command line interface *)
9999+100100+open Cmdliner
101101+102102+let target =
103103+ let doc = "Target to browse, see above for the list of targets." in
104104+ Arg.(value & pos 0 string "homepage" & info [] ~doc ~docv:"TARGET or URI")
105105+106106+let doc = "Browse the package's WWW links"
107107+let sdocs = Manpage.s_common_options
108108+let exits = Cli.exits
109109+let man_xrefs = [ `Main ]
110110+let man =
111111+ let target acc (t, _, doc) =
112112+ if doc = "" then acc else
113113+ let pad = String.v ~len:(max_target_len - String.length t) (fun _ -> ' ') in
114114+ `Pre (strf "%s$(b,%s) %s" pad t doc) :: `Noblank :: acc
115115+ in
116116+ [ `S Manpage.s_description;
117117+ `P "The $(tname) command opens or reloads URIs mentioned in the
118118+ opam file in a WWW browser. A few other useful logical target are
119119+ provided and arbitrary file, http or https schemed URIs can also
120120+ be specified as the target.";
121121+ `Blocks (List.(tl @@ rev @@ fold_left target [] targets)); ]
122122+123123+let cmd =
124124+ Cmd.v (Cmd.info "browse" ~doc ~sdocs ~exits ~man ~man_xrefs) @@
125125+ Term.(const browse $ Cli.setup $ Cli.pkg_file $ Cli.opam $
126126+ Webbrowser_cli.browser $ Webbrowser_cli.prefix $
127127+ Webbrowser_cli.background $ target)
+8
vendor/opam/topkg/src-bin/browse.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** The [browse] command. *)
77+88+val cmd : int Cmdliner.Cmd.t
+104
vendor/opam/topkg/src-bin/build.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+open Bos_setup
77+88+let build_args pkg_name build_dir dry_run raws tests debug args =
99+ let on_some_use_opt opt to_arg = function
1010+ | None -> Cmd.empty
1111+ | Some value -> Cmd.(v opt % to_arg value)
1212+ in
1313+ let verb = Cli.propagate_verbosity_to_pkg_file () in
1414+ let pkg_name = on_some_use_opt "--pkg-name" (fun x -> x) pkg_name in
1515+ let build_dir = on_some_use_opt "--build-dir" Cmd.p build_dir in
1616+ let dry_run = if dry_run then Cmd.(v "--dry-run") else Cmd.empty in
1717+ let raws = Cmd.of_list ~slip:"--raw" raws in
1818+ let tests = on_some_use_opt "--tests" String.of_bool tests in
1919+ let debug = on_some_use_opt "--debug" String.of_bool debug in
2020+ Cmd.(verb %% dry_run %% raws %% pkg_name %% build_dir %% tests %% debug %%
2121+ Cmd.of_list args)
2222+2323+let build () pkg_file pkg_name build_dir dry_run raws tests debug args =
2424+ let pkg = Topkg_care.Pkg.v pkg_file in
2525+ let args = build_args pkg_name build_dir dry_run raws tests debug args in
2626+ let out = OS.Cmd.out_stdout in
2727+ begin
2828+ OS.Dir.current ()
2929+ >>= fun dir -> Topkg_care.Pkg.build pkg ~dir ~args ~out
3030+ >>| (function ((), (_, `Exited 0)) -> 0 | _ -> 1)
3131+ end
3232+ |> Cli.handle_error
3333+3434+(* Command line interface *)
3535+3636+open Cmdliner
3737+3838+let args =
3939+ let doc = "Build configuration. Needs to be specified after a -- token
4040+ so that the command line options do not get interpreted by
4141+ $(b,topkg build) itself."
4242+ in
4343+ Arg.(value & pos_all string [] & info [] ~doc ~docv:"BUILD_CONF")
4444+4545+let pkg_name =
4646+ let doc = "The name $(docv) of the package (and of the opam install file).
4747+ This is equivalent to specify the same option after the -- token.
4848+ If absent provided by the package description."
4949+ in
5050+ let docv = "PKG_NAME" in
5151+ Arg.(value & opt (some string) None & info ["n"; "pkg-name"] ~doc ~docv)
5252+5353+let build_dir =
5454+ let doc = "Specifies the build directory $(docv). This is equivalent to
5555+ specify the same option after the -- token. If absent, provided
5656+ by the package description."
5757+ in
5858+ let docv = "BUILD_DIR" in
5959+ Arg.(value & opt (some Cli.path_arg) None & info ["build-dir"] ~doc ~docv)
6060+6161+let dry_run =
6262+ let doc = "Do not run build instructions, only determine and write the opam
6363+ install file. This is equivalent to specify the same option after
6464+ the -- token."
6565+ in
6666+ Arg.(value & flag & info ["d"; "dry-run"] ~doc)
6767+6868+let raws =
6969+ let doc = "Do not run build instructions or write the opam install file, only
7070+ invoke the build system with the given $(docv) argument."
7171+ in
7272+ Arg.(value & opt_all string [] & info ["r"; "raw"] ~doc ~docv:"ARG")
7373+7474+let tests =
7575+ let doc = "Specifies whether tests should be built. If absent depends on the
7676+ build context, true for development and false otherwise. This is
7777+ equivalent to specify the same option after the -- token."
7878+ in
7979+ Arg.(value & opt (some bool) None & info ["tests"] ~doc ~docv:"BOOL")
8080+8181+let debug =
8282+ let doc = "Debug build. Specifies if debugging information should be
8383+ saved in build artefacts. This is equivalent to specify the
8484+ same option after the -- token."
8585+ in
8686+ let env = Cmd.Env.info "TOPKG_CONF_DEBUG" in
8787+ Arg.(value & opt (some bool) None & info ["debug"] ~env ~doc ~docv:"BOOL")
8888+8989+let doc = "Build the package"
9090+let sdocs = Manpage.s_common_options
9191+let exits = Cmd.Exit.info 1 ~doc:"on build failure." :: Cli.exits
9292+let man_xrefs = [ `Main ]
9393+let man =
9494+ [ `S Manpage.s_synopsis;
9595+ `P "$(mname) $(tname) [$(i,OPTION)]... [-- $(i,BUILD_CONF)...]";
9696+ `S Manpage.s_description;
9797+ `P "The $(tname) command builds the package. This is equivalent to
9898+ invoke:";
9999+ `Pre "ocaml ./pkg/pkg.ml build $(i,BUILD_CONF)..."; ]
100100+101101+let cmd =
102102+ Cmd.v (Cmd.info "build" ~doc ~sdocs ~exits ~man ~man_xrefs) @@
103103+ Term.(const build $ Cli.setup $ Cli.pkg_file $ pkg_name $ build_dir $
104104+ dry_run $ raws $ tests $ debug $ args)
+8
vendor/opam/topkg/src-bin/build.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** The [build] command. *)
77+88+val cmd : int Cmdliner.Cmd.t
+49
vendor/opam/topkg/src-bin/clean.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+open Bos_setup
77+88+(* Command *)
99+1010+let clean_args name build_dir =
1111+ let on_some_use_opt opt to_arg = function
1212+ | None -> Cmd.empty
1313+ | Some value -> Cmd.(v opt % to_arg value)
1414+ in
1515+ let verb = Cli.propagate_verbosity_to_pkg_file () in
1616+ let build_dir = on_some_use_opt "--build-dir" Cmd.p build_dir in
1717+ let name = on_some_use_opt "--pkg-name" (fun n -> n) name in
1818+ Cmd.(verb %% name %% build_dir)
1919+2020+let clean () pkg_file name build_dir =
2121+ let pkg = Topkg_care.Pkg.v ?build_dir ?name pkg_file in
2222+ let args = clean_args name build_dir in
2323+ let out = OS.Cmd.out_stdout in
2424+ begin
2525+ OS.Dir.current ()
2626+ >>= fun dir -> Topkg_care.Pkg.clean pkg ~dir ~args ~out
2727+ >>| (function ((), (_, `Exited 0)) -> 0 | _ -> 1)
2828+ end
2929+ |> Cli.handle_error
3030+3131+(* Command line interface *)
3232+3333+open Cmdliner
3434+3535+let doc = "Clean the package's build"
3636+let sdocs = Manpage.s_common_options
3737+let exits = Cmd.Exit.info 1 ~doc:"on clean failure." :: Cli.exits
3838+let man_xrefs = [`Main; `Cmd "build"]
3939+let man =
4040+ [ `S Manpage.s_synopsis;
4141+ `P "$(mname) $(tname) [$(i,OPTION)]...";
4242+ `S Manpage.s_description;
4343+ `P "The $(tname) command deletes the package's build and its opam
4444+ install file. This is equivalent to invoke:";
4545+ `Pre "ocaml ./pkg/pkg.ml clean";]
4646+4747+let cmd =
4848+ Cmd.v (Cmd.info "clean" ~doc ~sdocs ~exits ~man ~man_xrefs) @@
4949+ Term.(const clean $ Cli.setup $ Cli.pkg_file $ Cli.pkg_name $ Cli.build_dir)
+8
vendor/opam/topkg/src-bin/clean.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** The [clean] command. *)
77+88+val cmd : int Cmdliner.Cmd.t
+176
vendor/opam/topkg/src-bin/cli.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+open Bos_setup
77+open Cmdliner
88+99+(* Converters and arguments *)
1010+1111+let path_arg = Arg.conv Fpath.(of_string, pp)
1212+1313+let pkg_file =
1414+ let doc = "Use $(docv) as the package description file." in
1515+ let docv = "FILE" in
1616+ Arg.(value & opt path_arg (Fpath.v "pkg/pkg.ml") &
1717+ info ["pkg-file"] ~docs:Manpage.s_common_options ~doc ~docv)
1818+1919+let pkg_name =
2020+ let doc = "The name $(docv) of the opam package. If absent provided
2121+ by the package description."
2222+ in
2323+ let docv = "PKG_NAME" in
2424+ Arg.(value & opt (some string) None & info ["n"; "pkg-name"] ~doc ~docv)
2525+2626+let opam =
2727+ let doc = "The opam file to use. If absent uses the default opam file
2828+ mentioned in the package description."
2929+ in
3030+ let docv = "FILE" in
3131+ Arg.(value & opt (some path_arg) None & info ["opam"] ~doc ~docv)
3232+3333+let dist_name =
3434+ let doc = "The name $(docv) of the package to use for the package
3535+ distribution. If absent, provided by the package description."
3636+ in
3737+ let docv = "NAME" in
3838+ Arg.(value & opt (some string) None & info ["dist-name"] ~doc ~docv)
3939+4040+let dist_version =
4141+ let doc = "The version string to use for the package distribution.
4242+ If absent, provided by the VCS tag description of the
4343+ HEAD commit."
4444+ in
4545+ let docv = "VERSION" in
4646+ Arg.(value & opt (some string) None & info ["dist-version"] ~doc ~docv)
4747+4848+let dist_file =
4949+ let doc = "The package distribution archive. If absent the file
5050+ $(i,BUILD_DIR)/$(i,NAME)-$(i,VERSION).tbz (see options
5151+ $(b,--build-dir), $(b,--dist-name) and $(b,--dist-version))."
5252+ in
5353+ let docv = "FILE" in
5454+ Arg.(value & opt (some path_arg) None & info ["dist-file"] ~doc ~docv)
5555+5656+let dist_opam =
5757+ let doc = "opam file to use for the distribution. If absent uses the opam
5858+ file mentioned in the package description that corresponds to
5959+ the distribution package name $(i,NAME) (see option
6060+ $(b,--dist-name))."
6161+ in
6262+ let docv = "FILE" in
6363+ Arg.(value & opt (some path_arg) None & info ["dist-opam"] ~doc ~docv)
6464+6565+let dist_uri =
6666+ let doc = "The distribution archive URI on the WWW. If absent, provided by the
6767+ package description."
6868+ in
6969+ let docv = "URI" in
7070+ Arg.(value & opt (some string) None & info ["dist-uri"] ~doc ~docv)
7171+7272+let readme =
7373+ let doc = "The readme to use. If absent, provided by the package
7474+ description."
7575+ in
7676+ let docv = "FILE" in
7777+ Arg.(value & opt (some path_arg) None & info ["readme"] ~doc ~docv)
7878+7979+let change_log =
8080+ let doc = "The change log to use. If absent, provided by the package
8181+ description."
8282+ in
8383+ let docv = "FILE" in
8484+ Arg.(value & opt (some path_arg) None & info ["change-log"] ~doc ~docv)
8585+8686+let delegate =
8787+ let doc = "The delegate tool $(docv) to use. If absent, see topkg-delegate(7)
8888+ for the lookup procedure."
8989+ in
9090+ let docv = "TOOL" in
9191+ let to_cmd = function None -> None | Some s -> Some (Bos.Cmd.v s) in
9292+ Term.(const to_cmd $
9393+ Arg.(value & opt (some string) None & info ["delegate"] ~doc ~docv))
9494+9595+let build_dir =
9696+ let doc = "Specifies the build directory $(docv). If absent, provided by the
9797+ package description."
9898+ in
9999+ let docv = "BUILD_DIR" in
100100+ Arg.(value & opt (some path_arg) None & info ["build-dir"] ~doc ~docv)
101101+102102+let publish_msg =
103103+ let doc = "The publication message $(docv). Defaults to the change
104104+ log of the last version (see $(b,topkg log -l))."
105105+ in
106106+ let docv = "MSG" in
107107+ Arg.(value & opt (some string) None & info ["m"; "message"] ~doc ~docv)
108108+109109+(* Terms *)
110110+111111+let logs_to_topkg_log_level = function
112112+| None -> None
113113+| Some Logs.App -> Some (Topkg.Log.App)
114114+| Some Logs.Error -> Some (Topkg.Log.Error)
115115+| Some Logs.Warning -> Some (Topkg.Log.Warning)
116116+| Some Logs.Info -> Some (Topkg.Log.Info)
117117+| Some Logs.Debug -> Some (Topkg.Log.Debug)
118118+119119+let setup style_renderer log_level cwd =
120120+ Fmt_tty.setup_std_outputs ?style_renderer ();
121121+ Topkg.Log.set_level (logs_to_topkg_log_level log_level);
122122+ Logs.set_level log_level;
123123+ Logs.set_reporter (Logs_fmt.reporter ~app:Fmt.stdout ());
124124+ Logs.info (fun m -> m "topkg %%VERSION%% running");
125125+ match cwd with
126126+ | None -> `Ok ()
127127+ | Some dir ->
128128+ match OS.Dir.set_current dir with
129129+ | Ok () -> `Ok ()
130130+ | Error (`Msg m) -> `Error (false, m) (* use cmdliner evaluation error *)
131131+132132+let setup =
133133+ let style_renderer =
134134+ let env = Cmd.Env.info "TOPKG_COLOR" in
135135+ Fmt_cli.style_renderer ~docs:Manpage.s_common_options ~env ()
136136+ in
137137+ let log_level =
138138+ let env = Cmd.Env.info "TOPKG_VERBOSITY" in
139139+ Logs_cli.level ~docs:Manpage.s_common_options ~env ()
140140+ in
141141+ let cwd =
142142+ let doc = "Change to directory $(docv) before doing anything." in
143143+ let docv = "DIR" in
144144+ Arg.(value & opt (some path_arg) None & info ["C"; "pkg-dir"]
145145+ ~docs:Manpage.s_common_options ~doc ~docv)
146146+ in
147147+ Term.(ret (const setup $ style_renderer $ log_level $ cwd))
148148+149149+(* Verbosity propagation. *)
150150+151151+let propagate_verbosity_to_pkg_file () = match Logs.level () with
152152+| None -> Bos.Cmd.(v "-q")
153153+| Some Logs.Info -> Bos.Cmd.(v "-v")
154154+| Some Logs.Debug -> Bos.Cmd.(v "-v" % "-v")
155155+| Some _ -> Bos.Cmd.empty
156156+157157+(* Error handling *)
158158+159159+let warn_if_vcs_dirty msg =
160160+ Topkg.Vcs.get ()
161161+ >>= fun repo -> Topkg.Vcs.is_dirty repo
162162+ >>= function
163163+ | false -> Ok ()
164164+ | true ->
165165+ Logs.warn
166166+ (fun m -> m "The repo is %a. %a" Topkg_care.Pp.dirty () Fmt.text msg);
167167+ Ok ()
168168+169169+let handle_error = function
170170+| Ok 0 -> if Logs.err_count () > 0 then 3 else 0
171171+| Ok n -> n
172172+| Error _ as r -> Logs.on_error_msg ~use:(fun _ -> 3) r
173173+174174+let exits =
175175+ Cmd.Exit.info 3 ~doc:"on indiscriminate errors reported on stderr." ::
176176+ Cmd.Exit.defaults
+82
vendor/opam/topkg/src-bin/cli.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** {!Cmdliner} and common definitions for commands. *)
77+88+open Cmdliner
99+open Rresult
1010+1111+(** {1 Converters and options} *)
1212+1313+val path_arg : Fpath.t Arg.conv
1414+(** [path_arg] is a path argument converter. *)
1515+1616+val pkg_file : Fpath.t Term.t
1717+(** A [--pkg-file] option to specify the package description file to use. *)
1818+1919+val pkg_name : string option Term.t
2020+(** A [--pkg-name] option to specify the opam package name. *)
2121+2222+val opam : Fpath.t option Term.t
2323+(** An [--opam] option for defining an opam file. *)
2424+2525+val dist_name : string option Term.t
2626+(** A [--dist-name] option to define the package name of the distribution. *)
2727+2828+val dist_version : string option Term.t
2929+(** A [--dist-version] option to define the package version. *)
3030+3131+val dist_file : Fpath.t option Term.t
3232+(** A [--dist-file] option to define the distribution archive file. *)
3333+3434+val dist_uri : string option Term.t
3535+(** A [--dist-uri] option to define the distribution archive URI on the WWW. *)
3636+3737+val dist_opam : Fpath.t option Term.t
3838+(** An [--dist-opam] option to define the opam file. *)
3939+4040+val readme : Fpath.t option Term.t
4141+(** A [--readme] option to define the readme. *)
4242+4343+val change_log : Fpath.t option Term.t
4444+(** A [--change-log] option to define the change log. *)
4545+4646+val opam : Fpath.t option Term.t
4747+(** An [--opam] option to define an opam file. *)
4848+4949+val delegate : Bos.Cmd.t option Term.t
5050+(** A [--delegate] option to define the delegate. *)
5151+5252+val build_dir : Fpath.t option Term.t
5353+(** A [--build-dir] option to define the build directory. *)
5454+5555+val publish_msg : string option Term.t
5656+(** A [--msg] option to define a publication message. *)
5757+5858+(** {1 Terms} *)
5959+6060+val setup : unit Term.t
6161+(** [setup env] defines a basic setup common to all commands. The
6262+ setup does, by side effect, set {!Logs} log verbosity, adjusts
6363+ colored output and sets the current working directory. *)
6464+6565+(** {1 Verbosity propagation} *)
6666+6767+val propagate_verbosity_to_pkg_file : unit -> Bos.Cmd.t
6868+(** [propagate_verbosity_to_pkg_file ()] is
6969+ a command line fragment that has the option to propagate
7070+ the current log verbosity to an invocation of the package
7171+ description. *)
7272+7373+(** {1 Warnings and errors} *)
7474+7575+val warn_if_vcs_dirty : string -> (unit, R.msg) result
7676+(** [warn_if_vcs_dirty msg] warns with [msg] if the VCS is dirty. *)
7777+7878+val handle_error : (int, R.msg) result -> int
7979+(** [handle_error r] is [r]'s result or logs [r]'s error and returns [3]. *)
8080+8181+val exits : Cmd.Exit.info list
8282+(** [exits] is are the exit codes common to all commands. *)
+164
vendor/opam/topkg/src-bin/distrib.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+open Bos_setup
77+88+let lint_distrib pkg ~dir =
99+ Logs.app (fun m -> m "@.Linting distrib in %a" Fpath.pp dir);
1010+ Topkg_care.Pkg.lint pkg ~dir Topkg_care.Pkg.lint_all
1111+1212+let build_distrib pkg ~dir skip_tests =
1313+ Logs.app (fun m -> m "@.Building package in %a" Fpath.pp dir);
1414+ let tests = if skip_tests then Cmd.empty else Cmd.(v "--tests" % "true") in
1515+ let args = Cmd.(v "--dev-pkg" % "false" % "--vcs" % "false" %% tests) in
1616+ let out = OS.Cmd.out_string in
1717+ Topkg_care.Pkg.build pkg ~dir ~args ~out >>= function
1818+ | (_, (_, `Exited 0)) ->
1919+ Logs.app (fun m -> m "%a package builds" Topkg_care.Pp.status `Ok); Ok 0
2020+ | (stdout, _) ->
2121+ Logs.app (fun m -> m "%s@\n%a package builds"
2222+ stdout Topkg_care.Pp.status `Fail); Ok 1
2323+2424+let test_distrib pkg ~dir =
2525+ Logs.app (fun m -> m "@.Running package tests in %a" Fpath.pp dir);
2626+ let out = OS.Cmd.out_string in
2727+ Topkg_care.Pkg.test pkg ~dir ~args:Cmd.empty ~out >>= function
2828+ | (_, (_, `Exited 0)) ->
2929+ Logs.app (fun m -> m "%a package tests"
3030+ Topkg_care.Pp.status `Ok); Ok 0
3131+ | (stdout, _) ->
3232+ Logs.app (fun m -> m "%s@\n%a package tests"
3333+ stdout Topkg_care.Pp.status `Fail); Ok 1
3434+3535+let check_archive pkg ar ~skip_lint ~skip_build ~skip_tests =
3636+ Topkg_care.Archive.untbz ~clean:true ar
3737+ >>= fun dir -> (if skip_lint then Ok 0 else lint_distrib pkg ~dir)
3838+ >>= fun c0 -> (if skip_build then Ok 0 else build_distrib pkg ~dir skip_tests)
3939+ >>= fun c1 -> (if skip_tests || skip_build then Ok 0 else
4040+ test_distrib pkg ~dir)
4141+ >>= fun c2 -> match c0 + c1 + c2 with
4242+ | 0 -> OS.Dir.delete ~recurse:true dir >>= fun () -> Ok 0
4343+ | n -> Ok 1
4444+4545+let warn_if_vcs_dirty ()=
4646+ Cli.warn_if_vcs_dirty "The distribution archive may be inconsistent."
4747+4848+let log_footprint pkg archive =
4949+ Topkg_care.Pkg.name pkg
5050+ >>= fun name -> Topkg_care.Pkg.version pkg
5151+ >>= fun version -> Topkg.Vcs.get ()
5252+ >>= fun repo -> Topkg.Vcs.commit_id repo ~dirty:false ~commit_ish:"HEAD"
5353+ >>= fun commit_ish ->
5454+ Logs.app
5555+ (fun m -> m "@.@[<v>@[Distribution for %a@ %a@]@,@[Commit %a@]@,\
5656+ @[Archive %a@]@]"
5757+ Topkg_care.Pp.name name Topkg_care.Pp.version version
5858+ Topkg_care.Pp.commit commit_ish Topkg_care.Pp.path archive);
5959+ Ok ()
6060+6161+let log_wrote_archive ar =
6262+ Logs.app (fun m -> m "Wrote archive %a" Topkg_care.Pp.path ar); Ok ()
6363+6464+let distrib
6565+ () pkg_file opam build_dir name version keep_dir skip_lint skip_build
6666+ skip_tests
6767+ =
6868+ begin
6969+ let pkg = Topkg_care.Pkg.v ?name ?version ?build_dir ?opam pkg_file in
7070+ Topkg_care.Pkg.distrib_archive pkg ~keep_dir
7171+ >>= fun ar -> log_wrote_archive ar
7272+ >>= fun () -> check_archive pkg ar ~skip_lint ~skip_build ~skip_tests
7373+ >>= fun errs -> log_footprint pkg ar
7474+ >>= fun () -> warn_if_vcs_dirty ()
7575+ >>= fun () -> Ok errs
7676+ end
7777+ |> Cli.handle_error
7878+7979+(* Command line interface *)
8080+8181+open Cmdliner
8282+8383+let keep_build_dir =
8484+ let doc = "Keep the distribution build directory after successful archival."
8585+ in
8686+ Arg.(value & flag & info ["keep-build-dir"] ~doc)
8787+8888+let skip_lint =
8989+ let doc = "Do not lint the archive distribution." in
9090+ Arg.(value & flag & info ["skip-lint"] ~doc)
9191+9292+let skip_build =
9393+ let doc = "Do not try to build the package from the archive." in
9494+ Arg.(value & flag & info ["skip-build"] ~doc)
9595+9696+let skip_tests =
9797+ let doc = "Do not try to build and run the package tests from the archive.
9898+ Implied by $(b,--skip-build)."
9999+ in
100100+ Arg.(value & flag & info ["skip-tests"] ~doc)
101101+102102+let doc = "Create a package distribution archive"
103103+let sdocs = Manpage.s_common_options
104104+let exits = Cli.exits
105105+let envs =
106106+ [ Cmd.Env.info "TOPKG_BZIP2" ~doc:"The $(b,bzip2) tool to use to compress the
107107+ archive. Gets the archive on stdin and must output the result on
108108+ standard out.";
109109+ Cmd.Env.info "TOPKG_TAR" ~doc:"The $(b,tar) tool to use to unarchive a tbz
110110+ archive (archive creation itself is handled by topkg)."; ]
111111+112112+let man_xrefs = [ `Main ]
113113+let man =
114114+ [ `S Manpage.s_description;
115115+ `P "The $(tname) command creates a package distribution
116116+ archive in the build directory of the package. The generated
117117+ archive should be bit-wise reproducible. There are however a few
118118+ caveats, see the section about this further down.";
119119+ `P "More detailed information about the archive creation process and its
120120+ customization can be found in topkg's API documentation.";
121121+ `P "Once the archive is created it is unpacked in the build directory,
122122+ linted and the package is built using the package description
123123+ contained in the archive. The build will use the default package
124124+ configuration so it may fail in the current environment
125125+ without this necessarily implying an actual problem with the
126126+ distribution; one should still worry about it though.
127127+ These checks can be prevented by using the $(b,--skip-lint) and
128128+ $(b,--skip-build) options.";
129129+ `S "REPRODUCIBLE DISTRIBUTION ARCHIVES";
130130+ `P "Given the package name, the HEAD commit identifier
131131+ and the version string, the $(tname) command should always
132132+ generate the same archive.";
133133+ `P "More precisely, files are added to the archive using a well
134134+ defined order on path names. Their file permissions are either
135135+ 0o775 for directories and files that are executable for the user
136136+ in the HEAD repository checkout or 0o664 for those that are not.
137137+ Their modification times are set to the commit date (note that if
138138+ git is used, git-log(1) shows the author date which may not
139139+ coincide). No other file metadata is recorded.";
140140+ `P "This should ensure that the resulting archive is bit-wise
141141+ identical regardless of the context in which it is
142142+ created. However this may fail for one or more of the
143143+ following reasons:";
144144+ `I ("Non-reproducible distribution massage", "The package
145145+ distribution massaging hook relies on external factors
146146+ that are not captured by the source repository checkout.
147147+ For example external data files, environment variables, etc.");
148148+ `I ("File paths with non US-ASCII characters",
149149+ "If these paths are encoded in UTF-8, different file systems
150150+ may return the paths with different Unicode normalization
151151+ forms which could yield different byte serializations in the
152152+ archive (note that this could be lifted at the cost of a
153153+ dependency on Uunf).");
154154+ `I ("The bzip2 utility", "The archive is compressed using the bzip2 utility.
155155+ Reproducibility relies on bzip2 to be a reproducible function
156156+ across platforms.");
157157+ `I ("Topkg changes", "Topkg could change its distribution procedure in
158158+ the future, for example to correct bugs."); ]
159159+160160+let cmd =
161161+ Cmd.v (Cmd.info "distrib" ~doc ~sdocs ~exits ~envs ~man ~man_xrefs) @@
162162+ Term.(const distrib $ Cli.setup $ Cli.pkg_file $ Cli.dist_opam $
163163+ Cli.build_dir $ Cli.dist_name $ Cli.dist_version $ keep_build_dir $
164164+ skip_lint $ skip_build $ skip_tests)
+8
vendor/opam/topkg/src-bin/distrib.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** The [distrib] command. *)
77+88+val cmd : int Cmdliner.Cmd.t
+131
vendor/opam/topkg/src-bin/doc.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+open Bos_setup
77+88+let unixy_path p =
99+ (* ocamlbuild doesn't like Windows paths it seems. Try to do our best here. *)
1010+ let volume, p = Fpath.split_volume p in
1111+ volume ^ (String.concat ~sep:"/" (Fpath.segs p))
1212+1313+let copy_assets src_dir dst_dir =
1414+ let copy_asset dst_dir file = match Fpath.get_ext file with
1515+ | ".css" | ".svg" | ".svgz" | ".png" | ".jpeg" | ".gif" | ".woff" | ".ttf"
1616+ | ".otf" | ".eot" ->
1717+ begin OS.File.exists file >>= function
1818+ | false -> Ok ()
1919+ | true ->
2020+ OS.File.read file
2121+ >>= fun cont -> OS.File.write Fpath.(dst_dir / filename file) cont
2222+ end
2323+ |> Logs.on_error_msg ~use:(fun () -> ())
2424+ | _ -> ()
2525+ in
2626+ OS.Dir.exists src_dir >>= function
2727+ | false -> Ok ()
2828+ | true ->
2929+ OS.Dir.contents src_dir
3030+ >>= fun files -> List.iter (copy_asset dst_dir) files; Ok ()
3131+3232+let copy_odig_css doc_dir dst_dir =
3333+ OS.File.exists Fpath.(doc_dir / "style.css") >>= function
3434+ | true -> Ok ()
3535+ | false ->
3636+ let get_odig_etc = Cmd.(v "opam" % "var" % "odig:etc") in
3737+ OS.Cmd.(run_out get_odig_etc |> to_string) >>= function
3838+ | "#undefined" (* no comment *) -> Ok ()
3939+ | etcdir ->
4040+ Fpath.of_string etcdir >>= fun etcdir ->
4141+ OS.File.read Fpath.(etcdir / "ocamldoc.css")
4242+ >>= fun cont -> OS.File.write Fpath.(dst_dir / "style.css") cont
4343+4444+let doc_build_args pkg_name build_dir dev target =
4545+ let verb = Cli.propagate_verbosity_to_pkg_file () in
4646+ let pkg_name = Cmd.(v "--pkg-name" % pkg_name) in
4747+ let build_dir = Cmd.(v "--build-dir" % Cmd.p build_dir) in
4848+ let target = unixy_path target in
4949+ let doc_flags = ["-docflags"; "-colorize-code,-charset,utf-8"; target ] in
5050+ let raws = Cmd.of_list ~slip:"--raw" doc_flags in
5151+ Cmd.(verb %% pkg_name %% build_dir %% raws)
5252+5353+let build_doc pkg pkg_name build_dir dev =
5454+ let out = OS.Cmd.to_stdout in
5555+ let doc_dir = Fpath.v "doc" in
5656+ let odocl = Fpath.(doc_dir / (if dev then "dev.odocl" else "api.odocl")) in
5757+ Ok Fpath.(set_ext ".docdir" odocl / "index.html")
5858+ >>= fun target -> Ok (doc_build_args pkg_name build_dir dev target)
5959+ >>= fun args -> OS.Dir.current ()
6060+ >>= fun dir -> Topkg_care.Pkg.build pkg ~dir ~args ~out
6161+ >>= fun () -> Ok Fpath.(build_dir // parent target)
6262+ >>= fun dst_dir -> copy_assets doc_dir dst_dir
6363+ >>= fun () -> copy_odig_css doc_dir dst_dir
6464+ >>= fun () -> Ok dst_dir
6565+6666+let browser_reload reload ~background ~browser dir =
6767+ OS.Dir.current ()
6868+ >>= fun cwd -> Ok Fpath.(cwd // dir)
6969+ >>= fun abs_dir -> match not (reload || background) with
7070+ | true -> Ok abs_dir
7171+ | false ->
7272+ let uri = strf "file://%s" Fpath.(to_string abs_dir) in
7373+ Webbrowser.reload ~background ~prefix:true ?browser uri
7474+ >>= fun () -> Ok abs_dir
7575+7676+let doc_cmd () pkg_file name build_dir dev reload background browser =
7777+ begin
7878+ let pkg = Topkg_care.Pkg.v ?build_dir ?name pkg_file in
7979+ Topkg_care.Pkg.name pkg
8080+ >>= fun pkg_name -> Topkg_care.Pkg.build_dir pkg
8181+ >>= fun build_dir -> build_doc pkg pkg_name build_dir dev
8282+ >>= fun docdir -> browser_reload reload ~background ~browser docdir
8383+ >>= fun abs_docdir ->
8484+ Logs.app (fun m ->
8585+ m "Generated %s doc in %a"
8686+ (if dev then "dev" else "API") Topkg_care.Pp.path abs_docdir);
8787+ Ok 0
8888+ end
8989+ |> Cli.handle_error
9090+9191+(* Command line interface *)
9292+9393+open Cmdliner
9494+9595+let reload_browser =
9696+ let doc = "Open an URI of the documentation directory or reload an
9797+ existing browser tab that holds a sub-page of the documentation."
9898+ in
9999+ Arg.(value & flag & info ["r"; "reload-browser"] ~doc)
100100+101101+let dev =
102102+ let doc = "Build the development documentation." in
103103+ Arg.(value & flag & info ["d"; "dev"] ~doc)
104104+105105+let doc = "Build the package's API documentation"
106106+let sdocs = Manpage.s_common_options
107107+let exits = Cli.exits
108108+let man_xrefs = [ `Main ]
109109+let man =
110110+ [ `S Manpage.s_description;
111111+ `P "The $(tname) command builds the package's API documentation. Use
112112+ the option $(b,-r) to open or refresh the documentation in
113113+ a WWW browser (see $(b,--browser) for details).";
114114+ `P "$(b,WARNING.) The way this command works is at the
115115+ moment very ad-hoc and ocamlbuild specific. It will
116116+ change in the future.";
117117+ `P "Current support relies on having a doc/ directory at the root of the
118118+ distribution. The ocamlbuild file doc/api.odocl defines the API
119119+ documentation and the doc/dev.odocl the development documentation.
120120+ The directory can also hold CSS, PNG, JPEG, GIF, SVG, WOFF, TTF, OTF
121121+ files that are copied over to the generated documentation directory.";
122122+ `P "The package's build system is invoked via `--raw` with the
123123+ ocamlbuild documentation targets.";
124124+ `P "If the doc/ directory has no doc/style.css file but odig(1) is
125125+ installed, its ocamldoc stylesheet is used." ]
126126+127127+let cmd =
128128+ Cmd.v (Cmd.info "doc" ~doc ~sdocs ~exits ~man ~man_xrefs) @@
129129+ Term.(const doc_cmd $ Cli.setup $ Cli.pkg_file $ Cli.pkg_name $ Cli.build_dir
130130+ $ dev $ reload_browser $ Webbrowser_cli.background $
131131+ Webbrowser_cli.browser)
+8
vendor/opam/topkg/src-bin/doc.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** The [doc] command. *)
77+88+val cmd : int Cmdliner.Cmd.t
+378
vendor/opam/topkg/src-bin/help.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+let topkg_manual = "Topkg manual"
77+let version = "%%VERSION%%"
88+99+(* Help manuals *)
1010+1111+open Cmdliner
1212+1313+let see_also ~cmds =
1414+ let cmds = (Astring.String.concat ~sep:"(1), " ("topkg" :: cmds)) ^ "(1)" in
1515+ [ `S Manpage.s_see_also; `P cmds ]
1616+1717+let release =
1818+ ("TOPKG-RELEASE", 7, "", version, topkg_manual),
1919+ [ `S Manpage.s_name;
2020+ `P "topkg-release - How to release a (topkg) package";
2121+ `S Manpage.s_description;
2222+ `P "The basic release script is the following. Each step is
2323+ refined and explained with more details below.";
2424+ `Pre "\
2525+topkg browse issues # Review remaining outstanding issues
2626+topkg status # Review the changes since last version
2727+topkg log edit # Write the release notes
2828+topkg log commit # Commit the release notes
2929+topkg tag # Tag the distribution with a version
3030+topkg distrib # Create the distribution archive
3131+topkg publish # Publish it on the WWW with its documentation
3232+topkg opam pkg # Create an opam package
3333+topkg opam submit # Submit it to OCaml's opam repository";
3434+ `P "The last four steps can be performed via a single invocation
3535+ to topkg-bistro(1).";
3636+ `S "BASIC CHECKS";
3737+ `P "First have a look at the outstanding issues the package may have
3838+ by checking the issue tracker.";
3939+ `Pre "topkg browse issues";
4040+ `P "If the package's delegate supports issue tracker interaction
4141+ (see topkg-delegate(7)), you can consult them directly in the
4242+ terminal with:";
4343+ `Pre "topkg issue list";
4444+ `P "Basic checks are performed on the distribution archive when it is
4545+ created, but save time by catching errors early. Hence test that
4646+ your source repository lints and that it builds in the current build
4747+ environment and that the package tests pass.";
4848+ `Pre "\
4949+topkg lint
5050+topkg build # Check out the generated opam install file too
5151+topkg test";
5252+ `S "WRITE THE RELEASE NOTES";
5353+ `P "Carefully write the release notes in the package's change log, these
5454+ are essential time savers for users of the package. It may help to
5555+ consult the list of changes that were committed since the last VCS
5656+ version tag with:";
5757+ `Pre "topkg status";
5858+ `P "You can then write the release notes and commit them to the VCS with:";
5959+ `Pre "\
6060+topkg log edit
6161+topkg log commit";
6262+ `P "The next step is simplified if the change log follows a certain
6363+ format, see topkg-log(1) for details.";
6464+ `P "The last two commands mentioned perform no magic, it is entirely up
6565+ to you to use them or not. The first one simply opens the change log
6666+ of the package in your \\$EDITOR and the second one commits it to
6767+ your VCS with a dull, canned, commit message.";
6868+ `S "VCS TAG THE RELEASE";
6969+ `P "Here again topkg provides a magic-less command that will simply
7070+ extract the latest version tag from the package's change log
7171+ and tag the VCS HEAD commit with it:";
7272+ `Pre "topkg tag";
7373+ `P "This will only work if the change log follows a certain format,
7474+ see tokpg-log(1) for details. You can check the extracted tag is
7575+ the one you wish before with:";
7676+ `Pre "topkg log -t";
7777+ `P "If you do not want to rely on topkg's broken extraction algorithms
7878+ just specify it on the command line:";
7979+ `Pre "topkg tag v1.0.1";
8080+ `P "And if you really think topkg does a bad job at this, simply
8181+ use your VCS directly to tag a release.";
8282+ `S "CREATE THE DISTRIBUTION ARCHIVE AND PUBLISH IT";
8383+ `P "Now that the release is tagged in your VCS, generate a distribution
8484+ archive for it in the build directory with:";
8585+ `Pre "topkg distrib";
8686+ `P "This uses the source tree of the HEAD commit for creating a
8787+ distribution in the build directory. The distribution version
8888+ string is the VCS tag description (e.g. git-describe(1)) of
8989+ the HEAD commit. Alternatively it can be specified on the command
9090+ line.";
9191+ `P "If everything went well you can now publish the distribution and
9292+ its documentation on the WWW. The exact actions that happen here
9393+ depend on the package's delegate, see topkg-delegate(7) for more
9494+ details.";
9595+ `Pre "topkg publish";
9696+ `P "The distribution is now public. It may already have been picked up
9797+ by other systems hence do not try to alter the archive and
9898+ republish it with a different bit-stream after that point (if
9999+ you are tempted to do this please consider taking a functional
100100+ programming course). At worst 410 the archive from
101101+ the WWW. But in most cases, if there is a problem with the
102102+ archive, simply leave it there and publish a new one with an
103103+ increased patch version number.";
104104+ `S "SUBMIT TO OCAML'S OPAM REPOSITORY";
105105+ `P "The following steps still need the distribution archive created in
106106+ the preceeding step to be in the build directory. If that's no
107107+ longer the case but nothing moved in your VCS, you can simply
108108+ invoke $(b,topkg distrib), it should produce a bit-wise identical
109109+ archive. If the VCS moved checkout the distribution commit to
110110+ regenerate the archive or provide, in the subsequent commands,
111111+ the archive manually via the $(b,--dist-file) option, see
112112+ topkg-opam(1) for details.";
113113+ `P "To add the package to OCaml's opam repository, start by creating an
114114+ opam package description in the build directory with:";
115115+ `Pre "topkg opam pkg";
116116+ `P "then simply submit it to the opam repository with:";
117117+ `Pre "topkg opam submit";
118118+ `P "The latter does nothing more than invoking opam-publish-submit(1) on
119119+ the package description generated earlier.";
120120+ `P "Congratulations. You are done. Ditch your computer.";
121121+ `S "TROUBLESHOOTING";
122122+ `P "Here are a few troubleshooting scenarios and possible resolution.";
123123+ `I ("Before publishing",
124124+ "Anything that happens before the $(b,topkg publish) step,
125125+ like a failing $(b,topkg distrib), is easy to resolve. Delete the
126126+ version tag of your VCS, a $(b,topkg tag -d) will do, add
127127+ some commits, adjust your release notes and start over.");
128128+ `I ("opam submission build failure",
129129+ "If the build failure is due to a missing constraint, follow the
130130+ instruction of the next item to correct the opam file. If the failure
131131+ is due to a defect in the distribution archive, call it a day and
132132+ start over with a patch version release that corrects the problem.
133133+ Do not try to reuse the version string of the failed release, other
134134+ systems may already have picked up the broken archive.");
135135+ `I ("opam repository maintainer and robot complaints",
136136+ "These pesky but loved maintainers and robots... If they
137137+ complain about certain aspects of your opam submission, you can either
138138+ try to correct it manually from the opam package description found
139139+ in the build directory and reinvoke $(b,topkg opam submit) or edit
140140+ the opam file of the source repository and regenerate the opam Package
141141+ description with $(b,topkg opam pkg) and the $(b,--pkg-opam)
142142+ option. Note that if the VCS moved meanwhile you may have to use
143143+ the various command line options of topkg-opam(1) to make sure
144144+ you point to the right package version and distribution archive.
145145+ In either case you should be aware that there will be a mismatch
146146+ between the opam file in the distribution archive and the one
147147+ you submitted to the opam repository. If this happens to be a
148148+ problem, start over with a new patch version release.");
149149+ `Blocks (see_also ~cmds:[]); ]
150150+151151+let delegate =
152152+ ("TOPKG-DELEGATE", 7, "", version, topkg_manual),
153153+ [ `S Manpage.s_name;
154154+ `P "topkg-delegate - The topkg delegate";
155155+ `S Manpage.s_description;
156156+ `P "The delegate of a package is a program invoked by topkg to perform
157157+ actions that are difficult or not desirable to standardize within
158158+ topkg itself, namely:";
159159+ `I ("$(b,topkg publish)",
160160+ "Publish distribution archives and derived artefacts.");
161161+ `I ("$(b,topkg issue)", "Interact with the package's issue tracker.");
162162+ `P "A sample delegate is provided at the end of this man page.";
163163+ `S "DELEGATE LOOKUP PROCEDURE";
164164+ `P "The delegate used by a package is defined by the first match in the
165165+ following lookup procedure.";
166166+ `I ("1. Command line", "Specified with the $(b,--delegate) option on
167167+ the command line.");
168168+ `I ("2. Package description.", "Specified in the package description file,
169169+ see topkg's API documentation.");
170170+ `I ("3. Environment variable.", "Specified in the TOPKG_DELEGATE
171171+ environment variable.");
172172+ `I ("4. Homepage derived discovery.", "Consult the 'homepage' field of the
173173+ package's opam file, extract the second-level domain of the URI as
174174+ \\$NAME and uses the tool $(b,\\$NAME-topkg-delegate) iff it exists
175175+ in the executable search path. For example if the homepage is
176176+ http://www.example.org/mypackage, an existing
177177+ $(b,example-topkg-delegate) tool will be used.");
178178+ `I ("5. Transitory toy github fallback.", "If the previous step yields
179179+ $(b,github-topkg-delegate) but that it doesn't exist in the
180180+ executable search path. The $(b,toy-github-topkg-delegate) tool
181181+ distributed with topkg-care is used. This tool will disappear in
182182+ the future whenever a good github delegate emerges. Consult
183183+ $(b,toy-github-topkg-delegate --help) for more information.");
184184+ `S "DELEGATE PROTOCOL";
185185+ `P "The delegate is invoked by $(b,topkg) with a request in order to
186186+ finish its own execution. This means that the delegate takes over
187187+ $(b,topkg)'s standard channels and is in charge until the end of
188188+ execution (except on errors, see below).";
189189+ `P "The delegate always gets information as command line arguments with
190190+ file paths arguments given as absolute paths. The first argument is
191191+ always 'ipc' and is followed by a verbosity parameter:";
192192+ `P "my-topkg-delegate ipc $(i,VERB) $(i,ARG) ...";
193193+ `P "$(i,VERB) will be either `quiet', `error', `warning', `info' or
194194+ `debug' and the delegate must adjust its logging level appropriately.
195195+ The remaining arguments are the request, see below for requests
196196+ made by $(b,topkg).";
197197+ `P "The delegate must always exit with one of the following exit codes:";
198198+ `I ("0", "The request is successful."); `Noblank;
199199+ `I ("1", "The request is unsupported."); `Noblank;
200200+ `I ("2", "The request errored.");
201201+ `P "Exit 0 must be returned iff the request could be fulfilled according
202202+ to its semantics.";
203203+ `P "Exit 1 must be returned iff the request arguments cannot be
204204+ understood or if the request is not implemented by the delegate.
205205+ In this case the delegate must produce no output.";
206206+ `P "Exit 2 must be returned iff the request could not be fulfilled
207207+ according to its semantics. In this case it is the delegate's duty
208208+ to provide good error messages for diagnosis on standard output";
209209+ `P "In both non-zero exit codes, it is not the delegate's duty to
210210+ try to save request data. In these cases $(b,topkg) will take over
211211+ again in order to prevent user input data loss. This
212212+ occurs for example on issue creation, so that the issue
213213+ description the user may have input interactively is not
214214+ lost but \"saved\" to standard output.";
215215+ `S "PUBLISH DELEGATION";
216216+ `P "Publish delegation requests have the form:";
217217+ `P "publish $(i,ACTION) $(i,ARG)...";
218218+ `P "The following actions are currently defined.";
219219+ `I ("publish distrib $(i,DISTRIB_URI) $(i,NAME) $(i,VERSION)
220220+ $(i,MSG) $(i,ARCHIVE)",
221221+ "Publish the distribution archive file $(i,ARCHIVE) for the package
222222+ named $(i,NAME) at version $(i,VERSION) with publication
223223+ message $(i,MSG). See topkg API's documentation
224224+ for information about the value of $(i,DISTRIB_URI).");
225225+ `I ("publish doc $(i,DOC_URI) $(i,NAME) $(i,VERSION) $(i,MSG) $(i,DOCDIR)",
226226+ "Publish the documentation directory $(i,DOCDIR) for the package
227227+ named $(i,NAME) at version $(i,VERSION) with publication message
228228+ $(i,MSG). $(i,DOC_URI) has the value of the doc field of the
229229+ package's opam file.");
230230+ `I ("publish alt $(i,DISTRIB_URI) $(i,KIND) $(i,NAME) $(i,VERSION)
231231+ $(i,MSG) $(i,ARCHIVE)",
232232+ "Alternative publication artefact named $(i,KIND). The semantics
233233+ of the action is left to the delegate. The request arguments
234234+ are the same as those of the distrib action.");
235235+ `S "ISSUE DELEGATION";
236236+ `P "Issue delegation requests have the form:";
237237+ `P "issue $(i,ACTION) $(i,ISSUES_URI) $(i,ARG) ...";
238238+ `P "with $(i,ISSUES_URI) the value of the bug-reports field of the
239239+ package's opam file or \"\" if there is no such field.";
240240+ `P "The following actions are currently defined.";
241241+ `I ("issue list $(i,ISSUES_URI)",
242242+ "List open issues on standard output. Each issue should be on its
243243+ own line with the format '$(i,ID) $(i,TITLE)'.");
244244+ `I ("issue show $(i,ISSUES_URI) $(i,ID)",
245245+ "Show details about issue $(i,ID) on standard output.");
246246+ `I ("issue open $(i,ISSUES_URI) $(i,TITLE) $(i,MSG)",
247247+ "Create an issue with title $(i,TITLE) and description $(i,MSG)
248248+ (may be an empty string). If the request is successful the
249249+ delegate should communicate the resulting issue identifier in some
250250+ way on standard output.");
251251+ `I ("issue close $(i,ID) $(i,MSG)",
252252+ "Close issue $(i,ID) with closing message $(i,MSG).");
253253+ `S "SAMPLE UNSUPPORTIVE DELEGATE";
254254+ `P "This delegate script can be used as a blueprint. All requests
255255+ are simply unsupported.";
256256+`Pre "\
257257+#!/usr/bin/env ocaml
258258+#use \"topfind\"
259259+#require \"bos.setup\"
260260+open Bos_setup
261261+262262+let unsupported = Ok 1
263263+264264+let publish = function
265265+| \"distrib\" :: uri :: name :: version :: msg :: archive :: _ ->
266266+ unsupported
267267+| \"doc\" :: uri :: name :: version :: msg :: docdir :: _ ->
268268+ unsupported
269269+| \"alt\" :: kind :: uri :: name :: version :: msg :: archive :: _ ->
270270+ unsupported
271271+| args ->
272272+ unsupported
273273+274274+let issue = function
275275+| \"list\" :: uri :: _ -> unsupported
276276+| \"show\" :: uri :: id :: _ -> unsupported
277277+| \"open\" :: uri :: title :: descr :: _ -> unsupported
278278+| \"close\" :: uri :: id :: msg :: _ -> unsupported
279279+| args -> unsupported
280280+281281+let request = function
282282+| \"publish\" :: args -> publish args
283283+| \"issue\" :: args -> issue args
284284+| args -> unsupported
285285+286286+let main () =
287287+ let doc = \"the unsupportive delegate\" in
288288+ begin match OS.Arg.(parse ~doc ~pos:string ()) with
289289+ | \"ipc\" :: verbosity :: req ->
290290+ Logs.level_of_string verbosity
291291+ >>= fun level -> Logs.set_level level; request req
292292+ | \"ipc\" :: [] ->
293293+ R.error_msg \"malformed delegate request, verbosity is missing\"
294294+ | args ->
295295+ R.error_msgf \"unknown arguments: %s\" (String.concat ~sep:\" \" args)
296296+ end
297297+ |> Logs.on_error_msg ~use:(fun () -> 2)
298298+299299+let () = exit (main ())
300300+";
301301+ `Blocks (see_also ~cmds:["topkg-issue"; "topkg-publish"]); ]
302302+303303+let troubleshoot =
304304+ ("TOPKG-TROUBLESHOOT", 7, "", version, topkg_manual),
305305+ [ `S Manpage.s_name;
306306+ `P "topkg-troubleshoot - A few troubleshooting tips";
307307+ `S Manpage.s_description;
308308+ `P "If you get into trouble try the following to get a better undersanding
309309+ of what is happening.";
310310+ `S "ASK FOR MORE LOGGING";
311311+ `P "Invoke $(b,topkg) with $(b,-v), $(b,-v -v), or use the
312312+ TOPKG_VERBOSITY environment variable; see the $(b,--verbosity)
313313+ option.";
314314+ `P "Messages comming from the $(b,topkg) tool are prefixed
315315+ by 'topkg:' while those comming from the package description are
316316+ prefixed by its base name, usually 'pkg.ml:'.";
317317+ `S "DEBUG THE GENERATED OPAM INSTALL FILE";
318318+ `P "To debug the generated opam install file according to the build
319319+ configuration you don't need to build the package. Use the
320320+ $(b,--dry-run) (or $(b,-d)) option and add a little bit of logging to
321321+ output the build configuration that was determined:";
322322+ `Pre "pkg/pkg.ml build -d -v [OPTION]...";`Noblank;
323323+ `Pre "topkg build -d -v [OPTION]... # mostly equivalent";
324324+ `S "DEBUG DEV PACKAGE INSTALLS";
325325+ `P "If you need more information about what happens when dev packages
326326+ are installed (VCS pins or VCS packages) in opam, for example the
327327+ actual watermark values, invoke opam as follows:";
328328+ `P "TOPKG_VERBOSITY=debug opam upgrade mypkg -v";
329329+ `S "RELEASE PROCESS TROUBLES";
330330+ `P "See the TROUBLESHOOTING section of topkg-release(7).";
331331+ `Blocks (see_also ~cmds:[]) ]
332332+333333+(* Help command *)
334334+335335+let pages =
336336+ [ "release", release;
337337+ "delegate", delegate;
338338+ "troubleshoot", troubleshoot; ]
339339+340340+let help man_format topic commands = match topic with
341341+| None -> `Help (man_format, None)
342342+| Some topic ->
343343+ let topics = "topics" :: commands @ (List.map fst pages) in
344344+ let topics = List.sort compare topics in
345345+ let conv =
346346+ Cmdliner.Arg.(conv_parser (enum (List.rev_map (fun s -> (s, s)) topics)))
347347+ in
348348+ match conv topic with
349349+ | Error (`Msg e) -> `Error (false, e)
350350+ | Ok t when List.mem t commands -> `Help (man_format, Some t)
351351+ | Ok t when t = "topics" ->
352352+ Fmt.pr "@[<v>%a@]@." Fmt.(list string) topics;
353353+ `Ok 0
354354+ | Ok t ->
355355+ let man = try List.assoc t pages with Not_found -> assert false in
356356+ Fmt.pr "%a" (Cmdliner.Manpage.print man_format) man;
357357+ `Ok 0
358358+359359+(* Command line interface *)
360360+361361+open Cmdliner
362362+363363+let topic =
364364+ let doc = "The topic to get help on, `topics' lists the topic." in
365365+ Arg.(value & pos 0 (some string) None & info [] ~docv:"TOPIC" ~doc)
366366+367367+let doc = "Show help about topkg"
368368+let sdocs = Manpage.s_common_options
369369+let exits = Cli.exits
370370+let man_xrefs = [`Main]
371371+let man =
372372+ [ `S Manpage.s_description;
373373+ `P "The $(tname) command shows help about $(mname).";
374374+ `P "Use `topics' as $(i,TOPIC) to get a list of topics." ]
375375+376376+let cmd =
377377+ Cmd.v (Cmd.info "help" ~doc ~exits ~man ~man_xrefs) @@
378378+ Term.(ret (const help $ Arg.man_format $ topic $ Term.choice_names))
+8
vendor/opam/topkg/src-bin/help.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** The [help] command. *)
77+88+val cmd : int Cmdliner.Cmd.t
+50
vendor/opam/topkg/src-bin/ipc.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+open Bos_setup
77+88+let opam_fields file =
99+ begin
1010+ Logs.info begin fun m ->
1111+ m ~header:"IPC" "opam fields of %s with cwd %a" file
1212+ (R.pp ~ok:Fpath.pp ~error:R.pp_msg) (OS.Dir.current ())
1313+ end;
1414+ Fpath.of_string file
1515+ >>= fun file -> Topkg_care.Opam.File.fields file
1616+ >>= fun fs -> Ok (String.Map.bindings fs)
1717+ >>= fun fs -> Ok (Topkg.Private.(Codec.enc Opam.File.codec fs))
1818+ >>= fun enc -> OS.File.(write dash enc)
1919+ end
2020+ |> R.reword_error_msg ~replace:true
2121+ (fun msg -> R.msgf "ipc opam-fields: %s" msg)
2222+2323+let ipc_answer = function
2424+| ["opam-fields"; file] -> opam_fields file
2525+| args -> R.error_msgf "ipc: unknown arguments %a" Cmd.dump (Cmd.of_list args)
2626+2727+let ipc () args = match ipc_answer args with
2828+| Ok () -> `Ok 0
2929+| Error (`Msg msg) -> `Error (false, msg)
3030+3131+(* Command line interface *)
3232+3333+open Cmdliner
3434+3535+let args =
3636+ let doc = "IPC call arguments" in
3737+ Arg.(value (pos_all string [] & info [] ~doc ~docv:"ARG"))
3838+3939+let doc = "Interprocess communication with package description files"
4040+let sdocs = Manpage.s_common_options
4141+let exits = Cli.exits
4242+let man_xrefs = [`Main]
4343+let man =
4444+ [ `S Manpage.s_description;
4545+ `P "The $(tname) command is used by package description files. It is
4646+ undocumented." ]
4747+4848+let cmd =
4949+ Cmd.v (Cmd.info "ipc" ~doc ~sdocs ~exits ~man ~man_xrefs) @@
5050+ Term.(ret (const ipc $ Cli.setup $ args))
+8
vendor/opam/topkg/src-bin/ipc.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** The [ipc] command. *)
77+88+val cmd : int Cmdliner.Cmd.t
+145
vendor/opam/topkg/src-bin/issue.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+open Bos_setup
77+88+let get_id = function
99+| Some id -> Ok id
1010+| None -> R.error_msgf "No issue ID specified"
1111+1212+let get_issue_msg ~info = function
1313+| Some "" -> Ok None
1414+| Some msg -> Ok (Some (String.cuts ~sep:"\n" msg))
1515+| None ->
1616+ let is_msg s = not (String.is_prefix ~affix:"#" s) in
1717+ let rec rem_white_prefix = function
1818+ | l :: ls when String.for_all Char.Ascii.is_white l -> rem_white_prefix ls
1919+ | ls -> ls
2020+ in
2121+ OS.File.tmp "topkg-issue-msg-%s"
2222+ >>= fun f -> OS.File.write f info
2323+ >>= fun () -> Topkg_care.Text.edit_file f
2424+ >>= function
2525+ | 0 ->
2626+ OS.File.read f >>= fun m ->
2727+ let msg = List.filter is_msg (String.cuts ~sep:"\n" m) in
2828+ begin match rem_white_prefix msg with
2929+ | [] -> Ok None
3030+ | lines -> Ok (Some lines)
3131+ end
3232+ | n ->
3333+ Logs.err (fun m -> m "Editor exited with non-zero error code.");
3434+ Ok None
3535+3636+(* Actions *)
3737+3838+let issue_show pkg ~id =
3939+ get_id id >>= fun id -> Topkg_care.Delegate.issue_show pkg ~id
4040+4141+let issue_open pkg msg =
4242+ let open_info =
4343+ "\n\
4444+ # Please enter an issue description. The first non-blank line will be\n\
4545+ # the issue title and the rest the issue description. Lines starting\n\
4646+ # with '#' will be ignored. An empty description aborts the action."
4747+ in
4848+ get_issue_msg ~info:open_info msg >>= function
4949+ | None ->
5050+ Logs.app (fun m -> m "Open issue aborted due to empty issue message.");
5151+ Ok ();
5252+ | Some lines ->
5353+ let title, body = match lines with
5454+ | title :: body -> title, String.(trim @@ concat ~sep:"\n" body)
5555+ | [] -> assert false
5656+ in
5757+ Topkg_care.Delegate.issue_open pkg ~title ~body
5858+5959+let issue_close pkg ~id msg =
6060+ let close_info =
6161+ "\n\
6262+ # Please enter a closing message. Lines starting with '#' will\n\
6363+ # be ignored. An empty message aborts the action."
6464+ in
6565+ get_id id
6666+ >>= fun id -> get_issue_msg ~info:close_info msg
6767+ >>= function
6868+ | None ->
6969+ Logs.app
7070+ (fun m -> m "Close issue %s aborted due to empty issue message." id);
7171+ Ok ()
7272+ | Some lines ->
7373+ let msg = String.(trim @@ concat ~sep:"\n" lines) in
7474+ Topkg_care.Delegate.issue_close pkg ~id ~msg
7575+7676+(* Command *)
7777+7878+let issue () pkg_file opam delegate action id msg =
7979+ begin
8080+ let pkg = Topkg_care.Pkg.v ?opam ?delegate pkg_file in
8181+ let ret = match action with
8282+ | `List -> Topkg_care.Delegate.issue_list pkg
8383+ | `Show -> issue_show pkg ~id
8484+ | `Open -> issue_open pkg msg
8585+ | `Close -> issue_close pkg ~id msg
8686+ in
8787+ ret >>= fun () -> Ok 0
8888+ end
8989+ |> Cli.handle_error
9090+9191+(* Command line interface *)
9292+9393+open Cmdliner
9494+9595+let action =
9696+ let action = ["list",`List; "show",`Show; "open",`Open; "close",`Close;] in
9797+ let doc = strf "The action to perform. $(docv) must be one of %s."
9898+ (Arg.doc_alts_enum action)
9999+ in
100100+ let cmd = Arg.enum action in
101101+ Arg.(value & pos 0 cmd `List & info [] ~doc ~docv:"ACTION")
102102+103103+let id =
104104+ let doc = "An issue ID of the package's issue tracker." in
105105+ Arg.(value & pos 1 (some string) None & info [] ~doc ~docv:"ID")
106106+107107+let msg =
108108+ let doc = "For $(b,open) and $(b,close), $(docv) is the issue message.
109109+ Prevents the interactive prompt for the message."
110110+ in
111111+ let docv = "MSG" in
112112+ Arg.(value & opt (some string) None & info ["m"; "message"] ~doc ~docv)
113113+114114+let doc = "Interact with the package's issue tracker"
115115+let sdocs = Manpage.s_common_options
116116+let exits = Cli.exits
117117+let envs =
118118+ [ Cmd.Env.info "EDITOR" ~doc:"The editor used to edit issue messages.";
119119+ Cmd.Env.info "TOPKG_DELEGATE" ~doc:"The package delegate to use, see
120120+ topkg-delegate(7)." ]
121121+122122+let man_xrefs = [ `Main ]
123123+let man =
124124+ [ `S Manpage.s_synopsis;
125125+ `P "$(mname) $(tname) [$(i,OPTION)]... [$(i,ACTION)]...";
126126+ `S Manpage.s_description;
127127+ `P "The $(tname) command interacts with the package's issue
128128+ tracker via the package delegate. See topkg-delegate(7) for more
129129+ details.";
130130+ `P "To consult the issues in a WWW browser invoke
131131+ $(b,topkg browse issues), no delegate is needed for this.";
132132+ `S "ACTIONS";
133133+ `I ("$(b,list) (default)",
134134+ "List open issues.");
135135+ `I ("$(b,show) $(i,ID)",
136136+ "Show information about issue $(i,ID).");
137137+ `I ("$(b,open)",
138138+ "Open a new issue.");
139139+ `I ("$(b,close) $(i,ID)",
140140+ "Close issue $(i,ID).") ]
141141+142142+let cmd =
143143+ Cmd.v (Cmd.info "issue" ~doc ~sdocs ~exits ~envs ~man ~man_xrefs) @@
144144+ Term.(const issue $ Cli.setup $ Cli.pkg_file $ Cli.opam $ Cli.delegate $
145145+ action $ id $ msg)
+8
vendor/opam/topkg/src-bin/issue.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** The [issue] command. *)
77+88+val cmd : int Cmdliner.Cmd.t
+54
vendor/opam/topkg/src-bin/lint.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+open Bos_setup
77+88+let lint () pkg_file ignore_pkg lints =
99+ begin
1010+ let pkg = Topkg_care.Pkg.v pkg_file in
1111+ OS.Dir.current ()
1212+ >>= fun dir -> Topkg_care.Pkg.lint ~ignore_pkg pkg ~dir lints
1313+ end
1414+ |> Cli.handle_error
1515+1616+(* Command line interface *)
1717+1818+open Cmdliner
1919+2020+let lints =
2121+ let test = [ "custom", `Custom;
2222+ "std-files", `Std_files;
2323+ "meta", `Meta;
2424+ "opam", `Opam;
2525+ "deps", `Deps; ]
2626+ in
2727+ let doc = strf "Test to perform. $(docv) must be one of %s. If unspecified
2828+ all tests are performed." (Arg.doc_alts_enum test)
2929+ in
3030+ let test = Arg.enum test in
3131+ let docv = "TEST" in
3232+ Arg.(value & pos_all test Topkg_care.Pkg.lint_all & info [] ~doc ~docv)
3333+3434+let ignore_pkg =
3535+ let doc = "Ignore package description file." in
3636+ Arg.(value & flag & info ["i"; "ignore-pkg"] ~doc)
3737+3838+let doc = "Check package distribution consistency and conventions"
3939+let sdocs = Manpage.s_common_options
4040+let exits = Cmd.Exit.info 1 ~doc:"on lint failure" :: Cli.exits
4141+let man_xrefs = [`Main; `Cmd "distrib"]
4242+let man =
4343+ [ `S Manpage.s_description;
4444+ `P "The $(tname) command makes tests on a package distribution or
4545+ source repository. It checks that standard files exist, that
4646+ ocamlfind META files pass the ocamlfind lint test, that opam package
4747+ files pass the opam lint test and that the opam dependencies are
4848+ consistent with those of the build system.";
4949+ `P "Linting is automatically performed on distribution generation, see
5050+ topkg-distrib(1) for more details." ]
5151+5252+let cmd =
5353+ Cmd.v (Cmd.info "lint" ~doc ~sdocs ~exits ~man ~man_xrefs)
5454+ Term.(const lint $ Cli.setup $ Cli.pkg_file $ ignore_pkg $ lints)
+8
vendor/opam/topkg/src-bin/lint.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** The [lint] command. *)
77+88+val cmd : int Cmdliner.Cmd.t
+126
vendor/opam/topkg/src-bin/log.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+open Bos_setup
77+88+(* Actions *)
99+1010+let show change_log last last_version no_pager =
1111+ let text =
1212+ if not (last || last_version) then OS.File.read change_log else
1313+ (Topkg_care.Text.change_log_file_last_entry change_log
1414+ >>= fun (v, (h, t)) -> Ok (if last_version then v else strf "%s\n%s" h t))
1515+ in
1616+ text
1717+ >>= fun text -> Topkg_care.Text.find_pager ~don't:(no_pager || last_version)
1818+ >>= function
1919+ | None -> Logs.app (fun m -> m "%s" text); Ok ()
2020+ | Some pager -> OS.Cmd.(in_string text |> run_in pager)
2121+2222+let commit change_log =
2323+ let change_log = Fpath.to_string change_log in
2424+ Topkg.Vcs.get ()
2525+ >>= fun repo -> Topkg.Vcs.file_is_dirty repo change_log
2626+ >>= function
2727+ | true -> Topkg.Vcs.commit_files repo ~msg:"Update change log." [change_log]
2828+ | false ->
2929+ Logs.app (fun m -> m "No changes to commit in %s" change_log); Ok ()
3030+3131+(* Command *)
3232+3333+let log () pkg_file change_log action last last_version no_pager =
3434+ begin
3535+ let pkg = Topkg_care.Pkg.v ?change_log pkg_file in
3636+ Topkg_care.Pkg.change_log pkg
3737+ >>= fun change_log -> match action with
3838+ | `Show -> show change_log last last_version no_pager >>= fun () -> Ok 0
3939+ | `Edit -> Topkg_care.Text.edit_file change_log
4040+ | `Commit -> commit change_log >>= fun () -> Ok 0
4141+ end
4242+ |> Cli.handle_error
4343+4444+(* Command line interface *)
4545+4646+open Cmdliner
4747+4848+let action =
4949+ let action = [ "show", `Show; "edit", `Edit; "commit", `Commit] in
5050+ let doc = strf "The action to perform. $(docv) must be one of %s."
5151+ (Arg.doc_alts_enum action)
5252+ in
5353+ let cmd = Arg.enum action in
5454+ Arg.(value & pos 0 cmd `Show & info [] ~doc ~docv:"ACTION")
5555+5656+let no_pager =
5757+ let doc = "Do not pipe the output into a pager. This automatically
5858+ happens if the TERM environment variable is 'dumb' or undefined."
5959+ in
6060+ Arg.(value & flag & info ["no-pager"] ~doc)
6161+6262+let last =
6363+ let doc = "Show only the change log of the last version. Extracted as the
6464+ first marked up section of the change log."
6565+ in
6666+ Arg.(value & flag & info ["l"; "last"] ~doc)
6767+6868+let last_version =
6969+ let doc = "Show only the version string of the last version. Extracted as
7070+ the first token of the title of the first marked up section of
7171+ the change log. Implies $(b,--no-pager).";
7272+ in
7373+ Arg.(value & flag & info ["t"; "last-version"] ~doc)
7474+7575+let doc = "Show and edit the package's change log"
7676+let sdocs = Manpage.s_common_options
7777+let exits = Cli.exits
7878+let envs =
7979+ [ Cmd.Env.info "EDITOR" ~doc:"The editor used to edit the change log.";
8080+ Cmd.Env.info "PAGER" ~doc:"The pager used to consult the change log.";
8181+ Cmd.Env.info "TERM" ~doc:"See option $(b,--no-pager)." ]
8282+8383+let man_xrefs = [ `Main; `Cmd "publish"; `Cmd "tag"; `Cmd "opam"; ]
8484+let man =
8585+ [ `S Manpage.s_description;
8686+ `P "The $(tname) command shows, edits and commits
8787+ the package's change log.";
8888+ `S "CHANGE LOG FORMAT";
8989+ `P "To be able to extract the version and changes of the last distribution,
9090+ a well defined change log format is assumed. Not abiding to the
9191+ format is not catastrophic but may hinder or derail some facilities
9292+ provided by topkg.";
9393+ `P "The format assumes that the change log is written either in Markdown
9494+ (default or .md extension) or Asciidoc (.asciidoc or .adoc extension).
9595+ A change log is a list of marked up sections. A section is
9696+ a header of any level until the next header at the same level or
9797+ the end of file. For example here are two Markdown sections:";
9898+ `Pre "\
9999+v2.0.0
100100+------
101101+### New features
102102+etc.
103103+### Breaking changes
104104+etc.
105105+106106+v1.6.0 1995-09-12
107107+-----------------
108108+etc.";
109109+ `P "The first marked up section in the file is taken as being the
110110+ change log for the last distribution; use $(b,topkg log -l)
111111+ to check that it is parsed correctly. This is used by topkg-publish(1)
112112+ and topkg-opam(1) to enrich distribution publication.";
113113+ `P "The first token of the first section header title is taken as being the
114114+ version string of the distribution; use $(b,topkg log -t) to check
115115+ that it is parsed correctly. It is used by topkg-tag(1) to tag the
116116+ source repository.";
117117+ `S "ACTIONS";
118118+ `I ("$(b,show) (default)", "shows the package's change log.");
119119+ `I ("$(b,edit)", "edit the package's change log.");
120120+ `I ("$(b,commit)", "commit changes made to the package's change log to the
121121+ VCS.") ]
122122+123123+let cmd =
124124+ Cmd.v (Cmd.info "log" ~doc ~sdocs ~exits ~envs ~man) @@
125125+ Term.(const log $ Cli.setup $ Cli.pkg_file $ Cli.change_log $ action $
126126+ last $ last_version $ no_pager)
+8
vendor/opam/topkg/src-bin/log.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** The [log] command. *)
77+88+val cmd : int Cmdliner.Cmd.t
+200
vendor/opam/topkg/src-bin/opam.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+open Bos_setup
77+88+let get_opam_publish_file p opam_publish_file = match opam_publish_file with
99+| Some file -> Ok file
1010+| None ->
1111+ Topkg_care.Pkg.build_dir p
1212+ >>= fun bdir -> Topkg_care.Pkg.distrib_filename ~opam:true p
1313+ >>= fun fname -> Ok Fpath.(bdir // fname + ".opam")
1414+1515+let descr pkg =
1616+ Topkg_care.Pkg.opam_descr pkg >>= fun (descr, _) ->
1717+ Logs.app (fun m -> m "%s" (Topkg_care.Opam.Descr.to_string descr));
1818+ Ok 0
1919+2020+let pkg pkg dist_pkg opam_publish_file =
2121+ let log_pkg dst =
2222+ Logs.app (fun m -> m "Wrote opam package %a" Topkg_care.Pp.path dst)
2323+ in
2424+ let warn_if_vcs_dirty () =
2525+ Cli.warn_if_vcs_dirty "The opam package may be inconsistent with the \
2626+ distribution."
2727+ in
2828+ let opam_file_content opam (descr, from_opam) url =
2929+ let descr =
3030+ if from_opam then "" else
3131+ (Topkg_care.Opam.Descr.to_opam_fields descr ^ "\n")
3232+ in
3333+ strf "%s\n%s%s" opam descr (Topkg_care.Opam.Url.to_opam_section url)
3434+ in
3535+ get_opam_publish_file pkg opam_publish_file
3636+ >>= fun dst -> Topkg_care.Pkg.opam pkg
3737+ >>= fun opam -> OS.File.read opam
3838+ >>= fun opam -> Topkg_care.Pkg.opam_descr pkg
3939+ >>= fun descr -> Topkg_care.Pkg.distrib_file dist_pkg
4040+ >>= fun distrib_file -> Topkg_care.Pkg.distrib_uri dist_pkg
4141+ >>= fun uri -> Topkg_care.Opam.Url.with_distrib_file ~uri distrib_file
4242+ >>= fun url -> OS.File.write dst (opam_file_content opam descr url)
4343+ >>= fun () -> log_pkg dst; warn_if_vcs_dirty ()
4444+ >>= fun () ->
4545+ Ok 0
4646+4747+let submit pkg opam_pkg_dst =
4848+ Topkg_care.Opam.ensure_publish ()
4949+ >>= fun () -> get_opam_publish_file pkg opam_pkg_dst
5050+ >>= fun opam_file -> OS.File.exists opam_file
5151+ >>= function
5252+ | false ->
5353+ Logs.err (fun m -> m "Package@ file %a@ does@ not@ exist. Did@ you@ \
5454+ forget@ to@ invoke 'topkg opam pkg' ?"
5555+ Fpath.pp opam_file);
5656+ Ok 1
5757+ | true ->
5858+ Logs.app (fun m -> m "Publishing %a" Topkg_care.Pp.path opam_file);
5959+ Topkg_care.Pkg.publish_msg pkg
6060+ >>= fun msg -> Topkg_care.Opam.submit ~opam_file ~msg ()
6161+ >>= fun () -> Ok 0
6262+6363+let field pkg field = match field with
6464+| None -> Logs.err (fun m -> m "Missing FIELD positional argument"); Ok 1
6565+| Some field ->
6666+ Topkg_care.Pkg.opam_field pkg field
6767+ >>= function
6868+ | Some v -> Logs.app (fun m -> m "%s" (String.concat ~sep:" " v)); Ok 0
6969+ | None ->
7070+ Topkg_care.Pkg.opam pkg >>= fun opam ->
7171+ Logs.err (fun m -> m "%a: field %s is undefined" Fpath.pp opam field);
7272+ Ok 1
7373+7474+(* Command *)
7575+7676+let opam () pkg_file build_dir
7777+ dist_name dist_version dist_opam dist_uri dist_file
7878+ opam_publish_file pkg_name pkg_version pkg_opam pkg_descr
7979+ readme change_log publish_msg action field_name
8080+ =
8181+ let p =
8282+ Topkg_care.Pkg.v
8383+ ?build_dir ?name:pkg_name ?version:pkg_version ?opam:pkg_opam
8484+ ?opam_descr:pkg_descr ?readme ?change_log ?publish_msg pkg_file
8585+ in
8686+ begin match action with
8787+ | `Descr -> descr p
8888+ | `Pkg ->
8989+ let dist_p =
9090+ Topkg_care.Pkg.v
9191+ ?build_dir ?name:dist_name ?version:dist_version ?opam:dist_opam
9292+ ?distrib_uri:dist_uri ?distrib_file:dist_file ?readme ?change_log
9393+ ?publish_msg pkg_file
9494+ in
9595+ pkg p dist_p opam_publish_file
9696+ | `Submit -> submit p opam_publish_file
9797+ | `Field -> field p field_name
9898+ end
9999+ |> Cli.handle_error
100100+101101+(* Command line interface *)
102102+103103+open Cmdliner
104104+105105+let action =
106106+ let action = [ "descr", `Descr; "pkg", `Pkg; "submit", `Submit;
107107+ "publish", `Submit; "field", `Field ]
108108+ in
109109+ let doc = strf "The action to perform. $(docv) must be one of %s."
110110+ (Arg.doc_alts_enum action)
111111+ in
112112+ let action = Arg.enum action in
113113+ Arg.(required & pos 0 (some action) None & info [] ~doc ~docv:"ACTION")
114114+115115+let field =
116116+ let doc = "the field to output ($(b,field) action)" in
117117+ Arg.(value & pos 1 (some string) None & info [] ~doc ~docv:"FIELD")
118118+119119+let opam_publish_file =
120120+ let doc = "The file to use to publish the opam package. If absent the
121121+ file $(i,BUILD_DIR)/$(i,PKG_NAME).$(i,PKG_VERSION).opam in the
122122+ build directory is used (see options $(b,--build-dir),
123123+ $(b,--pkg-name) and $(b,--pkg-version))"
124124+ in
125125+ let docv = "FILE" in
126126+ Arg.(value & opt (some Cli.path_arg) None & info ["opam-publish-file"]
127127+ ~doc ~docv)
128128+129129+let pkg_version =
130130+ let doc = "The version string $(docv) of the opam package. If absent provided
131131+ provided by the VCS tag description of the HEAD commit."
132132+ in
133133+ let docv = "PKG_NAME" in
134134+ Arg.(value & opt (some string) None & info ["pkg-version"] ~doc ~docv)
135135+136136+let pkg_opam =
137137+ let doc = "The opam file to use for the opam package. If absent uses the
138138+ opam file mentioned in the package description that corresponds
139139+ to the opam package name $(i,PKG_NAME) (see option
140140+ $(b,--pkg-name))"
141141+ in
142142+ let docv = "FILE" in
143143+ Arg.(value & opt (some Cli.path_arg) None & info ["pkg-opam"] ~doc ~docv)
144144+145145+let pkg_descr =
146146+ let doc = "The opam descr file to use for the opam package. If absent
147147+ and the opam file has synopsis and description fields this
148148+ is used for the description. It absent and there are no such
149149+ fields in the opam file and the opam file name
150150+ (see $(b,--pkg-opam)) has a `.opam`
151151+ extension, uses an existing file with the same path but a `.descr`
152152+ extension. If the opam file name is `opam` uses a `descr`
153153+ file in the same directory. If these files are not found
154154+ a description is extracted from the the readme (see
155155+ option $(b,--readme)) as follow: the first marked up
156156+ section of the readme is extracted, its title is parsed
157157+ according to the pattern '\\$(NAME) \\$(SEP) \\$(SYNOPSIS)',
158158+ the body of the section is the long description. A few
159159+ lines are filtered out: lines that start with either
160160+ 'Home page:', 'Contact:' or '%%VERSION'."
161161+ in
162162+ let docv = "FILE" in
163163+ Arg.(value & opt (some Cli.path_arg) None & info ["pkg-descr"] ~doc ~docv)
164164+165165+let doc = "Interaction with opam and the OCaml opam repository"
166166+let sdocs = Manpage.s_common_options
167167+let envs =
168168+ [ Cmd.Env.info "TOPKG_OPAM_PUBLISH" ~doc:"The $(b,opam-publish) tool to use
169169+ to submit packages." ]
170170+171171+let man_xrefs = [`Main; `Cmd "distrib" ]
172172+let man =
173173+ [ `S Manpage.s_synopsis;
174174+ `P "$(mname) $(tname) [$(i,OPTION)]... $(i,ACTION)";
175175+ `S Manpage.s_description;
176176+ `P "The $(tname) command provides a few actions to interact with
177177+ opam and the OCaml opam repository.";
178178+ `S "ACTIONS";
179179+ `I ("$(b,descr)",
180180+ "extract and print an opam descr file. This is used by the
181181+ $(b,pkg) action. See the $(b,--pkg-descr) option for details.");
182182+ `I ("$(b,pkg)",
183183+ "create an opam package description for a distribution.
184184+ The action needs a distribution archive to operate, see
185185+ topkg-distrib(1) or the $(b,--dist-file) option.");
186186+ `I ("$(b,submit) or $(b,publish)",
187187+ "submits a package created with the action $(b,pkg) the OCaml
188188+ opam repository. Requires the $(b,opam-publish) tool to be
189189+ installed.");
190190+ `I ("$(b,field) $(i,FIELD)",
191191+ "outputs the field $(i,FIELD) of the package's opam file."); ]
192192+193193+let cmd =
194194+ Cmd.v (Cmd.info "opam" ~doc ~sdocs ~envs ~man ~man_xrefs) @@
195195+ Term.(const opam $ Cli.setup $ Cli.pkg_file $ Cli.build_dir $
196196+ Cli.dist_name $ Cli.dist_version $ Cli.dist_opam $
197197+ Cli.dist_uri $ Cli.dist_file $
198198+ opam_publish_file $ Cli.pkg_name $ pkg_version $ pkg_opam $
199199+ pkg_descr $ Cli.readme $ Cli.change_log $ Cli.publish_msg $
200200+ action $ field)
+8
vendor/opam/topkg/src-bin/opam.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** The [opam] command. *)
77+88+val cmd : int Cmdliner.Cmd.t
+125
vendor/opam/topkg/src-bin/publish.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+open Bos_setup
77+88+let absolute path = OS.Dir.current () >>| fun cwd -> Fpath.(cwd // path)
99+1010+let gen_doc dir =
1111+ let do_doc () =
1212+ OS.Cmd.run Cmd.(v "topkg" % "doc")
1313+ >>| fun () -> Fpath.(dir / "_build" / "doc" / "api.docdir")
1414+ in
1515+ R.join @@ OS.Dir.with_current dir do_doc ()
1616+1717+let publish_doc pkg =
1818+ Topkg_care.Pkg.distrib_file pkg
1919+ >>= fun distrib_file -> Topkg_care.Pkg.publish_msg pkg
2020+ >>= fun msg -> Topkg_care.Archive.untbz ~clean:true distrib_file
2121+ >>= fun dir -> gen_doc dir
2222+ >>= fun docdir -> absolute docdir
2323+ >>= fun docdir -> Topkg_care.Delegate.publish_doc pkg ~msg ~docdir
2424+2525+let publish_distrib pkg =
2626+ Topkg_care.Pkg.distrib_file pkg
2727+ >>= fun distrib_file -> Topkg_care.Pkg.publish_msg pkg
2828+ >>= fun msg -> absolute distrib_file
2929+ >>= fun archive -> Topkg_care.Delegate.publish_distrib pkg ~msg ~archive
3030+3131+let publish_alt pkg kind =
3232+ Topkg_care.Pkg.distrib_file pkg
3333+ >>= fun distrib_file -> Topkg_care.Pkg.publish_msg pkg
3434+ >>= fun msg -> absolute distrib_file
3535+ >>= fun archive -> Topkg_care.Delegate.publish_alt pkg ~kind ~msg ~archive
3636+3737+let publish ()
3838+ pkg_file build_dir name version opam delegate change_log distrib_uri
3939+ distrib_file publish_msg publish_artefacts
4040+ =
4141+ begin
4242+ let publish_artefacts = match publish_artefacts with
4343+ | [] -> None
4444+ | v -> Some v
4545+ in
4646+ let pkg = Topkg_care.Pkg.v ?name ?version ?build_dir ?opam ?delegate
4747+ ?change_log ?distrib_uri ?distrib_file ?publish_msg
4848+ ?publish_artefacts pkg_file
4949+ in
5050+ let publish_artefact acc artefact =
5151+ acc >>= fun acc -> match artefact with
5252+ | `Doc -> publish_doc pkg
5353+ | `Distrib -> publish_distrib pkg
5454+ | `Alt kind -> publish_alt pkg kind
5555+ in
5656+ Topkg_care.Pkg.publish_artefacts pkg
5757+ >>= fun todo -> List.fold_left publish_artefact (Ok ()) todo
5858+ >>= fun () -> Ok 0
5959+ end
6060+ |> Cli.handle_error
6161+6262+(* Command line interface *)
6363+6464+open Cmdliner
6565+6666+let artefacts =
6767+ let alt_prefix = "alt-" in
6868+ let parser = function
6969+ | "do" | "doc" -> Ok `Doc
7070+ | "di" | "dis" | "dist" | "distr" | "distri" | "distrib" -> Ok `Distrib
7171+ | s when String.is_prefix ~affix:alt_prefix s ->
7272+ begin match String.(with_range ~first:(length alt_prefix) s) with
7373+ | "" -> Error ("`alt-' alternative artefact kind is missing")
7474+ | kind -> Ok (`Alt kind)
7575+ end
7676+ | s -> Error (strf "`%s' unknown publication artefact" s)
7777+ in
7878+ let printer ppf = function
7979+ | `Doc -> Fmt.string ppf "doc"
8080+ | `Distrib -> Fmt.string ppf "distrib"
8181+ | `Alt a -> Fmt.pf ppf "alt-%s" a
8282+ in
8383+ let artefact = Arg.conv' (parser, printer) in
8484+ let doc = strf "The artefact to publish. $(docv) must be one of `doc`,
8585+ `distrib` or `alt-$(i,KIND)`. If absent, the set of
8686+ default publication artefacts is determined by the
8787+ package description."
8888+ in
8989+ Arg.(value & pos_all artefact [] & info [] ~doc ~docv:"ARTEFACT")
9090+9191+let doc = "Publish package distribution archives and derived artefacts"
9292+let sdocs = Manpage.s_common_options
9393+let exits = Cli.exits
9494+let envs =
9595+ [ Cmd.Env.info "TOPKG_DELEGATE" ~doc:"The package delegate to use, see
9696+ topkg-delegate(7)."; ]
9797+9898+let man_xrefs = [`Main; `Cmd "distrib" ]
9999+let man =
100100+ [ `S Manpage.s_synopsis;
101101+ `P "$(mname) $(tname) [$(i,OPTION)]... [$(i,ARTEFACT)]...";
102102+ `S Manpage.s_description;
103103+ `P "The $(tname) command publishes package distribution archives
104104+ and other artefacts via the package delegate. See topkg-delegate(7) for
105105+ more details.";
106106+ `P "Artefact publication always relies on a distribution archive having
107107+ been generated before with topkg-distrib(1).";
108108+ `S "ARTEFACTS";
109109+ `I ("$(b,distrib)",
110110+ "Publishes a distribution archive on the WWW.");
111111+ `I ("$(b,doc)",
112112+ "Publishes the documentation of a distribution archive on the WWW.");
113113+ `I ("$(b,alt)-$(i,KIND)",
114114+ "Publishes the alternative artefact of kind $(i,KIND) of
115115+ a distribution archive. The semantics of alternative artefacts
116116+ is left to the delegate, it could be anything, an email,
117117+ a pointless tweet, a feed entry etc. See topkg-delegate(7) for
118118+ more details."); ]
119119+120120+let cmd =
121121+ Cmd.v (Cmd.info "publish" ~doc ~sdocs ~exits ~envs ~man ~man_xrefs) @@
122122+ Term.(const publish $ Cli.setup $ Cli.pkg_file $ Cli.build_dir $
123123+ Cli.dist_name $ Cli.dist_version $ Cli.dist_opam $
124124+ Cli.delegate $ Cli.change_log $ Cli.dist_uri $ Cli.dist_file $
125125+ Cli.publish_msg $ artefacts)
+8
vendor/opam/topkg/src-bin/publish.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** The [publish] command. *)
77+88+val cmd : int Cmdliner.Cmd.t
+103
vendor/opam/topkg/src-bin/run.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+open Bos_setup
77+88+let pp_exec = Fmt.(quote string)
99+1010+let blacklist = [ ".so"; ".cmxs" ] (* Don't try to run these kind of files *)
1111+1212+let exec_match exec p =
1313+ OS.File.exists p >>= function
1414+ | false -> Ok false
1515+ | true ->
1616+ OS.Path.Mode.get p >>= fun mode ->
1717+ if mode land 0o111 = 0 then Ok false else
1818+ let p_base, ext = Fpath.split_ext p in
1919+ let p_base = Fpath.to_string p_base in
2020+ let p = Fpath.to_string p in
2121+ Ok (not (List.mem ext blacklist) &&
2222+ (String.is_suffix ~affix:exec p_base ||
2323+ String.is_suffix ~affix:exec p))
2424+2525+let find_exec exec dir =
2626+ let ambiguous l =
2727+ R.error_msgf "Ambiguous matches for %a, could match any of %a"
2828+ pp_exec exec Fmt.(list ~sep:(any ", ") Fpath.pp) l
2929+ in
3030+ OS.Dir.exists dir >>= function
3131+ | false -> R.error_msgf "Build directory %a does not exist" Fpath.pp dir
3232+ | true ->
3333+ let elements = `Sat (exec_match exec) in
3434+ OS.Dir.fold_contents ~elements (fun p ps -> p :: ps) [] dir
3535+ >>= function
3636+ | [p0] -> Ok p0
3737+ | [p0; p1] as l ->
3838+ let p0, ext0 = Fpath.split_ext p0 in
3939+ let p1, ext1 = Fpath.split_ext p1 in
4040+ if Fpath.equal p0 p1 && ext0 = ".native" || ext1 = ".native"
4141+ then Ok (Fpath.add_ext ".native" p0)
4242+ else ambiguous l
4343+ | [] ->
4444+ R.error_msgf "No matches for %a in build directory %a"
4545+ Fmt.(quote string) exec Fpath.pp dir
4646+ | l ->
4747+ ambiguous l
4848+4949+let run () pkg_file build_dir exec args =
5050+ let pkg = Topkg_care.Pkg.v pkg_file ?build_dir in
5151+ begin
5252+ Topkg_care.Pkg.build_dir pkg
5353+ >>= fun build_dir -> find_exec exec build_dir
5454+ >>= fun exec -> Ok Cmd.(v (p exec) %% of_list args)
5555+ >>= fun cmd -> OS.Cmd.run_status cmd
5656+ >>= function
5757+ | `Exited 0 -> Ok 0
5858+ | status ->
5959+ Logs.err (fun m -> m "run %a %a"
6060+ Cmd.dump cmd OS.Cmd.pp_status status);
6161+ Ok 1
6262+ end
6363+ |> Cli.handle_error
6464+6565+(* Command line interface *)
6666+6767+open Cmdliner
6868+6969+let args =
7070+ let doc = "Arguments given to the executable. If options are being
7171+ passed, needs to be specified after a -- token so that the
7272+ command line options do not get interpreted by the $(tname)
7373+ command itself."
7474+ in
7575+ Arg.(value & pos_right 0 string [] & info [] ~doc ~docv:"ARG")
7676+7777+let exec =
7878+ let doc = "Executable name or path suffix, with or without its
7979+ extension. If multiple executable match the specification
8080+ the command errors except if two paths match and differ
8181+ only by their .byte and .native file extension. In the latter
8282+ case the .native path is used."
8383+ in
8484+ let docv = "EXEC" in
8585+ Arg.(required & pos 0 (some string) None & info [] ~doc ~docv)
8686+8787+let doc = "Run built executables"
8888+let sdocs = Manpage.s_common_options
8989+let exits = Cmd.Exit.info 1 ~doc:"on run non-zero status exit." :: Cli.exits
9090+let man_xrefs = [ `Main ]
9191+let man =
9292+ [ `S Manpage.s_synopsis;
9393+ `P "$(mname) $(tname) [$(i,OPTION)]... $(i,EXEC) \
9494+ [-- [$(i,ARG)]...]]";
9595+ `S Manpage.s_description;
9696+ `P "The $(tname) command runs executable files found
9797+ in the build directory.";
9898+ `P "$(b,WARNING) The way this command works is subject to change
9999+ in the future." ]
100100+101101+let cmd =
102102+ Cmd.v (Cmd.info "run" ~doc ~sdocs ~exits ~man ~man_xrefs) @@
103103+ Term.(const run $ Cli.setup $ Cli.pkg_file $ Cli.build_dir $ exec $ args)
+8
vendor/opam/topkg/src-bin/run.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** The [run] command. *)
77+88+val cmd : int Cmdliner.Cmd.t
+87
vendor/opam/topkg/src-bin/status.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+open Bos_setup
77+88+let pp_since ppf = function
99+| "" -> ()
1010+| v -> Fmt.pf ppf " since %a" Topkg_care.Pp.version v
1111+1212+let pp_dirty ppf = function
1313+| false -> ()
1414+| true -> Fmt.pf ppf "The repository is %a.@," Topkg_care.Pp.dirty ()
1515+1616+let pp_commit ppf (id, log) =
1717+ Fmt.pf ppf "%a %s" Topkg_care.Pp.commit id log
1818+1919+let pp_status ppf (dirty, version, changes) = match changes with
2020+| [] when not dirty -> Fmt.pf ppf "@[<v>No changes%a@]" pp_since version
2121+| changes ->
2222+ Fmt.pf ppf "@[<v>Changes%a:@,%a%a@]"
2323+ pp_since version pp_dirty dirty (Fmt.list pp_commit) changes
2424+2525+let find_latest_version_tag repo =
2626+ let rev_compare v v' = -1 * compare v v' in
2727+ let parse_tag acc t = match Topkg.String.parse_version t with
2828+ | None -> acc
2929+ | Some v -> (v, t) :: acc
3030+ in
3131+ Topkg.Vcs.tags repo >>| fun tags ->
3232+ match List.(sort rev_compare (fold_left parse_tag [] tags)) with
3333+ | (_, latest) :: _ -> Some latest
3434+ | [] -> None
3535+3636+let find_after repo = function
3737+| Some after -> Ok after
3838+| None ->
3939+ find_latest_version_tag repo >>| function
4040+ | None ->
4141+ Logs.info (fun m -> m "No VCS version tag found."); ""
4242+ | Some tag ->
4343+ Logs.info (fun m -> m "Latest VCS version tag found: %s" tag); tag
4444+4545+let status () _ after until =
4646+ begin
4747+ Topkg.Vcs.get ()
4848+ >>= fun repo -> Topkg.Vcs.is_dirty repo
4949+ >>= fun dirty -> find_after repo after
5050+ >>= fun after -> Topkg.Vcs.changes repo ~after ~until
5151+ >>= fun changes ->
5252+ Logs.app (fun m -> m "%a" pp_status (dirty, after, changes));
5353+ Ok (if dirty || changes <> [] then 0 else 1)
5454+ end
5555+ |> Cli.handle_error
5656+5757+(* Command line interface *)
5858+5959+open Cmdliner
6060+6161+let after =
6262+ let doc = "Commit-ish $(docv) after which commits are considered.
6363+ Default is the latest VCS version tag of the form [v]X.Y.Z[+info]."
6464+ in
6565+ Arg.(value & opt (some string) None & info ["after"] ~doc ~docv:"COMMIT-ISH")
6666+6767+let until =
6868+ let doc = "Commit-ish $(docv) until which commits are considered." in
6969+ let docv = "COMMIT-ISH" in
7070+ Arg.(value & opt string "HEAD" & info ["until"] ~doc ~docv)
7171+7272+let doc = "List commits to publish in the next distribution"
7373+let sdocs = Manpage.s_common_options
7474+let exits =
7575+ (Cmd.Exit.info 0 ~doc:"changes have been detected.") ::
7676+ (Cmd.Exit.info 1 ~doc:"no changes have been detected.") ::
7777+ Cmd.Exit.defaults
7878+7979+let man_xrefs = [ `Main ]
8080+let man =
8181+ [ `S Manpage.s_description;
8282+ `P "The $(tname) command consults the package's VCS and outputs the
8383+ list of commits that define the changes for the next distribution." ]
8484+8585+let cmd =
8686+ Cmd.v (Cmd.info "status" ~doc ~sdocs ~exits ~man ~man_xrefs) @@
8787+ Term.(const status $ Cli.setup $ Cli.pkg_file $ after $ until)
+8
vendor/opam/topkg/src-bin/status.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** The [status] command. *)
77+88+val cmd : int Cmdliner.Cmd.t
+80
vendor/opam/topkg/src-bin/tag.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+open Bos_setup
77+88+let extract_version change_log =
99+ Topkg_care.Text.change_log_file_last_entry change_log
1010+ >>= fun (version, _) -> Ok version
1111+1212+let vcs_tag tag ~commit_ish ~force ~sign ~delete ~msg =
1313+ let msg = match msg with None -> strf "Distribution %s" tag | Some m -> m in
1414+ Topkg.Vcs.get ()
1515+ >>= fun repo -> match delete with
1616+ | true -> Topkg.Vcs.delete_tag repo tag
1717+ | false ->
1818+ Topkg.Vcs.tag repo ~force ~sign ~msg ~commit_ish tag >>| fun () ->
1919+ Logs.app (fun m -> m "Tagged version %a" Topkg_care.Pp.version tag)
2020+2121+let tag () pkg_file change_log tag commit_ish force sign delete msg =
2222+ begin
2323+ let pkg = Topkg_care.Pkg.v ?change_log pkg_file in
2424+ let tag = match tag with
2525+ | Some t -> Ok t
2626+ | None -> Topkg_care.Pkg.change_log pkg >>= fun cl -> extract_version cl
2727+ in
2828+ tag
2929+ >>= fun tag -> vcs_tag tag ~commit_ish ~force ~sign ~delete ~msg
3030+ >>= fun () -> Ok 0
3131+ end
3232+ |> Cli.handle_error
3333+3434+(* Command line interface *)
3535+3636+open Cmdliner
3737+3838+let version =
3939+ let doc = "The version tag to use. If absent, automatically extracted
4040+ from the package's change log; see topkg-log(1) for details."
4141+ in
4242+ Arg.(value & pos 0 (some string) None & info [] ~doc ~docv:"VERSION")
4343+4444+let commit =
4545+ let doc = "Commit-ish $(docv) to tag." in
4646+ Arg.(value & opt string "HEAD" & info ["commit"] ~doc ~docv:"COMMIT-ISH")
4747+4848+let msg =
4949+ let doc = "Commit message for the tag. If absent, the message
5050+ 'Distribution $(i,VERSION)' is used."
5151+ in
5252+ Arg.(value & opt (some string) None & info ["m"; "message"] ~doc ~docv:"MSG")
5353+5454+let sign =
5555+ let doc = "Sign the tag using the VCS's default signing key." in
5656+ Arg.(value & flag & info ["s"; "sign"] ~doc)
5757+5858+let force =
5959+ let doc = "If the tag exists, replace it rather than fail." in
6060+ Arg.(value & flag & info ["f"; "force"] ~doc)
6161+6262+let delete =
6363+ let doc = "Delete the specified tag rather than create it." in
6464+ Arg.(value & flag & info ["d"; "delete"] ~doc)
6565+6666+let doc = "Tag the package's source repository with a version"
6767+let sdocs = Manpage.s_common_options
6868+let exits = Cli.exits
6969+let man_xrefs = [ `Main; `Cmd "log" ]
7070+let man =
7171+ [ `S Manpage.s_description;
7272+ `P "The $(tname) command tags the package's VCS HEAD commit with a
7373+ version. If the version is not specified on the command line it is
7474+ automatically extracted from the package's change log; use
7575+ $(b,topkg log -t) to check the extracted value." ]
7676+7777+let cmd =
7878+ Cmd.v (Cmd.info "tag" ~doc ~sdocs ~exits ~man ~man_xrefs) @@
7979+ Term.(const tag $ Cli.setup $ Cli.pkg_file $ Cli.change_log $
8080+ version $ commit $ force $ sign $ delete $ msg)
+8
vendor/opam/topkg/src-bin/tag.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** The [tag] command. *)
77+88+val cmd : int Cmdliner.Cmd.t
+81
vendor/opam/topkg/src-bin/test.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+open Bos_setup
77+88+let test_args name build_dir list args =
99+ let on_some_use_opt opt to_arg = function
1010+ | None -> Cmd.empty
1111+ | Some value -> Cmd.(v opt % to_arg value)
1212+ in
1313+ let verb = Cli.propagate_verbosity_to_pkg_file () in
1414+ let build_dir = on_some_use_opt "--build-dir" Cmd.p build_dir in
1515+ let list = if list then Cmd.(v "--list") else Cmd.empty in
1616+ let name = on_some_use_opt "--pkg-name" (fun n -> n) name in
1717+ Cmd.(verb %% name %% list %% build_dir %% Cmd.of_list args)
1818+1919+let test () pkg_file pkg_name build_dir list args =
2020+ let pkg = Topkg_care.Pkg.v pkg_file in
2121+ let args = test_args pkg_name build_dir list args in
2222+ let out = OS.Cmd.out_stdout in
2323+ begin
2424+ OS.Dir.current ()
2525+ >>= fun dir -> Topkg_care.Pkg.test pkg ~dir ~args ~out
2626+ >>| (function ((), (_, `Exited 0)) -> 0 | _ -> 1)
2727+ end
2828+ |> Cli.handle_error
2929+3030+(* Command line interface *)
3131+3232+open Cmdliner
3333+3434+let args =
3535+ let doc = "Tests and arguments to the tests. If options are being
3636+ passed, needs to be specified after a -- token so that the
3737+ command line options do not get interpreted by $(b,topkg test)
3838+ itself. If arguments need to be specified for the test itself a
3939+ second -- token is needed."
4040+ in
4141+ Arg.(value & pos_all string [] & info [] ~doc ~docv:"[TEST]... [-- ARG...]")
4242+4343+let build_dir =
4444+ let doc = "Specifies the build directory $(docv). If absent, provided
4545+ by the package description. This is equivalent to specify
4646+ the same option after the first -- token"
4747+ in
4848+ let docv = "BUILD_DIR" in
4949+ Arg.(value & opt (some Cli.path_arg) None & info ["build-dir"] ~doc ~docv)
5050+5151+let list =
5252+ let doc = "Do not run the tests, list them. This is equivalent to
5353+ specify the same option after the first -- token."
5454+ in
5555+ Arg.(value & flag & info ["l"; "list"] ~doc)
5656+5757+let doc = "Run built package tests"
5858+let sdocs = Manpage.s_common_options
5959+let exits = Cmd.Exit.info 1 ~doc:"on test failure." :: Cli.exits
6060+let man_xrefs = [ `Main ]
6161+let man =
6262+ [ `S Manpage.s_synopsis;
6363+ `P "$(mname) $(tname) [$(i,OPTION)]... [-- [$(i,TEST)]... \
6464+ [-- [$(i,ARG)]...]]";
6565+ `S Manpage.s_description;
6666+ `P "The $(tname) command runs the tests that were built by
6767+ topkg-build(1). This is equivalent to invoke:";
6868+ `Pre "ocaml ./pkg/pkg.ml test [$(i,TEST)]... [-- [$(i,ARG)]...]";
6969+ `P "The value for $(i,TEST) can be a full path to the test executable
7070+ or simply the basename of the test executable with or without the file
7171+ extension. The option $(b,--list) lists the tests that were built.";
7272+ `P "Note that if you want to pass command line arguments to a test you
7373+ need to specify the token -- twice. For example to pass 'arg' to a
7474+ test 'mytest' use one of the following invocation:";
7575+ `Pre "topkg test -- mytest -- arg"; `Noblank;
7676+ `Pre "topkg test mytest -- -- arg" ]
7777+7878+let cmd =
7979+ Cmd.v (Cmd.info "test" ~doc ~sdocs ~exits ~man ~man_xrefs) @@
8080+ Term.(const test $ Cli.setup $ Cli.pkg_file $ Cli.pkg_name $ build_dir $
8181+ list $ args)
+8
vendor/opam/topkg/src-bin/test.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** The [test] command. *)
77+88+val cmd : int Cmdliner.Cmd.t
+44
vendor/opam/topkg/src-bin/topkg_bin.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+open Cmdliner
77+88+let cmds =
99+ [ Bistro.cmd; Browse.cmd; Build.cmd; Clean.cmd; Distrib.cmd; Doc.cmd;
1010+ Help.cmd; Ipc.cmd; Issue.cmd; Lint.cmd; Log.cmd; Opam.cmd;
1111+ Publish.cmd; Run.cmd; Status.cmd; Tag.cmd; Test.cmd; ]
1212+1313+let main () = `Help (`Pager, None)
1414+1515+(* Command line interface *)
1616+1717+let doc = "Topkg package care"
1818+let sdocs = Manpage.s_common_options
1919+let exits = Cli.exits
2020+let man =
2121+ [ `S Manpage.s_description;
2222+ `P "$(mname) takes care of topkg packages.";
2323+ `P "Use '$(mname) help release' for help to release a package.";
2424+ `Noblank;
2525+ `P "Use '$(mname) help delegate' for help about the topkg delegate.";
2626+ `Noblank;
2727+ `P "Use '$(mname) help troubleshoot' for a few troubleshooting tips.";
2828+ `Noblank;
2929+ `P "Use '$(mname) help $(i,COMMAND)' for help about $(i,COMMAND).";
3030+ `S Manpage.s_bugs;
3131+ `P "Report them, see $(i,%%PKG_HOMEPAGE%%) for contact information.";
3232+ `S Manpage.s_authors;
3333+ `P "Daniel C. Buenzli, $(i,http://erratique.ch)"; ]
3434+3535+let main =
3636+ let default = Term.(ret (const main $ Cli.setup)) in
3737+ let info = Cmd.info "topkg" ~version:"%%VERSION%%" ~doc ~sdocs ~exits ~man in
3838+ Cmd.group ~default info cmds
3939+4040+let main () =
4141+ Topkg.Private.disable_main ();
4242+ Cmd.eval' main
4343+4444+let () = if !Sys.interactive then () else exit (main ())
+327
vendor/opam/topkg/src-bin/toy_github_delegate.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+open Bos_setup
77+88+(* Publish documentation *)
99+1010+let repo_docdir_owner_repo_and_path_from_doc_uri uri =
1111+ (* Parses the $PATH of $SCHEME://$HOST/$REPO/$PATH *)
1212+ let uri_error uri =
1313+ R.msgf "Could not derive publication directory $PATH from opam doc \
1414+ field value %a; expected the pattern \
1515+ $SCHEME://$OWNER.github.io/$REPO/$PATH" String.dump uri
1616+ in
1717+ match Topkg_care.Text.split_uri ~rel:true uri with
1818+ | None -> Error (uri_error uri)
1919+ | Some (_, host, path) ->
2020+ if path = "" then Error (uri_error uri) else
2121+ (match String.cut ~sep:"." host with
2222+ | Some (owner, g) when String.equal g "github.io" -> Ok owner
2323+ | _ -> Error (uri_error uri))
2424+ >>= fun owner ->
2525+ match String.cut ~sep:"/" path with
2626+ | None -> Error (uri_error uri)
2727+ | Some (repo, "") -> Ok (owner, repo, Fpath.v ".")
2828+ | Some (repo, path) ->
2929+ (Fpath.of_string path >>| fun p -> owner, repo, Fpath.rem_empty_seg p)
3030+ |> R.reword_error_msg (fun _ -> uri_error uri)
3131+3232+let publish_doc_gh_pages uri name version docdir =
3333+ Fpath.of_string docdir
3434+ >>= fun docdir -> repo_docdir_owner_repo_and_path_from_doc_uri uri
3535+ >>= fun (owner, repo, dir) ->
3636+ let remote = strf "git@@github.com:%s/%s.git" owner repo in
3737+ let git_for_repo r = Cmd.of_list (Topkg.Cmd.to_list @@ Topkg.Vcs.cmd r) in
3838+ let create_empty_gh_pages git =
3939+ let msg = "Initial commit by topkg." in
4040+ let create () =
4141+ OS.Cmd.run Cmd.(v "git" % "init")
4242+ >>= fun () -> Topkg.Vcs.get ()
4343+ >>= fun repo -> Ok (git_for_repo repo)
4444+ >>= fun git -> OS.Cmd.run Cmd.(git % "checkout" % "--orphan" % "gh-pages")
4545+ >>= fun () -> OS.File.write (Fpath.v "README") "" (* need some file *)
4646+ >>= fun () -> OS.Cmd.run Cmd.(git % "add" % "README")
4747+ >>= fun () -> OS.Cmd.run Cmd.(git % "commit" % "README" % "-m" % msg)
4848+ in
4949+ OS.Dir.with_tmp "gh-pages-%s.tmp" (fun dir () ->
5050+ OS.Dir.with_current dir create () |> R.join
5151+ >>= fun () -> OS.Cmd.run Cmd.(git % "fetch" % Fpath.to_string dir
5252+ % "gh-pages")
5353+ ) () |> R.join
5454+ in
5555+ Topkg.Vcs.get ()
5656+ >>= fun repo -> Ok (git_for_repo repo)
5757+ >>= fun git ->
5858+ (match OS.Cmd.run Cmd.(git % "fetch" % remote % "gh-pages") with
5959+ | Ok () -> Ok ()
6060+ | Error _ -> create_empty_gh_pages git)
6161+ >>= fun () -> (OS.Cmd.run_out Cmd.(git % "rev-parse" % "FETCH_HEAD")
6262+ |> OS.Cmd.to_string)
6363+ >>= fun id -> OS.Cmd.run Cmd.(git % "branch" % "-f" % "gh-pages" % id)
6464+ >>= fun () -> Topkg_care.Delegate.publish_in_git_branch ~remote
6565+ ~branch:"gh-pages" ~name ~version ~docdir ~dir
6666+ >>= fun () -> Ok 0
6767+6868+(* Publish releases *)
6969+7070+let repo_and_owner_of_uri uri =
7171+ let uri_error uri =
7272+ R.msgf "Could not derive owner and repo from opam dev-repo \
7373+ field value %a; expected the pattern \
7474+ $SCHEME://$HOST/$OWNER/$REPO[.$EXT][/$DIR]" String.dump uri
7575+ in
7676+ match Topkg_care.Text.split_uri ~rel:true uri with
7777+ | None -> Error (uri_error uri)
7878+ | Some (_, _, path) ->
7979+ if path = "" then Error (uri_error uri) else
8080+ match String.cut ~sep:"/" path with
8181+ | None -> Error (uri_error uri)
8282+ | Some (owner, path) ->
8383+ let repo = match String.cut ~sep:"/" path with
8484+ | None -> path
8585+ | Some (repo, path) -> repo
8686+ in
8787+ begin
8888+ Fpath.of_string repo
8989+ >>= fun repo -> Ok (owner, Fpath.(to_string @@ rem_ext repo))
9090+ end
9191+ |> R.reword_error_msg (fun _ -> uri_error uri)
9292+9393+let steal_opam_publish_github_auth () =
9494+ let opam = Cmd.(v "opam") in
9595+ let publish = Fpath.v "plugins/opam-publish" in
9696+ OS.Cmd.exists opam >>= function
9797+ | false -> Ok None
9898+ | true ->
9999+ OS.Cmd.(run_out Cmd.(opam % "var" % "root") |> to_string)
100100+ >>= fun root -> Fpath.of_string root
101101+ >>= fun root -> OS.Path.query Fpath.(root // publish / "$(user).token")
102102+ >>= function
103103+ | [] -> Ok None
104104+ | (file, defs) :: _ ->
105105+ OS.File.read file >>= fun token ->
106106+ Ok (Some (strf "%s:%s" (String.Map.get "user" defs) token))
107107+108108+let github_auth ~owner =
109109+ match
110110+ steal_opam_publish_github_auth ()
111111+ |> Logs.on_error_msg ~use:(fun _ -> None)
112112+ with
113113+ | Some auth -> auth
114114+ | None -> OS.Env.(value "TOPKG_GITHUB_AUTH" string ~absent:owner)
115115+116116+let create_release_json version msg =
117117+ let escape_for_json s =
118118+ let len = String.length s in
119119+ let max = len - 1 in
120120+ let rec escaped_len i l =
121121+ if i > max then l else
122122+ match String.get s i with
123123+ | '\\' | '\"' | '\n' | '\r' | '\t' -> escaped_len (i + 1) (l + 2)
124124+ | _ -> escaped_len (i + 1) (l + 1)
125125+ in
126126+ let escaped_len = escaped_len 0 0 in
127127+ if escaped_len = len then s else
128128+ let b = Bytes.create escaped_len in
129129+ let rec loop i k =
130130+ if i > max then Bytes.unsafe_to_string b else
131131+ match String.get s i with
132132+ | ('\\' | '\"' | '\n' | '\r' | '\t' as c) ->
133133+ Bytes.set b k '\\';
134134+ let c = match c with
135135+ | '\\' -> '\\' | '\"' -> '\"' | '\n' -> 'n' | '\r' -> 'r'
136136+ | '\t' -> 't'
137137+ | _ -> assert false
138138+ in
139139+ Bytes.set b (k + 1) c; loop (i + 1) (k + 2)
140140+ | c ->
141141+ Bytes.set b k c; loop (i + 1) (k + 1)
142142+ in
143143+ loop 0 0
144144+ in
145145+ strf "{ \"tag_name\" : \"%s\", \
146146+ \"body\" : \"%s\" }" (escape_for_json version) (escape_for_json msg)
147147+148148+let run_with_auth auth curl =
149149+ let auth = strf "-u %s" auth in
150150+ OS.Cmd.(in_string auth |> run_io curl)
151151+152152+let curl_create_release curl version msg owner repo =
153153+ let parse_release_id resp = (* FIXME this is retired. *)
154154+ let headers = String.cuts ~sep:"\r\n" resp in
155155+ try
156156+ let not_slash c = not (Char.equal '/' c) in
157157+ let loc = List.find (String.is_prefix ~affix:"Location:") headers in
158158+ let id = String.take ~rev:true ~sat:not_slash loc in
159159+ match String.to_int id with
160160+ | None -> R.error_msgf "Could not parse id from location header %S" loc
161161+ | Some id -> Ok id
162162+ with Not_found ->
163163+ R.error_msgf "Could not find release id in response:\n%s."
164164+ (String.concat ~sep:"\n" headers)
165165+ in
166166+ let data = create_release_json version msg in
167167+ let uri = strf "https://api.github.com/repos/%s/%s/releases" owner repo in
168168+ let auth = github_auth ~owner in
169169+ let cmd = Cmd.(curl % "-D" % "-" % "--data" % data % uri) in
170170+ run_with_auth auth cmd |> OS.Cmd.to_string ~trim:false
171171+ >>= parse_release_id
172172+173173+let curl_upload_archive curl archive owner repo release_id =
174174+ let uri =
175175+ (* FIXME upload URI prefix should be taken from release creation
176176+ response *)
177177+ strf "https://uploads.github.com/repos/%s/%s/releases/%d/assets?name=%s"
178178+ owner repo release_id (Fpath.filename archive)
179179+ in
180180+ let auth = github_auth ~owner in
181181+ let data = Cmd.(v "--data-binary" % strf "@@%s" (Fpath.to_string archive)) in
182182+ let ctype = Cmd.(v "-H" % "Content-Type:application/x-tar") in
183183+ let cmd = Cmd.(curl %% ctype %% data % uri) in
184184+ OS.Cmd.(run_with_auth auth cmd |> to_stdout)
185185+186186+let publish_distrib uri name version msg archive =
187187+ let git_for_repo r = Cmd.of_list (Topkg.Cmd.to_list @@ Topkg.Vcs.cmd r) in
188188+ let curl = Cmd.(v "curl" % "-L" % "-s" % "-S" % "-K" % "-") in
189189+ Fpath.of_string archive
190190+ >>= fun archive -> OS.Cmd.must_exist curl
191191+ >>= fun curl -> Topkg.Vcs.get ()
192192+ >>= fun repo -> Ok (git_for_repo repo)
193193+ >>= fun git -> OS.Cmd.run Cmd.(git % "push" % "--force" % "--tags")
194194+ >>= fun () -> repo_and_owner_of_uri uri
195195+ >>= fun (owner, repo) -> curl_create_release curl version msg owner repo
196196+ >>= fun id -> curl_upload_archive curl archive owner repo id
197197+ >>= fun () -> Ok 0
198198+199199+(* Publish delegations *)
200200+201201+let unsupported = Ok 1
202202+203203+let publish = function
204204+| "distrib" :: uri :: name :: version :: msg :: archive :: _ ->
205205+ publish_distrib uri name version msg archive
206206+| "doc" :: uri :: name :: version :: msg :: docdir :: _ ->
207207+ publish_doc_gh_pages uri name version docdir
208208+| "alt" :: kind :: uri :: name :: version :: msg :: archive :: _ ->
209209+ unsupported
210210+| args ->
211211+ unsupported
212212+213213+(* Issue delegations *)
214214+215215+let issue = function
216216+| "list" :: uri :: _ -> unsupported
217217+| "show" :: uri :: id :: _ -> unsupported
218218+| "open" :: uri :: title :: descr :: _ -> unsupported
219219+| "close" :: uri :: id :: msg :: _ -> unsupported
220220+| args -> unsupported
221221+222222+(* Delegation requests *)
223223+224224+let request = function
225225+| "publish" :: args -> publish args
226226+| "issue" :: args -> issue args
227227+| args -> unsupported
228228+229229+(* Delegate tool commands *)
230230+231231+let ipc_cmd args =
232232+ begin match args with
233233+ | verbosity :: req ->
234234+ Logs.level_of_string verbosity >>= fun logs_level ->
235235+ Topkg.Log.level_of_string verbosity >>= fun topkg_level ->
236236+ Topkg.Log.set_level topkg_level;
237237+ Logs.set_level logs_level;
238238+ request req
239239+ | [] ->
240240+ R.error_msg "malformed delegate request, verbosity is missing"
241241+ end
242242+ |> Logs.on_error_msg ~use:(fun () -> 2)
243243+244244+let main_cmd () = `Help (`Pager, None)
245245+246246+(* Cli interface *)
247247+248248+open Cmdliner
249249+250250+let ipc_cmd =
251251+ let doc = "Delegate request IPCs" in
252252+ let man =
253253+ [ `S "DESCRIPTION";
254254+ `P "The $(tname) command implements the topkg delegate protocol.
255255+ See topkg-delegate(7) and $(mname) $(b,--help) for more
256256+ information." ]
257257+ in
258258+ let args =
259259+ let doc = "IPC call arguments" in
260260+ Arg.(value (pos_all string [] & info [] ~doc ~docv:"ARG"))
261261+ in
262262+ Cmd.v (Cmd.info "ipc" ~doc ~man) @@
263263+ Term.(const ipc_cmd $ args)
264264+265265+let main_cmd =
266266+ let doc = "Topkg's toy GitHub delegate" in
267267+ let envs =
268268+ [ Cmd.Env.info "TOPKG_GITHUB_AUTH" ~doc:"GitHub authentication data, see
269269+ the section GITHUB AUTHENTICATION for details." ]
270270+ in
271271+ let man_xrefs = [ `Tool "topkg" ] in
272272+ let man =
273273+ [ `S "DESCRIPTION";
274274+ `P "$(mname) is a toy topkg delegate for GitHub. It will disappear
275275+ once a decent GitHub delegate emerges. For more
276276+ information about topkg delegates, see topkg-delegate(7).";
277277+ `P "This delegate only supports the following delegations:";
278278+ `I ("$(b,topkg publish doc)",
279279+ "Commits and pushes the documentation to the gh-pages of the
280280+ source repository. The publication directory PATH in the branch is
281281+ determined by matching the opam 'doc' field against the
282282+ pattern SCHEME://OWNER.github.io/REPO/PATH.");
283283+ `I ("$(b,topkg publish distrib)",
284284+ "This requires curl(1). Creates a GitHub release with the
285285+ version and publication message given to the delegate and
286286+ uploads the distribution archive as a release artefact. This
287287+ requires GitHub authentication, see section GITHUB AUTHENTICATION
288288+ for details. Also bear in mind that error reporting
289289+ (e.g. if the release already exists) is made of raw JSON
290290+ responses and thus very user-unfriendly.");
291291+ `S "GITHUB AUTHENTICATION";
292292+ `P "This being a toy delegate, you get toy authentication. Here
293293+ are the steps, in order, that are tried to authenticate you on
294294+ GitHub.";
295295+ `I ("1. opam-publish token stealing.",
296296+ "If you have already used opam-publish, an authorization token
297297+ was generated for it that is keept in
298298+ \\$(opam config var root)/plugins/opam-publish/\\$(user).token. If
299299+ such a file exists, \\$(user) and the corresponding token will
300300+ be used for authentication.");
301301+ `I ("2. Environment variable.",
302302+ "You scan specify the user and the password or token using
303303+ the TOPKG_GITHUB_AUTH environment variable with a username:token
304304+ value, see $(i,https://developer.github.com/v3/auth/).");
305305+ `I ("3. Cli prompt.",
306306+ "As a last resort the username used for authentication is
307307+ the name of the GitHub owner of the repo (determined from
308308+ the $(i,DISTRIB_URI) URI, itself determined from the 'dev-repo'
309309+ field of the opam file, see topkg-delegate(7) and topkg's API
310310+ documentation for more details); in this case your GitHub
311311+ password will be prompted twice on the command line by curl (ugh).")]
312312+ in
313313+ let version = "%%VERSION%%" in
314314+ let default = Term.(ret (const main_cmd $ const ())) in
315315+ let info =
316316+ Cmd.info "toy-github-topkg-delegate" ~version ~doc ~envs ~man ~man_xrefs
317317+ in
318318+ Cmd.group ~default info [ipc_cmd]
319319+320320+let main () =
321321+ Topkg.Private.disable_main ();
322322+ match Cmd.eval_value main_cmd with
323323+ | Error _ -> 3
324324+ | Ok (`Ok ret) -> ret
325325+ | Ok _ -> 0
326326+327327+let () = if !Sys.interactive then () else exit (main ())
+13
vendor/opam/topkg/src-care/topkg_care.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+module Text = Topkg_care_text
77+module Pp = Topkg_care_text.Pp
88+module Opam = Topkg_care_opam
99+module OCamlbuild = Topkg_care_ocamlbuild
1010+module OCamlfind = Topkg_care_ocamlfind
1111+module Archive = Topkg_care_archive
1212+module Pkg = Topkg_care_pkg
1313+module Delegate = Topkg_care_delegate
+487
vendor/opam/topkg/src-care/topkg_care.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** Topkg package care.
77+88+ Tools to help the package developer in the life cycle of the
99+ package. Most of these tools can be invoked directly from the
1010+ command line via the [topkg] binary installed by the [topkg-care]
1111+ package.
1212+1313+ {b WARNING.} Do not use this API in your package description file, use
1414+ only {!Topkg}. This API was not thoroughly designed, is not stable and
1515+ may change even between minor versions of [topkg]. Use at your own
1616+ risk. *)
1717+1818+open Bos_setup
1919+2020+(** {1 Helpers} *)
2121+2222+(** Text processing helpers. *)
2323+module Text : sig
2424+2525+ (** {1 Marked-up text files}
2626+2727+ {b Warning.} Some of the following functions are not serious and can
2828+ break on certain valid inputs in all sorts of fashion. To understand
2929+ breakage bear in mind that they operate line-wise. *)
3030+3131+ type flavour = [ `Markdown | `Asciidoc ]
3232+ (** The type for text document formats. *)
3333+3434+ val flavour_of_fpath : Fpath.t -> flavour option
3535+ (** [flavour_of_fpath p] determines a flavour according to the
3636+ extension of [p] as follows:
3737+ {ul
3838+ {- [Some `Markdown] for [.md]}
3939+ {- [Some `Asciidoc] for [.asciidoc] or [.adoc]}
4040+ {- [None] otherwise}} *)
4141+4242+ val head : ?flavour:flavour -> string -> (string * string) option
4343+ (** [head ~flavour text] extracts the {e head} of the document [text] of
4444+ flavour [flavour] (defaults to [`Markdown]).
4545+4646+ The head is defined as follows:
4747+ {ul
4848+ {- Anything before the first header is discarded.}
4949+ {- The first header is kept in the first component}
5050+ {- Everything that follows until the next header of the same or greater
5151+ level is kept discarding trailing blank lines.}} *)
5252+5353+ val header_title : ?flavour:flavour -> string -> string
5454+ (** [header_title ~flavour text] extract the title of a header [text]
5555+ of flavour [flavour] (defaults to [`Markdown]). *)
5656+5757+ (** {1 Toy change log parsing} *)
5858+5959+ val change_log_last_entry :
6060+ ?flavour:flavour -> string -> (string * (string * string)) option
6161+ (** [change_log_last_version ~flavour text] tries to parse the last
6262+ change log entry of [text] (i.e. the {!head} of [text]) into
6363+ [Some (version, (header, text))], where [(header,text)] is the
6464+ result of {!head} and [version] a version number extracted from
6565+ [header] (see [topkg-log(2)] for details). *)
6666+6767+ val change_log_file_last_entry :
6868+ Fpath.t -> ((string * (string * string)), R.msg) result
6969+ (** [change_log_file_last_entry file] tries to parse the last
7070+ change log entry of the file [file] using {!flavour_of_fpath} and
7171+ and {!change_log_last_entry}. *)
7272+7373+ (** {1 Toy URI parsing} *)
7474+7575+ val split_uri : ?rel:bool -> string -> (string * string * string) option
7676+ (** [split_uri uri] splits [uri] into a triple [(scheme, host, path)]. If
7777+ [rel] is [true] (defaults to [false]), a leading ["/"] in [path] is
7878+ removed. *)
7979+8080+ (** {1 Edit and page text} *)
8181+8282+ val edit_file : Fpath.t -> (int, R.msg) result
8383+ (** [edit_file f] invokes the tool mentioned in the [EDITOR]
8484+ environment variable with [f] and returns the exit code of
8585+ the program. *)
8686+8787+ val find_pager : don't:bool -> (Cmd.t option, R.msg) result
8888+ (** [find ~no_pager] is an optional pager command. If [don't] is
8989+ [true] returns [None]. Otherwise first consults the [PAGER] environment
9090+ variable, then tries [less] or [more] in that order. If the [TERM]
9191+ environment variable is ["dumb"] or undefined unconditionaly returns
9292+ [None]. *)
9393+end
9494+9595+(** Pretty printers. *)
9696+module Pp : sig
9797+9898+ (** {1 Pretty printers} *)
9999+100100+ val name : string Fmt.t
101101+ (** [name] formats a package name. *)
102102+103103+ val version : string Fmt.t
104104+ (** [version] formats a package version. *)
105105+106106+ val commit : string Fmt.t
107107+ (** [commit] formats a commit-ish. *)
108108+109109+ val dirty : unit Fmt.t
110110+ (** [dirty] formats a "dirty" string. *)
111111+112112+ val path : Fpath.t Fmt.t
113113+ (** [path] formats a bold path *)
114114+115115+ val status : [`Ok | `Fail] Fmt.t
116116+ (** [status] formats a result status. *)
117117+end
118118+119119+(** [opam] helpers. *)
120120+module Opam : sig
121121+122122+ (** {1:cmd Command} *)
123123+124124+ val cmd : Cmd.t
125125+ (** [cmd] is a command for [opam] looked up using
126126+ {!Topkg.Conf.tool}[ "opam" `Host_os]. *)
127127+128128+ (** {1:publish Publish} *)
129129+130130+ val ensure_publish : unit -> (unit, R.msg) result
131131+ (** [ensure_publish ()] makes sure [opam-publish] is in the executable
132132+ search PATH. *)
133133+134134+ val submit : ?msg:string -> opam_file:Fpath.t -> unit -> (unit, R.msg) result
135135+ (** [submit ~opam_file] submits the opam file [opam_file] with [opam-publish]
136136+ and submission message [msg] (if any) to the OCaml opam repository. *)
137137+138138+ (** {1:pkgs Packages} *)
139139+140140+ val ocaml_base_packages : String.set
141141+ (** [ocaml_base_packages] are the base opam packages distributed
142142+ with OCaml: ["base-bigarray"], ["base-bytes"], ["base-threads"],
143143+ ["base-unix"]. *)
144144+145145+ (** {1:file Files} *)
146146+147147+ (** opam files *)
148148+ module File : sig
149149+150150+ (** {1:file opam file} *)
151151+152152+ val field_names : String.set
153153+ (** [field_names] is the maximal domain of the map returned by
154154+ {!fields}, excluding extension fields (not yet supported by
155155+ [opam-lib] 1.2.2). *)
156156+157157+ val fields : Fpath.t -> ((string list) String.map , R.msg) result
158158+ (** [fields f] returns a simplified model of the fields of the opam
159159+ file [f]. The domain of the result is included in
160160+ {!field_names}. Note that the [depends:] and [depopts:] fields
161161+ are returned without version constraints. *)
162162+163163+ (** {1:deps Dependencies} *)
164164+165165+ val deps : ?opts:bool -> (string list) String.map -> String.set
166166+ (** [deps ~opts fields] returns the packages mentioned in the [depends:]
167167+ fields, if [opts] is [true] (default) those from [depopts:] are added
168168+ aswell. *)
169169+ end
170170+171171+ (** [descr] files. *)
172172+ module Descr : sig
173173+174174+ (** {1:descr Descr file} *)
175175+176176+ type t = string * string
177177+ (** The type for opam [descr] files, the package synopsis and the
178178+ description. *)
179179+180180+ val of_string : string -> (t, R.msg) result
181181+ (** [of_string s] is a description from the string [s]. *)
182182+183183+ val to_string : t -> string
184184+ (** [to_string d] is [d] as a string. *)
185185+186186+ val to_opam_fields : t -> string
187187+ (** [to_opam_fields d] is [d] as synopsis and description opam v2
188188+ fields. *)
189189+190190+ val of_readme :
191191+ ?flavour:Text.flavour -> string -> (t, R.msg) result
192192+ (** [of_readme r] extracts an opam description file from a readme [r]
193193+ with a certain structure. *)
194194+195195+ val of_readme_file : Fpath.t -> (t, R.msg) result
196196+ (** [of_readme_file f] extracts an opam description file from
197197+ a readme file [f] using {!Text.flavour_of_fpath} and
198198+ {!of_readme}. *)
199199+ end
200200+201201+ (** [url] files. *)
202202+ module Url : sig
203203+204204+ (** {1:url Url file} *)
205205+206206+ type t
207207+ (** The type for url file contents. *)
208208+209209+ val v : uri:string -> checksum:string -> t
210210+ (** [v ~uri ~checksum] is an URL file for URI [uri] with
211211+ checksum [checksum]. *)
212212+213213+ val with_distrib_file : uri:string -> Fpath.t -> (t, R.msg) result
214214+ (** [with_distrib_file ~uri f] is an URL file for URI [uri] with
215215+ the checksum of file [f]. *)
216216+217217+ val to_opam_section : t -> string
218218+ (** [to_opam_section u] is [u] as an opam v2 url section. *)
219219+ end
220220+end
221221+222222+(** [ocamlbuild] helpers. *)
223223+module OCamlbuild : sig
224224+225225+ (** {1:cmd Command} *)
226226+227227+ val cmd : Cmd.t
228228+ (** [cmd] is a command for [ocamlbuild] looked up using
229229+ {!Topkg.Conf.tool}[ "ocamlbuild" `Host_os]. *)
230230+231231+ (** {1 Packages} *)
232232+233233+ val package_tags : ?roots:bool -> Fpath.t -> (String.set, R.msg) result
234234+ (** [packages ~roots f] is the set of packages identifiers
235235+ mentioned by the
236236+ {{:https://github.com/ocaml/ocamlbuild/blob/master/manual/manual.adoc#17-findlib-based-packages}
237237+ package tags} of the [_tags] file [f]. If [roots] is [true]
238238+ (defaults to [false]) only root packages, i.e. the identifier
239239+ before the first ['.'], are in the set.
240240+241241+ {b Warning.} This is a very dumb parsing that simply looks up
242242+ for all ["package($ID)"] and ["package($ID0[, ]*$ID1...)"]
243243+ patterns in the [_tags] file. *)
244244+end
245245+246246+(** [ocamlfind] helpers. *)
247247+module OCamlfind : sig
248248+249249+ (** {1:cmd Command} *)
250250+251251+ val cmd : Cmd.t
252252+ (** [cmd] is a command for [ocamlfind] looked up using
253253+ {!Topkg.Conf.tool}[ "ocamlfind" `Host_os]. *)
254254+255255+ (** {1 Packages} *)
256256+257257+ val base_packages : String.set
258258+ (** [base_packages] are the OCamlfind packages that are
259259+ distributed with OCaml. *)
260260+end
261261+262262+(** Archive file creation. *)
263263+module Archive : sig
264264+265265+ (** {1 Ustar archives} *)
266266+267267+ val tar :
268268+ Fpath.t -> exclude_paths:Fpath.set -> root:Fpath.t -> mtime:int ->
269269+ (string, R.msg) result
270270+ (** [tar dir ~exclude_paths ~root ~mtime] is a (us)tar archive that
271271+ contains the file hierarchy [dir] except the relative
272272+ hierarchies present in [exclude_paths]. In the archive, members
273273+ of [dir] are rerooted at [root] and sorted according to
274274+ {!Fpath.compare}. They have their modification time set to
275275+ [mtime] and their file permissions are [0o775] for directories
276276+ and files executable by the user and [0o664] for other files. No other
277277+ file metadata is preserved.
278278+279279+ {b Note.} This is a pure OCaml implementation, no [tar] tool is needed. *)
280280+281281+ (** {1 Bzip2 compression and unarchiving} *)
282282+283283+ val ensure_bzip2 : unit -> (unit, R.msg) result
284284+ (** [ensure_bzip2 ()] makes sure the [bzip2] utility is available. *)
285285+286286+ val bzip2 : string -> dst:Fpath.t -> (unit, R.msg) result
287287+ (** [bzip2 s dst] compresses [s] to [dst] using bzip2. *)
288288+289289+ val ensure_tar : unit -> (unit, R.msg) result
290290+ (** [ensure_tar ()] makes sure the [tar] utility is available. *)
291291+292292+ val untbz : ?clean:bool -> Fpath.t -> (Fpath.t, R.msg) result
293293+ (** [untbz ~clean ar] untars the tar bzip2 archive [ar] in the same
294294+ directory as [ar] and returns a base directory for [ar]. If [clean] is
295295+ [true] (defaults to [false]) first delete the base directory if it
296296+ exists. *)
297297+end
298298+299299+(** {1 Package care} *)
300300+301301+(** Package description. *)
302302+module Pkg : sig
303303+304304+ (** {1 Packages} *)
305305+306306+ type t
307307+ (** The type for package descriptions. *)
308308+309309+ val v :
310310+ ?name:string ->
311311+ ?version:string ->
312312+ ?delegate:Cmd.t ->
313313+ ?build_dir:Fpath.t ->
314314+ ?opam:Fpath.t ->
315315+ ?opam_descr:Fpath.t ->
316316+ ?readme:Fpath.t ->
317317+ ?change_log:Fpath.t ->
318318+ ?license:Fpath.t ->
319319+ ?distrib_uri:string ->
320320+ ?distrib_file:Fpath.t ->
321321+ ?publish_msg:string ->
322322+ ?publish_artefacts:[ `Distrib | `Doc | `Alt of string] list ->
323323+ Fpath.t -> t
324324+ (** [v pkg_file] is a package from description file [pkg_file] which
325325+ is loaded only if needed. The optional parameters allow to
326326+ override [pkg_file]'s definition. *)
327327+328328+ val pkg_file : t -> Fpath.t
329329+ (** [pkg_file p] is [p]'s description file. *)
330330+331331+ val name : t -> (string, R.msg) result
332332+ (** [name p] is [p]'s name. *)
333333+334334+ val version : t -> (string, R.msg) result
335335+ (** [version p] is [p]'s version string.*)
336336+337337+ val delegate : t -> (Cmd.t, R.msg) result
338338+ (** [delegate p] is [p]'s delegate. *)
339339+340340+ val build_dir : t -> (Fpath.t, R.msg) result
341341+ (** [build_dir p] is [p]'s build directory. *)
342342+343343+ val opam : t -> (Fpath.t, R.msg) result
344344+ (** [opam p] is [p]'s opam file. *)
345345+346346+ val opam_descr : t -> (Opam.Descr.t * bool, R.msg) result
347347+ (** [opam_descr p] is [p]'s opam description. The boolean indicates
348348+ if the description was found in the opam file itself. *)
349349+350350+ val opam_field : t -> string -> (string list option, R.msg) result
351351+ (** [opam_field p f] looks up field [f] of [p]'s opam file. *)
352352+353353+ val opam_fields : t -> (string list String.map, R.msg) result
354354+ (** [opam_fields p] are [p]'s opam file fields. *)
355355+356356+ val readmes : t -> (Fpath.t list, R.msg) result
357357+ (** [readmes p] are [p]'s readme files. *)
358358+359359+ val readme : t -> (Fpath.t, R.msg) result
360360+ (** [readme p] is the first element of [readmes p]. *)
361361+362362+ val change_logs : t -> (Fpath.t list, R.msg) result
363363+ (** [change_logs p] are [p]'s change logs. *)
364364+365365+ val change_log : t -> (Fpath.t, R.msg) result
366366+ (** [change_log p] is the first element of [change_logs p]. *)
367367+368368+ val licenses : t -> (Fpath.t list, R.msg) result
369369+ (** [licenses p] are [p]'s license files. *)
370370+371371+ val distrib_uri : ?raw:bool -> t -> (string, R.msg) result
372372+ (** [distrib_uri p] is [p]'s distribution URI. If [raw] is [true]
373373+ defaults to [false], [p]'s raw URI distribution pattern is returned. *)
374374+375375+ val distrib_file : t -> (Fpath.t, R.msg) result
376376+ (** [distrib_file p] is [p]'s distribution archive. *)
377377+378378+ val publish_msg : t -> (string, R.msg) result
379379+ (** [publish_msg p] is [p]'s distribution publication message. *)
380380+381381+ (** {1 Test} *)
382382+383383+ val test :
384384+ t -> dir:Fpath.t -> args:Cmd.t ->
385385+ out:(OS.Cmd.run_out -> ('a, R.msg) result) -> ('a, R.msg) result
386386+387387+ (** {1 Build} *)
388388+389389+ val build :
390390+ t -> dir:Fpath.t -> args:Cmd.t ->
391391+ out:(OS.Cmd.run_out -> ('a, R.msg) result) -> ('a, R.msg) result
392392+393393+ (** {1 Clean} *)
394394+395395+ val clean :
396396+ t -> dir:Fpath.t -> args:Cmd.t ->
397397+ out:(OS.Cmd.run_out -> ('a, R.msg) result) -> ('a, R.msg) result
398398+399399+ (** {1 Distribution} *)
400400+401401+ val distrib_archive : t -> keep_dir:bool -> (Fpath.t, R.msg) result
402402+ (** [distrib_archive p ~keep_dir] creates a distribution archive
403403+ for [p] and returns its path. If [keep_dir] is [true] the
404404+ repository checkout used to create the distribution archive
405405+ is kept in the build directory. *)
406406+407407+ val distrib_filename : ?opam:bool -> t -> (Fpath.t, R.msg) result
408408+ (** [distrib_filename ~opam p] is a distribution filename for [p].
409409+ If [opam] is [true] (defaults to [false]), the name follows
410410+ opam's naming conventions. *)
411411+412412+ val publish_artefacts : t ->
413413+ ([ `Distrib | `Doc | `Alt of string ] list, R.msg) result
414414+ (** [publish_artefacts p] are [p]'s publication artefacts. *)
415415+416416+ (** {1 Lint} *)
417417+418418+ type lint = [ `Custom | `Std_files | `Meta | `Opam | `Deps ]
419419+ (** The type for lints. *)
420420+421421+ val lint_all : lint list
422422+ (** [lint_all] is a list with all lint values. *)
423423+424424+ val lint :
425425+ ?ignore_pkg:bool -> t -> dir:Fpath.t -> lint list ->
426426+ (int, R.msg) result
427427+ (** [distrib ~ignore_pkg p ~dir lints] performs the lints
428428+ mentioned in [lints] in a directory [dir] on the package [p].
429429+ If [ignore_pkg] is [true] [p]'s definitions are ignored. *)
430430+end
431431+432432+(** Package delegate. *)
433433+module Delegate : sig
434434+435435+ (** {1 Publish} *)
436436+437437+ val publish_distrib :
438438+ Pkg.t -> msg:string -> archive:Fpath.t -> (unit, R.msg) result
439439+ (** [publish_distrib p ~msg ~archive] publishes the distribution
440440+ archive [archive] of package [p] with publication message [msg]. *)
441441+442442+ val publish_doc :
443443+ Pkg.t -> msg:string -> docdir:Fpath.t -> (unit, R.msg) result
444444+ (** [publish_distrib p ~msg ~docdir] publishes the documentation
445445+ directory [docdir] of package [p] with publication message [msg]. *)
446446+447447+ val publish_alt :
448448+ Pkg.t -> kind:string -> msg:string -> archive:Fpath.t ->
449449+ (unit, R.msg) result
450450+ (** [publish_alt p ~kind ~msg ~archive] publishes the
451451+ artefact [kind] for distribution archive [archive] of package [p]
452452+ with publication message [msg]. *)
453453+454454+ (** {2 Helpers} *)
455455+456456+ val publish_in_git_branch :
457457+ remote:string -> branch:string ->
458458+ name:string -> version:string -> docdir:Fpath.t ->
459459+ dir:Fpath.t -> (unit, R.msg) result
460460+ (** [publish_in_git_branch ~remote ~branch ~name ~version ~docdir ~dir]
461461+ publishes the documentation directory [docdir] of a package
462462+ named [name] at version [version] by replacing the [dir]
463463+ sub-directory of the branch [branch] of the current working
464464+ directory git repository (use ["."] to copy the docdir at the
465465+ root directory of the branch) and pushes the branch to [remote].
466466+467467+ {b Note.} The publication procedure first checkouts the
468468+ [gh-pages] in a temporary clone located in the {!Fpath.parent}
469469+ directory of [docdir]. The [branch] branch of this clone is then
470470+ pushed to the current working git repository, whose [branch]
471471+ branch is then pushed to the [remote] repository. *)
472472+473473+ (** {1 Issues} *)
474474+475475+ val issue_list : Pkg.t -> (unit, R.msg) result
476476+ (** [issue_list p] outputs the issue list on stdout. *)
477477+478478+ val issue_show : Pkg.t -> id:string -> (unit, R.msg) result
479479+ (** [issue_show p ~id] outputs information about issue [id] on stdout. *)
480480+481481+ val issue_open : Pkg.t -> title:string -> body:string -> (unit, R.msg) result
482482+ (** [issue_open p ~title ~body] create a new issue with title [title]
483483+ and description body [body]. *)
484484+485485+ val issue_close : Pkg.t -> id:string -> msg:string -> (unit, R.msg) result
486486+ (** [issue_close p ~id ~msg] closes issue [id] with message [msg]. *)
487487+end
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+open Bos_setup
77+88+(* Ustar archives *)
99+1010+module Tar = struct
1111+1212+ type ptime = int
1313+ type t = string list
1414+1515+ let empty = []
1616+1717+ (* Header.
1818+1919+ See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/\
2020+ pax.html#tag_20_92_13_06 *)
2121+2222+ let to_unix_path_string =
2323+ if Fpath.dir_sep = "/" then Fpath.to_string else
2424+ fun f -> String.concat ~sep:"/" (Fpath.segs f)
2525+2626+ let set_filename h f =
2727+ let s = to_unix_path_string f in
2828+ match String.length s with
2929+ | n when n <= 100 -> Bytes.blit_string s 0 h 0 (String.length s)
3030+ | n ->
3131+ try match String.cut ~rev:true ~sep:"/" s with
3232+ | None -> raise Exit
3333+ | Some (p, n) ->
3434+ (* This could be made more clever by trying to find
3535+ the slash nearest to the half string position. *)
3636+ if String.length p > 155 || String.length n > 100 then raise Exit;
3737+ Bytes.blit_string n 0 h 0 (String.length n);
3838+ Bytes.blit_string p 0 h 345 (String.length p);
3939+ with
4040+ | Exit -> failwith (strf "%a: file name too long" Fpath.pp f)
4141+4242+ let set_string off h s = Bytes.blit_string s 0 h off (String.length s)
4343+ let set_octal field off len (* terminating NULL included *) h n =
4444+ let octal = Printf.sprintf "%0*o" (len - 1) n in
4545+ if String.length octal < len
4646+ then Bytes.blit_string octal 0 h off (String.length octal) else
4747+ failwith (strf "field %s: can't encode %d in %d-digit octal number"
4848+ field (len - 1) n)
4949+5050+ let header_checksum h =
5151+ let len = Bytes.length h in
5252+ let rec loop acc i =
5353+ if i > len then acc else
5454+ loop (acc + (Char.to_int @@ Bytes.unsafe_get h i)) (i + 1)
5555+ in
5656+ loop 0 0
5757+5858+ let header fname mode mtime size typeflag =
5959+ try
6060+ let h = Bytes.make 512 '\x00' in
6161+ set_filename h fname;
6262+ set_octal "mode" 100 8 h mode;
6363+ set_octal "owner" 108 8 h 0;
6464+ set_octal "group" 116 8 h 0;
6565+ set_octal "size" 124 12 h size;
6666+ set_octal "mtime" 136 12 h mtime;
6767+ set_string 148 h " "; (* Checksum *)
6868+ set_string 156 h typeflag;
6969+ set_string 257 h "ustar";
7070+ set_string 263 h "00";
7171+ set_octal "devmajor" 329 8 h 0;
7272+ set_octal "devminor" 329 8 h 0;
7373+ let c = header_checksum h in
7474+ set_octal "checksum" 148 9 (* not NULL terminated *) h c;
7575+ Ok (Bytes.unsafe_to_string h)
7676+ with Failure msg -> R.error_msg msg
7777+7878+ (* Files *)
7979+8080+ let padding content = match String.length content mod 512 with
8181+ | 0 -> ""
8282+ | n -> Bytes.unsafe_to_string (Bytes.make (512 - n) '\x00')
8383+8484+ let add t fname ~mode ~mtime kind =
8585+ let typeflag, size, data = match kind with
8686+ | `Dir -> "5", 0, []
8787+ | `File cont -> "0", String.length cont, [cont; padding cont]
8888+ in
8989+ header fname mode mtime size typeflag
9090+ >>| fun header -> List.rev_append data (header :: t)
9191+9292+ (* Encode *)
9393+9494+ let to_string t =
9595+ let end_of_file = Bytes.unsafe_to_string (Bytes.make 1024 '\x00') in
9696+ String.concat (List.rev (end_of_file :: t))
9797+end
9898+9999+let path_set_of_dir dir ~exclude_paths =
100100+ let add_prefix p acc = Fpath.(Set.add (dir // p) acc) in
101101+ let exclude_paths = Fpath.Set.(fold add_prefix exclude_paths empty) in
102102+ let not_excluded p = Ok (not (Fpath.Set.mem p exclude_paths)) in
103103+ let traverse = `Sat not_excluded in
104104+ let elements = `Sat not_excluded in
105105+ let err _ e = e in
106106+ OS.Dir.fold_contents ~dotfiles:true ~err ~elements ~traverse
107107+ Fpath.Set.add Fpath.Set.empty dir
108108+109109+let tar dir ~exclude_paths ~root ~mtime =
110110+ let tar_add file tar =
111111+ let fname = match Fpath.rem_prefix dir file with
112112+ | None -> assert false
113113+ | Some file -> Fpath.(root // file)
114114+ in
115115+ Logs.info (fun m -> m "Archiving %a" Fpath.pp fname);
116116+ tar
117117+ >>= fun tar -> OS.Dir.exists file
118118+ >>= function
119119+ | true -> Tar.add tar fname ~mode:0o775 ~mtime `Dir
120120+ | false ->
121121+ OS.Path.Mode.get file
122122+ >>= fun mode -> OS.File.read file
123123+ >>= fun contents ->
124124+ let mode = if 0o100 land mode > 0 then 0o775 else 0o664 in
125125+ Tar.add tar fname ~mode ~mtime (`File contents)
126126+ in
127127+ path_set_of_dir dir ~exclude_paths
128128+ >>= fun fset -> Fpath.Set.fold tar_add fset (Ok Tar.empty)
129129+ >>| fun tar -> Tar.to_string tar
130130+131131+(* Bzip2 compression and unarchiving *)
132132+133133+let bzip2_cmd = OS.Env.(value "TOPKG_BZIP2" cmd ~absent:(Cmd.v "bzip2"))
134134+let ensure_bzip2 () = OS.Cmd.must_exist bzip2_cmd >>| fun _ -> ()
135135+let bzip2 s ~dst = OS.Cmd.(in_string s |> run_io bzip2_cmd |> to_file dst)
136136+137137+let tar_cmd = OS.Env.(value "TOPKG_TAR" cmd ~absent:(Cmd.v "tar"))
138138+let ensure_tar () = OS.Cmd.must_exist tar_cmd >>| fun _ -> ()
139139+let untbz ?(clean = false) ar =
140140+ let clean_dir dir = OS.Dir.exists dir >>= function
141141+ | true when clean -> OS.Dir.delete ~recurse:true dir
142142+ | _ -> Ok ()
143143+ in
144144+ let archive_dir, ar = Fpath.split_base ar in
145145+ let unarchive ar =
146146+ let dir = Fpath.rem_ext ar in
147147+ OS.Cmd.must_exist tar_cmd
148148+ >>= fun cmd -> clean_dir dir
149149+ >>= fun () -> OS.Cmd.run Cmd.(tar_cmd % "-xjf" % p ar)
150150+ >>= fun () -> Ok Fpath.(archive_dir // dir)
151151+ in
152152+ R.join @@ OS.Dir.with_current archive_dir unarchive ar
+51
vendor/opam/topkg/src-care/topkg_care_archive.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** Archive creation.
77+88+ See {!Topkg_care.Archive}. *)
99+1010+open Bos_setup
1111+1212+(** {1 Ustar archives} *)
1313+1414+(** ustar encoder.
1515+1616+ {b References}.
1717+ {{:http://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html#tag_20_92_13_06}ustar Interchange Format} in POSIX 1003.1, 2013. *)
1818+module Tar : sig
1919+2020+ (** {1 Ustar encoder} *)
2121+2222+ type ptime = int
2323+ (** The type for POSIX times in seconds since the epoch. *)
2424+2525+ type t
2626+ (** The type for ustar archives. *)
2727+2828+ val empty : t
2929+ (** [empty] is the empty ustar archive. *)
3030+3131+ val add :
3232+ t -> Fpath.t -> mode:int -> mtime:ptime -> [`Dir | `File of string ] ->
3333+ (t, R.msg) result
3434+ (** [add a f mode mtime kind] adds to archive [a] an element of
3535+ type [kind] with file path [f], permission mode [mode] and modificaton
3636+ time [mtime]. *)
3737+3838+ val to_string : t -> string
3939+ (** [to_string a] is the byte serialization of the archive [a]. *)
4040+end
4141+4242+val tar :
4343+ Fpath.t -> exclude_paths:Fpath.set -> root:Fpath.t -> mtime:int ->
4444+ (string, R.msg) result
4545+4646+(** {1 Bzip2 compression and unarchiving} *)
4747+4848+val ensure_bzip2 : unit -> (unit, R.msg) result
4949+val bzip2 : string -> dst:Fpath.t -> (unit, R.msg) result
5050+val ensure_tar : unit -> (unit, R.msg) result
5151+val untbz : ?clean:bool -> Fpath.t -> (Fpath.t, R.msg) result
+148
vendor/opam/topkg/src-care/topkg_care_delegate.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+open Bos_setup
77+88+(* Running the delegate *)
99+1010+let run_delegate pkg args =
1111+ let verbosity = Logs.level_to_string (Logs.level ()) in
1212+ Topkg_care_pkg.delegate pkg
1313+ >>= fun del -> Ok Cmd.(del % "ipc" % verbosity %% args)
1414+ >>= fun cmd -> OS.Cmd.run_status cmd
1515+ >>= function
1616+ | `Exited 0 -> Ok ()
1717+ | `Exited 1 ->
1818+ R.error_msgf "Action unsupported by delegate %a" Cmd.pp del
1919+ | (`Exited n | `Signaled n) ->
2020+ R.error_msgf "Delegate %a errored with %d" Cmd.pp del n
2121+2222+(* Publish request *)
2323+2424+let publish_distrib p ~msg ~archive =
2525+ Topkg_care_pkg.name p
2626+ >>= fun name -> Topkg_care_pkg.version p
2727+ >>= fun version -> Topkg_care_pkg.distrib_uri p
2828+ >>= fun distrib_uri ->
2929+ run_delegate p Cmd.(v "publish" % "distrib" % distrib_uri %
3030+ name % version % msg % p archive)
3131+3232+let publish_doc p ~msg ~docdir =
3333+ let doc_uri p = Topkg_care_pkg.opam_field_hd p "doc" >>= function
3434+ | None -> Ok ""
3535+ | Some uri -> Ok uri
3636+ in
3737+ Topkg_care_pkg.name p
3838+ >>= fun name -> Topkg_care_pkg.version p
3939+ >>= fun version -> doc_uri p
4040+ >>= fun doc_uri ->
4141+ run_delegate p Cmd.(v "publish" % "doc" % doc_uri % name % version % msg %
4242+ p docdir)
4343+4444+let publish_alt p ~kind ~msg ~archive =
4545+ Topkg_care_pkg.name p
4646+ >>= fun name -> Topkg_care_pkg.version p
4747+ >>= fun version -> Topkg_care_pkg.distrib_uri p
4848+ >>= fun distrib_uri ->
4949+ run_delegate p Cmd.(v "publish" % "alt" % distrib_uri % kind %
5050+ name % version % msg % p archive)
5151+5252+let publish_in_git_branch ~remote ~branch ~name ~version ~docdir ~dir =
5353+ let pp_distrib ppf (name, version) =
5454+ Fmt.pf ppf "%a %a"
5555+ Topkg_care_text.Pp.name name Topkg_care_text.Pp.version version
5656+ in
5757+ let log_publish_result msg distrib dir =
5858+ Logs.app (fun m -> m "%s %a@ in@ directory@ %a@ of@ gh-pages@ branch"
5959+ msg pp_distrib distrib Fpath.pp dir)
6060+ in
6161+ let cp src dst =
6262+ let dst_is_root = Fpath.is_current_dir dst in
6363+ let src =
6464+ if dst_is_root then Fpath.to_dir_path src else Fpath.rem_empty_seg src
6565+ in
6666+ (* FIXME we lost Windows friends here, fix bos #30 *)
6767+ OS.Cmd.run Cmd.(v "cp" % "-R" % p src % p dst)
6868+ in
6969+ let delete dir =
7070+ if not (Fpath.is_current_dir dir) then OS.Dir.delete ~recurse:true dir else
7171+ let delete acc p = acc >>= fun () -> OS.Path.delete ~recurse:true p in
7272+ let gitdir = Fpath.v ".git" in
7373+ let not_git p = not (Fpath.equal p gitdir) in
7474+ OS.Dir.contents dir
7575+ >>= fun files -> List.fold_left delete (Ok ()) (List.filter not_git files)
7676+ in
7777+ let git_for_repo r = Cmd.of_list (Topkg.Cmd.to_list @@ Topkg.Vcs.cmd r) in
7878+ let replace_dir_and_push docdir dir =
7979+ let msg = strf "Update %s doc to %s." name version in
8080+ Topkg.Vcs.get ()
8181+ >>= fun repo -> Ok (git_for_repo repo)
8282+ >>= fun git -> OS.Cmd.run Cmd.(git % "checkout" % branch)
8383+ >>= fun () -> delete dir
8484+ >>= fun () -> cp docdir dir
8585+ >>= fun () -> Topkg.Vcs.is_dirty repo
8686+ >>= function
8787+ | false -> Ok false
8888+ | true ->
8989+ OS.Cmd.run Cmd.(git % "add" % p dir)
9090+ >>= fun () -> OS.Cmd.run Cmd.(git % "commit" % "-m" % msg)
9191+ >>= fun () -> OS.Cmd.run Cmd.(git % "push")
9292+ >>= fun () -> Ok true
9393+ in
9494+ if not (Fpath.is_rooted ~root:Fpath.(v ".") dir)
9595+ then
9696+ R.error_msgf "%a directory is not rooted in the repository or not relative"
9797+ Fpath.pp dir
9898+ else
9999+ let clonedir = Fpath.(parent docdir / strf "%s-%s.pubdoc" name version) in
100100+ OS.Dir.delete ~recurse:true clonedir
101101+ >>= fun () -> Topkg.Vcs.get ()
102102+ >>= fun repo -> Topkg.Vcs.clone repo ~dir:(Fpath.to_string clonedir)
103103+ >>= fun () -> OS.Dir.with_current clonedir (replace_dir_and_push docdir) dir
104104+ >>= fun res -> res
105105+ >>= function
106106+ | false (* no changes *) ->
107107+ log_publish_result "No documentation changes for" (name, version) dir;
108108+ Ok ()
109109+ | true ->
110110+ let push_spec = strf "%s:%s" branch branch in
111111+ Ok (git_for_repo repo) >>= fun git ->
112112+ OS.Cmd.run Cmd.(git % "push" % remote % push_spec)
113113+ >>= fun () -> OS.Dir.delete ~recurse:true clonedir
114114+ >>= fun () ->
115115+ log_publish_result "Published documentation for" (name, version) dir;
116116+ Ok ()
117117+118118+(* Issue requests *)
119119+120120+let issues_uri p = Topkg_care_pkg.opam_field p "dev-repo" >>| function
121121+| None | Some [] -> ""
122122+| Some (u :: _) -> u
123123+124124+let issue_list p =
125125+ issues_uri p >>= fun issues_uri ->
126126+ run_delegate p Cmd.(v "issue" % "list" % issues_uri)
127127+128128+let issue_show p ~id =
129129+ issues_uri p >>= fun issues_uri ->
130130+ run_delegate p Cmd.(v "issue" % "show" % issues_uri % "id")
131131+132132+let issue_open p ~title ~body =
133133+ issues_uri p >>= fun issues_uri ->
134134+ match run_delegate p Cmd.(v "issue" % "open" % issues_uri % title % body) with
135135+ | Ok _ as v -> v
136136+ | Error _ as e ->
137137+ let pp_body ppf = function "" -> () | body -> Fmt.pf ppf "@,@,%s" body in
138138+ Logs.app (fun m -> m "@[<v>Your open issue message was:@,---@,%s%a@]"
139139+ title pp_body body);
140140+ e
141141+142142+let issue_close p ~id ~msg =
143143+ issues_uri p >>= fun issues_uri ->
144144+ match run_delegate p Cmd.(v "issue" % "close" % issues_uri % id % msg) with
145145+ | Ok _ as v -> v
146146+ | Error _ as e ->
147147+ Logs.app (fun m -> m "@[<v>Your closing message was:@,---@,%s@]" msg);
148148+ e
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+open Bos_setup
77+88+let ocaml =
99+ Cmd.of_list @@ Topkg.Cmd.to_list @@ Topkg.Conf.tool "ocaml" `Build_os
1010+1111+let pkg_must_exist pkg_file = match OS.File.must_exist pkg_file with
1212+| Ok _ -> Ok pkg_file
1313+| Error _ ->
1414+ let p = match OS.Dir.current () with
1515+ | Error _ -> (* ignore *) pkg_file
1616+ | Ok dir -> Fpath.(dir // pkg_file)
1717+ in
1818+ R.error_msgf "Not a package: no file %a" Fpath.pp p
1919+2020+let ask ~pkg_file ipc =
2121+ let codec = Topkg.Private.Ipc.codec ipc in
2222+ let verbosity = Logs.(level_to_string (level ())) in
2323+ let ipc_cmd = Cmd.of_list @@ Topkg.Cmd.to_list @@ Topkg.Private.Ipc.cmd ipc in
2424+ let cmd = Cmd.(ocaml % p pkg_file % "ipc" % verbosity %% ipc_cmd) in
2525+ pkg_must_exist pkg_file >>= fun pkg_file ->
2626+ begin
2727+ OS.Cmd.run cmd
2828+ >>= fun () -> Fpath.of_string (Topkg.Private.Ipc.answer ipc)
2929+ >>= fun answer -> OS.File.read answer
3030+ >>= fun data -> Topkg.Private.Codec.dec_result codec data
3131+ end
3232+ |> R.reword_error_msg
3333+ (fun _ -> R.msgf "Failed to load package description %a" Fpath.pp pkg_file)
+18
vendor/opam/topkg/src-care/topkg_care_ipc.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** IPC with package description files *)
77+88+(** {1 Asking packages} *)
99+1010+open Bos_setup
1111+1212+val ocaml : Cmd.t
1313+(** [ocaml] is a command for [ocaml] looked up using
1414+ {!Topkg.Conf.tool}[ "ocaml" `Build_os]. *)
1515+1616+val ask : pkg_file:Fpath.t -> 'a Topkg.Private.Ipc.t -> ('a, R.msg) result
1717+(** [ask pkg_file ipc] performs the IPC [ipc] with the package description
1818+ file [pkg_file] using the interpreter {!ocaml}. *)
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+open Bos_setup
77+88+let cmd =
99+ Cmd.of_list @@ Topkg.Cmd.to_list @@ Topkg.Conf.tool "ocamlbuild" `Host_os
1010+1111+let find_packages ~roots s =
1212+ let package = String.sub "package(" in
1313+ let not_rpar c = not (Char.equal ')' c) in
1414+ let not_dot c = not (Char.equal '.' c) in
1515+ let is_comma c = Char.equal ',' c in
1616+ let is_sep c = Char.Ascii.is_white c || is_comma c in
1717+ let rec loop acc s = match String.Sub.find_sub ~sub:package s with
1818+ | None -> acc
1919+ | Some s ->
2020+ let rest = String.Sub.(extend (stop s)) in
2121+ let ids, rest = String.Sub.span ~sat:not_rpar rest in
2222+ let ids = String.Sub.fields ~empty:false ~is_sep ids in
2323+ let add_id acc id =
2424+ let id = if roots then String.Sub.take ~sat:not_dot id else id in
2525+ String.Set.add (String.Sub.to_string id) acc
2626+ in
2727+ let acc = List.fold_left add_id acc ids in
2828+ loop acc (String.Sub.tail rest)
2929+ in
3030+ loop String.Set.empty (String.sub s)
3131+3232+let package_tags ?(roots = false) file =
3333+ OS.File.read file >>= fun contents -> Ok (find_packages ~roots contents)
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** [ocamlfind] helpers.
77+88+ See {!Topkg_care.OCamlfind}. *)
99+1010+(** {1 OCamlfind} *)
1111+1212+open Bos_setup
1313+1414+val cmd : Cmd.t
1515+val base_packages : String.set
+184
vendor/opam/topkg/src-care/topkg_care_opam.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+open Bos_setup
77+88+(* Command *)
99+1010+let cmd =
1111+ Cmd.of_list @@ Topkg.Cmd.to_list @@ Topkg.Conf.tool "opam" `Host_os
1212+1313+(* Publish *)
1414+1515+let publish =
1616+ let absent = Cmd.(v "opam-publish") in
1717+ OS.Env.(value "TOPKG_OPAM_PUBLISH" cmd ~absent)
1818+1919+let ensure_publish () =
2020+ OS.Cmd.must_exist publish >>= fun cmd ->
2121+ OS.Cmd.run_out Cmd.(publish % "--version") |> OS.Cmd.out_string
2222+ >>= fun (version, _) -> match Topkg.String.parse_version version with
2323+ | None -> R.error_msgf "Could not determine the version of opam-publish"
2424+ | Some (m, _, _, _) when m < 2 ->
2525+ R.error_msgf "topkg needs at least opam-publish 2.0.0"
2626+ | Some _ -> Ok ()
2727+2828+let submit ?msg ~opam_file () =
2929+ let msg = match msg with
3030+ | None -> Ok (Cmd.empty)
3131+ | Some msg ->
3232+ OS.File.tmp "topkg-opam-submit-msg-%s"
3333+ >>= fun m -> OS.File.write m msg
3434+ >>= fun () -> Ok Cmd.(v "--msg-file" % p m)
3535+ in
3636+ msg >>= fun msg -> OS.Cmd.run Cmd.(publish %% msg % p opam_file)
3737+3838+(* Packages *)
3939+4040+let ocaml_base_packages = String.Set.of_list
4141+ [ "base-bigarray"; "base-bytes"; "base-threads"; "base-unix"; ]
4242+4343+(* Files *)
4444+4545+module File = struct
4646+4747+ (* Try to compose with the OpamFile.OPAM API *)
4848+4949+ let id x = x
5050+ let list f = fun v -> [f v]
5151+ let field name field conv =
5252+ name, fun acc o -> String.Map.add name (conv (field o)) acc
5353+5454+ let opt_field name field conv =
5555+ name, fun acc o -> match field o with
5656+ | None -> acc
5757+ | Some v -> String.Map.add name (conv v) acc
5858+5959+ let deps_conv d =
6060+ let add_pkg acc (n, _) = OpamPackage.Name.to_string n :: acc in
6161+ OpamFormula.fold_left add_pkg [] d
6262+6363+ let fields = [
6464+ opt_field "name" OpamFile.OPAM.name_opt (list OpamPackage.Name.to_string);
6565+ opt_field "version" OpamFile.OPAM.version_opt
6666+ (list OpamPackage.Version.to_string);
6767+ field "opam-version" OpamFile.OPAM.opam_version
6868+ (list OpamVersion.to_string);
6969+ field "available" OpamFile.OPAM.available (list OpamFilter.to_string);
7070+ field "maintainer" OpamFile.OPAM.maintainer id;
7171+ field "homepage" OpamFile.OPAM.homepage id;
7272+ field "authors" OpamFile.OPAM.author id;
7373+ field "license" OpamFile.OPAM.license id;
7474+ field "doc" OpamFile.OPAM.doc id;
7575+ field "tags" OpamFile.OPAM.tags id;
7676+ field "bug-reports" OpamFile.OPAM.bug_reports id;
7777+ opt_field "dev-repo" OpamFile.OPAM.dev_repo (list OpamUrl.to_string);
7878+ field "depends" OpamFile.OPAM.depends deps_conv;
7979+ field "depopts" OpamFile.OPAM.depopts deps_conv;
8080+ opt_field "synopsis" OpamFile.OPAM.synopsis (list id);
8181+ opt_field "description" OpamFile.OPAM.descr_body (list id);
8282+ ]
8383+8484+ let field_names =
8585+ let add acc (name, field) = String.Set.add name acc in
8686+ List.fold_left add String.Set.empty fields
8787+8888+ let fields file =
8989+ let parse file =
9090+ let file = OpamFilename.of_string (Fpath.to_string file) in
9191+ let opam = OpamFile.OPAM.read (OpamFile.make file) in
9292+ let known_fields =
9393+ let add_field acc (_, field) = field acc opam in
9494+ List.fold_left add_field String.Map.empty fields
9595+ in
9696+ (* FIXME add OpamFile.OPAM.extensions when supported *)
9797+ known_fields
9898+ in
9999+ Logs.info (fun m -> m "Parsing opam file %a" Fpath.pp file);
100100+ try Ok (parse file) with
101101+ | exn ->
102102+ (* Apparently in at least opam-lib 1.2.2, the error will be logged
103103+ on stdout. *)
104104+ R.error_msgf "%a: could not parse opam file" Fpath.pp file
105105+106106+ let deps ?(opts = true) fields =
107107+ let deps = match String.Map.find "depends" fields with
108108+ | None -> [] | Some deps -> deps
109109+ in
110110+ let dep_opts =
111111+ if not opts then [] else
112112+ match String.Map.find "depopts" fields with
113113+ | None -> [] | Some deps -> deps
114114+ in
115115+ String.Set.of_list (List.rev_append dep_opts deps)
116116+end
117117+118118+module Descr = struct
119119+ type t = string * string
120120+121121+ let of_string s = match String.cuts ~sep:"\n" s with
122122+ | [] -> R.error_msgf "Cannot extract opam descr."
123123+ | synopsis :: descr -> Ok (synopsis, String.concat ~sep:"\n" descr)
124124+125125+ let to_string (synopsis, descr) = strf "%s\n%s" synopsis descr
126126+ let to_opam_fields (synopsis, descr) =
127127+ strf "synopsis: \"\"\"%s\"\"\"\ndescription: \"\"\"\\\n%s\"\"\""
128128+ synopsis descr
129129+130130+ let of_readme ?flavour r =
131131+ let parse_synopsis l =
132132+ let error l = R.error_msgf "%S: can't extract opam synopsis" l in
133133+ let ok s = Ok String.(Ascii.capitalize @@ String.Sub.to_string s) in
134134+ let not_white c = not (Char.Ascii.is_white c) in
135135+ let skip_non_white l = String.Sub.drop ~sat:not_white l in
136136+ let skip_white l = String.Sub.drop ~sat:Char.Ascii.is_white l in
137137+ let start =
138138+ String.sub l |> skip_white |> skip_non_white |> skip_white
139139+ in
140140+ match String.Sub.head start with
141141+ | None -> error l
142142+ | Some c when Char.Ascii.is_letter c -> ok start
143143+ | Some c -> (* Try to skip a separator. *)
144144+ let start = start |> skip_non_white |> skip_white in
145145+ match String.Sub.head start with
146146+ | None -> error l
147147+ | Some _ -> ok start
148148+ in
149149+ let drop_line l =
150150+ String.is_prefix ~affix:"Home page:" l ||
151151+ String.is_prefix ~affix:"Homepage:" l ||
152152+ String.is_prefix ~affix:"Contact:" l ||
153153+ String.is_prefix ~affix:"%%VERSION" l
154154+ in
155155+ let keep_line l = not (drop_line l) in
156156+ match Topkg_care_text.head ?flavour r with
157157+ | None -> R.error_msgf "Could not extract opam description."
158158+ | Some (title, text) ->
159159+ let sep = "\n" in
160160+ let title = Topkg_care_text.header_title ?flavour title in
161161+ parse_synopsis title
162162+ >>= fun synopsis -> Ok (String.cuts ~sep text)
163163+ >>= fun text -> Ok (List.filter keep_line text)
164164+ >>= fun text -> Ok (synopsis, String.concat ~sep text)
165165+166166+ let of_readme_file file =
167167+ let flavour = Topkg_care_text.flavour_of_fpath file in
168168+ (OS.File.read file
169169+ >>= fun text -> of_readme ?flavour text)
170170+ |> R.reword_error_msg ~replace:true
171171+ (fun m -> R.msgf "%a: %s" Fpath.pp file m)
172172+end
173173+174174+module Url = struct
175175+ type t = string
176176+ let v ~uri ~checksum = strf "archive: \"%s\"\nchecksum: \"%s\"" uri checksum
177177+ let with_distrib_file ~uri distrib_file =
178178+ try
179179+ let checksum = Digest.(to_hex @@ file (Fpath.to_string distrib_file)) in
180180+ Ok (v ~uri ~checksum)
181181+ with Failure msg | Sys_error msg -> R.error_msg msg
182182+183183+ let to_opam_section u = strf "url {\n%s\n}" u
184184+end
+40
vendor/opam/topkg/src-care/topkg_care_opam.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** opam interaction.
77+88+ See {!Topkg_care.Opam}. *)
99+1010+(** {1 opam} *)
1111+1212+open Bos_setup
1313+1414+val cmd : Cmd.t
1515+val ensure_publish : unit -> (unit, R.msg) result
1616+val submit : ?msg:string -> opam_file:Fpath.t -> unit -> (unit, R.msg) result
1717+val ocaml_base_packages : String.set
1818+1919+module File : sig
2020+ val field_names : String.set
2121+ val fields : Fpath.t -> ((string list) String.map , R.msg) result
2222+ val deps : ?opts:bool -> (string list) String.map -> String.set
2323+end
2424+2525+module Descr : sig
2626+ type t = string * string
2727+ val of_string : string -> (t, R.msg) result
2828+ val to_string : t -> string
2929+ val to_opam_fields : t -> string
3030+ val of_readme :
3131+ ?flavour:Topkg_care_text.flavour -> string -> (t, R.msg) result
3232+ val of_readme_file : Fpath.t -> (t, R.msg) result
3333+end
3434+3535+module Url : sig
3636+ type t = string
3737+ val v : uri:string -> checksum:string -> t
3838+ val with_distrib_file : uri:string -> Fpath.t -> (t, R.msg) result
3939+ val to_opam_section : t -> string
4040+end
+564
vendor/opam/topkg/src-care/topkg_care_pkg.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+open Bos_setup
77+88+(* Misc *)
99+1010+let path_set_of_strings ps =
1111+ try
1212+ let add_excl acc p = match Fpath.of_string p with
1313+ | Error (`Msg msg) -> failwith msg
1414+ | Ok p -> Fpath.Set.add p acc
1515+ in
1616+ Ok (List.fold_left add_excl Fpath.Set.empty ps)
1717+ with
1818+ Failure msg -> R.error_msg msg
1919+2020+let rec path_list_of_strings ps =
2121+ let rec loop acc = function
2222+ | [] -> Ok (List.rev acc)
2323+ | p :: ps ->
2424+ match Fpath.of_string p with
2525+ | Error _ as e -> e
2626+ | Ok p -> loop (p :: acc) ps
2727+ in
2828+ loop [] ps
2929+3030+let uri_sld uri = match Topkg_care_text.split_uri uri with
3131+| None -> None
3232+| Some (_, host, _) ->
3333+ match List.rev (String.cuts ~sep:"." host) with
3434+ | fst :: snd :: _ -> Some snd
3535+ | _ -> None
3636+3737+let uri_append u s = match String.head ~rev:true u with
3838+| None -> s
3939+| Some '/' -> strf "%s%s" u s
4040+| Some _ -> strf "%s/%s" u s
4141+4242+let chop_ext u = match String.cut ~rev:true ~sep:"." u with
4343+| None -> u
4444+| Some (u, _) -> u
4545+4646+let chop_git_prefix u = match String.cut ~sep:"git+" u with
4747+| Some ("", uri) -> uri
4848+| _ -> u
4949+5050+(* Package *)
5151+5252+type t =
5353+ { name : string option;
5454+ version : string option;
5555+ delegate : Cmd.t option;
5656+ build_dir : Fpath.t option;
5757+ opam : Fpath.t option;
5858+ opam_descr : Fpath.t option;
5959+ opam_fields : (string list String.map, R.msg) result Lazy.t;
6060+ readmes : Fpath.t list option;
6161+ change_logs : Fpath.t list option;
6262+ licenses : Fpath.t list option;
6363+ distrib_uri : string option;
6464+ distrib_file : Fpath.t option;
6565+ publish_msg : string option;
6666+ publish_artefacts : [ `Distrib | `Doc | `Alt of string ] list option;
6767+ pkg_file : Fpath.t;
6868+ pkg : (Topkg.Private.Pkg.t, R.msg) result Lazy.t; }
6969+7070+let empty p = { p with pkg = lazy (Ok (Topkg.Private.Pkg.empty)) }
7171+let pkg p = Lazy.force p.pkg
7272+7373+let opam_fields p = Lazy.force p.opam_fields
7474+let opam_field p f = opam_fields p >>| fun fields -> String.Map.find f fields
7575+let opam_field_hd p f = opam_field p f >>| function
7676+| None | Some [] -> None
7777+| Some (v :: _) -> Some v
7878+7979+let opam_homepage_sld p = opam_field_hd p "homepage" >>| function
8080+| None -> None
8181+| Some uri -> match uri_sld uri with None -> None | Some sld -> Some (uri, sld)
8282+8383+let pkg_file p = p.pkg_file
8484+8585+let name p = match p.name with
8686+| Some n -> Ok n
8787+| None -> pkg p >>| Topkg.Private.Pkg.name
8888+8989+let version p = match p.version with
9090+| Some v -> Ok v
9191+| None -> Topkg.Vcs.get () >>= fun r -> Topkg.Vcs.describe ~dirty:false r
9292+9393+let delegate p =
9494+ let not_found () =
9595+ R.error_msg "No package delegate found. \
9696+ Try `topkg help delegate` for more information."
9797+ in
9898+ match p.delegate with
9999+ | Some cmd -> Ok cmd
100100+ | None ->
101101+ pkg p >>| Topkg.Private.Pkg.delegate >>= function
102102+ | Some cmd -> Ok (Cmd.of_list @@ Topkg.Cmd.to_list cmd)
103103+ | None ->
104104+ match OS.Env.(value "TOPKG_DELEGATE" (some cmd) ~absent:None) with
105105+ | Some cmd -> Ok cmd
106106+ | None ->
107107+ opam_homepage_sld p >>= function
108108+ | None -> not_found ()
109109+ | Some (_, sld) ->
110110+ let exec = strf "%s-topkg-delegate" sld in
111111+ let cmd = Cmd.v exec in
112112+ OS.Cmd.exists cmd >>= function
113113+ | true -> Ok cmd
114114+ | false ->
115115+ if exec <> "github-topkg-delegate"
116116+ then not_found ()
117117+ else Ok (Cmd.v "toy-github-topkg-delegate")
118118+119119+let build_dir p = match p.build_dir with
120120+| Some b -> Ok b
121121+| None -> pkg p >>| Topkg.Private.Pkg.build_dir >>= Fpath.of_string
122122+123123+let readmes p = match p.readmes with
124124+| Some f -> Ok f
125125+| None -> pkg p >>| Topkg.Private.Pkg.readmes >>= path_list_of_strings
126126+127127+let readme p = readmes p >>= function
128128+| [] -> R.error_msgf "No readme file specified in the package description"
129129+| r :: _ -> Ok r
130130+131131+let opam p = match p.opam with
132132+| Some f -> Ok f
133133+| None ->
134134+ name p
135135+ >>= fun name -> pkg p
136136+ >>| Topkg.Private.Pkg.opam ~name >>= Fpath.of_string
137137+138138+let opam_field_descr p =
139139+ opam_field p "synopsis" >>= function
140140+ | None -> Ok None
141141+ | Some syn ->
142142+ opam_field p "description" >>= function
143143+ | None -> Ok None
144144+ | Some descr -> Ok (Some (List.hd syn, List.hd descr))
145145+146146+let opam_descr p =
147147+ let descr_file_for_opam opam =
148148+ if Fpath.has_ext ".opam" opam then Fpath.(rem_ext opam + ".descr") else
149149+ Fpath.(parent opam / "descr")
150150+ in
151151+ let read f =
152152+ OS.File.read f
153153+ >>= fun c -> Topkg_care_opam.Descr.of_string c
154154+ >>| fun d -> d, false
155155+ in
156156+ match p.opam_descr with
157157+ | Some f -> read f
158158+ | None ->
159159+ opam_field_descr p >>= function
160160+ | Some descr -> Ok (descr, true)
161161+ | None ->
162162+ opam p
163163+ >>= fun opam -> Ok (descr_file_for_opam opam)
164164+ >>= fun descr_file -> OS.File.exists descr_file
165165+ >>= function
166166+ | true ->
167167+ Logs.info (fun m -> m "Found opam descr file %a"
168168+ Fpath.pp descr_file);
169169+ read descr_file
170170+ | false ->
171171+ readme p
172172+ >>= fun readme ->
173173+ Logs.info
174174+ (fun m -> m "Extracting opam descr from %a" Fpath.pp readme);
175175+ Topkg_care_opam.Descr.of_readme_file readme
176176+ >>| fun d -> d, false
177177+178178+let change_logs p = match p.change_logs with
179179+| Some f -> Ok f
180180+| None -> pkg p >>| Topkg.Private.Pkg.change_logs >>= path_list_of_strings
181181+182182+let change_log p = change_logs p >>= function
183183+| [] -> R.error_msgf "No change log specified in the package description."
184184+| l :: _ -> Ok l
185185+186186+let licenses p = match p.licenses with
187187+| Some f -> Ok f
188188+| None -> pkg p >>| Topkg.Private.Pkg.licenses >>= path_list_of_strings
189189+190190+let distrib_uri ?(raw = false) p =
191191+ let subst_uri p uri =
192192+ uri
193193+ >>= fun uri -> name p
194194+ >>= fun name -> version p
195195+ >>= fun version -> Ok (Topkg.String.drop_initial_v version)
196196+ >>= fun version_num ->
197197+ let defs = String.Map.(empty
198198+ |> add "NAME" name |> add "VERSION" version
199199+ |> add "VERSION_NUM" version_num)
200200+ in
201201+ Pat.of_string uri >>| fun pat -> Pat.format defs pat
202202+ in
203203+ let not_found () =
204204+ R.error_msg "no distribution URI found, see topkg's API documentation."
205205+ in
206206+ let uri = match p.distrib_uri with
207207+ | Some u -> Ok u
208208+ | None ->
209209+ pkg p
210210+ >>= fun pkg -> match Topkg.Private.Pkg.distrib_uri pkg with
211211+ | Some u -> Ok u
212212+ | None ->
213213+ opam_homepage_sld p >>= function
214214+ | None -> not_found ()
215215+ | Some (uri, sld) ->
216216+ if sld <> "github"
217217+ then (Ok (uri_append uri "releases/$(NAME)-$(VERSION_NUM).tbz"))
218218+ else
219219+ opam_field_hd p "dev-repo">>= function
220220+ | None -> not_found ()
221221+ | Some dev_repo ->
222222+ Ok (uri_append (chop_git_prefix (chop_ext dev_repo))
223223+ "releases/download/$(VERSION)/$(NAME)-$(VERSION_NUM).tbz")
224224+ in
225225+ if raw then uri else subst_uri p uri
226226+227227+let distrib_filename ?(opam = false) p =
228228+ let sep = if opam then '.' else '-' in
229229+ name p
230230+ >>= fun name -> version p
231231+ >>= fun version -> Ok (Topkg.String.drop_initial_v version)
232232+ >>= fun version_num -> Fpath.of_string (strf "%s%c%s" name sep version_num)
233233+234234+let distrib_archive_path p =
235235+ build_dir p
236236+ >>= fun build_dir -> distrib_filename ~opam:false p
237237+ >>| fun b -> Fpath.(build_dir // b + ".tbz")
238238+239239+let distrib_file p = match p.distrib_file with
240240+| Some f -> Ok f
241241+| None ->
242242+ (distrib_archive_path p
243243+ >>= fun f -> OS.File.must_exist f)
244244+ |> R.reword_error_msg
245245+ (fun _ -> R.msgf "Did you forget to call 'topkg distrib' ?")
246246+247247+let publish_msg p = match p.publish_msg with
248248+| Some msg -> Ok msg
249249+| None ->
250250+ change_log p
251251+ >>= fun change_log -> Topkg_care_text.change_log_file_last_entry change_log
252252+ >>= fun (_, (h, txt)) -> Ok (strf "%s\n%s" h txt)
253253+254254+let publish_artefacts p = match p.publish_artefacts with
255255+| Some arts -> Ok arts
256256+| None -> pkg p >>| Topkg.Private.Pkg.publish_artefacts
257257+258258+let v
259259+ ?name ?version ?delegate ?build_dir ?opam:opam_file ?opam_descr
260260+ ?readme ?change_log ?license ?distrib_uri ?distrib_file ?publish_msg
261261+ ?publish_artefacts
262262+ pkg_file
263263+ =
264264+ let readmes = match readme with Some r -> Some [r] | None -> None in
265265+ let change_logs = match change_log with Some c -> Some [c] | None -> None in
266266+ let licenses = match license with Some l -> Some [l] | None -> None in
267267+ let pkg = lazy (Topkg_care_ipc.ask ~pkg_file (Topkg.Private.Ipc.pkg ())) in
268268+ let rec opam_fields = lazy (opam p >>= fun o -> Topkg_care_opam.File.fields o)
269269+ and p =
270270+ { name; version; delegate; build_dir; opam = opam_file; opam_descr;
271271+ opam_fields; readmes; change_logs; licenses; distrib_uri; distrib_file;
272272+ publish_msg; publish_artefacts; pkg_file; pkg }
273273+ in
274274+ p
275275+276276+(* Distrib *)
277277+278278+let distrib_prepare_ipc p ~dist_build_dir ~name ~version ~opam ~opam_adds =
279279+ let dist_build_dir = Fpath.to_string dist_build_dir in
280280+ let opam = Fpath.to_string opam in
281281+ R.join @@ Topkg_care_ipc.ask ~pkg_file:p.pkg_file
282282+ (Topkg.Private.Ipc.distrib_prepare ~dist_build_dir ~name ~version ~opam
283283+ ~opam_adds)
284284+285285+let distrib_archive p ~keep_dir =
286286+ Topkg_care_archive.ensure_bzip2 ()
287287+ >>= fun () -> name p
288288+ >>= fun name -> build_dir p
289289+ >>= fun build_dir -> version p
290290+ >>= fun version -> opam p
291291+ >>= fun opam -> distrib_filename p
292292+ >>= fun root -> Ok Fpath.(build_dir // root + ".build")
293293+ >>= fun dist_build_dir -> OS.Dir.delete ~recurse:true dist_build_dir
294294+ >>= fun () -> Topkg_vcs.get ()
295295+ >>= fun repo -> Topkg_vcs.commit_id repo ~dirty:false
296296+ >>= fun head -> Topkg_vcs.commit_ptime_s repo ~commit_ish:head
297297+ >>= fun mtime -> Topkg_vcs.clone repo ~dir:(Fpath.to_string dist_build_dir)
298298+ >>= fun () -> Topkg_vcs.get ~dir:(Fpath.to_string dist_build_dir) ()
299299+ >>= fun clone -> Ok (Topkg_string.strf "topkg-dist-%s" head)
300300+ >>= fun branch -> Topkg_vcs.checkout clone ~branch ~commit_ish:head
301301+ >>= fun () -> opam_descr p
302302+ >>= fun (descr, from_opam) ->
303303+ let opam_adds =
304304+ if from_opam then "" else
305305+ (Topkg_care_opam.Descr.to_opam_fields descr ^ "\n")
306306+ in
307307+ distrib_prepare_ipc p ~dist_build_dir ~name ~version ~opam ~opam_adds
308308+ >>= fun exclude_paths -> path_set_of_strings exclude_paths
309309+ >>= fun exclude_paths ->
310310+ Topkg_care_archive.tar dist_build_dir ~exclude_paths ~root ~mtime
311311+ >>= fun tar -> distrib_archive_path p
312312+ >>= fun archive -> Topkg_care_archive.bzip2 tar ~dst:archive
313313+ >>= fun () ->
314314+ (if keep_dir then Ok () else OS.Dir.delete ~recurse:true dist_build_dir)
315315+ >>= fun () -> Ok archive
316316+317317+(* Test & build *)
318318+319319+let run p ~dir cmd ~args ~out =
320320+ let pkg_file = p.pkg_file in
321321+ let cmd = Cmd.(v "ocaml" % p pkg_file % cmd %% args) in
322322+ let run () = OS.Cmd.run_out cmd |> out in
323323+ R.join @@ OS.Dir.with_current dir run ()
324324+325325+let test p ~dir ~args ~out = run p ~dir "test" ~args ~out
326326+let build p ~dir ~args ~out = run p ~dir "build" ~args ~out
327327+let clean p ~dir ~args ~out = run p ~dir "clean" ~args ~out
328328+329329+(* Lint *)
330330+331331+let pp_path = Topkg_care_text.Pp.path
332332+let pp_status = Topkg_care_text.Pp.status
333333+334334+let lint_log msg = Logs.info (fun m -> m ~header:"LINT" "%a" Fmt.text msg); 0
335335+let lint_disabled test =
336336+ Logs.info (fun m -> m ~header:"LINT" "Package@ disabled@ %a." Fmt.text test);
337337+ 0
338338+339339+let lint_custom p =
340340+ let report errs res =
341341+ let status, msg, errs = match res with
342342+ | Ok (`Msg msg) -> `Ok, msg, errs
343343+ | Error (`Msg msg) -> `Fail, msg, errs + 1
344344+ in
345345+ Logs.app (fun m -> m "%a @[%a@]" pp_status status Fmt.text msg);
346346+ errs
347347+ in
348348+ begin
349349+ let pkg_file = p.pkg_file in
350350+ pkg p >>= fun p -> match Topkg.Private.Pkg.lint_custom p with
351351+ | None -> Ok (lint_disabled "custom linting")
352352+ | _ ->
353353+ Topkg_care_ipc.ask ~pkg_file (Topkg.Private.Ipc.lint_custom ())
354354+ >>= function
355355+ | None -> assert false
356356+ | Some results -> Ok (List.fold_left report 0 results)
357357+ end
358358+ |> Logs.on_error_msg ~use:(fun () -> 1)
359359+360360+let lint_std_files p =
361361+ let lint_exists file errs =
362362+ let report exists =
363363+ let status, errs = if exists then `Ok, errs else `Fail, errs + 1 in
364364+ Logs.app (fun m ->
365365+ m "%a @[File %a@ is@ present.@]" pp_status status pp_path file);
366366+ errs
367367+ in
368368+ (OS.File.exists file >>= fun exists -> Ok (report exists))
369369+ |> Logs.on_error_msg ~use:(fun () -> errs + 1)
370370+ in
371371+ begin
372372+ pkg p >>= fun p -> match Topkg.Private.Pkg.lint_files p with
373373+ | None -> Ok (lint_disabled "standard files linting")
374374+ | Some files ->
375375+ path_set_of_strings files
376376+ >>= fun files -> Ok (Fpath.Set.fold lint_exists files 0)
377377+ end
378378+ |> Logs.on_error_msg ~use:(fun () -> 1)
379379+380380+let lint_file_with_cmd file_kind ~cmd file errs handle_exit =
381381+ let run_linter cmd file ~exists =
382382+ if not exists then Ok (`Fail (strf "%a: No such file" Fpath.pp file)) else
383383+ OS.Cmd.(run_out ~err:err_run_out Cmd.(cmd % p file) |> out_string)
384384+ >>| fun (out, status) -> handle_exit (snd status) out
385385+ in
386386+ begin
387387+ OS.File.exists file
388388+ >>= fun exists -> run_linter cmd file ~exists
389389+ >>| function
390390+ | `Ok ->
391391+ Logs.app
392392+ (fun m -> m "%a @[lint@ %s %a.@]"
393393+ pp_status `Ok file_kind pp_path file);
394394+ errs
395395+ | `Fail msgs ->
396396+ Logs.app
397397+ (fun m -> m "%a @[<v>@[lint@ %s %a:@]@,@[%a messages:@]@,%a@]"
398398+ pp_status `Fail file_kind pp_path file Cmd.pp cmd Fmt.lines msgs);
399399+ errs + 1
400400+ end
401401+ |> Logs.on_error_msg ~use:(fun () -> errs + 1)
402402+403403+let lint_metas p =
404404+ begin
405405+ pkg p
406406+ >>= fun p -> match Topkg.Private.Pkg.lint_metas p with
407407+ | [] -> Ok (lint_log "No ocamlfind META file to lint")
408408+ | metas ->
409409+ let cmd = Cmd.(Topkg_care_ocamlfind.cmd % "lint") in
410410+ let handle_exit status out = match status with
411411+ | `Exited 0 -> `Ok
412412+ | _ -> `Fail out
413413+ in
414414+ let lint errs (f, lint) =
415415+ begin
416416+ Fpath.of_string f >>| fun f ->
417417+ if not lint
418418+ then lint_log (strf "ocamlfind META lint disabled for %a" Fpath.pp f)
419419+ else lint_file_with_cmd "META file" ~cmd f errs handle_exit
420420+ end
421421+ |> Logs.on_error_msg ~use:(fun () -> errs + 1)
422422+ in
423423+ Ok (List.fold_left lint 0 metas)
424424+ end
425425+ |> Logs.on_error_msg ~use:(fun () -> 1)
426426+427427+let lint_opams p =
428428+ begin
429429+ pkg p >>= fun p -> match Topkg.Private.Pkg.lint_opams p with
430430+ | [] -> Ok (lint_log "No opam file to lint")
431431+ | opams ->
432432+ (* We first run opam lint with -s and if there's something beyond
433433+ 5 we rerun it without it for the error messages. It's ugly
434434+ since 5 will still but opam lint's cli is broken. *)
435435+ let cmd = Cmd.(Topkg_care_opam.cmd % "lint") in
436436+ let handle_exit file status out = match status, out with
437437+ | `Exited 0,
438438+ ("" | "5" (* dirname version vs opam file version *)) -> `Ok
439439+ | _ ->
440440+ let err = OS.Cmd.err_run_out in
441441+ match OS.Cmd.(run_out ~err Cmd.(cmd % p file) |> out_string) with
442442+ | Ok (out, _) -> `Fail out
443443+ | Error (`Msg out) -> `Fail out
444444+ in
445445+ let cmd = Cmd.(cmd % "-s") in
446446+ let lint errs (f, lint, _) =
447447+ begin
448448+ Fpath.of_string f >>| fun f ->
449449+ if not lint
450450+ then lint_log (strf "opam file lint disabled for %a" Fpath.pp f)
451451+ else lint_file_with_cmd "opam file" ~cmd f errs (handle_exit f)
452452+ end
453453+ |> Logs.on_error_msg ~use:(fun () -> errs + 1)
454454+ in
455455+ Ok (List.fold_left lint 0 opams)
456456+ end
457457+ |> Logs.on_error_msg ~use:(fun () -> 1)
458458+459459+let lint_deps_default_excludes =
460460+ let exclude = ["ocamlfind"; "ocamlbuild"; "topkg"; "ocaml"] in
461461+ Topkg_care_ocamlfind.base_packages
462462+ |> String.Set.union Topkg_care_opam.ocaml_base_packages
463463+ |> String.Set.union (String.Set.of_list exclude)
464464+465465+let lint_opam_deps errs (opam, _, exclude) =
466466+ let pp_deps_mismatches ppf (opam_only, tags_only) =
467467+ let pp_miss present absent ppf id =
468468+ Fmt.pf ppf "@[%a: %a present but %a absent@]"
469469+ Fmt.(styled `Underline string) id
470470+ Fmt.(styled `Green string) present
471471+ Fmt.(styled `Red string) absent
472472+ in
473473+ let sep =
474474+ if String.Set.(is_empty opam_only || is_empty tags_only)
475475+ then Fmt.nop else Fmt.cut
476476+ in
477477+ Fmt.pf ppf "@[<v>%a%a%a@]"
478478+ (String.Set.pp (pp_miss "opam" "_tags")) opam_only sep ()
479479+ (String.Set.pp (pp_miss "_tags" "opam")) tags_only
480480+ in
481481+ let lint_deps ~exclude ~opam ~tags =
482482+ begin
483483+ Topkg_care_ocamlbuild.package_tags ~roots:true tags
484484+ >>= fun tags -> Topkg_care_opam.File.fields opam
485485+ >>| fun fields ->
486486+ let exclude = String.Set.union exclude lint_deps_default_excludes in
487487+ let keep id =
488488+ not (String.Set.mem id exclude) &&
489489+ not (String.is_prefix ~affix:"conf-" id)
490490+ in
491491+ let opam_deps = Topkg_care_opam.File.deps ~opts:true fields in
492492+ let opam = String.Set.filter keep opam_deps in
493493+ let tags = String.Set.filter keep tags in
494494+ let opam_only = String.Set.diff opam tags in
495495+ let tags_only = String.Set.diff tags opam in
496496+ if String.Set.is_empty opam_only && String.Set.is_empty tags_only
497497+ then None
498498+ else Some (opam_only, tags_only)
499499+ end
500500+ |> R.reword_error_msg ~replace:true
501501+ (fun msg -> R.msgf "could not lint dependencies: %s" msg)
502502+ in
503503+ begin
504504+ Fpath.of_string opam
505505+ >>= fun opam -> match exclude with
506506+ | None ->
507507+ Ok (lint_disabled @@
508508+ Fmt.(str_like
509509+ stdout "opam dependency linting for %a" pp_path opam))
510510+ | Some exclude ->
511511+ let tags = Fpath.v "_tags" in
512512+ let exclude = String.Set.of_list exclude in
513513+ lint_deps ~exclude ~opam ~tags
514514+ >>| function
515515+ | None ->
516516+ Logs.app (fun m -> m "%a @[opam file %a@ and %a dependency check."
517517+ pp_status `Ok pp_path opam pp_path tags);
518518+ errs
519519+ | Some miss ->
520520+ Logs.app
521521+ (fun m -> m "%a @[<v>@[File %a and %a dependency check:@]@,%a@]"
522522+ pp_status `Fail pp_path opam pp_path tags
523523+ pp_deps_mismatches miss);
524524+ errs + 1
525525+ end
526526+ |> Logs.on_error_msg ~use:(fun () -> errs + 1)
527527+528528+let lint_deps p =
529529+ begin
530530+ pkg p >>= fun p -> match Topkg.Private.Pkg.lint_opams p with
531531+ | [] -> Ok (lint_log "No opam file to dependency lint")
532532+ | opams -> Ok (List.fold_left lint_opam_deps 0 opams)
533533+ end
534534+ |> Logs.on_error_msg ~use:(fun () -> 1)
535535+536536+type lint = [ `Custom | `Std_files | `Meta | `Opam | `Deps ]
537537+538538+let lints =
539539+ [`Custom, lint_custom;
540540+ `Std_files, lint_std_files;
541541+ `Meta, lint_metas;
542542+ `Opam, lint_opams;
543543+ `Deps, lint_deps ]
544544+545545+let lint_all = List.map fst lints
546546+547547+let lint ?(ignore_pkg = false) p ~dir todo =
548548+ let lint pkg =
549549+ let do_lint acc (l, f) = acc + if List.mem l todo then f pkg else 0 in
550550+ match List.fold_left do_lint 0 lints with
551551+ | 0 ->
552552+ Logs.app (fun m -> m "%a lint@ %a %a"
553553+ pp_status `Ok pp_path dir
554554+ Fmt.(styled `Green (any "success")) ()); 0
555555+ | n ->
556556+ Logs.app (fun m -> m "%a lint@ %a@ %a:@ %d@ errors."
557557+ pp_status `Fail pp_path dir
558558+ Fmt.(styled `Red (any "failure")) () n); 1
559559+ in
560560+ let p =
561561+ if ignore_pkg then Ok (empty p) else
562562+ pkg p >>| fun _ -> p (* verify the pkg_file exists *)
563563+ in
564564+ p >>= fun p -> OS.Dir.with_current dir lint p
+81
vendor/opam/topkg/src-care/topkg_care_pkg.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** Package descriptions.
77+88+ See {!Topkg_care.Pkg} *)
99+1010+open Bos_setup
1111+1212+(** {1 Package} *)
1313+1414+type t
1515+1616+val v :
1717+ ?name:string ->
1818+ ?version:string ->
1919+ ?delegate:Cmd.t ->
2020+ ?build_dir:Fpath.t ->
2121+ ?opam:Fpath.t ->
2222+ ?opam_descr:Fpath.t ->
2323+ ?readme:Fpath.t ->
2424+ ?change_log:Fpath.t ->
2525+ ?license:Fpath.t ->
2626+ ?distrib_uri:string ->
2727+ ?distrib_file:Fpath.t ->
2828+ ?publish_msg:string ->
2929+ ?publish_artefacts:[ `Distrib | `Doc | `Alt of string] list ->
3030+ Fpath.t -> t
3131+3232+val pkg_file : t -> Fpath.t
3333+val name : t -> (string, R.msg) result
3434+val version : t -> (string, R.msg) result
3535+val delegate : t -> (Cmd.t, R.msg) result
3636+val build_dir : t -> (Fpath.t, R.msg) result
3737+val opam : t -> (Fpath.t, R.msg) result
3838+val opam_field : t -> string -> (string list option, R.msg) result
3939+val opam_field_hd : t -> string -> (string option, R.msg) result
4040+val opam_fields : t -> (string list String.map, R.msg) result
4141+val opam_descr : t -> (Topkg_care_opam.Descr.t * bool, R.msg) result
4242+val readmes : t -> (Fpath.t list, R.msg) result
4343+val readme : t -> (Fpath.t, R.msg) result
4444+val change_logs : t -> (Fpath.t list, R.msg) result
4545+val change_log : t -> (Fpath.t, R.msg) result
4646+val licenses : t -> (Fpath.t list, R.msg) result
4747+val distrib_uri : ?raw:bool -> t -> (string, R.msg) result
4848+val distrib_file : t -> (Fpath.t, R.msg) result
4949+val publish_msg : t -> (string, R.msg) result
5050+val publish_artefacts : t ->
5151+ ([ `Distrib | `Doc | `Alt of string] list, R.msg) result
5252+5353+(** {1 Test} *)
5454+5555+val test :
5656+ t -> dir:Fpath.t -> args:Cmd.t ->
5757+ out:(OS.Cmd.run_out -> ('a, R.msg) result) -> ('a, R.msg) result
5858+5959+(** {1 Build} *)
6060+6161+val build :
6262+ t -> dir:Fpath.t -> args:Cmd.t ->
6363+ out:(OS.Cmd.run_out -> ('a, R.msg) result) -> ('a, R.msg) result
6464+6565+(** {1 Clean} *)
6666+6767+val clean :
6868+ t -> dir:Fpath.t -> args:Cmd.t ->
6969+ out:(OS.Cmd.run_out -> ('a, R.msg) result) -> ('a, R.msg) result
7070+7171+(** {1 Distrib} *)
7272+7373+val distrib_filename : ?opam:bool -> t -> (Fpath.t, R.msg) result
7474+val distrib_archive : t -> keep_dir:bool -> (Fpath.t, R.msg) result
7575+7676+(** {1 Lint} *)
7777+7878+type lint = [ `Custom | `Std_files | `Meta | `Opam | `Deps ]
7979+val lint_all : lint list
8080+val lint :
8181+ ?ignore_pkg:bool -> t -> dir:Fpath.t -> lint list -> (int, R.msg) result
+166
vendor/opam/topkg/src-care/topkg_care_text.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+open Bos_setup
77+88+type flavour = [ `Markdown | `Asciidoc ]
99+1010+let flavour_of_fpath f = match String.Ascii.lowercase (Fpath.get_ext f) with
1111+| ".md" -> Some `Markdown
1212+| ".asciidoc" | ".adoc" -> Some `Asciidoc
1313+| _ -> None
1414+1515+let rec drop_blanks = function "" :: ls -> drop_blanks ls | ls -> ls
1616+let last_line = function [] -> None | l :: rev_ls -> Some l
1717+1818+(* Detecting headers *)
1919+2020+let simple_header hchar l before rest =
2121+ match String.(length @@ take ~sat:(Char.equal hchar) l) with
2222+ | 0 -> None
2323+ | n -> Some (n, l, before, rest)
2424+2525+let underline_header n uchar l before rest =
2626+ let is_underline_header uchar l =
2727+ String.(length @@ take ~sat:(Char.equal uchar) l) >= 2
2828+ in
2929+ if not (is_underline_header uchar l) then None else
3030+ match last_line before with
3131+ | None -> None
3232+ | Some t -> Some (n, strf "%s\n%s" t l, List.tl before, rest)
3333+3434+let rec find_markdown_header before = function
3535+| [] -> None
3636+| l :: ls ->
3737+ match simple_header '#' l before ls with
3838+ | Some _ as h -> h
3939+ | None ->
4040+ match underline_header 1 '=' l before ls with
4141+ | Some _ as h -> h
4242+ | None ->
4343+ match underline_header 2 '-' l before ls with
4444+ | Some _ as h -> h
4545+ | None -> find_markdown_header (l :: before) ls
4646+4747+let rec find_asciidoc_header before = function
4848+| [] -> None
4949+| l :: ls ->
5050+ match simple_header '=' l before ls with
5151+ | Some _ as h -> h
5252+ | None ->
5353+ match underline_header 1 '-' l before ls with
5454+ | Some _ as h -> h
5555+ | None ->
5656+ match underline_header 2 '~' l before ls with
5757+ | Some _ as h -> h
5858+ | None ->
5959+ match underline_header 3 '^' l before ls with
6060+ | Some _ as h -> h
6161+ | None ->
6262+ match underline_header 4 '+' l before ls with
6363+ | Some _ as h -> h
6464+ | None -> find_asciidoc_header (l :: before) ls
6565+6666+let head find_header text =
6767+ let lines = String.cuts ~sep:"\n" text in
6868+ let ret h acc =
6969+ let contents = String.concat ~sep:"\n" (List.rev @@ drop_blanks acc) in
7070+ Some (h, contents)
7171+ in
7272+ match find_header [] lines with
7373+ | None -> None
7474+ | Some (n, first, _ (* discard *), rest) ->
7575+ let rec loop acc rest = match find_header acc rest with
7676+ | None -> ret first (List.rev_append rest acc)
7777+ | Some (n', h, before, rest) ->
7878+ if n' > n then loop (h :: before) rest else
7979+ ret first before
8080+ in
8181+ loop [] rest
8282+8383+let head ?(flavour = `Markdown) text = match flavour with
8484+| `Markdown -> head find_markdown_header text
8585+| `Asciidoc -> head find_asciidoc_header text
8686+8787+let header_title ?(flavour = `Markdown) h = match String.cuts ~sep:"\n" h with
8888+| [h] ->
8989+ begin match flavour with
9090+ | `Markdown -> String.(trim @@ drop ~sat:(Char.equal '#') h)
9191+ | `Asciidoc -> String.(trim @@ drop ~sat:(Char.equal '=') h)
9292+ end
9393+| h :: _ -> h (* underline headers *)
9494+| [] -> assert false
9595+9696+(* Toy change log parsing *)
9797+9898+let change_log_last_entry ?flavour text = match head ?flavour text with
9999+| None -> None
100100+| Some (h, changes) ->
101101+ let title = header_title ?flavour h in
102102+ match String.take ~sat:Char.Ascii.is_graphic title with
103103+ | "" -> Logs.app (fun m -> m "%S %S" h changes); None
104104+ | version -> Some (version, (h, changes))
105105+106106+let change_log_file_last_entry file =
107107+ let flavour = flavour_of_fpath file in
108108+ OS.File.read file
109109+ >>= fun text -> match change_log_last_entry ?flavour text with
110110+ | None -> R.error_msgf "%a: Could not parse change log." Fpath.pp file
111111+ | Some (version, (header, changes)) -> Ok (version, (header, changes))
112112+113113+(* Toy URI parsing *)
114114+115115+let split_uri ?(rel = false) uri = match String.(cut ~sep:"//" (trim uri)) with
116116+| None -> None
117117+| Some (scheme, rest) ->
118118+ match String.cut ~sep:"/" rest with
119119+ | None -> Some (scheme, rest, "")
120120+ | Some (host, path) ->
121121+ let path = if rel then path else "/" ^ path in
122122+ Some (scheme, host, path)
123123+124124+(* Edit and page text *)
125125+126126+let find_pager ~don't =
127127+ if don't then Ok None else
128128+ match OS.Env.var "TERM" with
129129+ | Some "dumb" | None -> Ok None
130130+ | _ ->
131131+ let add_env v cmds = match OS.Env.(value v (some cmd) ~absent:None) with
132132+ | None -> cmds
133133+ | Some cmd -> cmd :: cmds
134134+ in
135135+ let cmds = [Cmd.v "less"; Cmd.v "more" ] in
136136+ let cmds = add_env "PAGER" cmds in
137137+ let rec loop = function
138138+ | [] -> Ok None
139139+ | cmd :: cmds ->
140140+ OS.Cmd.exists cmd >>= function
141141+ | true -> Ok (Some cmd)
142142+ | false -> loop cmds
143143+ in
144144+ loop cmds
145145+146146+let edit_file f = match OS.Env.(value "EDITOR" (some cmd) ~absent:None) with
147147+| None -> R.error_msg "EDITOR environment variable undefined."
148148+| Some editor ->
149149+ OS.Cmd.exists editor >>= function
150150+ | false -> R.error_msgf "Editor %a not in search path" Cmd.pp editor
151151+ | true ->
152152+ OS.Cmd.(run_status Cmd.(editor % p f)) >>= function
153153+ | `Exited n | `Signaled n -> Ok n
154154+155155+(* Pretty-printers. *)
156156+157157+module Pp = struct
158158+ let name = Fmt.(styled `Bold string)
159159+ let version = Fmt.(styled `Cyan string)
160160+ let commit = Fmt.(styled `Yellow string)
161161+ let dirty = Fmt.(styled `Red (any "dirty"))
162162+ let path = Fmt.(styled `Bold Fpath.pp)
163163+ let status ppf = function
164164+ | `Ok -> Fmt.(brackets @@ styled `Green (any " OK ")) ppf ()
165165+ | `Fail -> Fmt.(brackets @@ styled `Red (any "FAIL")) ppf ()
166166+end
+38
vendor/opam/topkg/src-care/topkg_care_text.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** Text processing helpers.
77+88+ See {!Topkg_care.Text}. *)
99+1010+(** {1 Text} *)
1111+1212+open Bos_setup
1313+1414+type flavour = [ `Markdown | `Asciidoc ]
1515+1616+val flavour_of_fpath : Fpath.t -> flavour option
1717+val head : ?flavour:flavour -> string -> (string * string) option
1818+val header_title : ?flavour:flavour -> string -> string
1919+2020+val change_log_last_entry :
2121+ ?flavour:flavour -> string -> (string * (string * string)) option
2222+2323+val change_log_file_last_entry :
2424+ Fpath.t -> ((string * (string * string)), R.msg) result
2525+2626+val split_uri : ?rel:bool -> string -> (string * string * string) option
2727+2828+val find_pager : don't:bool -> (Cmd.t option, R.msg) result
2929+val edit_file : Fpath.t -> (int, R.msg) result
3030+3131+module Pp : sig
3232+ val name : string Fmt.t
3333+ val version : string Fmt.t
3434+ val commit : string Fmt.t
3535+ val dirty : unit Fmt.t
3636+ val path : Fpath.t Fmt.t
3737+ val status : [`Ok | `Fail] Fmt.t
3838+end
+103
vendor/opam/topkg/src/topkg.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(* Preliminaries *)
77+88+include Topkg_result
99+1010+let strf = Topkg_string.strf
1111+module String = Topkg_string
1212+1313+type fpath = string
1414+module Fpath = Topkg_fpath
1515+1616+module Cmd = Topkg_cmd
1717+module Log = Topkg_log
1818+module OS = Topkg_os
1919+module Vcs = Topkg_vcs
2020+2121+(* Package description *)
2222+2323+module Conf = Topkg_conf
2424+module Exts = Topkg_fexts
2525+module Pkg = struct
2626+2727+ (* Install *)
2828+2929+ type install = Topkg_install.t
3030+ type field = Topkg_install.field
3131+ type exec_field = ?auto:bool -> field
3232+3333+ let nothing = Topkg_install.nothing
3434+ let flatten = Topkg_install.flatten
3535+ let bin = Topkg_install.bin
3636+ let doc = Topkg_install.doc
3737+ let etc = Topkg_install.etc
3838+ let lib = Topkg_install.lib
3939+ let lib_root = Topkg_install.lib_root
4040+ let libexec = Topkg_install.libexec
4141+ let libexec_root = Topkg_install.libexec_root
4242+ let man = Topkg_install.man
4343+ let misc = Topkg_install.misc
4444+ let sbin = Topkg_install.sbin
4545+ let share = Topkg_install.share
4646+ let share_root = Topkg_install.share_root
4747+ let stublibs = Topkg_install.stublibs
4848+ let toplevel = Topkg_install.toplevel
4949+ let unknown = Topkg_install.unknown
5050+ let test = Topkg_install.test
5151+5252+ let mllib = Topkg_install.mllib
5353+ let clib = Topkg_install.clib
5454+5555+ (* Distrib *)
5656+5757+ type watermark = Topkg_distrib.watermark
5858+ type distrib = Topkg_distrib.t
5959+6060+ let distrib = Topkg_distrib.v
6161+ let watermarks = Topkg_distrib.default_watermarks
6262+ let files_to_watermark = Topkg_distrib.default_files_to_watermark
6363+ let massage = Topkg_distrib.default_massage
6464+ let exclude_paths = Topkg_distrib.default_exclude_paths
6565+6666+ (* Publish *)
6767+6868+ type publish = Topkg_publish.t
6969+ let publish = Topkg_publish.v
7070+7171+ (* Build *)
7272+7373+ type build = Topkg_build.t
7474+ let build = Topkg_build.v
7575+ let build_cmd = Topkg_build.build_cmd
7676+ let clean_cmd = Topkg_build.clean_cmd
7777+ let ocb_tag = Topkg_build.ocb_tag
7878+ let ocb_bool_tag = Topkg_build.ocb_bool_tag
7979+ let ocb_bool_tags = Topkg_build.ocb_bool_tags
8080+8181+ (* Package *)
8282+8383+ type std_file = Topkg_pkg.std_file
8484+ let std_file = Topkg_pkg.std_file
8585+8686+ type meta_file = Topkg_pkg.meta_file
8787+ let meta_file = Topkg_pkg.meta_file
8888+8989+ type opam_file = Topkg_pkg.opam_file
9090+ let opam_file = Topkg_pkg.opam_file
9191+9292+ (* Describe *)
9393+9494+ let describe = Topkg_main.describe
9595+end
9696+9797+module Private = struct
9898+ let disable_main = Topkg_main.disable
9999+ module Codec = Topkg_codec
100100+ module Pkg = Topkg_pkg
101101+ module Ipc = Topkg_ipc
102102+ module Opam = Topkg_opam
103103+end
+2522
vendor/opam/topkg/src/topkg.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** The transitory OCaml package builder.
77+88+ See the {{!basics}basics} and the {{!menagerie}menagerie} of [pkg.ml]
99+ files.
1010+1111+ {e %%VERSION%% - {{:%%PKG_HOMEPAGE%% }homepage}} *)
1212+1313+(** {1:prels Preliminaries}
1414+1515+ In the most simple cases you won't need this, jump directly to the
1616+ {{!basics}basics} or {{!Pkg}package description API}. *)
1717+1818+val ( >>= ) : ('a, 'b) result -> ('a -> ('c, 'b) result) -> ('c, 'b) result
1919+(** [r >>= f] is [f v] if [r = Ok v] and [r] if [r = Error _]. *)
2020+2121+val ( >>| ) : ('a, 'b) result -> ('a -> 'c) -> ('c, 'b) result
2222+(** [r >>| f] is [Ok (f v)] if [r = Ok v] and [r] if [r = Error _]. *)
2323+2424+(** This definition re-export [result]'s constructors so that an
2525+ [open Topkg] gets them in scope. *)
2626+type ('a, 'b) r = ('a, 'b) result = Ok of 'a | Error of 'b
2727+2828+type 'a result = ('a, [ `Msg of string]) r
2929+(** The type for topkg results. *)
3030+3131+(** Result value combinators. *)
3232+module R : sig
3333+3434+ (** {1:err Errors} *)
3535+3636+ val reword_error : ('b -> 'c) -> ('a, 'b) r -> ('a, 'c) r
3737+ (** [reword_error reword r] is:
3838+ {ul
3939+ {- [r] if [r = Ok v]}
4040+ {- [Error (reword e)] if [r = Error e]}} *)
4141+4242+ (** {1:errmsg Error messages} *)
4343+4444+ type msg = [ `Msg of string ]
4545+ (** The type for (error) messages. *)
4646+4747+ val error_msg : string -> ('b, [> msg]) r
4848+ (** [error_msg s] is [Error (`Msg s)]. *)
4949+5050+ val error_msgf :
5151+ ('a, Format.formatter, unit, ('b, [> msg]) r) format4 -> 'a
5252+ (** [error_msgf fmt ...] is an error formatted according to [fmt]. *)
5353+5454+ val reword_error_msg :
5555+ ?replace:bool -> (string -> msg) -> ('a, msg) r -> ('a, [> msg]) r
5656+ (** [reword_error_msg ~replace reword r] is like {!reword_error} except
5757+ if [replace] is [false] (default), the result of [reword old_msg] is
5858+ concatened, on a new line to the old message. *)
5959+end
6060+6161+val strf : ('a, Format.formatter, unit, string) format4 -> 'a
6262+(** [strf] is [Printf.asprintf]. *)
6363+6464+(** Strings. *)
6565+module String : sig
6666+6767+ (** {1:adds String additions} *)
6868+6969+ include module type of String
7070+7171+ val head : string -> char option
7272+ (** [head s] if [Some s.[0]] if [s <> ""] and [None] otherwise. *)
7373+7474+ (** {1:preds Predicates} *)
7575+7676+ val is_prefix : affix:string -> string -> bool
7777+ (** [is_prefix ~affix s] is [true] iff [affix.[i] = s.[i]] for
7878+ all indices [i] of [affix]. *)
7979+8080+ val is_suffix : affix:string -> string -> bool
8181+ (** [is_suffix ~affix s] is true iff [affix.[n - i] = s.[m - i]] for all
8282+ indices [i] of [affix] with [n = String.length affix - 1] and [m =
8383+ String.length s - 1]. *)
8484+8585+ val for_all : (char -> bool) -> string -> bool
8686+ (** [for_all p s] is [true] iff for all indices [i] of [s], [p s.[i]
8787+ = true]. *)
8888+8989+ val exists : (char -> bool) -> string -> bool
9090+ (** [exists p s] is [true] iff there exists an index [i] of [s] with
9191+ [p s.[i] = true]. *)
9292+9393+ (** {1:subs Extracting substrings} *)
9494+9595+ val with_index_range : ?first:int -> ?last:int -> string -> string
9696+ (** [with_index_range ~first ~last s] are the consecutive bytes of [s]
9797+ whose indices exist in the range \[[first];[last]\].
9898+9999+ [first] defaults to [0] and last to [String.length s - 1].
100100+101101+ Note that both [first] and [last] can be any integer. If
102102+ [first > last] the interval is empty and the empty string is
103103+ returned. *)
104104+105105+ val cut : ?rev:bool -> sep:char -> string -> (string * string) option
106106+ (** [cut ~sep s] is either the pair [Some (l,r)] of the two
107107+ (possibly empty) substrings of [s] that are delimited by the
108108+ first match of the separator character [sep] or [None] if
109109+ [sep] can't be matched in [s]. Matching starts from the
110110+ beginning of [s] ([rev] is [false], default) or the end ([rev]
111111+ is [true]).
112112+113113+ The invariant [l ^ (String.make 1 sep) ^ r = s] holds. *)
114114+115115+ val cuts : ?empty:bool -> sep:char -> string -> string list
116116+ (** [cuts ~sep s] is the list of all substring of [s] that are delimited by
117117+ matches of [sep]. Empty substrings are ommited in the list if
118118+ [empty] is [falsee] (defaults to [true]). The invariant
119119+ [String.concat (String.make 1 sep) (split ~sep s) = s] holds. *)
120120+121121+ (** {1:vers Parsing version strings} *)
122122+123123+ val parse_version : string -> (int * int * int * string option) option
124124+ (** [parse_version] parses version strings of the form:
125125+{[
126126+"[v]major.minor[.patchlevel][(+|~)additional-info]"
127127+]}
128128+ into [(major, minor, patch, (+|~)additional_info)] tuples. *)
129129+130130+ val drop_initial_v : string -> string
131131+ (** [drop_initial_v s] drops a leading ['v'] or ['V'] from [s]. *)
132132+end
133133+134134+type fpath = string
135135+(** The type for file system paths. *)
136136+137137+(** File system paths.
138138+139139+ {b Note.} Only use ["/"] as a directory separator, even on
140140+ Windows platforms. *)
141141+module Fpath : sig
142142+143143+ (** {1:fpath File system paths} *)
144144+145145+ type t = fpath
146146+ (** The type for file system paths. *)
147147+148148+ val append : t -> t -> t
149149+ (** [append p q] appends [q] to [p] as follows:
150150+ {ul
151151+ {- If [q] is absolute then [q] is returned}
152152+ {- Otherwise appends [q]'s segments to [p] using a ["/"] if needed.}} *)
153153+154154+ val ( // ) : t -> t -> t
155155+ (** [p // q] is [append p q]. *)
156156+157157+ val is_dir_path : t -> bool
158158+ (** [is_dir_path p] is [true] iff [p] represents a directory. This means
159159+ that [p] is [.], [..] or ends with [/], [/..] or [/.]. *)
160160+161161+ val is_file_path : t -> bool
162162+ (** [is_file_path p] is [not (is_dir_path true)]. *)
163163+164164+ val basename : t -> string
165165+ (** [basename p] is [p]'s basename, the last non empty segment of [p]. *)
166166+167167+ val dirname : t -> string
168168+ (** [dirname p] is [p]'s dirname, [p] without its last non empty segment. *)
169169+170170+ (** {1:exts File extensions} *)
171171+172172+ val get_ext : t -> string
173173+ (** [get_ext p] is [p]'s filename extension (including the ['.']) or
174174+ the empty string if there is no extension *)
175175+176176+ val has_ext : string -> t -> bool
177177+ (** [has_ext e p] is [true] iff [e] is a suffix of [p]. *)
178178+179179+ val rem_ext : t -> t
180180+ (** [rem_ext p] is [p] without its filename extension. *)
181181+182182+end
183183+184184+(** Command lines.
185185+186186+ Both command lines and command line fragments using the same are
187187+ represented with the same {{!t}type}.
188188+189189+ When a command line is {{!section:OS.Cmd.run}run}, the first
190190+ element of the line defines the program name and each other
191191+ element is an argument that is passed {e as is} in the
192192+ program's [argv] array: no shell interpretation or any form of
193193+ argument quoting and/or concatenation occurs. *)
194194+module Cmd : sig
195195+196196+ (** {1:lines Command line fragments} *)
197197+198198+ type t
199199+ (** The type for command line fragments. *)
200200+201201+ val v : string -> t
202202+ (** [v cmd] is a new command line (or command line fragment)
203203+ whose first argument is [cmd]. *)
204204+205205+ val empty : t
206206+ (** [empty] is an empty command line. *)
207207+208208+ val is_empty : t -> bool
209209+ (** [is_empty l] is [true] iff [l] is empty. *)
210210+211211+ val ( % ) : t -> string -> t
212212+ (** [l % arg] adds [arg] to the command line [l]. *)
213213+214214+ val ( %% ) : t -> t -> t
215215+ (** [l %% frag] appends the line fragment [frag] to [l]. *)
216216+217217+ val add_arg : t -> string -> t
218218+ (** [add_arg l arg] is [l % arg]. *)
219219+220220+ val add_args : t -> t -> t
221221+ (** [add_args l frag] is [l %% frag]. *)
222222+223223+ val on : bool -> t -> t
224224+ (** [on bool line] is [line] if [bool] is [true] and {!empty}
225225+ otherwise. *)
226226+227227+ val p : fpath -> string
228228+ (** [p] is [(fun f -> f)]. *)
229229+230230+ (** {1:predicates Predicates and comparison} *)
231231+232232+ val equal : t -> t -> bool
233233+ (** [equal l l'] is [true] iff [l] and [l'] are litterally equal. *)
234234+235235+ val compare : t -> t -> int
236236+ (** [compare l l'] is a total order on command lines. *)
237237+238238+ (** {1:convert Conversions and pretty printing} *)
239239+240240+ val to_list : t -> string list
241241+ (** [to_list l] is [l] as a list of strings. *)
242242+243243+ val of_list : ?slip:string -> string list -> t
244244+ (** [of_list ?slip l] is a command line from the list of arguments
245245+ [l]. If [slip] is specified it is added on the command line
246246+ before each element of [l]. *)
247247+248248+ val dump : Format.formatter -> t -> unit
249249+ (** [dump ppf cmd] formats an unspecified representation of [cmd] on
250250+ [ppf]. *)
251251+end
252252+253253+(** Topkg log. *)
254254+module Log : sig
255255+256256+ (** {1:level Reporting levels} *)
257257+258258+ (** The type for reporting levels. *)
259259+ type level = App | Error | Warning | Info | Debug
260260+261261+ val level : unit -> level option
262262+ (** [level ()] is the current reporting level. *)
263263+264264+ val set_level : level option -> unit
265265+ (** [set_level l] sets the current reporting level to [l]. *)
266266+267267+ val level_to_string : level option -> string
268268+ (** [level_to_string l] converts [l] to an unspecified human-readable
269269+ US-ASCII string that can be parsed back by {!level_of_string}. *)
270270+271271+ val level_of_string : string -> (level option, [`Msg of string]) r
272272+ (** [level_of_string s] parses the representation of {!level_to_string}
273273+ from [s]. *)
274274+275275+ (** {1:logf Log functions} *)
276276+277277+ type 'a msgf =
278278+ (?header:string -> ('a, Format.formatter, unit) format -> 'a) -> unit
279279+280280+ val msg : level -> 'a msgf -> unit
281281+ (** [msg l (fun m -> m fmt ...)] logs with level [l] a message formatted
282282+ with [fmt]. *)
283283+284284+ val app : 'a msgf -> unit
285285+ (** [app] is msg [App]. *)
286286+287287+ val err : 'a msgf -> unit
288288+ (** [err] is [msg Error]. *)
289289+290290+ val warn : 'a msgf -> unit
291291+ (** [err] is [msg Warning]. *)
292292+293293+ val info : 'a msgf -> unit
294294+ (** [err] is [msg Info]. *)
295295+296296+ val debug : 'a msgf -> unit
297297+ (** [err] is [msg Debug]. *)
298298+299299+ val on_error_msg : ?level:level -> use:(unit -> 'a) -> 'a result -> 'a
300300+ (** [on_error_msg ~level r] is:
301301+ {ul
302302+ {- [v] if [r = Ok v]}
303303+ {- [use e] if [r = Error (`Msg e)]. As a side effect [e] is logged
304304+ with level [level] (defaults to [Error]).}} *)
305305+306306+ (** {1:monitoring Monitoring} *)
307307+308308+ val err_count : unit -> int
309309+ (** [err_count ()] is the number of messages logged with level [Error]. *)
310310+311311+ val warn_count : unit -> int
312312+ (** [warn_count ()] is the number of messages logged with level [Warning]. *)
313313+end
314314+315315+(** OS interaction. *)
316316+module OS : sig
317317+318318+ (** {1:os OS} *)
319319+320320+ (** Environment variables *)
321321+ module Env : sig
322322+323323+ (** {1:vars Variables} *)
324324+325325+ val var : string -> string option
326326+ (** [var name] is the value of the environment variable [name], if
327327+ defined. *)
328328+329329+ val opt_var : string -> absent:string -> string
330330+ (** [opt_var name ~absent] is the value of the optionally defined
331331+ environment variable [name], if defined, and [absent] if undefined. *)
332332+ end
333333+334334+ (** File operations. *)
335335+ module File : sig
336336+337337+ (** {1:paths Famous file paths} *)
338338+339339+ val null : fpath
340340+ (** [null] represents a file on the OS that discards all writes
341341+ and returns end of file on reads. *)
342342+343343+ val dash : fpath
344344+ (** [dash] is ["-"]. This value is is used by {!read} and {!write}
345345+ to respectively denote [stdin] and [stdout]. *)
346346+347347+ (** {1:exdel Existence and deletion} *)
348348+349349+ val exists : fpath -> bool result
350350+ (** [exists file] is [true] if [file] is a regular in the file
351351+ system and false otherwise. Symbolic links are followed. *)
352352+353353+ val must_exist : fpath -> fpath result
354354+ (** [must_exist file] is [file] if [file] is a regular file in the
355355+ file system and an error otherwise. Symbolic links are followed. *)
356356+357357+ val delete : ?must_exist:bool -> fpath -> unit result
358358+ (** [delete ~must_exist file] deletes file [file]. If [must_exist]
359359+ is [true] (defaults to [false]) an error is returned if [file]
360360+ doesn't exist. *)
361361+362362+ (** {1:fold Folding over file hierarchies} *)
363363+364364+ val fold :
365365+ ?skip:(fpath -> bool) -> (fpath -> 'a -> 'a) -> 'a ->
366366+ fpath list -> 'a result
367367+ (** [fold_files skip f acc paths] folds [f] over the {e files}
368368+ found in the file hierarchies starting at [paths]. Files
369369+ and directories [p] for which [skip p] is [true] are skipped.
370370+ [skip] defaults to [(fun _ -> false)]. *)
371371+372372+ (** {1:rw Reading and writing} *)
373373+374374+ val read : fpath -> string result
375375+ (** [read file] is [file]'s contents. If [file] is {!dash} reads
376376+ from {!stdin} and the channel is not closed when
377377+ the function returns. *)
378378+379379+ val write : fpath -> string -> unit result
380380+ (** [write file content] writes [content] to [file]. If [file] is {!dash}
381381+ writes to {!stdout} and flushes but doesn't close the channel
382382+ when the function returns. *)
383383+384384+ val write_subst : fpath -> (string * string) list -> string -> unit result
385385+ (** [write_subst file vars content] is like {!write} except any occurence
386386+ of a string of the form ["%%ID%%"] in [content] is replaced by the
387387+ value of [List.assoc "ID" vars], if any. *)
388388+389389+ (** {1:tmpfiles Temporary files} *)
390390+391391+ val tmp : unit -> fpath result
392392+ (** [tmp ()] creates a temporary file and returns its name. The file
393393+ is destroyed at the end of program execution. *)
394394+ end
395395+396396+ (** Directory operations. *)
397397+ module Dir : sig
398398+399399+ (** {1:exists Existence and contents} *)
400400+401401+ val exists : fpath -> bool result
402402+ (** [exists dir] is [true] if directory [dir] exists in the file
403403+ system. Symbolic links are followed. *)
404404+405405+ val must_exist : fpath -> fpath result
406406+ (** [must_exist dir] is [dir] if [file] is a regular file in the
407407+ file system and an error otherwise. Symbolic links are followed. *)
408408+409409+ val contents : ?dotfiles:bool -> ?rel:bool -> fpath -> fpath list result
410410+ (** [contents ~dotfiles ~rel dir] is the list of directories and filse
411411+ in [dir]. If [rel] is [true] (defaults to [false]) the resulting
412412+ paths are relative to [dir], otherwise they have [dir] prepended.
413413+ If [dotfiles] is [false] (default) elements that start with a [.]
414414+ are omitted. *)
415415+416416+ (** {1:current Current working directory} *)
417417+418418+ val current : unit -> fpath result
419419+ (** [current ()] is the current working directory. *)
420420+421421+ val set_current : fpath -> unit result
422422+ (** [set_current dir] sets the current working directory to [dir]. *)
423423+424424+ val with_current : fpath -> ('a -> 'b) -> 'a -> 'b result
425425+ (** [with_current dir f v] is [f v] with the current working directory
426426+ bound to [dir]. After the function returns the current working
427427+ directory is back to its initial value. *)
428428+ end
429429+430430+ (** Running commands. *)
431431+ module Cmd : sig
432432+433433+ (** {1:exists Command existence} *)
434434+435435+ val exists : Cmd.t -> bool result
436436+ (** [exists cmd] is [true] if the executable of [cmd] can be found
437437+ in the path and [false] otherwise. *)
438438+439439+ val must_exist : Cmd.t -> Cmd.t result
440440+ (** [must_exist cmd] is [cmd] if the executable of [cmd] can be found
441441+ in the path and an error otherwise. *)
442442+443443+ (** {1:run Running commands} *)
444444+445445+ val run : ?err:fpath -> Cmd.t -> unit result
446446+ (** [run cmd] runs the command [cmd]. [std{i,o,err}] are connected
447447+ to the invoking process' standard channels. If [err] is specified
448448+ [stderr] is redirected to the given file (e.g. {!File.null}). *)
449449+450450+ val run_status : ?err:fpath -> Cmd.t -> [`Exited of int] result
451451+ (** [run_status cmd] is like {!run}, but doesn't error on non-zero
452452+ exit status. *)
453453+454454+ (** {1:stdout Capturing standard output} *)
455455+456456+ type run_status = Cmd.t * [`Exited of int ]
457457+ (** The type for run statuses, the command that was run and the run
458458+ status. *)
459459+460460+ val success : ('a * run_status) result -> 'a result
461461+ (** [success r] is:
462462+ {ul
463463+ {- [Ok v] if [r = Ok (v, (_, `Exited 0))]}
464464+ {- [Error _] otherwise. Non [`Exited 0] statuses are turned into
465465+ an error message.}} *)
466466+467467+ type run_out
468468+ (** The type for representing the standard output of a command run. *)
469469+470470+ val out_string : ?trim:bool -> run_out -> (string * run_status) result
471471+ (** [out_string ~trim o] captures the standard output [o] as a [string].
472472+ If [trim] is [true] (default) the result is passed through
473473+ {!String.trim}. *)
474474+475475+ val out_lines : ?trim:bool -> run_out -> (string list * run_status) result
476476+ (** [out_lines ~trim] is like {!out_string} but the result is
477477+ {{!String.cuts}cut} on newlines (['\n']). *)
478478+479479+ val out_file : fpath -> run_out -> (unit * run_status) result
480480+ (** [out_file f o] writes the standard output [o] to file [f]. *)
481481+482482+ val out_stdout : run_out -> (unit * run_status) result
483483+ (** [out_stdout o] redirects the standard output [o] to the current
484484+ process standard output. *)
485485+486486+ val to_string : ?trim:bool -> run_out -> string result
487487+ (** [to_string] is [(out_string ?trim o |> success)]. *)
488488+489489+ val to_lines : ?trim:bool -> run_out -> string list result
490490+ (** [to_lines ?trim o] is [(out_string ?trim o |> success)]. *)
491491+492492+ val to_file : fpath -> run_out -> unit result
493493+ (** [to_file f o] is [(out_file f o |> success)] *)
494494+495495+ val run_out : ?err:fpath -> Cmd.t -> run_out
496496+ (** [run_out cmd] represents the standard output of the command run [cmd].
497497+ [std{i,err}] are connected to the invoking prcoess stream and standard
498498+ output can be consumed with {!to_string}, {!to_lines} or {!to_file}.
499499+ If [err] is specified [stderr] is redirected to the given file. *)
500500+ end
501501+end
502502+503503+(** Version control system repositories. *)
504504+module Vcs : sig
505505+506506+ (** {1:vcsops Version control system repositories} *)
507507+508508+ type kind = [ `Git | `Hg ]
509509+ (** The type for version control systems (VCS). *)
510510+511511+ val pp_kind : Format.formatter -> kind -> unit
512512+ (** [pp_kind ppf k] prints an unspecified representation of [k] on [ppf]. *)
513513+514514+ type commit_ish = string
515515+ (** The type for symbols resolving to a commit. The module uses
516516+ ["HEAD"] for specifying the current checkout; use
517517+ this symbol even if the underlying VCS is [`Hg]. *)
518518+519519+ type t
520520+ (** The type for version control systems repositories. *)
521521+522522+ val kind : t -> kind
523523+ (** [kind r] is [r]'s VCS kind. *)
524524+525525+ val dir : t -> fpath
526526+ (** [dir r] is [r]'s repository directory. *)
527527+528528+ val cmd : t -> Cmd.t
529529+ (** [cmd r] is the base VCS command to use to act on [r].
530530+531531+ {b Warning} Prefer the functions below to remain VCS independent. *)
532532+533533+ val find : ?dir:fpath -> unit -> t option result
534534+ (** [find ~dir ()] looks for a VCS repository in working directory [dir]
535535+ (not the repository directory like [.git], default is guessed
536536+ automatically). *)
537537+538538+ val get : ?dir:fpath -> unit -> t result
539539+ (** [get] is like {!find} but returns an error if no VCS was found. *)
540540+541541+ val pp : Format.formatter -> t -> unit
542542+ (** [pp ppf r] prints an unspecified representation of [r] on [ppf]. *)
543543+544544+ (** {1:state Repository state} *)
545545+546546+ val is_dirty : t -> bool result
547547+ (** [is_dirty r] is [Ok true] iff the working tree of [r] has uncommited
548548+ changes. *)
549549+550550+ val not_dirty : t -> unit result
551551+ (** [not_dirty] is [Ok ()] iff the working directory of [r] is not dirty and
552552+ an error that enjoins to stash or commit otherwise. *)
553553+554554+ val file_is_dirty : t -> fpath -> bool result
555555+ (** [file_id_dirty r f] is [Ok true] iff [f] has uncommited changes. *)
556556+557557+ val head : ?dirty:bool -> t -> string result
558558+ (** [head ~dirty r] is the HEAD commit identifier of the repository
559559+ [r]. If [dirty] is [true] (default), and indicator is appended to
560560+ the commit identifier if the working tree of [r] {!is_dirty}. *)
561561+562562+ val commit_id : ?dirty:bool -> ?commit_ish:commit_ish -> t -> string result
563563+ (** [commit_id ~dirty ~commit_ish r] is the object name (identifier)
564564+ of [commit_ish] (defaults to ["HEAD"]). If [commit_ish] is
565565+ ["HEAD"] and [dirty] is [true] (default) and indicator is
566566+ appended to the identifier if the working tree is dirty. *)
567567+568568+ val commit_ptime_s : ?commit_ish:commit_ish -> t -> int result
569569+ (** [commit_ptime_s t ~commit_ish] is the POSIX time in seconds
570570+ of commit [commit_ish] (defaults to ["HEAD"]) of repository [r]. *)
571571+572572+ val describe : ?dirty:bool -> ?commit_ish:commit_ish -> t -> string result
573573+ (** [describe ~dirty ~commit_ish r] identifies [commit_ish] (defaults to
574574+ ["HEAD"]) using tags from the repository [r]. If [commit_ish] is
575575+ ["HEAD"] and [dirty] is [true] (default) an indicator is appended to
576576+ the identifier if the working tree is dirty. *)
577577+578578+ val tags : t -> string list result
579579+ (** [tags r] is the list of tags in the repo [r]. *)
580580+581581+ val changes :
582582+ ?until:commit_ish -> t -> after:commit_ish -> (string * string) list result
583583+ (** [changes r ~after ~until] is the list of commits with their
584584+ one-line message from commit-ish [after] to commit-ish [until]
585585+ (defaults to ["HEAD"]). *)
586586+587587+ val tracked_files : ?tree_ish:string -> t -> fpath list result
588588+ (** [tracked_files ~tree_ish r] are the files tracked by the tree object
589589+ [tree_ish] (defaults to ["HEAD"]). *)
590590+591591+ (** {1:ops Repository operations} *)
592592+593593+ val clone : t -> dir:fpath -> unit result
594594+ (** [clone r ~dir] clones [r] in directory [dir]. *)
595595+596596+ val checkout : ?branch:string -> t -> commit_ish:commit_ish -> unit result
597597+ (** [checkout r ~branch commit_ish] checks out [commit_ish]. Checks out
598598+ in a new branch [branch] if provided. *)
599599+600600+ val commit_files : ?msg:string -> t -> fpath list -> unit result
601601+ (** [commit_files r ~msg files] commits the file [files] with message
602602+ [msg] (if unspecified the VCS should prompt). *)
603603+604604+ val tag :
605605+ ?force:bool -> ?sign:bool -> ?msg:string -> ?commit_ish:string -> t ->
606606+ string -> unit result
607607+ (** [tag r ~force ~sign ~msg ~commit_ish t] tags [commit_ish] with [t]
608608+ and message [msg] (if unspecified the VCS should prompt). if
609609+ [sign] is [true] (defaults to [false]) signs the tag ([`Git] repos only).
610610+ If [force] is [true] (default to [false]) doesn't fail if the tag
611611+ already exists. *)
612612+613613+ val delete_tag : t -> string -> unit result
614614+ (** [delete_tag r t] deletes tag [t] in repo [r]. *)
615615+end
616616+617617+(** {1:pkgdescr Package description} *)
618618+619619+(** Build configuration. *)
620620+module Conf : sig
621621+622622+ (** {1:kconv Key value converters} *)
623623+624624+ type 'a conv
625625+ (** The type for key value converters. *)
626626+627627+ val conv :
628628+ ?docv:string -> (string -> 'a result) -> (Format.formatter -> 'a -> unit) ->
629629+ 'a conv
630630+ (** [conv ~docv parse print] is a configuration value converter
631631+ parsing values with [parse] and printing them with
632632+ [print]. [docv] is a documentation meta-variable used in the
633633+ documentation to stand for the configuration value, defaults to
634634+ ["VALUE"]. *)
635635+636636+ val bool : bool conv
637637+ (** [bool] is a converter for booleans. *)
638638+639639+ val int : int conv
640640+ (** [int] is a converter for integers. *)
641641+642642+ val string : string conv
643643+ (** [string] is a converter for strings. *)
644644+645645+ val fpath : fpath conv
646646+ (** [fpath] is a converter for file paths. *)
647647+648648+ val some : ?none:string -> 'a conv -> 'a option conv
649649+ (** [some conv] is like [conv] but wraps the parsed value in [Some].
650650+ [none] is the string printed for [None] by the converter printer,
651651+ defaults to [""]. *)
652652+653653+ (** {1:key Keys}
654654+655655+ {b Warning.} Configuration keys must be created before the call
656656+ to {!Pkg.describe}. Yes you are right, that's a little bit dirty. *)
657657+658658+ type 'a key
659659+ (** The type for configuration keys whose lookup value is of type ['a].
660660+661661+ A configuration key has a name and represents a value of type
662662+ ['a] in a build configuration. If ["name"] is the name of the key
663663+ then its value can be specified on the command line using
664664+ [--name v] where [v] is the value of the key and is parsed
665665+ according to the key's {{!conv}value converter}.
666666+667667+ There are a few predefined key, see the {{!conf}configuration section}. *)
668668+669669+ val key :
670670+ ?docv:string -> ?doc:string -> ?env:string -> string -> 'a conv ->
671671+ absent:'a -> 'a key
672672+ (** [key name conv ~absent ~env ~doc ~docv] is a configuration key
673673+ with name [name] parsed from the command line with [conv].
674674+ [absent] is used if the value is not specified on the command
675675+ line. If [env] is specified and exists in the process environment,
676676+ the value of the variable is parsed with [conv] and used instead
677677+ of [absent] when needed.
678678+679679+ [doc] is a documentation string for the key. [docv] is a documentation
680680+ meta-variable to stand for the key value, defaulting to the
681681+ [docv] of [conv]. In [doc], occurences of the substring ["$(docv)"]
682682+ are replaced by the value of [docv]. *)
683683+684684+ val discovered_key :
685685+ ?docv:string -> ?doc:string -> ?env:string -> string -> 'a conv ->
686686+ absent:(unit -> 'a result) -> 'a key
687687+ (** [discovered_key] is like {!key} but the absent value is discovered,
688688+ {e if needed}, with [absent]. *)
689689+690690+ val with_pkg : ?default:bool -> string -> bool key
691691+ (** [with_pkg ~default pkg] is a boolean configuration key named
692692+ [(strf "with-%s" pkg)] to assert existence of opam packages.
693693+ If absent defaults to [default].
694694+695695+ Usually specified in opam build instructions with:
696696+ {["--with-thatpkg" thatpkg:installed]} along with an entry in the
697697+ depopt field of the opam file.
698698+699699+ {b Warning.} Only use this combinator for denoting opam
700700+ package existence, the resulting key may switch to a discovery
701701+ process in the future. *)
702702+703703+ (** {1:conf Configurations} *)
704704+705705+ type t
706706+ (** The type for configurations. *)
707707+708708+ val value : t -> 'a key -> 'a
709709+ (** [value c k] is the value of configuration key [k] in [c].
710710+711711+ @raise Invalid_argument If [k] was (illegaly) created after the call
712712+ to {!Pkg.describe} or if dirty tricks are being played. *)
713713+714714+ val pkg_name : t -> string
715715+ (** [pkg_name c] is either the value of the package name as given to
716716+ {!Pkg.describe} or the value of a predefined key [--pkg-name] which
717717+ overrides the package name. This defines the name of the generated
718718+ opam install file. Used to handle {{!multiopam}multiple
719719+ opam packages}. *)
720720+721721+ val build_dir : t -> fpath
722722+ (** [build_dir c] is either the value of build directory as given
723723+ to {!Pkg.describe} via {!Pkg.build} or the value of a predefined
724724+ key [--build-dir] which overrides the package definition. *)
725725+726726+ val vcs : t -> bool
727727+ (** [vcs c] is the value of a predefined key [--vcs].
728728+ It is [true] if the package directory is VCS managed. Usually
729729+ should not be specified manually: if absent it is determined
730730+ automatically by using {!Topkg.Vcs.find} and used to determine
731731+ the {!build_context}. *)
732732+733733+ val dev_pkg : t -> bool
734734+ (** [dev_pkg c] is the value of a predefined key [--dev-pkg].
735735+ It is [true] if the build is initiated by an installer like opam
736736+ and the package sources are a checkout from a VCS rather
737737+ than a distribution archive. Usually specified in opam build instruction
738738+ with either:
739739+{[
740740+"--dev-pkg" "%{dev}%" # for opam >= 2.0
741741+"--dev-pkg" "%{pinned}%" # < 2.0
742742+]}
743743+*)
744744+745745+ val pinned : t -> bool
746746+ (** @deprecated use {!dev_pkg}
747747+748748+ [pinned c] is the value of a predefined key [--pinned]. It has
749749+ exactly the same semantics as {!dev_pkg} but is misnamed. *)
750750+751751+ val jobs : t -> int
752752+ (** [jobs c] is the value of a predefined key [--jobs].
753753+ If absent it is determined from the build context as follows.
754754+ {ul
755755+ {- [`Dev] builds default to number of CPU cores, or 4 if it cannot be determined.}
756756+ {- all other contexts default to 4}} *)
757757+758758+ type build_context = [`Dev | `Distrib | `Pin ]
759759+ (** The type for build contexts. See {!val:build_context} for semantics. *)
760760+761761+ val build_context : t -> [`Dev | `Distrib | `Pin ]
762762+ (** [build_context c] is the build context of [c]. This is derived from
763763+ {!vcs} and {!dev_pkg} (or the deprecated {!pinned}) as follows.
764764+ {ul
765765+ {- [`Distrib] iff [not (vcs c)]. No VCS is present, this is a build from
766766+ a distribution. If there are configuration bits they should
767767+ be setup according to the build configuration.}
768768+ {- [`Dev] iff [vcs c && not (dev_pkg c || pinned c)]. This is a
769769+ development build invoked manually in a source repository. The
770770+ repository checkout should likely not be touched and configuration
771771+ bits not be setup. This is happening for example if the developer
772772+ is testing the package description in her working source repository
773773+ by invoking [pkg/pkg.ml] or [topkg build].}
774774+ {- [`Pin] iff [vcs c && (dev_pkg c || pinned c)]. This is a package
775775+ manager development build. In this case the repository checkout may
776776+ need to be massaged into a pseudo-distribution for the package to be
777777+ installed. This means that distribution watermarking and massaging
778778+ should be performed, see {!Pkg.distrib} and the [prepare_on_pin]
779779+ argument of {!Pkg.build}. Besides exisiting configuration bits
780780+ should be setup according to the
781781+ build environment. {b Note.} This is called [`Pin] due to a blind
782782+ spot, a more approriate name would be something like [`Dev_pkg]
783783+ build.}} *)
784784+785785+ val build_tests : t -> bool
786786+ (** [build_tests c] is the value of a predefined key [--tests] that
787787+ indicates if the tests should be built. If absent the value
788788+ depends on the {!build_context}, it is [true] in the [`Dev]
789789+ context and [false] in the other contexts. *)
790790+791791+ val debug : t -> bool
792792+ (** [debug c] is the value of a predefined key [--debug] that
793793+ indicates if the build should include debugging information
794794+ in the build artefacts. If absent the value is [true] or the
795795+ value of the environment variable TOPKG_CONF_DEBUG if
796796+ specified. *)
797797+798798+ val debugger_support : t -> bool
799799+ (** [debugger_support c] is the value of a predefined key
800800+ [--debugger-support] that indicates if build artefacts needed
801801+ for source level debuggers should be built and installed. If
802802+ absent the value is [true] or the value of the environment
803803+ variable TOPKG_CONF_DEBUGGER_SUPPORT if specified. *)
804804+805805+ val profile : t -> bool
806806+ (** [profile c] is the value of a predefined key [--profile] that
807807+ indicates if the build artefacts include run-time profiling support. If
808808+ absent the value is [false] or the value of the environment variable
809809+ TOPKG_CONF_PROFILE if specified. *)
810810+811811+ val toolchain : t -> string option
812812+ (** [toolchain c] is the value of a predefined key [--toolchain] that
813813+ specifies the ocamlbuild toolchain. If absent the value is [None] or
814814+ the value of the environment variable TOPKG_CONF_TOOLCHAIN if
815815+ specified. *)
816816+817817+ val dump : Format.formatter -> t -> unit
818818+ (** [dump ppf c] formats an unspecified representation of [c] on [ppf]. *)
819819+820820+ (** {1:tool Tool lookup}
821821+822822+ If your package description needs to run tools (e.g. in the
823823+ pre and post build hooks, see {!Pkg.build}) it should get the
824824+ tool to invoke with the following function. This allows to
825825+ control the executable being run which is useful for
826826+ cross-compilation scenarios. *)
827827+828828+ type os = [ `Build_os | `Host_os ]
829829+ (** The type for operating systems.
830830+ {ul
831831+ {- [`Build_os] is the build OS, the OS on which the package is built.}
832832+ {- [`Host_os] is the host OS, the OS on which the package is hosted
833833+ and runs.}} *)
834834+835835+ val tool : ?conf:t -> string -> os -> Cmd.t
836836+ (** [tool ~conf cmd os] is a command [cmd] which can be run on the build OS
837837+ which produces output suitable for the OS [os], taking into account
838838+ configuration [conf].
839839+840840+ The actual command returned depends on the following lookup procedure,
841841+ here exemplified on the invocation [tool "mytool" `Host_os] (resp.
842842+ [`Build_os]).
843843+ {ol
844844+ {- [Cmd.v "cmd"], if the environment variable HOST_OS_MYTOOL (resp.
845845+ BUILD_OS_MYTOOL) is set to ["cmd"]}
846846+ {- [Cmd.v (Fpath.append path "mytool")], if the environment variable
847847+ HOST_OS_XBIN (resp. BUILD_OS_BIN) is set to [path].}
848848+ {- [Cmd.v ("mytool" ^ "suff")], if the environment variable
849849+ HOST_OS_SUFF (resp. BUILD_OS_SUFF).}
850850+ {- [Cmd.(v "ocamlfind" % "-toolchain" % "toolchain" % "mytool")] if
851851+ ["mytool"] is part of the OCaml tools that can be invoked through
852852+ ocamlfind, [toolchain conf] is not [None], and [os] is [`Host_os].}
853853+ {- [Cmd.(v "ocamlfind" % "mytool")] if ["mytool"] is part of
854854+ the OCaml tools that can be invoked through ocamlfind.}
855855+ {- [Cmd.v "mytool"] otherwise.}} *)
856856+857857+ (** {1:ocaml OCaml configuration} *)
858858+859859+ (** OCaml configuration. *)
860860+ module OCaml : sig
861861+862862+ (** {1:conf OCaml system configuration} *)
863863+864864+ type conf = t
865865+866866+ type t
867867+ (** The type for OCaml configurations. *)
868868+869869+ val v : conf -> os -> t
870870+ (** [v c os] is the configuration of the OCaml system for the OS
871871+ [os] obtained by reading the output of [tool "ocamlc" os] with
872872+ the [-config] option. *)
873873+874874+ val find : string -> t -> string option
875875+ (** [find k c] looks up key [k] in configuration [c]. *)
876876+877877+ val version : t -> int * int * int * string option
878878+ (** [version] is the compiler version string
879879+ ["major.minor[.patchlevel][+additional-info]"] parsed into
880880+ [(major, minor, patch, additional-info)]. *)
881881+882882+ val ext_asm : t -> string
883883+ (** [ext_asm] is the file extension for assembly files, e.g. [".s"]. *)
884884+885885+ val ext_obj : t -> string
886886+ (** [ext_asm] is the file extension for C object files, e.g. [".o"]. *)
887887+888888+ val ext_lib : t -> string
889889+ (** [ext_lib] is the file extension for C static libraries, e.g. [".a"]. *)
890890+891891+ val ext_dll : t -> string
892892+ (** [ext_dll] is the file extension for C dynamically linked libraries,
893893+ e.g. [".so"]. *)
894894+895895+ val ext_exe : t -> string
896896+ (** [ext_exe] is the file extension for binary executables, e.g. [".exe"]
897897+ or [""]. Until at least OCaml 4.03 this information is not readily
898898+ available (see
899899+ {{:http://caml.inria.fr/mantis/view.php?id=7172}PR #7173}) and
900900+ discovered as described in
901901+ {{:http://lists.ocaml.org/pipermail/wg-windows/2015-July/000037.html}
902902+ this message}. *)
903903+904904+ val native : t -> bool
905905+ (** [native] is [true] if native compilation (i.e. [ocamlopt]) is
906906+ available. Until at least OCaml 4.03 this information is not
907907+ readily available (see
908908+ {{:http://caml.inria.fr/mantis/view.php?id=7172}PR #7173}) and
909909+ [true] is returned iff the standard library directory has the
910910+ [libasmrun] C static library. *)
911911+912912+ val native_dynlink : t -> bool
913913+ (** [native_dynlink] is [true] if native dynamic linking is
914914+ available. Until at least OCaml 4.03 this information is not
915915+ readily available (see
916916+ {{:http://caml.inria.fr/mantis/view.php?id=7172}PR #7173}) and
917917+ [true] is returned iff the standard library directory has the
918918+ [dynlink.cmxa] library. *)
919919+920920+ val supports_shared_libraries : t -> bool
921921+ (** [supports_shared_libraries] is [true] if compilation of C
922922+ shared libraries is supported. Until OCaml 4.08 this
923923+ information is not readily available (see
924924+ {{:https://github.com/ocaml/ocaml/pull/1691}PR 1691}). Before
925925+ that it checks for the existence of either ["libasmrun_shared"
926926+ ^ ext_dll c] or ["libcamlrun_shared" ^ ext_dll c] in the standard
927927+ library directory. *)
928928+929929+ val word_size : t -> int
930930+ (** [word_size] is the bit size of one word on the OS that will
931931+ execute the programs produced by the compiler (i.e. the value
932932+ of {!Sys.word_size} in these programs). Until at least OCaml
933933+ 4.03 this information is not readily available (see
934934+ {{:http://caml.inria.fr/mantis/view.php?id=7172}PR #7173}) and
935935+ the information is read from the C SIZEOF_PTR macro defined in
936936+ the file [caml/config.h] of the standard library directory. *)
937937+938938+ val dump : Format.formatter -> t -> unit
939939+ (** [dump ppf c] prints an unspecified representation of [c] on [ppf]. *)
940940+ end
941941+end
942942+943943+(** Exts defines sets of file extensions. *)
944944+module Exts : sig
945945+946946+ (** {1:fexts File extensions} *)
947947+948948+ type ext
949949+ (** The type for file extensions. *)
950950+951951+ type t = ext list
952952+ (** The type for lists of file extensions. *)
953953+954954+ val interface : t
955955+ (** [interface] is [exts [".mli"; ".cmi"; ".cmti"]]. *)
956956+957957+ val cmx : ext list
958958+ (** [cmx] is [ext ".cmx"]. *)
959959+960960+ val api : t
961961+ (** [api] is [interface @ cmx]. *)
962962+963963+ val real_c_library : ext list
964964+ (** [real_c_library] is the extension for C libraries (archives).
965965+ This should be used by C libraries (e.g. stubs) compiled by
966966+ OCaml. For example {!Topkg.Pkg.clib} uses this. The actual value
967967+ is determined from {{!Conf.OCaml}OCaml's configuration}. *)
968968+969969+ val c_library : ext list
970970+ (** [c_library] is the extension for C libraries (archives). This is like
971971+ {!real_c_library} but for those C archive that are generated by OCaml
972972+ build artefacts. The actual value is determined from
973973+ {{!Conf.OCaml}OCaml's configuration}. *)
974974+975975+ val c_dll_library : ext list
976976+ (** [c_dll_library] is the extension for C dynamic libraries (archives). The
977977+ actual value is determined from {{!Conf.OCaml}OCaml's configuration}. *)
978978+979979+ val library : ext list
980980+ (** [library] is [exts [".cma"; ".cmxa"; ".cmxs"] @ c_library] *)
981981+982982+ val module_library : ext list
983983+ (** [module_library] is [(api @ library)]. *)
984984+985985+ val exe : ext list
986986+ (** [exe] is the extension for executables. The actual value is determined
987987+ from {{!Conf.OCaml}OCaml's configuration}. *)
988988+989989+ val exts : string list -> ext list
990990+ (** [exts ss] is [ss] as a list of extensions. *)
991991+992992+ val ext : string -> ext list
993993+ (** [ext s] is [s] as a list of extensions. *)
994994+995995+ (**/**)
996996+ val ext_to_string : Conf.OCaml.t -> ext -> string
997997+ (**/**)
998998+end
999999+10001000+(** Package description.
10011001+10021002+ See the {{!basics}basics}. *)
10031003+module Pkg : sig
10041004+10051005+ (** {1:install Installation description}
10061006+10071007+ The installation description generates an opam install file
10081008+ which is simply a description of file moves (in the [mv] sense)
10091009+ from the build or source directory to standard install
10101010+ directories. Describing these moves in a given build
10111011+ configuration effectively determines what needs to built by the
10121012+ {{!build}package build command}.
10131013+10141014+ {b Note.} Always use ["/"] as a directory seperator for paths, even
10151015+ on Windows. *)
10161016+10171017+ type install
10181018+ (** The type for representing a set of install moves. *)
10191019+10201020+ val nothing : install
10211021+ (** [nothing] is an empty set of install moves. *)
10221022+10231023+ val flatten : install list -> install
10241024+ (** [flatten installs] is the union of all the install moves in [installs]. *)
10251025+10261026+ type field =
10271027+ ?force:bool -> ?built:bool -> ?cond:bool -> ?exts:Exts.t -> ?dst:fpath ->
10281028+ fpath -> install
10291029+ (** The type for an install field, a function that describe file
10301030+ moves to a particular installation directory. In the simplest form
10311031+ a call [field src] simply moves the file [src] at the root of the
10321032+ install directory of the field.
10331033+10341034+ In general [field ~force ~built ~cond ~exts ~dst src] generates install
10351035+ move as follows:
10361036+ {ul
10371037+ {- [dst] is the path were the source is written to. Expressed
10381038+ relative to the install directory of the field. Defaults
10391039+ to [Fpath.basename src], i.e. at the root of the install directory.
10401040+ If [dst] is a {{!Fpath.is_dir_path}directory path}, the destination
10411041+ is [(dst ^ Fpath.basename src)].}
10421042+ {- If [exts] is present and non empty, generates the list of
10431043+ paths [List.map (fun e -> src ^ e)] and a move for
10441044+ each of these. For example [field ~exts:]{!Exts.api}[ "src/m"] would
10451045+ generate a move for the files ["src/m.mli"], ["src/m.cmi"],
10461046+ ["src/m.cmti"], ["src/m.cmx"].}
10471047+ {- If [cond] is [false] (defaults to [true]) no file move is generated.
10481048+ This provides a convenient way to conditionalize installation based
10491049+ on the build configuration for example:
10501050+ {[let jsoo = Conf.value jsoo in
10511051+Pkg.mllib ~cond:jsoo "src/mylib_jsoo.mllib"
10521052+ ]}}
10531053+ {- If [built] is [true] (default), [src] is expressed relative
10541054+ to the {{!build}build directory} of the distribution and the path
10551055+ [src] is be given to {{!build}build system invocation}
10561056+ for construction.
10571057+ If [false], [src] is relative to the root of the distribution
10581058+ and is excluded from the build system invocation; this can
10591059+ be used for installing files that don't need to be built.}
10601060+ {- If [force] is [true] (defaults to [false]) it disables the automatic
10611061+ [src] filtering performed by the library. When [false],
10621062+ the library automatically disable certain build artefact
10631063+ depending on {{!Conf.OCaml}OCaml's configuration}, one such
10641064+ example is filtering native code build artefact if the OCaml install
10651065+ doesn't support
10661066+ {{!Conf.OCaml.native}native code compilation}}} *)
10671067+10681068+ type exec_field = ?auto:bool -> field
10691069+ (** The type for fields that install executable files. This is like {!field}
10701070+ except for the additional [auto] parameter:
10711071+ {ul
10721072+ {- If [auto] is [true] (default) then [field src dst]
10731073+ automatically adds the [".native"] suffix to [src] if
10741074+ {!Conf.OCaml.native} is [true] and the [".byte"] suffix
10751075+ otherwise. Besides it automatically adds {!Exts.exe} to
10761076+ [dst], which handles things correctly on the various Windows
10771077+ ports.}
10781078+ {- If [auto] is [false] you have full control according to
10791079+ {!field}.}} *)
10801080+10811081+ val bin : exec_field
10821082+ (** [bin] is a field that installs to a common [bin/] directory. *)
10831083+10841084+ val doc : field
10851085+ (** [doc] is a field that installs to a package specific [doc/]
10861086+ directory *)
10871087+10881088+ val etc : field
10891089+ (** [etc] is a field that installs to a package specific [etc/]
10901090+ directory. *)
10911091+10921092+ val lib : field
10931093+ (** [lib] is a field that installs to a package specific [lib/]
10941094+ directory. *)
10951095+10961096+ val lib_root : field
10971097+ (** [lib_root] is a field that install to a common [lib/] directory. *)
10981098+10991099+ val libexec : exec_field
11001100+ (** [libexec] is a field that installs to a package specific [lib/]
11011101+ directory but with the executable bit set. *)
11021102+11031103+ val libexec_root : exec_field
11041104+ (** [libexec_root] is a field that install to a common [lib/] directory
11051105+ but with the executable bit set. *)
11061106+11071107+ val man : field
11081108+ (** [man] is a field that installs to a common [man/] directory. See
11091109+ the {{:https://opam.ocaml.org/doc/manual/dev-manual.html#sec25}
11101110+ opam manual} for details. *)
11111111+11121112+ val misc : field
11131113+ (** [misc] is a field that installs to an arbitrary absolute path,
11141114+ the user is prompted for authorization,
11151115+ see the {{:https://opam.ocaml.org/doc/manual/dev-manual.html#sec25}
11161116+ opam manual} for details. *)
11171117+11181118+ val sbin : exec_field
11191119+ (** [sbin] is a field that installs to a common [sbin/] directory. *)
11201120+11211121+ val share : field
11221122+ (** [share] is a field that installs to a package specific [share/]
11231123+ directory. *)
11241124+11251125+ val share_root : field
11261126+ (** [share_root] is a field that installs to a common [share/]
11271127+ directory. *)
11281128+11291129+ val stublibs : field
11301130+ (** [stublibs] is a field that install to a common [lib/stublibs/]
11311131+ directory. Used for OCaml C stub directory. *)
11321132+11331133+ val toplevel : field
11341134+ (** [toplevel] is a field that installs to a common [lib/toplevel/]
11351135+ directory. *)
11361136+11371137+(**/**)
11381138+ val unknown : string -> field
11391139+(**/**)
11401140+11411141+ (** {2:tests Test executable description} *)
11421142+11431143+ val test : ?run:bool -> ?dir:fpath -> ?args:Cmd.t -> exec_field
11441144+ (** [test] is a special {{!exec_field}executable field}: it doesn't
11451145+ install the described executable (as such the [dst] argument is
11461146+ ignored at the moment). If [run] is [true] (default) executes
11471147+ the test with [args] when [ocaml pkg/pkg.ml test] is run; this
11481148+ will notably run to test the distribution tarball. If
11491149+ [run] is [false] the test needs to be invoked explicitely.
11501150+ [dir] specifies the current working directory for the test, expressed
11511151+ relative to the root directory of the package (defaults to [.]). *)
11521152+11531153+ (** {2 OCamlbuild higher-level installs}
11541154+11551155+ The following functions are OCamlbuild specific higher level
11561156+ installs that generate moves by reading OCamlbuild specification
11571157+ files. They also automatically handle the {!Conf.debugger_support}
11581158+ configuration key by building and installing the build artefacts
11591159+ needed by debuggers whenever its value is [true]. *)
11601160+11611161+ val mllib :
11621162+ ?field:field -> ?cond:bool -> ?cma:bool -> ?cmxa:bool -> ?cmxs:bool ->
11631163+ ?api:string list -> ?dst_dir:fpath -> fpath -> install
11641164+ (** [mllib ~field ~cond ~cma ~cmxa ~cmxs ~api ~dst_dir mllib] installs an
11651165+ OCaml library described by the
11661166+ {{:https://github.com/ocaml/ocamlbuild/blob/master/manual/manual.adoc#Sec_Archives_documentation}OCamlbuild .mllib file} [mllib] with:
11671167+ {ul
11681168+ {- [field] is the field where it gets installed (defaults to {!lib}).}
11691169+ {- If [cond] is [false] (defaults to [true]), no move is generated.}
11701170+ {- [cma], [cmxa], [cmxs] determine if corresponding archives are
11711171+ built and installed, they all default to [true].}
11721172+ {- [api] is the list of modules that defines the public interface
11731173+ of the library, if [None] all the modules mentioned in [mllib]
11741174+ are part of the public interface.}
11751175+ {- [dst_dir] is the destination directory of the library
11761176+ in the field. If unspecified this is the root of the field's
11771177+ directory.}} *)
11781178+11791179+ val clib :
11801180+ ?dllfield:field ->
11811181+ ?libfield:field ->
11821182+ ?cond:bool -> ?lib_dst_dir:fpath -> fpath -> install
11831183+ (** [clib clib] installs C stubs described by the {{:https://github.com/ocaml/ocamlbuild/blob/master/manual/manual.adoc#advanced-targets}OCamlbuild .clib file} [clib] with:
11841184+ {ul
11851185+ {- [dllfield] is the field where the C DLL archive gets installed,
11861186+ (defaults to {!stublibs})}
11871187+ {- [libfield] is the field where the C static archive gets installed
11881188+ (defaults to {!lib})}
11891189+ {- If [cond] is [false] (defaults to [true]), no move is generated.}
11901190+ {- [lib_dst_dir] is the destination directory of the library in the
11911191+ [libfield] field. If unspecified this is the root of the field's
11921192+ directory. This does not affect the [dllfield], DLLs are always
11931193+ installed at the root directory of the [dllfield].}} *)
11941194+11951195+ (** {1:build Build description} *)
11961196+11971197+ type build
11981198+ (** The type for package build description. *)
11991199+12001200+ val build :
12011201+ ?prepare_on_pin:bool ->
12021202+ ?dir:fpath ->
12031203+ ?pre:(Conf.t -> unit result) ->
12041204+ ?cmd:(Conf.t -> Conf.os -> fpath list -> unit result) ->
12051205+ ?post:(Conf.t -> unit result) ->
12061206+ ?clean:(Conf.os -> build_dir:fpath -> unit result) -> unit -> build
12071207+ (** [build ~prepare_on_pin ~dir ~cmd ~pre ~post] describes the package
12081208+ build procedure.
12091209+ {ul
12101210+ {- [prepare_on_pin] if [true] (default) distribution
12111211+ preparation is performed if a [`Pin]
12121212+ {{!Conf.build_context}build context} is detected. This means that
12131213+ the checkout is watermarked and the massage hook is invoked,
12141214+ see step 2. of {{!distdetails}distribution creation}.}
12151215+ {- [dir] is the directory where build artefacts are generated,
12161216+ (defaults to ["_build"]). Note that his value can be overriden
12171217+ from the command line.}
12181218+ {- [pre] is a hook that is invoked with the build context, after
12191219+ distribution preparation if applicable, but before the build
12201220+ command. It can be used to adapt the build setup according to
12211221+ the build configuration. Default is a nop.}
12221222+ {- [cmd] invokes the build system to build the files
12231223+ determined by {{!install}install} moves.
12241224+ It is given the build configuration, an {{!Conf.os}OS
12251225+ specification}, the list of files to build relative to the
12261226+ {{!Conf.build_dir}build directory}, and build the given
12271227+ files in the build directory. The default is:
12281228+{[
12291229+fun c os files -> OS.Cmd.run @@ Cmd.(Pkg.build_cmd c os %% of_list files)
12301230+]}}
12311231+ {- [post] is a hook that is invoked with the build context after
12321232+ the build command returned sucessfully. Default is a nop.}
12331233+ {- [clean] is invoked to clean a build. It is given
12341234+ an {{!Conf.os}OS specification} and a build directory to
12351235+ clean. The default is:
12361236+{[
12371237+let clean os ~build_dir = OS.Cmd.run @@ Pkg.clean_cmd os ~build_dir
12381238+]}}}
12391239+ {b Warning.} If you are invoking tools in your hooks consider
12401240+ using {!Conf.tool} to look them up it helps for cross-compilation. *)
12411241+12421242+ (** {2:ocamlbuild OCamlbuild support}
12431243+12441244+ If you are using [ocamlbuild], the following functions
12451245+ help to customize the build system invocation according to the
12461246+ configuration. *)
12471247+12481248+ val build_cmd : Conf.t -> Conf.os -> Cmd.t
12491249+ (** [build_cmd c os] is the default build command to which
12501250+ files to build are given. Its value is defined by:
12511251+{[fun c os ->
12521252+ let ocamlbuild = Conf.tool "ocamlbuild" os in
12531253+ let build_dir = Conf.build_dir c in
12541254+ let toolchain =
12551255+ match Topkg_conf.toolchain c with
12561256+ | Some toolchain -> Topkg_cmd.(v "-toolchain" % toolchain)
12571257+ | _ -> Topkg_cmd.empty
12581258+ in
12591259+ let debug = Cmd.(on (Conf.debug c) (v "-tag" % "debug")) in
12601260+ let profile = Cmd.(on (Conf.profile c) (v "-tag" % "profile")) in
12611261+ Cmd.(ocamlbuild % "-use-ocamlfind" %% toolchain % "-classic-display" %%
12621262+ debug %% profile % "-build-dir" % build_dir)]} *)
12631263+12641264+ val clean_cmd : Conf.os -> build_dir:fpath -> Cmd.t
12651265+ (** [clean_cmd os ~build_dir] is the default clean command. Its value
12661266+ is defined by:
12671267+{[fun os ~build_dir ->
12681268+ let ocamlbuild = Conf.tool "ocamlbuild" os in
12691269+ Cmd.(ocamlbuild % "-use-ocamlfind" % "-classic-display" %
12701270+ "-build-dir" % build_dir % "-clean") ]} *)
12711271+12721272+ val ocb_tag : Conf.t -> 'a Conf.key -> string -> Cmd.t
12731273+ (** [ocb_tag c key name] is a command fragment adding the
12741274+ [ocamlbuild] parameterized tag [name] with [key]'s value to
12751275+ the default set of tags. The key value is converted to a string
12761276+ using the printer of the key value {{!Conf.conv}converter}.
12771277+12781278+ For example with a key [k : bool Conf.key] whose value is
12791279+ [true], [ocb_tag c k "foo"] adds the tag [foo(true)] to the
12801280+ default tags. A sample build command for {!build} using
12811281+ this key would be:
12821282+{[
12831283+ let cmd c os files =
12841284+ OS.Cmd.run Cmd.(build_cmd c os %% ocb_tag c k "foo" %% of_list files)]} *)
12851285+12861286+ val ocb_bool_tag : Conf.t -> bool Conf.key -> string -> Cmd.t
12871287+ (** [ocb_bool_tag c key name] is a command fragment adding
12881288+ the [ocamlbuild] tag [name] to the default set of tags iff [key]'s
12891289+ value is [true]. *)
12901290+12911291+ val ocb_bool_tags : Conf.t -> (bool Conf.key * string) list -> Cmd.t
12921292+ (** [ocb_bool_tags c assoc] is the concatenation of {!ocb_bool_tag}
12931293+ for all pairs in [assoc]. *)
12941294+12951295+ (** {1:distrib Distribution description} *)
12961296+12971297+ type watermark = string * [ `String of string | `Version | `Version_num
12981298+ | `Name | `Vcs of [`Commit_id]
12991299+ | `Opam of fpath option * string * string]
13001300+ (** The type for watermarks. A watermark identifier, e.g. ["ID"] and its
13011301+ definition:
13021302+ {ul
13031303+ {- [`String s], [s] is the definition.}
13041304+ {- [`Name], is the name of package.}
13051305+ {- [`Version], is the version of the distribution.}
13061306+ {- [`Version_num], is the version of the distribution with a potential
13071307+ leading ['v'] or ['V'] dropped.}
13081308+ {- [`Vcs `Commit_id], is the commit identifier (hash) of the
13091309+ distribution. May be post-fixed by ["dirty"] in
13101310+ {{!Conf.build_context}dev package ([`Pin]) builds}.}
13111311+ {- [`Opam (file, field, sep)], is the values of the field
13121312+ [field] concatenated with separator [sep] of the opam file
13131313+ [file], expressed relative to the distribution root directory, if
13141314+ [file] is [None] this is the package's default opam file, see
13151315+ {!describe}. Not all fields are supported see the value of
13161316+ {!Topkg_care.Opam.File.field_names}. {b Warning.} In
13171317+ {{!Conf.build_context}dev package ([`Pin]) builds}, [`Opam]
13181318+ watermarks are only substituted if the package [topkg-care] is
13191319+ installed.}}
13201320+13211321+ When a file is watermarked with an identifier ["ID"], any occurence of
13221322+ the sequence [%%ID%%] in its content is substituted by its definition. *)
13231323+13241324+ type distrib
13251325+ (** The type for describing distribution creation. *)
13261326+13271327+ val distrib :
13281328+ ?watermarks:watermark list ->
13291329+ ?files_to_watermark:(unit -> fpath list result) ->
13301330+ ?massage:(unit -> unit result) ->
13311331+ ?exclude_paths:(unit -> fpath list result) ->
13321332+ ?uri:string -> unit -> distrib
13331333+ (** [distrib ~watermarks ~files_to_watermark ~massage
13341334+ ~exclude_paths ~uri ()] influences the distribution creation
13351335+ process performed by the [topkg] tool.
13361336+ See the {{!distdetails}full details about distribution creation}.
13371337+13381338+ In the following the {e distribution build directory} is a
13391339+ private clone of the package's source repository's [HEAD] when
13401340+ [topkg distrib] is invoked.
13411341+ {ul
13421342+ {- [watermarks] defines the source watermarks for the distribution,
13431343+ defaults to {!watermarks}.}
13441344+ {- [files_to_watermark] is invoked in the distribution build
13451345+ directory to determine the files to watermark, defaults
13461346+ to {!files_to_watermark}.}
13471347+ {- [massage] is invoked in the distribution build directory,
13481348+ after watermarking, but before archiving. It can be used to
13491349+ generate distribution time build artefacts. Defaults to {!massage}.}
13501350+ {- [exclude_paths ()] is invoked in the distribution build
13511351+ directory, after massaging, to determine the paths that are
13521352+ excluded from being added to the distribution archive. Defaults to
13531353+ {!exclude_paths}.}
13541354+ {- [uri] is an URI pattern that specifies the location of the
13551355+ distribution on the WWW. In this string any sub-string
13561356+ ["$(NAME)"] is replaced by the package name, ["$(VERSION)"] is replaced
13571357+ by the distribution version string and ["$(VERSION_NUM)"] by the
13581358+ distribution version string, chopping an initial
13591359+ ['v'] or ['V'] character if present. This argument is used to
13601360+ generate the [url] file of an opam package for the distribution;
13611361+ it will be deprecated in the future in favour of a [x-distrib-uri]
13621362+ field in the opam file. If the value is unspecified it defaults to:
13631363+{[PKG_HOMEPAGE/releases/$(NAME)-$(VERSION_NUM).tbz]}
13641364+ where PKG_HOMEPAGE is the package's opam file [homepage] field.
13651365+ As a special case if the
13661366+ hostname of PKG_HOMEPAGE is [github] the following is used:
13671367+{[PKG_DEV_REPO/releases/download/$(VERSION)/$(NAME)-$(VERSION_NUM).tbz]}
13681368+ where PKG_DEV_REPO is the package's opam file [dev-repo] field
13691369+ without the [.git] suffix and a possible [git+] prefix.}} *)
13701370+13711371+ val watermarks : watermark list
13721372+ (** [watermarks] is the default list of watermarks. It has the following
13731373+ elements:
13741374+ {ul
13751375+ {- [("NAME", `Name)]}
13761376+ {- [("VERSION", `Version)]}
13771377+ {- [("VERSION_NUM", `Version_num)]}
13781378+ {- [("VCS_COMMIT_ID", `Vcs [`Commit_id])]}
13791379+ {- [("PKG_MAINTAINER", `Opam (None, "maintainer", ", "))]}
13801380+ {- [("PKG_AUTHORS", `Opam (None, "authors", ", ")]}
13811381+ {- [("PKG_HOMEPAGE", `Opam (None, "homepage", " ")]}
13821382+ {- [("PKG_ISSUES", `Opam (None, "bug-reports", " ")]}
13831383+ {- [("PKG_DOC", `Opam (None, "doc", " "))]}
13841384+ {- [("PKG_LICENSE", `Opam (None, "license", ", ")]}
13851385+ {- [("PKG_REPO", `Opam (None, "dev-repo", " "))]}}
13861386+ Prepending to the list overrides default definitions. *)
13871387+13881388+ val files_to_watermark : unit -> fpath list result
13891389+ (** [files_to_watermark ()] is the default list of files to
13901390+ watermark. It is invoked in the distribution build directory
13911391+ and gets the set of {{!Vcs.tracked_files}tracked files} of this
13921392+ directory from which it removes the files that end with [.eps], [.flv],
13931393+ [.gif], [.ico], [.jpeg], [.jpg], [.mov], [.mp3], [.mp4], [.otf],
13941394+ [.pdf], [.png], [.ps], [.ttf], [.woff]. *)
13951395+13961396+ val massage : unit -> unit result
13971397+ (** [massage] is the default distribution massaging function. It is
13981398+ invoked in the distribution build directory and does nothing. *)
13991399+14001400+ val exclude_paths : unit -> fpath list result
14011401+ (** [exclude_paths ()] is the default list of paths to exclude
14021402+ from the distribution archive. It is invoked in the distribution build
14031403+ directory and returns the following static set of files.
14041404+{[
14051405+fun () -> Ok [".git"; ".gitignore"; ".gitattributes"; ".hg"; ".hgignore";
14061406+ "build"; "Makefile"; "_build"]]} *)
14071407+14081408+ (** {1 Distribution publication description} *)
14091409+14101410+ type publish
14111411+ (** The type for describing distribution publication. *)
14121412+14131413+ val publish :
14141414+ ?artefacts:[`Doc | `Distrib | `Alt of string ] list -> unit -> publish
14151415+ (** [publish ~artefacts ()] influences the distribution publication process
14161416+ performed by the [topkg] tool:
14171417+ {ul
14181418+ {- [artefacts] defines which artefacts are published by an invocation
14191419+ of [topkg publish] without arguments (defaults to
14201420+ [[`Doc;`Distrib]]).}} *)
14211421+14221422+ (** {1 Package description} *)
14231423+14241424+ type std_file
14251425+ (** The type for specifying a standard file. *)
14261426+14271427+ val std_file : ?install:bool -> fpath -> std_file
14281428+ (** [std_file ~install p] is a standard file [p] expressed relative
14291429+ to the distribution root directory. The file is
14301430+ automatically installed if [install] is [true] (default). *)
14311431+14321432+ type meta_file
14331433+ (** The type for specifying an OCamlfind META file. *)
14341434+14351435+ val meta_file : ?lint:bool -> ?install:bool -> fpath -> meta_file
14361436+ (** [meta_file ~lint ~install p] is a META file [p] expressed relative
14371437+ to the distribution root directory. The file is automatically
14381438+ installed in the {!lib} field if [install] is [true] (default).
14391439+ If [lint] is [true] (default), it is OCamlfind linted. *)
14401440+14411441+ type opam_file
14421442+ (** The type for specifying an opam file. *)
14431443+14441444+ val opam_file :
14451445+ ?lint:bool -> ?lint_deps_excluding:string list option -> ?install:bool ->
14461446+ fpath -> opam_file
14471447+ (** [opam_file ~lint ~lint_deps_excluding ~install p] is an opam file
14481448+ [p] expressd relative to the distribution root directory such that:
14491449+ {ul
14501450+ {- If [install] is [true] (default), it is automatically installed
14511451+ in the {!lib} field.}
14521452+ {- If [lint] is [true] (default), it is opam linted.}
14531453+ {- If [lint_deps_excluding] is [Some excludes], [topkg]
14541454+ checks that each of the opam package dependencies is mentioned
14551455+ as a root package in the OCamlbuild [_tags] file and vice-versa. The
14561456+ following package names are excluded from this test:
14571457+ {ul
14581458+ {- The packages names mentioned in [excludes].}
14591459+ {- Package names that start with ["conf-"]}
14601460+ {- {!Topkg_care.OCamlfind.base_packages}}
14611461+ {- {!Topkg_care.Opam.ocaml_base_packages}}}
14621462+ If [None] the dependency check is disabled.}} *)
14631463+14641464+ val describe :
14651465+ ?delegate:Cmd.t ->
14661466+ ?readmes:std_file list ->
14671467+ ?licenses:std_file list ->
14681468+ ?change_logs:std_file list->
14691469+ ?metas:meta_file list ->
14701470+ ?opams:opam_file list ->
14711471+ ?lint_files:fpath list option ->
14721472+ ?lint_custom:(unit -> R.msg result list) ->
14731473+ ?distrib:distrib ->
14741474+ ?publish:publish ->
14751475+ ?build:build ->
14761476+ string -> (Conf.t -> install list result) -> unit
14771477+ (** [describe name install] describes a package named [name] with:
14781478+ {ul
14791479+ {- [delegate], the package delegate command to use. If unspecfied
14801480+ determined by the delegate lookup procedure, see
14811481+ [topkg help delegate] for more information.}
14821482+ {- [readmes] are readme files, defaults to
14831483+ [[std_file "README.md"]]. Automatic install is in the
14841484+ {!doc} field.}
14851485+ {- [licenses] are license files, defaults to
14861486+ [[std_file "LICENSE.md"]]. Automatic install is in the {!doc} field.}
14871487+ {- [change_logs] are change logs, defaults to
14881488+ [[std_file "CHANGES.md"]]. The first file of the list is the
14891489+ one that is acted upon by the [topkg log] command.
14901490+ Automatic install is in the {!doc} field.}
14911491+ {- [metas] the package's ocamlfind META files, defaults to
14921492+ [[ meta_file "pkg/META" ]].}
14931493+ {- [opams] the package's opam package files, defaults to
14941494+ [[opam_file "opam"]]. The default opam file used by a package
14951495+ description depends on the package [name] (which can
14961496+ be overriden from the command line). The opam file lookup
14971497+ procedure selects the first path in [opams] whose filename is
14981498+ [(name ^ ".opam")] and, failing
14991499+ to do so, it fallbacks to an ["opam"] file at the root of the
15001500+ distribution.}
15011501+ {- [lint_files] if [Some files], ensures that all files mentioned in
15021502+ [readme], [license], [change_log], [metas], [opams] and [files]
15031503+ are present in the distribution. Defaults to [Some []].
15041504+ If [None] disables the file existence tests (including readme,
15051505+ change_log, license, metas, opams, metas.)}
15061506+ {- [lint_custom] defines a custom linting process run with the current
15071507+ directory set at the root of the distribution. Successes and errors
15081508+ in the returned list are reported as such and any error in the list
15091509+ makes the lint fail. Defaults to [None].}
15101510+ {- [distrib], specifies the distribution process, defaults to
15111511+ {!distrib}[ ()].}
15121512+ {- [publish], specifies the publication process, defaults to
15131513+ {!publish}[ ()].}
15141514+ {- [build], specifies the build process, defaults to {!build}[ ()].}
15151515+ {- [install] given a {{!Conf.t}build configuration} specifies
15161516+ the install moves. Note that some of standard files are
15171517+ automatically installed and don't need to be specified, see
15181518+ {!std_file}, {!meta_file} and {!opam_file}.}} *)
15191519+15201520+ (** {1:distdetails Package distribution creation details}
15211521+15221522+ The following describes the exact steps performed by [topkg
15231523+ distrib] to create the distribution archive. Note that [topkg]
15241524+ allows to override or disable part of the process via command
15251525+ line arguments, e.g. to specify the version string manually or
15261526+ skip linting. See [topkg distrib --help] for more information.
15271527+15281528+ The distribution process assumes that the source repository
15291529+ working directory is clean so that its definitions are consistent
15301530+ with those of the distribution build directory. A warning is
15311531+ generated if this is not the case as it may end up in inconsistent
15321532+ distribution archives (but which may be fine to only publish
15331533+ a documentation update).
15341534+15351535+ Let [$NAME] be the name of the package, [$BUILD] be its
15361536+ {{!build}build directory}, [$VERSION] be the VCS tag description
15371537+ (e.g. [git-describe(1)] if you are using [git]) of the source
15381538+ repository HEAD commit and [distrib] the {{!distrib}distribution
15391539+ description} found in the source's repository [pkg/pkg.ml] file.
15401540+ {ol
15411541+ {- Clone the source repository at [HEAD] as the distribution build
15421542+ directory [$BUILD/$NAME-$VERSION.build].}
15431543+ {- Prepare the distribution:
15441544+ {ol
15451545+ {- Invoke the [files_to_watermark] function of [distrib] in the
15461546+ distribution build directory to determine the files to watermark
15471547+ with [watermarks] and perform the watermarking process.}
15481548+ {- Adds a version field with value [$VERSION] to the opam files
15491549+ mentioned by {!Pkg.describe}.}
15501550+ {- Run the [massage] function of [distrib] in the distribution
15511551+ build directory. This can be used to create distribution time
15521552+ build artefacts.}}}
15531553+ {- Invoke the [exclude_paths] function of [distrib] in the
15541554+ distribution build directory to determine the paths to exclude
15551555+ from the archive.}
15561556+ {- Create a distribution tarball [$BUILD/$NAME-$VERSION.tbz] with the
15571557+ file hierarchy in [$BUILD/$NAME-$VERSION.build],
15581558+ excluding the paths determined at the preceeding point and delete the
15591559+ clone [$BUILD/$NAME-$VERSION.build]. File modifications times in
15601560+ the archive are set to [HEAD]'s commit time and file
15611561+ permissions are preserved. Any other form of file metadata is
15621562+ discarded in the archive.}
15631563+ {- Test the distribution. Unpack it in directory [$BUILD/$NAME-$VERSION],
15641564+ lint the distribution, build the package in the current
15651565+ build environment with its tests, run the tests, on success
15661566+ delete [$BUILD/$NAME-$VERSION]. Note that this uses the archive's
15671567+ [pkg/pkg.ml] file, which should not be different from the source's
15681568+ repository file if the latter was clean when [topkg distrib] was
15691569+ invoked.}}
15701570+15711571+ {2:watnote Note on watermarking}
15721572+15731573+ It is right to doubt the beauty and be concerned about the
15741574+ watermarking process. However experience shows that alternatives
15751575+ like having an OCaml module generated with the appropriate
15761576+ information doesn't work well in practice. Version numbers do
15771577+ not only show up in OCaml source code. They also appear in
15781578+ documentation comments, metadata files, textual data files and
15791579+ non-OCaml source files.
15801580+15811581+ Watermarking by default all the non binary files of the
15821582+ distribution allows one to write %%VERSION%% in any context and
15831583+ be sure it is be substituted with the right version number in
15841584+ dev package ([`Pin]) and distribution ([`Distrib])
15851585+ {{!Conf.build_context}build contexts} (this occurence was not
15861586+ subsituted because a ZERO WIDTH NON-JOINER U+200C was introduced between
15871587+ the first two percent characters).
15881588+15891589+ If this scheme poses a problem for certain files or you remain
15901590+ unconvinced, simply filter the result of {!files_to_watermark} or
15911591+ replace it by the exact files you would like to watermark. *)
15921592+end
15931593+15941594+(** {1:private Private} *)
15951595+15961596+(** Private definitions.
15971597+15981598+ {b Warning.} The following definitions are subject to change even
15991599+ between minor versions of the library. [Topkg] users {b must not}
16001600+ use these definitions to describe their package. *)
16011601+module Private : sig
16021602+16031603+ (** {1:private Private} *)
16041604+16051605+ val disable_main : unit -> unit
16061606+ (** [disable_main ()] disables [Topkg]'s main invoked on
16071607+ {!Pkg.describe}. Invoke this function in your main function if
16081608+ you are not using [Topkg] in a description file but as as a
16091609+ library. *)
16101610+16111611+ (** Topkg interprocess communication codec.
16121612+16131613+ Codecs for communication between the [topkg] tool and topkg
16141614+ description files. *)
16151615+ module Codec : sig
16161616+16171617+ (** {1 Decode errors} *)
16181618+16191619+ (** The type for decode errors.
16201620+ {ul
16211621+ {- [Corrupted (kind, data)], an error occured while decoding
16221622+ [data] for [kind].}
16231623+ {- [Version (exp, fnd)], a {{!version}versioned} decoder
16241624+ expected version [exp] but found [fnd]}} *)
16251625+ type error = Corrupted of (string * string) | Version of int * int
16261626+16271627+ val pp_error : Format.formatter -> error -> unit
16281628+ (** [pp_error ppf e] prints an unspecified representation of [e]
16291629+ on [ppf]. *)
16301630+16311631+ exception Error of error
16321632+ (** Raised on decode errors. *)
16331633+16341634+ (** {1:codecs Codecs} *)
16351635+16361636+ type 'a t
16371637+ (** The type for codec for OCaml values of type ['a]. *)
16381638+16391639+ val v : kind:string -> enc:('a -> string) -> dec:(string -> 'a) -> 'a t
16401640+ (** [v kind enc dec] is a codec for value identified as [kind] using
16411641+ [enc] to encode and [dec] to decode. *)
16421642+16431643+ val kind : 'a t -> string
16441644+ (** [kind c] is [c]'s kind. *)
16451645+16461646+ val enc : 'a t -> 'a -> string
16471647+ (** [enc c] is [c]'s encoder. *)
16481648+16491649+ val dec : 'a t -> string -> 'a
16501650+ (** [dec c] is [c]'s decoder. The decoder @raise Error in case of
16511651+ decode error *)
16521652+16531653+ val dec_result : 'a t -> string -> 'a result
16541654+ (** [dec c] is like {!dec} but doesn't raise. The exception is
16551655+ turned into an error message using {!pp_error}. *)
16561656+16571657+ val with_kind : string -> 'a t -> 'a t
16581658+ (** [with_kind k c] is [c] with kind [k]. *)
16591659+16601660+ val write : fpath -> 'a t -> 'a -> unit result
16611661+ (** [write f c v] encodes value [v] with [c] to [f]. *)
16621662+16631663+ val read : fpath -> 'a t -> 'a result
16641664+ (** [read f c] reads a value with [c] from [f]. *)
16651665+16661666+ (** {1:base Base type codecs} *)
16671667+16681668+ val unit : unit t
16691669+ (** [unit] codecs a [()]. *)
16701670+16711671+ val const : 'a -> 'a t
16721672+ (** [const v] codecs the constant [v]. *)
16731673+16741674+ val bool : bool t
16751675+ (** [bool] codecs booleans. *)
16761676+16771677+ val int : int t
16781678+ (** [int] codecs integers. *)
16791679+16801680+ val string : string t
16811681+ (** [string] codecs strings. *)
16821682+16831683+ val option : 'a t -> 'a option t
16841684+ (** [option el] codecs [el] options. *)
16851685+16861686+ val result : ok:'a t -> error:'b t -> ('a, 'b) r t
16871687+ (** [result ~ok ~error] codecs [ok], [error] results. *)
16881688+16891689+ val list : 'a t -> 'a list t
16901690+ (** [list el] codecs [el] lists. *)
16911691+16921692+ val pair : 'a t -> 'b t -> ('a * 'b) t
16931693+ (** [pair c0 c1] codecs [c0], [c1] pairs. *)
16941694+16951695+ val t3 : 'a t -> 'b t -> 'c t -> ('a * 'b * 'c) t
16961696+ (** [t3] is like {!pair} but for triples. *)
16971697+16981698+ val t4 : 'a t -> 'b t -> 'c t -> 'd t -> ('a * 'b * 'c * 'd) t
16991699+ (** [t4] is like {!pair} but for quadruples. *)
17001700+17011701+ val t5 : 'a t -> 'b t -> 'c t -> 'd t -> 'e t ->
17021702+ ('a * 'b * 'c * 'd * 'e) t
17031703+ (** [t5] is like {!pair} but for qintuples. *)
17041704+17051705+ val alt : kind:string -> ('a -> int) -> 'a t array -> 'a t
17061706+ (** [alt tag cs] codecs values by tagging them with [tag] and
17071707+ using the corresponding codec in [cs].
17081708+17091709+ @raise Invalid_argument if [Array.length cs > 256]. *)
17101710+17111711+ val version : int -> 'a t -> 'a t
17121712+ (** [version num c] versions codec [c] with number [num].
17131713+ On decode a version number mismatch raises an error
17141714+ see {!error}. *)
17151715+17161716+ val view : ?kind:string -> ('a -> 'b) * ('b -> 'a) -> 'b t -> 'a t
17171717+ (** [view kind t c] views [t] as [c] for codecing. *)
17181718+17191719+ (** {1:topkg Topkg types} *)
17201720+17211721+ val msg : [`Msg of string ] t
17221722+ (** [msg] codecs error messages. *)
17231723+17241724+ val result_error_msg : 'a t -> 'a result t
17251725+ (** [result_error_msg ok] codecs [ok] or error message results. *)
17261726+17271727+ val fpath : Fpath.t t
17281728+ (** [fpath] codecs files paths. *)
17291729+17301730+ val cmd : Cmd.t t
17311731+ (** [cmd] codecs command line fragments. *)
17321732+ end
17331733+17341734+ (** Package description. *)
17351735+ module Pkg : sig
17361736+17371737+ type t
17381738+ (** The type for package descriptions. *)
17391739+17401740+ val empty : t
17411741+ (** [empty] is an empty package description. *)
17421742+17431743+ val name : t -> string
17441744+ (** [name p] is [p]'s name. *)
17451745+17461746+ val delegate : t -> Cmd.t option
17471747+ (** [delegate p]is [p]'s delegate. *)
17481748+17491749+ val build_dir : t -> fpath
17501750+ (** [build_dir p] is [p]'s build directory. *)
17511751+17521752+ val readmes : t -> fpath list
17531753+ (** [readme p] is [p]'s readme files. *)
17541754+17551755+ val change_logs : t -> fpath list
17561756+ (** [change_logs p] is [p]'s change logs. *)
17571757+17581758+ val licenses : t -> fpath list
17591759+ (** [licenses p] is [p]'s license files. *)
17601760+17611761+ val opam : name:string -> t -> fpath
17621762+ (** [opam name p] is [p]'s opam file for opam package [name]. *)
17631763+17641764+ (** {1:distrib Distrib} *)
17651765+17661766+ val distrib_uri : t -> string option
17671767+ (** [distrib_uri p] is [p]'s distribution location URI pattern.
17681768+ See {!Pkg.distrib}. *)
17691769+17701770+ (** {1:publish Publish} *)
17711771+17721772+ val publish_artefacts : t -> [`Distrib | `Doc | `Alt of string ] list
17731773+ (** [publish_artefacts p] is [p]'s distribution publication artefacts.
17741774+ See {!Pkg.publish}. *)
17751775+17761776+ (** {1:lints Lints}
17771777+17781778+ {b Note.} In the following [None] values mean that
17791779+ the lint is disabled by the package description. *)
17801780+17811781+ val lint_custom : t -> (unit -> R.msg result list) option
17821782+ (** [lint_custom p] is [p]'s custom linting function (if any).
17831783+17841784+ {b Note.} Use {!Ipc.lint_custom} to run the function
17851785+ from another program. *)
17861786+17871787+ val lint_files : t -> fpath list option
17881788+ (** [lint_files p] are [p]'s files to check for existence. *)
17891789+17901790+ val lint_metas : t -> (fpath * bool) list
17911791+ (** [lint_metas p] are [p]'s META file to OCamlfind lint. *)
17921792+17931793+ val lint_opams : t -> (fpath * bool * string list option) list
17941794+ (** [lint_opams p] are [p]'s opam file opam lint and dependency
17951795+ lint. *)
17961796+17971797+ (** {1:codec Codec} *)
17981798+17991799+ val codec : t Codec.t
18001800+ (** [codec] is a codec for package descriptions. *)
18011801+ end
18021802+18031803+ (** Topkg interprocess communication. *)
18041804+ module Ipc : sig
18051805+18061806+ (** {1:ipc Interprocess communication} *)
18071807+18081808+ type 'a t
18091809+ (** The type for interpocess communication transfering values of
18101810+ type ['a]. *)
18111811+18121812+ val cmd : 'a t -> Cmd.t
18131813+ (** [cmd ipc] are the command line arguments provided to the child
18141814+ process. *)
18151815+18161816+ val codec : 'a t -> 'a Codec.t
18171817+ (** [codec ipc] is the codec used to transfer the value. *)
18181818+18191819+ val answer : 'a t -> fpath
18201820+ (** [answer ipc] is the file path from which the value can
18211821+ be decoded from. *)
18221822+18231823+ (** {1:req Requests} *)
18241824+18251825+ val pkg : unit -> Pkg.t t
18261826+ (** [pkg ()] is an IPC to get the package description. *)
18271827+18281828+ val lint_custom : unit -> R.msg result list option t
18291829+ (** [lint_custom ()] is an IPC to run the custom linting. *)
18301830+18311831+ val distrib_prepare :
18321832+ dist_build_dir:fpath -> name:string -> version:string -> opam:fpath ->
18331833+ opam_adds:string -> fpath list result t
18341834+ (** [distrib_prepare dist_build_dir name version opam] is an IPC to
18351835+ prepare a distribution in directory [dist_build_dir]. This
18361836+ sets the cwd to [dist_build_dir], performs the distribution
18371837+ watermarking process with [name] used for [`Name], [version] used
18381838+ for [`Version] and [opam] as the default file for opam watermarks.
18391839+ It then performs distribution massaging and returns the file paths
18401840+ to exclude from the distribution archive. *)
18411841+ end
18421842+18431843+ (** opam helpers. *)
18441844+ module Opam : sig
18451845+18461846+ (** {1:opam opam} *)
18471847+18481848+ (** opam package file access.
18491849+18501850+ Normally opam metadata access is only needed at distribution
18511851+ time and this is handled by {!Topkg_care.Opam.File} using the
18521852+ [opam-format] library.
18531853+18541854+ However there is one case where we want to be able to access
18551855+ the metadata from [Topkg]: on pin builds where the
18561856+ {{!Pkg.watermark}watermarking} process needs to be run to turn
18571857+ the repo into a pseudo-distribution.
18581858+18591859+ Since we don't want [Topkg] to have any dependency and that
18601860+ [opam] currently doesn't allow to consult the fields of arbitrary
18611861+ opam files (see
18621862+ {{:https://github.com/ocaml/opam/issues/2446} issue #2446}) we
18631863+ assume a pin build has the [topkg] tool installed and call
18641864+ to it to get the opam fields for watermarking (if [topkg] is
18651865+ unavailable the watermarks are simply undefined). *)
18661866+ module File : sig
18671867+18681868+ (** {1:file opam file} *)
18691869+18701870+ type t = (string * string list) list
18711871+ (** The type for a simplified model the fields of an opam
18721872+ file. See {!Topkg_care.Opam.File}. *)
18731873+18741874+ val codec : t Codec.t
18751875+ (** [codec] is a codec for opam file fields. *)
18761876+18771877+ val fields : fpath -> ((string * string list) list) result
18781878+ (** [fields file] are the fields of the opam file [file] which
18791879+ are obtained by calling the [topkg] topkg executable. *)
18801880+ end
18811881+ end
18821882+end
18831883+18841884+(** {1:basics Basics}
18851885+18861886+{!Topkg} is a packager for distributing OCaml software. Fundamentally
18871887+it only provides a simple and flexible mechanism to describe an opam
18881888+{{:http://opam.ocaml.org/doc/manual/dev-manual.html#sec25}[install]
18891889+file} according to the build configuration which is used to infer one
18901890+corresponding invocation of your build system.
18911891+18921892+This simple idea brings the following advantages:
18931893+{ol
18941894+{- It frees you from implementing an install procedure in your build
18951895+ system: this task is delegated to {{:http://opam.ocaml.org}opam},
18961896+ to the [opam-installer] tool or anything that understands an opam
18971897+ install file.}
18981898+{- It doesn't reclaim control over your build system. It only invokes
18991899+ it {e once} with a list of targets determined by the package
19001900+ {{!section:Pkg.install}install description}.}
19011901+{- It is very flexible, supports a wide range of installation scenarios
19021902+ and is expressed in the reasonable language.}}
19031903+19041904+Beyond this a [Topkg] package description provides information about
19051905+the package's distribution creation and publication procedures. This
19061906+enables swift and correct package distribution management via the [topkg]
19071907+tool, see {!care}.
19081908+19091909+{ul
19101910+{- {!setup}}
19111911+{- {!build}}
19121912+{- {!descr}}
19131913+{- {!installdescr}}
19141914+{- {!care}}
19151915+{- {!advanced}
19161916+ {ul {- {!config_store}}
19171917+ {- {!multiopam}}}}
19181918+{- {!menagerie}}}
19191919+19201920+{1:setup Source repository setup}
19211921+19221922+The root of you source repository should have the following layout:
19231923+{ul
19241924+{- [pkg/pkg.ml], the package description written with {!Topkg}'s API.
19251925+ See {{!descr}package description.}}
19261926+{- [pkg/META], an
19271927+ {{:http://projects.camlcity.org/projects/findlib.html}ocamlfind}
19281928+ META file describing your package. See {!Pkg.describe} to configure this.}
19291929+{- [_tags], ocamlbuild file with at least a [true : bin_annot] line.
19301930+ See {{!cmt}handling cmt and cmti} files for details.}
19311931+{- [opam], the package's opam metadata. See {!build}.}
19321932+{- [README.md], your readme file. See {!Pkg.describe} to configure this.}
19331933+{- [LICENSE.md], your license file. See {!Pkg.describe} to configure this.}
19341934+{- [CHANGES.md], your change log. See {!Pkg.describe} to configure this.}}
19351935+19361936+{2:carcass_ad Quick setup (advertisement)}
19371937+19381938+If you start a new library
19391939+{{:http://erratique.ch/software/carcass}[carcass]} can generate
19401940+the structural boilerplate with your personal information and preferences.
19411941+Invoke:
19421942+{v
19431943+carcass body topkg/pkg mypkg
19441944+(cd mypkg && git init && git add . && git commit -m "First commit.")
19451945+opam pin add -kgit mypkg mypkg#master
19461946+v}
19471947+19481948+and you have a library that is [{opam,ocamlfind}]-able with correct
19491949+version watermarks on releases and opam pins. You are now only a few
19501950+invocations away to release it in the OCaml opam repository, see
19511951+[topkg help release] for doing so; but don't forget to document it and
19521952+make it do something useful.
19531953+19541954+{1:build opam and package build instructions}
19551955+19561956+The package needs to build-depend on [topkg] as well as [ocamlfind]
19571957+which is used by the package description file [pkg/pkg.ml] to find the
19581958+[topkg] library; it is likely that you are using [ocamlbuild] too. So
19591959+the depends field of your opam file should at least have:
19601960+19611961+{v
19621962+depends: [
19631963+ "ocamlfind" {build}
19641964+ "ocamlbuild" {build}
19651965+ "topkg" {build & >= 0.9.0} ]
19661966+v}
19671967+19681968+The build instructions of the package are simply an invocation of
19691969+[pkg/pkg.ml] with the [build] command and a specification of the
19701970+build configuration on the command line. In the simplest case, if
19711971+your package has no configuration options, this simply boils
19721972+down to:
19731973+{[
19741974+# For opam >= 2.0
19751975+build: [[ "ocaml" "pkg/pkg.ml" "build" "--dev-pkg" "%{dev}%" ]]
19761976+19771977+# For opam < 2.0
19781978+build: [[ "ocaml" "pkg/pkg.ml" "build" "--dev-pkg" "%{pinned}%" ]]
19791979+]}
19801980+19811981+The ["--dev-pkg"] configuration key is used to inform the package
19821982+description about the {{!Conf.build_context}build context}. This
19831983+invocation of [pkg/pkg.ml] executes your build system with a set of
19841984+targets determined from the build configuration and generates in the
19851985+root directory of your distribution an opam [install] file that opam
19861986+uses to install and uninstall your package.
19871987+19881988+This is all you need to specify. Do not put anything in the remove
19891989+field of the opam file. Likewise there is no need to invoke
19901990+[ocamlfind] with your [META] file. Your [META] file should simply be
19911991+installed in the directory of the [lib] field which happens
19921992+automatically by default.
19931993+19941994+If you described {{!Pkg.tests}tests} then you should specify
19951995+the instructions as follows (unfortunately for opam < 2.0 this involves
19961996+the repetition of the build line):
19971997+{[
19981998+# For opam >= 2.0
19991999+20002000+build:
20012001+[[ "ocaml" "pkg/pkg.ml" "build" "--dev-pkg" "%{dev}%"
20022002+ "--tests" "%{build-test}%" ]]
20032003+run-test:
20042004+[[ "ocaml" "pkg/pkg.ml" "test" ]]
20052005+20062006+# For opam < 2.0
20072007+20082008+build:
20092009+[[ "ocaml" "pkg/pkg.ml" "build" "--dev-pkg" "%{pinned}%" ]]
20102010+20112011+build-test:
20122012+[[ "ocaml" "pkg/pkg.ml" "build" "--dev-pkg" "%{pinned}%" "--tests" "true" ]
20132013+ [ "ocaml" "pkg/pkg.ml" "test" ]]
20142014+]}
20152015+20162016+{b Beyond opam.} If you need to support another package system you
20172017+can invoke [pkg/pkg.ml] as above and then manage the installation and
20182018+uninstallation at a given [$DESTDIR] with the generated opam [install]
20192019+file using [opam-installer] tool or any other program that understand
20202020+these files.
20212021+20222022+{1:descr Package description}
20232023+20242024+The {!Pkg.describe} function has a daunting number of arguments and
20252025+configuration options. However if you keep things simple and stick to
20262026+the defaults, much of this does not need to be specified.
20272027+20282028+For example, if we consider the basic {{!setup}setup} mentioned
20292029+above for a library described with an OCamlbuild [src/mylib.mllib]
20302030+file, your package description file [pkg/pkg.ml] simply looks like
20312031+this:
20322032+{[
20332033+#!/usr/bin/env ocaml
20342034+#use "topfind"
20352035+#require "topkg"
20362036+open Topkg
20372037+20382038+let () =
20392039+ Pkg.describe "mylib" @@ fun c ->
20402040+ Ok [ Pkg.mllib "src/mylib.mllib" ]
20412041+]}
20422042+20432043+{b Tip.} To allow
20442044+{{:https://github.com/the-lambda-church/merlin}merlin} to function
20452045+correctly in your package description, issue [M-x merlin-use topkg] in
20462046+[emacs] or [:MerlinUse topkg] in [vim].
20472047+20482048+This simple description builds and installs the library described by
20492049+the [src/mylib.mllib] file. It works correctly if native code
20502050+compilation or native dynamic linking is not available. It
20512051+automatically installs the package's META file in the directory of the
20522052+[lib] field and your readme, change log and license file in the
20532053+directory of the [doc] field.
20542054+20552055+Try to test the package build description with [topkg build], this
20562056+builds the package according to the description and build
20572057+configuration and writes a [mylib.install] at the root of the
20582058+distribution that describes the package installation. If everything
20592059+went well, you are now ready to release, see [topkg help release] for
20602060+the procedure.
20612061+20622062+To debug package descriptions it useful to dry run the build. This
20632063+prevents the package from building and only writes the [mylib.install] file
20642064+determined according to the build configuration.
20652065+{[
20662066+topkg build -d # Only write the opam install file
20672067+topkg build -d -v # Also print the build configuration
20682068+topkg help troubleshoot # More troubleshooting tips
20692069+]}
20702070+Note that [topkg build] does nothing more than invoke
20712071+[ocaml "pkg/pkg.ml" build]. If you would like to see the build
20722072+{{!section:Conf.key}configuration
20732073+options} of a package description you should do:
20742074+{v
20752075+ocaml pkg/pkg.ml help
20762076+./pkg/pkg.ml help # If has exec right
20772077+v}
20782078+20792079+{1:installdescr Install description}
20802080+20812081+An opam [install] file is a description of a standard UNIX install. It
20822082+has fields for each of the standard directories [lib], [bin], [man],
20832083+[etc], etc. Each of these fields lists the files to install in the
20842084+corresponding directory (or subdirectories). See the
20852085+{{:http://opam.ocaml.org/doc/manual/dev-manual.html#sec25}install file
20862086+specification} in the opam developer manual for more information.
20872087+20882088+A topkg install description is just a convenient and compact way to
20892089+describe an opam install file according to the build configuration. In
20902090+turn this also describes what needs to be built which allows topkg to
20912091+call the build system appropriately.
20922092+20932093+For each opam install field there is a corresponding field function
20942094+that you can use to generate install moves. The documentation of
20952095+{!Pkg.field} and {!Pkg.exec_field} describes how you can use or omit
20962096+their various arguments to simplify the description. Topkg also provides
20972097+a few higher-level convenience functions like {!Pkg.mllib} and
20982098+{!Pkg.clib} which allow to reuse the description work already done
20992099+for OCamlbuild.
21002100+21012101+In the following we review a few basic install use cases. The
21022102+{{!menagerie}menagerie} provides links to full and more complex examples.
21032103+21042104+{2:installlib Installing libraries and C stubs}
21052105+21062106+It is possible to use the {!Pkg.lib} field function and appropriate
21072107+{{!Exts}file extensions} to manually install a library, but this
21082108+quickly becomes tedious. The higher-level {!Pkg.mllib} install function
21092109+brings this to a single line by reading from a OCamlbuild [mllib] file.
21102110+Given a library described in [src/mylib.mllib] file use:
21112111+{[
21122112+Pkg.mllib "src/mylib.mllib"
21132113+]}
21142114+This will make all the modules mentioned in the [mllib] file part of
21152115+the API (i.e. install its [cmi] files). You can restrict
21162116+the API by using the [~api] optional argument. In the following, only
21172117+[Mod1] and [Mod2] define the API, the other modules of [mllib] remain hidden
21182118+to the end user (but unfortunately not to the linkers...):
21192119+{[
21202120+Pkg.mllib ~api:["Mod1"; "Mod2"] "src/myllib.mllib"
21212121+]}
21222122+21232123+A shortcut also exists for installing C stubs: {!Pkg.clib}. Simply use
21242124+it on an existing [src/libmystub.clib] file (N.B. OCamlbuild
21252125+mandates that your [clib] file name starts with [lib]):
21262126+{[
21272127+Pkg.clib "src/libmystub.clib"
21282128+]}
21292129+This will generate the appropriate install moves in the [lib] and [stublib]
21302130+fields.
21312131+21322132+{2:installbin Installing binaries}
21332133+21342134+In opam, binaries can only be installed by using the [bin], [sbin] and
21352135+[libexec] fields. Trying to install a binary in other fields will not
21362136+set its executable bit
21372137+({{:https://github.com/ocaml/opam/issues/2430}discussion}).
21382138+21392139+Most of the time one wants to install native binaries if native code
21402140+compilation is available and bytecode ones if it is not. The [auto]
21412141+argument of {!Pkg.exec_field} does exactly this if omited.
21422142+21432143+So if you have an executable to install in the [bin] field whose [main]
21442144+is in [src/myexec.ml], describe it with:
21452145+{[
21462146+Pkg.bin "src/myexec"
21472147+]}
21482148+As with any other field it easy to rename the executable along the
21492149+way with the [dst] argument:
21502150+{[
21512151+Pkg.bin "src/myexec" ~dst:"my-exec"
21522152+]}
21532153+Note that using the default value of [auto] also automatically handles
21542154+the extension business for Windows platforms.
21552155+21562156+{2:installcond Conditional install}
21572157+21582158+An easy and readable way to handle conditional installs is to use the
21592159+[cond] argument of {{!Pkg.field}field functions}. The following shows
21602160+how to conditionaly build and install a binary depending on the opam
21612161+[cmdliner] package being installed:
21622162+{[
21632163+let cmdliner = Conf.with_pkg "cmdliner"
21642164+let () =
21652165+ Pkg.describe "mypkg" @@ fun c ->
21662166+ let cmdliner = Conf.value c cmdliner in
21672167+ Ok [ Pkg.bin ~cond:cmdliner "src/myexec" ]
21682168+]}
21692169+Note that {{!Conf.key}configuration keys} must be created before the
21702170+call to {!Pkg.describe}. Their value can then be accessed with the
21712171+{!Conf.value} from the configuration given to the install move
21722172+function you specify.
21732173+21742174+For this conditional install the opam build instructions look like this:
21752175+{[
21762176+depopts: [ "cmdliner" ]
21772177+build: [[
21782178+ "ocaml" "pkg/pkg.ml" "build"
21792179+ "--dev-pkg" "%{dev}%" # use "%{pinned}%" for opam < 2.0
21802180+ "--with-cmdliner" cmdliner:installed ]]
21812181+]}
21822182+21832183+{2:installautoconds Automatic install conditions}
21842184+21852185+Some conditions related to native code and native code dynamic linking
21862186+happen automatically. For example moves with paths ending with [.cmxs]
21872187+are automatically dropped if {!Conf.OCaml.native_dynlink} is [false]
21882188+in the current build configuration. This behaviour can be disabled by
21892189+using the [force] argument of {{!Pkg.field}field functions}.
21902190+21912191+{2:installexts Extension sets and platform dependent extensions}
21922192+21932193+The {{!Pkg.field}field functions} have an optional [exts] argument. If
21942194+present these extensions are appended to the [src] path given to the
21952195+function. The module {!Exts} defines a few predefined extension
21962196+sets. For example a single module library archive implemented in
21972197+[src/mylib.ml] can be declared by:
21982198+{[
21992199+Pkg.lib ~exts:Exts.module_library "src/mylib"
22002200+]}
22012201+which is, effectively, a shortcut for:
22022202+{[
22032203+[ Pkg.lib "src/mylib.mli";
22042204+ Pkg.lib "src/mylib.cmti";
22052205+ Pkg.lib "src/mylib.cmi";
22062206+ Pkg.lib "src/mylib.cmx";
22072207+ Pkg.lib "src/mylib.cma";
22082208+ Pkg.lib "src/mylib.a";
22092209+ Pkg.lib "src/mylib.cmxa";
22102210+ Pkg.lib "src/mylib.cmxs"; ]
22112211+]}
22122212+22132213+Extensions sets are also used to support platform independent installs.
22142214+For example to install a static C library archive you should use
22152215+the second invocation, not the first one:
22162216+{[
22172217+Pkg.lib "src/libmylib.a" (* DON'T do this *)
22182218+Pkg.lib ~exts:Exts.c_library "src/libmylib" (* Do this *)
22192219+]}
22202220+this ensures that the correct, platform dependent, suffix is used.
22212221+22222222+{2:installrename Renaming and installing in subdirectories}
22232223+22242224+By default install moves simply install the file at the root directory
22252225+of the field. Using the [dst] optional argument you can rename the file and/or
22262226+install it to subdirectories.
22272227+{[
22282228+Pkg.lib "src/b.cmo" ~dst:"hey" (* Install as [hey] file. *)
22292229+Pkg.lib "src/b.cmo" ~dst:"hey/" (* Install in [hey] subdirectory *)
22302230+Pkg.lib "src/b.cmo" ~dst:"hey/ho.cmo" (* Install as [ho.cmo] in [hey]. *)
22312231+]}
22322232+22332233+{2:cmt Handling cmt and cmti files}
22342234+22352235+Since the OCaml tools generate [.cmt] and [.cmti] files only as a side
22362236+effect they are treated specially: they are not built. For
22372237+OCamlbuild you should add this line to your [_tags] file:
22382238+{[
22392239+true : bin_annot
22402240+]}
22412241+this will build them as a side effect of other build invocations. In
22422242+the generated opam install file the [cmt] and [cmti] files are
22432243+prefixed by a ? so that if they are not built the install does not
22442244+fail.
22452245+22462246+{1:care Package care}
22472247+22482248+Developing and distributing packages implies a lot of mundane but
22492249+nevertheless important tasks. The [topkg] tool, guided by information
22502250+provided by {!Pkg.describe} helps you with these tasks.
22512251+22522252+For example [topkg lint] makes sure that the package source repository
22532253+or distribution follows a few established (or your) conventions and
22542254+that the META and opam files of the package pass their respective
22552255+linter. [topkg distrib] will create watermarked and reproducible
22562256+distribution archives (see {!Pkg.distrib}) while [topkg publish] and
22572257+[topkg opam] will help publishing them. All this and more is described
22582258+with details in [topkg]'s help system, invoke:
22592259+{v
22602260+topkg help release
22612261+topkg help
22622262+v}
22632263+22642264+to get an extended introduction and pointers to these features.
22652265+22662266+{2:doccare Documentation care}
22672267+22682268+Topkg provides support to make it easier to write and publish your
22692269+package documentation. The [topkg doc -r] command generates and refreshes
22702270+renderings of the package documentation while you work on it.
22712271+22722272+Documentation publication is always derived from a generated
22732273+distribution archive (the latter doesn't need to be published of
22742274+course). So to push documentation fixes and clarifications simply invoke:
22752275+22762276+{v
22772277+topkg distrib
22782278+topkg publish doc
22792279+v}
22802280+22812281+Make sure you include %%VERSION%% watermarks in the documentation so
22822282+that readers know exactly which version they are reading. By default
22832283+this will be substituted by a VCS tag description so it will be
22842284+precise even though you may not have properly tagged a new release
22852285+for the package.
22862286+22872287+{1:advanced Advanced topics}
22882288+22892289+{2:config_store Storing build configuration information in software}
22902290+22912291+The following sample setup shows how to store build configuration
22922292+information in build artefacts.
22932293+22942294+In this example we store the location of the install's [etc] directory
22952295+in the build artefacts. The setup also works seamlessly during
22962296+development if build artefacts are invoked from the root directory of
22972297+the source repository.
22982298+22992299+We have the following file layout in our source repository:
23002300+{v
23012301+etc/mypkg.conf # Configuration file
23022302+src/mypkg_etc.ml # Module with path to the etc dir
23032303+v}
23042304+23052305+the contents of [src/mypkg_etc.ml] is simply:
23062306+{[
23072307+(* This file is overwritten by distribution builds. During development
23082308+ it refers to the [etc] directory at the root directory of the
23092309+ source repository. *)
23102310+23112311+let dir = "etc"
23122312+]}
23132313+the value [Mypkg_etc.dir] is used in the sources to refer to the [etc]
23142314+directory of the install. In the package description file
23152315+[pkg/pkg.ml] we have the following lines:
23162316+{[
23172317+let (* 1 *) etc_dir =
23182318+ let doc = "Use $(docv) as the etc install directory" in
23192319+ Conf.(key "etc-dir" fpath ~absent:"etc" ~doc)
23202320+23212321+let (* 2 *) etc_config c = match Conf.build_context c with
23222322+| `Dev -> Ok () (* Do nothing, the repo src/mypkg_etc.ml will do *)
23232323+| `Pin | `Distrib ->
23242324+ let config = strf "let dir = %S" (Conf.value c etc_dir) in
23252325+ OS.File.write "src/mypkg_etc.ml" config
23262326+23272327+let () =
23282328+ let build = Pkg.build ~pre:etc_config () in
23292329+ Pkg.describe "mypkg" ~build @@ fun c ->
23302330+ Ok [ (* 3 *) Pkg.etc "etc/mpypkg.conf"; ... ]
23312331+]}
23322332+23332333+In words:
23342334+{ol
23352335+{- We declare a configuration key ["etc-dir"] that holds the location
23362336+of the install [etc] directory.}
23372337+{- We have a pre-build hook that writes the file
23382338+[src/mypkg_etc.ml] with its actual value on [`Pin] and [`Distrib] builds.}
23392339+{- We install the [etc/mypkg.conf] configuration in the install [etc]
23402340+ directory.}}
23412341+The opam build instructions for the package are:
23422342+{[
23432343+build: [[
23442344+ "ocaml" "pkg/pkg.ml" "build"
23452345+ "--dev-pkg" "%{dev}%" # use "%{pinned}%" for opam < 2.0
23462346+ "--etc-dir" mypkg:etc ]]
23472347+]}
23482348+23492349+{2:multiopam Multiple opam packages for a single distribution}
23502350+23512351+It is not too hard to define multiple opam packages for the same
23522352+distribution. Topkg itself
23532353+{{:https://github.com/dbuenzli/topkg/blob/master/DEVEL.md}uses this
23542354+trick} to manage its dependencies between [topkg] and [topkg-care].
23552355+23562356+To achieve this your package description file can simply condition
23572357+the package install description on the package name
23582358+{{!Conf.pkg_name}communicated by the configuration}. In this setup
23592359+you'll likely have one [$PKG.opam] per [$PKG] at the root of your source
23602360+repository, you should declare them in the description too, so that
23612361+they get properly linted and used by the [topkg] tool when appropriate
23622362+(see how the opam file is looked up according to the package name
23632363+in {!Pkg.describe}). Here is a blueprint:
23642364+{[
23652365+let () =
23662366+ let opams =
23672367+ let install = false in
23682368+ [ Pkg.opam_file ~install "mypkg-main.opam";
23692369+ Pkg.opam_file ~install "mypkg-snd.opam"; ]
23702370+ in
23712371+ Pkg.describe ~opams "mypkg-main" @@ fun c ->
23722372+ match Conf.pkg_name c with
23732373+ | "mypkg-main" ->
23742374+ Ok [ Pkg.lib "mypkg-main.opam" ~dst:"opam";
23752375+ (* mypkg-main install *) ]
23762376+ | "mypkg-snd" ->
23772377+ Ok [ Pkg.lib "mypkg-snd.opam" ~dst:"opam";
23782378+ (* mypkg-snd install *) ]
23792379+ | other ->
23802380+ R.error_msgf "unknown package name: %s" other
23812381+]}
23822382+The build instructions of these opam files need to give the name of
23832383+the package to the build invocation so that the right install description
23842384+can be selected:
23852385+{[
23862386+build: [[
23872387+ "ocaml" "pkg/pkg.ml" "build"
23882388+ "--pkg-name" name
23892389+ "--dev-pkg" "%{dev}%" # use "%{pinned}%" for opam < 2.0
23902390+]]
23912391+]}
23922392+23932393+In general you will use the default, main, package name and its opam file to
23942394+create and publish the distribution archive file and all packages
23952395+will use the same distribution; the opam packages will only differ in their
23962396+opam file. Releasing the set of packages then becomes:
23972397+{[
23982398+# Release the distribution and base package (use topkg bistro
23992399+# for doing this via a single invocation)
24002400+topkg distrib
24012401+topkg publish
24022402+topkg opam pkg
24032403+topkg opam submit
24042404+24052405+# Create and release the other opam package based on the ditrib
24062406+topkg opam pkg --pkg-name mypkg-snd
24072407+topkg opam submit --pkg-name mypkg-snd
24082408+]}
24092409+24102410+See [topkg help release] for more information about releasing
24112411+packages with [topkg].
24122412+24132413+{1:menagerie Menagerie of [pkg.ml] files}
24142414+24152415+This is a menagerie of [pkg.ml] with a description of what they
24162416+showcase. The examples are approximatively sorted by increasing
24172417+complexity.
24182418+24192419+In all these packages the readme, change log and license file are
24202420+automatically installed in the directory of the [doc] field and the
24212421+ocamlfind META file and opam file of the package are automatically installed
24222422+in the directory of the [lib] field.
24232423+24242424+{{:https://github.com/dbuenzli/hmap/blob/master/pkg/pkg.ml}Hmap}
24252425+({{:https://github.com/dbuenzli/hmap/blob/master/pkg/META}META},
24262426+{{:https://github.com/dbuenzli/hmap/blob/master/opam}[opam]})
24272427+{ul {- Single module library archive [hmap]. The simplest you can get.}}
24282428+24292429+{{:https://github.com/dbuenzli/fpath/blob/master/pkg/pkg.ml}Fpath}
24302430+({{:https://github.com/dbuenzli/fpath/blob/master/pkg/META}META},
24312431+{{:https://github.com/dbuenzli/fpath/blob/master/opam}[opam]})
24322432+{ul
24332433+{- Single module library archive [fpath].}
24342434+{- Private library archive [fpath_top] for toplevel support.}}
24352435+24362436+{{:https://github.com/dbuenzli/astring/blob/master/pkg/pkg.ml}Astring}
24372437+({{:https://github.com/dbuenzli/astring/blob/master/pkg/META}META},
24382438+{{:https://github.com/dbuenzli/astring/blob/master/opam}[opam]})
24392439+{ul
24402440+{- Library archive [astring] namespaced by one module.}
24412441+{- Private library archive for toplevel support ([astring_top]).}
24422442+{- Installation of sample code in the [doc/] directory.}}
24432443+24442444+{{:https://github.com/dbuenzli/fmt/blob/master/pkg/pkg.ml}Fmt}
24452445+({{:https://github.com/dbuenzli/fmt/blob/master/pkg/META}META},
24462446+{{:https://github.com/dbuenzli/fmt/blob/master/opam}[opam]})
24472447+{ul
24482448+{- Single module library archive ([fmt]).}
24492449+{- Private library archive [fmt_top] for toplevel support.}
24502450+{- Single module library archive [fmt_tty]
24512451+ conditional on the presence of the opam [base-unix] package.}
24522452+{- Single module library archive [fmt_cli] conditional
24532453+ on the presence of the opam [cmdliner] package.}}
24542454+24552455+{{:https://github.com/dbuenzli/ptime/blob/master/pkg/pkg.ml}Ptime}
24562456+({{:https://github.com/dbuenzli/ptime/blob/master/pkg/META}META},
24572457+ {{:https://github.com/dbuenzli/ptime/blob/master/opam}opam})
24582458+{ul
24592459+{- Single module library archive [ptime].}
24602460+{- Private library archive [ptime_top] for toplevel support.}
24612461+{- Library archive [ptime_clock] targeting regular OSes using
24622462+ C stubs and installed in the [os/] subdirectory of [lib] field along
24632463+ with a private library archive [ptime_clock_top] for toplevel support.}
24642464+{- Library archive [ptime_clock] targeting
24652465+ JavaScript installed in the [jsoo/] subdirectory of [lib] field conditional
24662466+ on the presence of the opam [js_of_ocaml] package.}
24672467+{- Installation of sample code in the [doc/] directory.}}
24682468+24692469+{{:https://github.com/dbuenzli/uucp/blob/master/pkg/pkg.ml}Uucp}
24702470+({{:https://github.com/dbuenzli/uucp/blob/master/pkg/META}META},
24712471+ {{:https://github.com/dbuenzli/uucp/blob/master/opam}opam})
24722472+{ul
24732473+{- Library archive [uucp] namespaced by a single module.}
24742474+{- Custom watermark for substituting the supported Unicode version.}
24752475+{- Generation of distribution time build artefacts via a
24762476+ {{!Pkg.distrib}massage} hook which invokes an
24772477+ {{:https://github.com/dbuenzli/uucp/blob/master/pkg/build_support.ml}
24782478+ OCaml script} that downloads the UCD XML file
24792479+ and extracts compact and efficient representation of it as OCaml
24802480+ source data structures it writes in the [src/] directory.}
24812481+{- Adds the [support/] path to the {{!Pkg.distrib}paths to exclude}
24822482+ from the distribution.}
24832483+{- Installation of development information and sample code in the
24842484+ [doc/] directory.}}
24852485+24862486+{{:https://github.com/dbuenzli/carcass/blob/master/pkg/pkg.ml}Carcass}
24872487+({{:https://github.com/dbuenzli/carcass/blob/master/pkg/META}META},
24882488+ {{:https://github.com/dbuenzli/carcass/blob/master/opam}opam})
24892489+{ul
24902490+{- Library archive [carcass] namespaced by a single module.}
24912491+{- Single module library archive [carcass_cli].}
24922492+{- Executable [carcass].}
24932493+{- {{!config_store}Stores} install [etc] location in the software artefacts
24942494+ with a {{!Pkg.build}pre-build hook}.}
24952495+{- Adjusts the {{!Pkg.distrib}files to watermark} to ignore the files in the
24962496+ [etc] file hierarchy of the distribution.}
24972497+{- Installs the [etc] hierarchy of the distribution in the [etc] field
24982498+ directly from the source tree (i.e. the files are not built).}}
24992499+25002500+{{:https://github.com/dbuenzli/topkg/blob/master/pkg/pkg.ml}Topkg}
25012501+({{:https://github.com/dbuenzli/topkg/blob/master/pkg/META}META},
25022502+{{:https://github.com/dbuenzli/topkg/blob/master/topkg.opam}topkg.opam},
25032503+{{:https://github.com/dbuenzli/topkg/blob/master/topkg-care.opam}
25042504+topkg-care.opam})
25052505+{ul
25062506+{- Ignore the funky source bootstraping ([#mod_use] directives), that's
25072507+ only for using [topkg] on itself.}
25082508+{- Makes {{!multiopam}multiple opam packages} for the same
25092509+ distribution.}
25102510+{- Multiple opam file declaration and dependency linting exclusions:
25112511+ the build system mentions packages that are not relevant to
25122512+ all opam files. Manual, per package, opam file install in the [lib]
25132513+ field.}
25142514+{- Manual [META] install, a single one is installed for all opam packages
25152515+ by the base package [topkg]. This leverages the [if_exists] ocamlfind
25162516+ mecanism.}
25172517+{- The [topkg] package installs the library archive [topkg] namespaced
25182518+ by a single module.}
25192519+{- The [topkg-care] package installs the library archive [topkg-care]
25202520+ namespaced by a single module and the binaries [topkg] and
25212521+ [toy-github-topkg-delegate]}}
25222522+*)
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+open Topkg_result
77+88+let ocamlbuild_flags =
99+ Topkg_cmd.(empty % "-use-ocamlfind" % "-classic-display")
1010+1111+let build_cmd c os =
1212+ let ocamlbuild = Topkg_conf.tool "ocamlbuild" os in
1313+ let build_dir = Topkg_conf.build_dir c in
1414+ let toolchain =
1515+ match Topkg_conf.toolchain c with
1616+ | Some toolchain -> Topkg_cmd.(v "-toolchain" % toolchain)
1717+ | _ -> Topkg_cmd.empty
1818+ in
1919+ let debug = Topkg_cmd.(on (Topkg_conf.debug c) (v "-tag" % "debug")) in
2020+ let profile = Topkg_cmd.(on (Topkg_conf.profile c) (v "-tag" % "profile")) in
2121+ let jobs =
2222+ let n = Topkg_conf.jobs c in
2323+ Topkg_log.info (fun m -> m "using %d jobs" n);
2424+ Topkg_cmd.(on (n != 1) (v "-j" % string_of_int n)) in
2525+ Topkg_cmd.(ocamlbuild %% ocamlbuild_flags %% toolchain %% jobs %%
2626+ debug %% profile % "-build-dir" % build_dir)
2727+2828+let clean_cmd os ~build_dir =
2929+ let ocamlbuild = Topkg_conf.tool "ocamlbuild" os in
3030+ Topkg_cmd.(ocamlbuild %% ocamlbuild_flags %
3131+ "-build-dir" % build_dir % "-clean")
3232+3333+type t =
3434+ { prepare_on_pin : bool;
3535+ dir : Topkg_fpath.t;
3636+ pre : Topkg_conf.t -> unit result;
3737+ cmd : Topkg_conf.t -> Topkg_conf.os -> Topkg_fpath.t list -> unit result;
3838+ post : Topkg_conf.t -> unit result;
3939+ clean : Topkg_conf.os -> build_dir:Topkg_fpath.t -> unit result; }
4040+4141+let with_dir b dir = { b with dir }
4242+4343+let nop = fun _ -> Ok ()
4444+4545+let cmd c os files =
4646+ let targets = String.concat "\n" files in
4747+ Topkg_os.File.write "pkg.itarget" targets >>= fun () ->
4848+ Topkg_os.Cmd.run @@ Topkg_cmd.(build_cmd c os % "pkg.otarget")
4949+5050+let clean os ~build_dir =
5151+ Topkg_os.Cmd.run @@ clean_cmd os ~build_dir
5252+5353+let v
5454+ ?(prepare_on_pin = true) ?(dir = "_build") ?(pre = nop) ?(cmd = cmd)
5555+ ?(post = nop) ?(clean = clean) () =
5656+ { prepare_on_pin; dir; pre; cmd; post; clean; }
5757+5858+let prepare_on_pin b = b.prepare_on_pin
5959+let dir b = b.dir
6060+let pre b = b.pre
6161+let cmd b = b.cmd
6262+let post b = b.post
6363+let clean b = b.clean
6464+let codec =
6565+ let prepare_on_pin = Topkg_codec.(with_kind "prepare_on_pin" @@ bool) in
6666+ let dir = Topkg_codec.(with_kind "dir" @@ string) in
6767+ let fields =
6868+ let stub _ = invalid_arg "not executable outside package definition" in
6969+ (fun b -> b.prepare_on_pin, b.dir),
7070+ (fun (prepare_on_pin, dir) ->
7171+ { prepare_on_pin; dir; pre = stub; cmd = stub; post = stub;
7272+ clean = stub })
7373+ in
7474+ Topkg_codec.version 0 @@
7575+ Topkg_codec.(view ~kind:"build" fields (pair prepare_on_pin dir))
7676+7777+let ocb_tag c key tag =
7878+ let tag = Topkg_string.strf "%s(%a)" tag (Topkg_conf.pp_value c) key in
7979+ Topkg_cmd.(v "-tag" % tag)
8080+8181+let ocb_bool_tag c key tag =
8282+ Topkg_cmd.(on (Topkg_conf.value c key) @@ v "-tag" % tag)
8383+8484+let ocb_bool_tags c tags =
8585+ let f (key, tag) = Topkg_cmd.(%%) (ocb_bool_tag c key tag) in
8686+ List.fold_right f tags Topkg_cmd.empty
+42
vendor/opam/topkg/src/topkg_build.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** Package build description. *)
77+88+open Topkg_result
99+1010+(** {1 Build} *)
1111+1212+type t
1313+1414+val build_cmd : Topkg_conf.t -> Topkg_conf.os -> Topkg_cmd.t
1515+val clean_cmd : Topkg_conf.os -> build_dir:Topkg_fpath.t -> Topkg_cmd.t
1616+1717+val v :
1818+ ?prepare_on_pin:bool ->
1919+ ?dir:Topkg_fpath.t ->
2020+ ?pre:(Topkg_conf.t -> unit result) ->
2121+ ?cmd:(Topkg_conf.t -> Topkg_conf.os -> Topkg_fpath.t list -> unit result) ->
2222+ ?post:(Topkg_conf.t -> unit result) ->
2323+ ?clean:(Topkg_conf.os -> build_dir:Topkg_fpath.t -> unit result) ->
2424+ unit -> t
2525+2626+val with_dir : t -> Topkg_fpath.t -> t
2727+2828+val prepare_on_pin : t -> bool
2929+val dir : t -> Topkg_fpath.t
3030+val pre : t -> (Topkg_conf.t -> unit result)
3131+val cmd :
3232+ t -> (Topkg_conf.t -> Topkg_conf.os -> Topkg_fpath.t list -> unit result)
3333+3434+val post : t -> (Topkg_conf.t -> unit result)
3535+val clean : t -> (Topkg_conf.os -> build_dir:Topkg_fpath.t -> unit result)
3636+3737+val codec : t Topkg_codec.t
3838+3939+val ocb_tag : Topkg_conf.t -> 'a Topkg_conf.key -> string -> Topkg_cmd.t
4040+val ocb_bool_tag : Topkg_conf.t -> bool Topkg_conf.key -> string -> Topkg_cmd.t
4141+val ocb_bool_tags :
4242+ Topkg_conf.t -> (bool Topkg_conf.key * string) list -> Topkg_cmd.t
+41
vendor/opam/topkg/src/topkg_cmd.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(* Command line fragments *)
77+88+type t = string list
99+1010+let empty = []
1111+let is_empty = function [] -> true | _ -> false
1212+let v a = [a]
1313+let ( % ) l a = a :: l
1414+let ( %% ) l0 l1 = List.rev_append (List.rev l1) l0
1515+let add_arg l a = l % a
1616+let add_args l a = l %% a
1717+let on bool l = if bool then l else []
1818+let p f = f
1919+2020+(* Predicates and comparison *)
2121+2222+let equal l l' = l = l'
2323+let compare l l' = compare l l'
2424+2525+(* Conversions and pretty printing *)
2626+2727+let to_rev_list line = line
2828+let to_list line = List.rev line
2929+let of_list ?slip line = match slip with
3030+| None -> List.rev line
3131+| Some slip -> List.fold_left (fun acc v -> v :: slip :: acc) [] line
3232+3333+let dump ppf cmd =
3434+ let pp_elt ppf s = Format.fprintf ppf "%s" (Filename.quote s) in
3535+ let rec loop = function
3636+ | [] -> ()
3737+ | v :: vs ->
3838+ if vs = [] then pp_elt ppf v else
3939+ (Format.fprintf ppf "%a@ " pp_elt v; loop vs)
4040+ in
4141+ Format.fprintf ppf "@[<1>["; loop (List.rev cmd); Format.fprintf ppf "]@]"
+30
vendor/opam/topkg/src/topkg_cmd.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** Command lines
77+88+ See {!Topkg.Cmd} for documentation. *)
99+1010+(** {1 Command lines} *)
1111+1212+type t
1313+1414+val v : string -> t
1515+val empty : t
1616+val is_empty : t -> bool
1717+val ( % ) : t -> string -> t
1818+val ( %% ) : t -> t -> t
1919+val add_arg : t -> string -> t
2020+val add_args : t -> t -> t
2121+val on : bool -> t -> t
2222+val p : Topkg_fpath.t -> string
2323+2424+val equal : t -> t -> bool
2525+val compare : t -> t -> int
2626+2727+val to_rev_list : t -> string list
2828+val to_list : t -> string list
2929+val of_list : ?slip:string -> string list -> t
3030+val dump : Format.formatter -> t -> unit
+251
vendor/opam/topkg/src/topkg_codec.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+open Topkg_result
77+88+(* Decode errors *)
99+1010+type error = Corrupted of (string * string) | Version of int * int
1111+1212+let pp_error ppf = function
1313+| Corrupted (kind, v) ->
1414+ Format.fprintf ppf "corrupted %s in %S" kind v
1515+| Version (exp, fnd) ->
1616+ Format.fprintf ppf "version mismatch, expected %d found %d" exp fnd
1717+1818+exception Error of error
1919+2020+let err ~kind v = raise (Error (Corrupted (kind, v)))
2121+let err_version ~exp ~fnd = raise (Error (Version (exp, fnd)))
2222+2323+(* Codecs *)
2424+2525+type 'a t =
2626+ { kind : string;
2727+ enc : 'a -> string;
2828+ dec : string -> 'a; }
2929+3030+let v ~kind ~enc ~dec = { kind; enc; dec }
3131+let kind c = c.kind
3232+let enc c = c.enc
3333+let dec c = c.dec
3434+let with_kind kind c = { c with kind }
3535+3636+let dec_result c s = try Ok (dec c s) with
3737+| Error err ->
3838+ R.error_msgf "Decode %s: %a Input data: %S" (kind c) pp_error err s
3939+4040+let write file c v =
4141+ Topkg_os.File.write file (enc c v)
4242+ |> R.reword_error_msg ~replace:true
4343+ (fun err -> R.msgf "Encode %s to %s: %s" (kind c) file err)
4444+4545+let read file c =
4646+ Topkg_os.File.read file >>= fun s ->
4747+ try Ok (dec c s) with
4848+ | Error e -> R.error_msgf "Decode %s from %s: %a" (kind c) file pp_error e
4949+5050+(* Base type codecs *)
5151+5252+let tail s = Topkg_string.with_index_range ~first:1 s
5353+5454+let unit =
5555+ let kind = "unit" in
5656+ let enc = function () -> "\x00" in
5757+ let dec = function "\x00" -> () | s -> err ~kind s in
5858+ v ~kind ~enc ~dec
5959+6060+let const c =
6161+ let kind = "const" in
6262+ let enc = function _ -> "" in
6363+ let dec = function "" -> c | s -> err ~kind s in
6464+ v ~kind ~enc ~dec
6565+6666+let bool =
6767+ let kind = "bool" in
6868+ let enc = function false -> "\x00" | true -> "\x01" in
6969+ let dec = function "\x00" -> false | "\x01" -> true | s -> err ~kind s in
7070+ v ~kind ~enc ~dec
7171+7272+let int =
7373+ let kind = "int" in
7474+ let enc = string_of_int (* will do for now *) in
7575+ let dec s = try int_of_string s with Failure _ -> err ~kind s in
7676+ v ~kind ~enc ~dec
7777+7878+let string =
7979+ let kind = "string" in
8080+ let enc s = s in
8181+ let dec s = s in
8282+ v ~kind ~enc ~dec
8383+8484+let option some =
8585+ let kind = Printf.sprintf "(%s) option" (kind some) in
8686+ let enc = function None -> "\x00" | Some v -> "\x01" ^ (enc some v) in
8787+ let dec s = match Topkg_string.head s with
8888+ | Some '\x00' -> None
8989+ | Some '\x01' -> Some (dec some (tail s))
9090+ | _ -> err ~kind s
9191+ in
9292+ v ~kind ~enc ~dec
9393+9494+let result ~ok ~error =
9595+ let kind = Printf.sprintf "(%s, %s) result" (kind ok) (kind error) in
9696+ let enc = function
9797+ | Ok v -> "\x00" ^ (enc ok v)
9898+ | Error e -> "\x01" ^ (enc error e)
9999+ in
100100+ let dec s = match Topkg_string.head s with
101101+ | Some '\x00' -> Ok (dec ok (tail s))
102102+ | Some '\x01' -> Error (dec error (tail s))
103103+ | _ -> err ~kind s
104104+ in
105105+ v ~kind ~enc ~dec
106106+107107+let list el =
108108+ let kind = Printf.sprintf "(%s) list" (kind el) in
109109+ let enc vs =
110110+ let b = Buffer.create 255 in
111111+ let rec loop = function
112112+ | [] -> Buffer.add_char b '\x00'
113113+ | v :: vs ->
114114+ let venc = (enc el) v in
115115+ let venc_len = String.length venc in
116116+ Buffer.add_char b '\x01';
117117+ Buffer.add_string b (string_of_int venc_len) (* will do for now *);
118118+ Buffer.add_char b '\x01';
119119+ Buffer.add_string b venc;
120120+ loop vs
121121+ in
122122+ loop vs; Buffer.contents b
123123+ in
124124+ let dec s =
125125+ let rec loop acc s = match Topkg_string.head s with
126126+ | Some '\x00' -> acc
127127+ | Some '\x01' ->
128128+ begin match Topkg_string.find_byte ~start:1 '\x01' s with
129129+ | None -> err ~kind s
130130+ | Some one ->
131131+ try
132132+ let last = one - 1 in
133133+ let len = Topkg_string.with_index_range ~first:1 ~last s in
134134+ let len = int_of_string len in
135135+ let first = one + 1 in
136136+ let last = first + len - 1 in
137137+ let venc = Topkg_string.with_index_range ~first ~last s in
138138+ let rest = Topkg_string.with_index_range ~first:(last + 1) s in
139139+ loop ((dec el venc) :: acc) rest
140140+ with Failure _ (* of int_of_string *) -> err ~kind s
141141+ end
142142+ | _ -> err ~kind s
143143+ in
144144+ List.rev (loop [] s)
145145+ in
146146+ v ~kind ~enc ~dec
147147+148148+let seq = list string
149149+150150+let pair c0 c1 =
151151+ let kind = Printf.sprintf "%s * %s" (kind c0) (kind c1) in
152152+ let enc (v0, v1) = enc seq [enc c0 v0; enc c1 v1] in
153153+ let dec s = match dec seq s with
154154+ | [lenc; renc] -> (dec c0 lenc), (dec c1 renc)
155155+ | _ -> err ~kind s
156156+ in
157157+ v ~kind ~enc ~dec
158158+159159+let t2 = pair
160160+161161+let t3 c0 c1 c2 =
162162+ let kind = Printf.sprintf "%s * %s * %s" (kind c0) (kind c1) (kind c2) in
163163+ let seq = list string in
164164+ let enc (v0, v1, v2) = enc seq [enc c0 v0; enc c1 v1; enc c2 v2] in
165165+ let dec s = match (dec seq) s with
166166+ | [v0; v1; v2] -> (dec c0 v0), (dec c1 v1), (dec c2 v2)
167167+ | _ -> err ~kind s
168168+ in
169169+ v ~kind ~enc ~dec
170170+171171+let t4 c0 c1 c2 c3 =
172172+ let kind =
173173+ Printf.sprintf "%s * %s * %s * %s" (kind c0) (kind c1) (kind c2) (kind c3)
174174+ in
175175+ let seq = list string in
176176+ let enc (v0, v1, v2, v3) =
177177+ enc seq [enc c0 v0; enc c1 v1; enc c2 v2; enc c3 v3]
178178+ in
179179+ let dec s = match (dec seq) s with
180180+ | [v0; v1; v2; v3] -> (dec c0 v0), (dec c1 v1), (dec c2 v2), (dec c3 v3)
181181+ | _ -> err ~kind s
182182+ in
183183+ v ~kind ~enc ~dec
184184+185185+let t5 c0 c1 c2 c3 c4 =
186186+ let kind =
187187+ Printf.sprintf "%s * %s * %s * %s * %s"
188188+ (kind c0) (kind c1) (kind c2) (kind c3) (kind c4)
189189+ in
190190+ let seq = list string in
191191+ let enc (v0, v1, v2, v3, v4) =
192192+ enc seq [enc c0 v0; enc c1 v1; enc c2 v2; enc c3 v3; enc c4 v4]
193193+ in
194194+ let dec s = match (dec seq) s with
195195+ | [v0; v1; v2; v3; v4] ->
196196+ (dec c0 v0), (dec c1 v1), (dec c2 v2), (dec c3 v3), (dec c4 v4)
197197+ | _ -> err ~kind s
198198+ in
199199+ v ~kind ~enc ~dec
200200+201201+let alt ~kind tag cs =
202202+ let l = Array.length cs in
203203+ if l > 256 then invalid_arg @@ Topkg_string.strf "too many codecs (%d)" l;
204204+ let enc v =
205205+ let tag = tag v in
206206+ Printf.sprintf "%c%s" (Char.chr tag) (enc cs.(tag) v)
207207+ in
208208+ let dec s = match Topkg_string.head s with
209209+ | None -> err ~kind s
210210+ | Some tag ->
211211+ let tag = Char.code tag in
212212+ if tag < Array.length cs then dec cs.(tag) (tail s) else
213213+ err ~kind s
214214+ in
215215+ v ~kind ~enc ~dec
216216+217217+let version version =
218218+ let enc_version = string_of_int version in
219219+ fun c ->
220220+ let kind = Printf.sprintf "(%s) v%s" (kind c) enc_version in
221221+ let enc v = String.concat "\x00" [enc_version; enc c v] in
222222+ let dec s = match Topkg_string.cut ~sep:'\x00' s with
223223+ | None -> err ~kind s
224224+ | Some (fnd_version, s) ->
225225+ try
226226+ let fnd = int_of_string fnd_version in
227227+ if fnd <> version then err_version ~exp:version ~fnd else
228228+ dec c s
229229+ with
230230+ | Failure _ (* of int_of_string *) -> err ~kind s
231231+ in
232232+ v ~kind ~enc ~dec
233233+234234+let view ?kind:k (inj, proj) c =
235235+ let kind = match k with None -> kind c | Some k -> k in
236236+ let enc v = enc c (inj v) in
237237+ let dec s = proj (dec c s) in
238238+ v ~kind ~enc ~dec
239239+240240+let msg =
241241+ let msg = (fun (`Msg m) -> m), (fun m -> `Msg m) in
242242+ view msg string
243243+244244+let result_error_msg ok = result ~ok ~error:msg
245245+let fpath = string
246246+let cmd =
247247+ let cmd =
248248+ (fun cmd -> Topkg_cmd.to_list cmd),
249249+ (fun l -> Topkg_cmd.of_list l)
250250+ in
251251+ view cmd (list string)
+51
vendor/opam/topkg/src/topkg_codec.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** Topkg interprocess communication codec.
77+88+ See {!Topkg.Private.Codec} for documentation. *)
99+1010+(** {1 Codec} *)
1111+1212+open Topkg_result
1313+1414+type error = Corrupted of (string * string) | Version of int * int
1515+val pp_error : Format.formatter -> error -> unit
1616+exception Error of error
1717+val err : kind:string -> string -> 'a
1818+1919+type 'a t
2020+2121+val v : kind:string -> enc:('a -> string) -> dec:(string -> 'a) -> 'a t
2222+val kind : 'a t -> string
2323+val enc : 'a t -> 'a -> string
2424+val dec : 'a t -> string -> 'a
2525+val dec_result : 'a t -> string -> 'a result
2626+val with_kind : string -> 'a t -> 'a t
2727+val write : Topkg_fpath.t -> 'a t -> 'a -> unit result
2828+val read : Topkg_fpath.t -> 'a t -> 'a result
2929+3030+val unit : unit t
3131+val const : 'a -> 'a t
3232+val bool : bool t
3333+val int : int t
3434+val string : string t
3535+val option : 'a t -> 'a option t
3636+val result : ok:'a t -> error:'b t -> ('a, 'b) r t
3737+val list : 'a t -> 'a list t
3838+val pair : 'a t -> 'b t -> ('a * 'b) t
3939+val t2 : 'a t -> 'b t -> ('a * 'b) t
4040+val t3 : 'a t -> 'b t -> 'c t -> ('a * 'b * 'c) t
4141+val t4 : 'a t -> 'b t -> 'c t -> 'd t -> ('a * 'b * 'c * 'd) t
4242+val t5 : 'a t -> 'b t -> 'c t -> 'd t -> 'e t -> ('a * 'b * 'c * 'd * 'e) t
4343+val alt : kind:string -> ('a -> int) -> 'a t array -> 'a t
4444+val version : int -> 'a t -> 'a t
4545+val view : ?kind:string -> ('a -> 'b) * ('b -> 'a) -> 'b t -> 'a t
4646+4747+val msg : [`Msg of string ] t
4848+val result_error_msg : 'a t -> 'a result t
4949+5050+val fpath : Topkg_fpath.t t
5151+val cmd : Topkg_cmd.t t
+632
vendor/opam/topkg/src/topkg_conf.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+open Topkg_result
77+88+(* Configuration value converters *)
99+1010+type 'a conv =
1111+ { parse : (string -> 'a result);
1212+ print : (Format.formatter -> 'a -> unit);
1313+ docv : string; }
1414+1515+let conv ?(docv = "VALUE") parse print = { parse; print; docv }
1616+let conv_parser conv = conv.parse
1717+let conv_printer conv = conv.print
1818+let conv_docv conv = conv.docv
1919+let conv_with_docv conv ~docv = { conv with docv }
2020+2121+let bool =
2222+ let parse s = try Ok (bool_of_string s) with
2323+ | Invalid_argument _ -> R.error_msgf "%S: Can't parse boolean value" s
2424+ in
2525+ conv ~docv:"BOOL" parse Format.pp_print_bool
2626+2727+let int =
2828+ let parse s = try Ok (int_of_string s) with
2929+ | Failure _ -> R.error_msgf "%S: Can't parse integer value" s
3030+ in
3131+ conv ~docv:"INT" parse Format.pp_print_int
3232+3333+let string = conv ~docv:"STRING" (fun s -> Ok s) Format.pp_print_string
3434+let fpath = conv_with_docv string ~docv:"PATH"
3535+3636+let some ?(none = "") conv =
3737+ let parse s = match conv.parse s with
3838+ | Ok v -> Ok (Some v)
3939+ | Error _ as e -> e
4040+ in
4141+ let print ppf = function
4242+ | None -> Format.pp_print_string ppf none
4343+ | Some v -> conv.print ppf v
4444+ in
4545+ { conv with parse; print }
4646+4747+(* Universal type, see http://mlton.org/UniversalType *)
4848+4949+type univ = exn
5050+let univ (type s) () =
5151+ let module M = struct exception E of s option end in
5252+ (fun x -> M.E (Some x)), (function M.E x -> x | _ -> None)
5353+5454+(* Configuration keys *)
5555+5656+let key_id =
5757+ let count = ref (-1) in
5858+ fun () -> incr count; !count
5959+6060+type 'a absent = Value of 'a | Discover of (unit -> 'a result)
6161+type 'a key =
6262+ { id : int; (* unique id for the key. *)
6363+ name : string; (* key name. *)
6464+ conv : 'a conv; (* key value converter. *)
6565+ absent : 'a absent; (* value if unspecified. *)
6666+ env : string option; (* environment variable to override [absent]. *)
6767+ to_univ : 'a -> univ; (* convert to universal value. *)
6868+ of_univ : univ -> 'a option; (* convert from universal value. *)
6969+ doc : string; } (* documentation for the key. *)
7070+7171+module Key = struct
7272+ type t = V : 'a key -> t
7373+ let compare (V k0) (V k1) = (compare : int -> int -> int) k0.id k1.id
7474+end
7575+7676+module Kset = Set.Make (Key)
7777+module Kmap = Map.Make (Key)
7878+7979+let key_index = ref Kset.empty
8080+8181+let cli_opts_of_key_index () =
8282+ let add_key (Key.V k as key) acc = ("--" ^ k.name, key) :: acc in
8383+ Kset.fold add_key !key_index []
8484+8585+let _key ?docv ?(doc = "Undocumented") ?env name conv absent =
8686+ let id = key_id () in
8787+ let to_univ, of_univ = univ () in
8888+ let conv = match docv with
8989+ | None -> conv
9090+ | Some docv -> conv_with_docv conv ~docv
9191+ in
9292+ let key = { id; name; conv; absent; env; to_univ; of_univ; doc } in
9393+ key_index := Kset.add (Key.V key) !key_index;
9494+ key
9595+9696+let key ?docv ?doc ?env name conv ~absent =
9797+ _key ?docv ?doc ?env name conv (Value absent)
9898+9999+let discovered_key ?docv ?doc ?env name conv ~absent =
100100+ _key ?docv ?doc ?env name conv (Discover absent)
101101+102102+let key_absent_value k = (* WARNING raises Failure *)
103103+ let absent_field k = match k.absent with
104104+ | Value v -> v
105105+ | Discover discover ->
106106+ match discover () (* exciting... *) with
107107+ | Ok v -> v
108108+ | Error (`Msg m) -> failwith (Topkg_string.strf "key %s: %s" k.name m)
109109+ in
110110+ match k.env with
111111+ | None -> absent_field k
112112+ | Some var ->
113113+ match Topkg_os.Env.var var with
114114+ | None -> absent_field k
115115+ | Some ev ->
116116+ match conv_parser k.conv ev with
117117+ | Ok v -> v
118118+ | Error (`Msg m) ->
119119+ failwith (Topkg_string.strf "key %s: env %s: %s" k.name var m)
120120+121121+let with_pkg ?(default = true) pkg =
122122+ let doc = Topkg_string.strf "true if package %s is installed." pkg in
123123+ key ("with-" ^ pkg) bool ~absent:default ~doc
124124+125125+(* Predefined keys *)
126126+127127+let pkg_name =
128128+ let doc = "The name $(docv) of the package (and hence the opam install \
129129+ file). If absent provided by the package description."
130130+ in
131131+ let absent () = assert false (* handled specially by [of_cli_args] *) in
132132+ discovered_key "pkg-name" string ~absent ~doc ~docv:"NAME"
133133+134134+let build_dir =
135135+ let doc = "Specifies the build directory $(docv)." in
136136+ let absent () = assert false (* handled specially by [of_cli_args] *) in
137137+ discovered_key "build-dir" string ~absent ~doc ~docv:"BUILD_DIR"
138138+139139+let vcs =
140140+ let doc = "Specifies if the package directory is VCS managed." in
141141+ let absent () = Topkg_vcs.find ~dir:"." () >>= function
142142+ | None -> Ok false
143143+ | Some _ -> Ok true
144144+ in
145145+ discovered_key "vcs" bool ~absent ~doc
146146+147147+let pinned =
148148+ let doc = "Deprecated, use --dev-pkg. The semantics is the same."
149149+ in
150150+ key "pinned" bool ~absent:false ~doc
151151+152152+let dev_pkg =
153153+ let doc = "Specifies that the build is a dev package build (e.g. in opam)." in
154154+ key "dev-pkg" bool ~absent:false ~doc
155155+156156+157157+let tests =
158158+ let doc = "Specifies whether tests should be built. If absent depends \
159159+ on the build context, true for development and false otherwise."
160160+ in
161161+ let absent () = Ok None in
162162+ discovered_key "tests" (some bool) ~absent ~doc
163163+164164+let debug =
165165+ let doc = "Debug build. Save debugging information in build artefacts. This \
166166+ key should not be specified explicitly in your package build \
167167+ instructions."
168168+ in
169169+ key "debug" bool ~env:"TOPKG_CONF_DEBUG" ~absent:true ~doc
170170+171171+let debugger_support =
172172+ let doc = "Debugger support. Build and install build artefacts needed \
173173+ by debuggers. This key should not be specified explicitly in \
174174+ your package build instructions."
175175+ in
176176+ key "debugger-support" bool ~env:"TOPKG_CONF_DEBUGGER_SUPPORT" ~absent:true
177177+ ~doc
178178+179179+let profile =
180180+ let doc = "Profiling build. Include run-time profiling support in build \
181181+ artefacts. This key should not be specified explicitly \
182182+ in your package build instructions."
183183+ in
184184+ key "profile" bool ~env:"TOPKG_CONF_PROFILE" ~absent:false ~doc
185185+186186+let toolchain =
187187+ let doc = "Specifies the ocamlfind toolchain." in
188188+ key "toolchain" (some string) ~env:"TOPKG_CONF_TOOLCHAIN" ~absent:None ~doc
189189+190190+(* Key documentation *)
191191+192192+let pp_cli_opt ppf opt_name absent env doc docv =
193193+ let prf = Format.fprintf in
194194+ let pp_doc ppf doc =
195195+ let subst = function "docv" -> docv | s -> Topkg_string.strf "$(%s)" s in
196196+ let b = Buffer.create 244 in
197197+ let doc =
198198+ try Buffer.add_substitute b subst doc; Buffer.contents b
199199+ with Not_found -> doc
200200+ in
201201+ Topkg_string.pp_text ppf doc
202202+ in
203203+ let pp_absent absent env ppf () = match absent, env with
204204+ | "", None -> ()
205205+ | "", Some var -> prf ppf "@ (or %s env)" var
206206+ | absent, None -> prf ppf "@ (absent=%s)" absent
207207+ | absent, Some var -> prf ppf "@ (absent=%s or %s env)" absent var
208208+ in
209209+ prf ppf "@[<v4>@[%s %s %a@]@,@[%a@]@]"
210210+ opt_name docv (pp_absent absent env) () pp_doc doc
211211+212212+let pp_key ppf k =
213213+ let absent = match k.absent with
214214+ | Discover _ -> "discovered"
215215+ | Value v -> Topkg_string.strf "@[<h>%a@]" (conv_printer k.conv) v
216216+ in
217217+ let opt_name = match k.name with
218218+ | "pkg-name" -> "-n NAME, --pkg-name" (* a bit ugly to special case *)
219219+ | n -> Topkg_string.strf "--%s" n
220220+ in
221221+ let docv = conv_docv k.conv in
222222+ pp_cli_opt ppf opt_name absent k.env k.doc docv
223223+224224+let pp_keys_cli_opts ppf () =
225225+ let pp_key is_first (Key.V k) =
226226+ if is_first then () else Format.pp_print_cut ppf ();
227227+ pp_key ppf k; false
228228+ in
229229+ let by_name (Key.V k) (Key.V k') = compare k.name k'.name in
230230+ let keys = List.sort by_name (Kset.elements !key_index) in
231231+ Format.fprintf ppf "@[<v>";
232232+ ignore (List.fold_left pp_key true keys);
233233+ Format.fprintf ppf "@]";
234234+ ()
235235+236236+(* Configurations *)
237237+238238+type t = univ Kmap.t
239239+240240+let empty = Kmap.empty
241241+let is_empty = Kmap.is_empty
242242+let mem k c = Kmap.mem (Key.V k) c
243243+let add k v c = Kmap.add (Key.V k) (k.to_univ v) c
244244+let rem k c = Kmap.remove (Key.V k) c
245245+let find k c = try k.of_univ (Kmap.find (Key.V k) c) with Not_found -> None
246246+let value c k = match find k c with
247247+| Some v -> v
248248+| None ->
249249+ invalid_arg
250250+ (Topkg_string.strf "configuration key %s undefined, did you create
251251+ a key after the call to Pkg.describe ?" (* dirty bastard *) k.name)
252252+253253+let pp_value c ppf k = k.conv.print ppf (value c k)
254254+255255+let dump ppf c =
256256+ let dump_binding (Key.V k) v is_first =
257257+ if is_first then () else Format.pp_print_cut ppf ();
258258+ match k.of_univ v with
259259+ | None -> assert false
260260+ | Some v ->
261261+ Format.fprintf ppf "%s: @[%a@]" k.name (conv_printer k.conv) v;
262262+ false
263263+ in
264264+ Format.fprintf ppf "@[<v>";
265265+ ignore (Kmap.fold dump_binding c true);
266266+ Format.fprintf ppf "@]";
267267+ ()
268268+269269+let of_cli_args ~pkg_name:name ~build_dir:bdir args =
270270+ let cli_opts = cli_opts_of_key_index () in
271271+ let cli_opts = ("-n", Key.V pkg_name) :: cli_opts in
272272+ let strf = Topkg_string.strf in
273273+ let rec parse_keys conf = function (* WARNING raises *)
274274+ | key :: def :: defs ->
275275+ begin match try Some (List.assoc key cli_opts) with Not_found -> None with
276276+ | None -> failwith (Topkg_string.strf "key %s: Unknown key." key)
277277+ | Some (Key.V k) ->
278278+ if mem k conf
279279+ then failwith (strf "key %s: Repeated definition." key) else
280280+ match (conv_parser k.conv) def with
281281+ | Ok v -> parse_keys (add k v conf) defs
282282+ | Error (`Msg e) -> failwith (strf "key %s: %s." key e)
283283+ end
284284+ | [] -> conf
285285+ | key :: [] -> failwith (strf "key %s: No value specified." key)
286286+ in
287287+ let add_if_absent (Key.V k) conf = (* WARNING raises *)
288288+ if mem k conf then conf else
289289+ add k (key_absent_value k) conf
290290+ in
291291+ let ensure_pkg_name_and_bdir conf =
292292+ let conf = if mem pkg_name conf then conf else add pkg_name name conf in
293293+ if mem build_dir conf then conf else add build_dir bdir conf
294294+ in
295295+ try
296296+ let cli_conf = ensure_pkg_name_and_bdir (parse_keys empty args) in
297297+ Ok (Kset.fold add_if_absent !key_index cli_conf)
298298+ with
299299+ | Failure e -> R.error_msg e
300300+301301+let pkg_name c = value c pkg_name
302302+let build_dir c = value c build_dir
303303+let toolchain c = value c toolchain
304304+let vcs c = value c vcs
305305+let pinned c = value c pinned
306306+let dev_pkg c = value c dev_pkg
307307+308308+type build_context = [`Dev | `Distrib | `Pin ]
309309+let build_context c =
310310+ if not (vcs c) then `Distrib else
311311+ if (pinned c || dev_pkg c) then `Pin else
312312+ `Dev
313313+314314+let build_tests c = match value c tests with
315315+| Some b -> b
316316+| None ->
317317+ match build_context c with
318318+ | `Dev -> true
319319+ | _ -> false
320320+321321+let debug c = value c debug
322322+let debugger_support c = value c debugger_support
323323+let profile c = value c profile
324324+325325+(* Tool lookup *)
326326+327327+type os = [ `Build_os | `Host_os ]
328328+329329+let os_to_string = function
330330+| `Build_os -> "build-os"
331331+| `Host_os -> "host-os"
332332+333333+let os_tool_env name os =
334334+ let pre = match os with `Build_os -> "BUILD_OS_" | `Host_os -> "HOST_OS_" in
335335+ pre ^ Topkg_string.uppercase_ascii name
336336+337337+let os_bin_dir_env = function
338338+| `Build_os -> "BUILD_OS_BIN"
339339+| `Host_os -> "HOST_OS_XBIN"
340340+341341+let os_suff_env = function
342342+| `Build_os -> "BUILD_OS_SUFF"
343343+| `Host_os -> "HOST_OS_SUFF"
344344+345345+let ocamlfindable ?conf name os = match name with
346346+| "ocamlc" | "ocamlcp" | "ocamlmktop" | "ocamlopt" | "ocamldoc" | "ocamldep"
347347+| "ocamlmklib" | "ocamlbrowser" as tool ->
348348+ let toolchain =
349349+ match conf with
350350+ | None -> Topkg_cmd.empty
351351+ | Some c ->
352352+ match os, toolchain c with
353353+ | `Host_os, Some toolchain -> Topkg_cmd.(v "-toolchain" % toolchain)
354354+ | _ -> Topkg_cmd.empty
355355+ in
356356+ Some Topkg_cmd.(v "ocamlfind" %% toolchain % tool)
357357+| _ -> None
358358+359359+let tool ?conf name os = match Topkg_os.Env.var (os_tool_env name os) with
360360+| Some cmd -> Topkg_cmd.v cmd
361361+| None ->
362362+ match Topkg_os.Env.var (os_bin_dir_env os) with
363363+ | Some path -> Topkg_cmd.v Topkg_fpath.(path // name)
364364+ | None ->
365365+ match Topkg_os.Env.var (os_suff_env os) with
366366+ | Some suff -> Topkg_cmd.v (name ^ suff)
367367+ | None ->
368368+ match ocamlfindable ?conf name os with
369369+ | Some cmd -> cmd
370370+ | None -> Topkg_cmd.v name
371371+372372+let get_ncpus () =
373373+ let on_fail () = 1 in
374374+ let decode_int = Topkg_codec.(dec_result int) in
375375+ if Sys.win32 then
376376+ match Topkg_os.Env.var "NUMBER_OF_PROCESSORS" with
377377+ | Some s -> decode_int s |>
378378+ Topkg_log.on_error_msg ~level:Topkg_log.Debug ~use:on_fail
379379+ | None -> on_fail ()
380380+ else
381381+ let run_tool name args ~on_fail =
382382+ Topkg_log.on_error_msg ~level:Topkg_log.Debug ~use:on_fail @@
383383+ Topkg_os.Cmd.(run_out Topkg_cmd.(tool name `Build_os %% of_list args) |>
384384+ out_string |> success >>= decode_int)
385385+ in
386386+ run_tool "getconf" ["_NPROCESSORS_ONLN"] ~on_fail:(fun () ->
387387+ run_tool "sysctl" ["-n"; "hw.ncpu"] ~on_fail)
388388+389389+let jobs =
390390+ let doc = "Allow to run $(docv) commands at once when building." in
391391+ let absent () = Ok None in
392392+ discovered_key "jobs" (some int) ~absent ~doc ~docv:"JOBS"
393393+394394+let default_jobs = 4
395395+let jobs c = match value c jobs with
396396+| Some n -> n
397397+| None ->
398398+ match build_context c with
399399+ | `Dev -> get_ncpus ()
400400+ | _ -> default_jobs
401401+402402+(* OCaml configuration, as communicated by ocamlc -config *)
403403+404404+module OCaml = struct
405405+406406+ type conf = t
407407+408408+ (* Log strings *)
409409+410410+ let conf fmt = "OCaml %s conf: " ^^ fmt
411411+ let conf_key fmt = conf ("key %s: " ^^ fmt)
412412+413413+ (* Configuration *)
414414+415415+ type t =
416416+ { os : os; mutable conf : (string * string) list;
417417+ (* Mutability is only used to add the value found by a discover
418418+ procedure for keys that are not yet exposed in ocamlc -config.
419419+ See http://caml.inria.fr/mantis/view.php?id=7172 *) }
420420+421421+ let empty os = { os; conf = [] }
422422+423423+ let read_config c os =
424424+ let parse_line acc l = match Topkg_string.cut ~sep:':' l with
425425+ | Some (k, v) -> (k, String.trim v) :: acc
426426+ | None ->
427427+ Topkg_log.warn (fun m ->
428428+ m (conf "cannot parse line %S") (os_to_string os) l);
429429+ acc
430430+ in
431431+ begin
432432+ let ocamlc = tool ?conf:c "ocamlc" os in
433433+ Topkg_os.Cmd.(run_out Topkg_cmd.(ocamlc % "-config") |> to_lines)
434434+ >>= fun lines -> Ok (List.(rev (fold_left parse_line [] lines)))
435435+ >>= fun conf -> Ok { os; conf }
436436+ end
437437+ |> R.reword_error_msg ~replace:true
438438+ (fun msg -> R.msgf (conf " %s") (os_to_string os) msg)
439439+ |> Topkg_log.on_error_msg ~level:Topkg_log.Warning ~use:(fun () -> empty os)
440440+441441+ let cache = Hashtbl.create 2
442442+ let v c os =
443443+ try Hashtbl.find cache (c, os)
444444+ with Not_found ->
445445+ let config = read_config (Some c) os in
446446+ Hashtbl.add cache (c, os) config;
447447+ config
448448+449449+ let add_discovery k v c = c.conf <- (k, v) :: c.conf
450450+ let find k c = try Some (List.assoc k c.conf) with Not_found -> None
451451+ let get ~absent k c = match find k c with
452452+ | Some v -> v
453453+ | None ->
454454+ Topkg_log.warn (fun m ->
455455+ m (conf_key "undefined, using %S") (os_to_string c.os) k absent);
456456+ absent
457457+458458+ let get_string_with_discovery k c ~discover = match find k c with
459459+ | Some v -> v
460460+ | None -> let v = discover k c in add_discovery k v c; v
461461+462462+ let get_bool_with_discovery k c ~discover =
463463+ let maybe_v = match find k c with
464464+ | None -> None
465465+ | Some v ->
466466+ try Some (bool_of_string v) with
467467+ | (* That good old joke... *) Invalid_argument _ ->
468468+ Topkg_log.warn (fun m ->
469469+ m (conf_key "could not parse boolean,@ trying to discover")
470470+ (os_to_string c.os) k);
471471+ None
472472+ in
473473+ match maybe_v with
474474+ | Some v -> v
475475+ | None -> let v = discover k c in add_discovery k (string_of_bool v) c; v
476476+477477+ let get_int_with_discovery k c ~discover =
478478+ let maybe_v = match find k c with
479479+ | None -> None
480480+ | Some v ->
481481+ try Some (int_of_string v) with
482482+ | Failure _ ->
483483+ Topkg_log.warn (fun m ->
484484+ m (conf_key "could not parse integer,@ trying to discover")
485485+ (os_to_string c.os) k);
486486+ None
487487+ in
488488+ match maybe_v with
489489+ | Some v -> v
490490+ | None -> let v = discover k c in add_discovery k (string_of_int v) c; v
491491+492492+ let find_stdlib c = find "standard_library" c
493493+494494+ let get_bool_stdlib_file_exists_discovery k c ~file ~on_error =
495495+ get_bool_with_discovery k c ~discover:begin fun k c ->
496496+ match find_stdlib c with
497497+ | None ->
498498+ Topkg_log.warn (fun m ->
499499+ m (conf_key
500500+ "undefined, stdlib dir not found for discovery@ using %B")
501501+ (os_to_string c.os) k on_error);
502502+ on_error
503503+ | Some stdlib_dir ->
504504+ match Topkg_os.File.exists (Topkg_fpath.(stdlib_dir // file c)) with
505505+ | Ok exist -> exist
506506+ | Error (`Msg e) ->
507507+ Topkg_log.warn (fun m ->
508508+ m (conf_key "undefined,@ discovery error: %s,@ using %B")
509509+ (os_to_string c.os) k e on_error);
510510+ on_error
511511+ end
512512+513513+ let version c = (* parses the specification described in Sys.ocaml_version *)
514514+ let dumb_version = 0, 0, 0, None in
515515+ let k = "version" in
516516+ match find k c with
517517+ | None ->
518518+ Topkg_log.warn (fun m ->
519519+ m (conf_key "missing, using 0.0.0") (os_to_string c.os) k);
520520+ dumb_version
521521+ | Some version ->
522522+ match Topkg_string.parse_version version with
523523+ | Some version -> version
524524+ | None ->
525525+ Topkg_log.warn (fun m ->
526526+ m (conf_key "cannot parse from %S, using 0.0.0")
527527+ (os_to_string c.os) k version);
528528+ dumb_version
529529+530530+ let ext_obj c = get ~absent:".o" "ext_obj" c
531531+ let ext_asm c = get ~absent:".s" "ext_asm" c
532532+ let ext_lib c = get ~absent:".a" "ext_lib" c
533533+ let ext_dll c = get ~absent:".so" "ext_dll" c
534534+ let ext_exe c =
535535+ get_string_with_discovery "ext_exe" c ~discover:begin fun k c ->
536536+ (* Not exposed until at least 4.03. The discover logic is based on
537537+ the knowledge articulated in this message:
538538+ http://lists.ocaml.org/pipermail/wg-windows/2015-July/000037.html *)
539539+ let find_c_toolchain c = match find "ccomp_type" c, find "os_type" c with
540540+ | None, _ | _, None -> None
541541+ | Some ccomp_type, Some os_type ->
542542+ match ccomp_type, os_type with
543543+ | "msvc", _ -> Some `Win_msvc
544544+ | "cc", "Win32" -> Some `Win_cc
545545+ | _, _ -> Some `Other
546546+ in
547547+ match find_c_toolchain c with
548548+ | Some (`Win_msvc | `Win_cc) -> ".exe"
549549+ | Some `Other -> ""
550550+ | None ->
551551+ Topkg_log.warn (fun m ->
552552+ m (conf_key "undefined and@ no C toolchain@ detected,@ using \"\"")
553553+ (os_to_string c.os) k;);
554554+ ""
555555+ end
556556+557557+ let native c =
558558+ let file c = "libasmrun" ^ (ext_lib c) in
559559+ get_bool_stdlib_file_exists_discovery "native" c ~file ~on_error:false
560560+561561+ let native_dynlink c =
562562+ let file c =
563563+ let (major_ocaml_version, _minor, _patch, _extra) = version c in
564564+ if major_ocaml_version >= 5 then
565565+ "dynlink/dynlink.cmxa"
566566+ else
567567+ "dynlink.cmxa"
568568+ in
569569+ get_bool_stdlib_file_exists_discovery "natdynlink" c ~file ~on_error:false
570570+571571+ let supports_shared_libraries c =
572572+ let key = "supports_shared_libraries" in
573573+ let asm_dynlib c = "libasmrun_shared" ^ (ext_dll c) in
574574+ let caml_dynlib c = "libcamlrun_shared" ^ (ext_dll c) in
575575+ let on_error = false in
576576+ get_bool_stdlib_file_exists_discovery key c ~file:asm_dynlib ~on_error ||
577577+ get_bool_stdlib_file_exists_discovery key c ~file:caml_dynlib ~on_error
578578+579579+ let word_size c =
580580+ let sizeof_ptr_of_config_h file =
581581+ let err l =
582582+ R.error_msgf "could not parse SIZEOF_PTR from %S in %s" l file
583583+ in
584584+ let rec parse = function
585585+ | [] -> R.error_msgf "could not find SIZEOF_PTR in %s" file
586586+ | l :: ls ->
587587+ let l = Topkg_string.trim l in
588588+ let affix = "#define SIZEOF_PTR" in
589589+ let is_size_of_ptr = Topkg_string.is_prefix ~affix l in
590590+ if not is_size_of_ptr then parse ls else
591591+ match Topkg_string.cut ~rev:true ~sep:' ' l with
592592+ | None -> err l
593593+ | Some (_, size) ->
594594+ try Ok (int_of_string size * 8) with Failure _ -> err l
595595+ in
596596+ Topkg_os.File.read file
597597+ >>= fun conf -> Ok (Topkg_string.cuts ~sep:'\n' conf)
598598+ >>= fun lines -> parse lines
599599+ in
600600+ get_int_with_discovery "word_size" c ~discover:begin fun k c ->
601601+ let on_error = 64 in
602602+ match find_stdlib c with
603603+ | None ->
604604+ Topkg_log.warn (fun m ->
605605+ m (conf_key
606606+ "undefined, stdlib dir not found for discovery@ using %d")
607607+ (os_to_string c.os) k on_error);
608608+ on_error
609609+ | Some stdlib_dir ->
610610+ let config_h = "caml/config.h" in
611611+ match
612612+ Topkg_os.File.must_exist Topkg_fpath.(stdlib_dir // config_h)
613613+ >>= fun conf -> sizeof_ptr_of_config_h conf
614614+ with
615615+ | Ok v -> v
616616+ | Error (`Msg e) ->
617617+ Topkg_log.warn (fun m ->
618618+ m (conf_key "undefined,@ discovery error: %s,@ using %d")
619619+ (os_to_string c.os) k e on_error);
620620+ on_error
621621+ end
622622+623623+ let dump ppf c =
624624+ let pp_elt ppf (k, v) = Format.fprintf ppf "(%S, %S)" k v in
625625+ let rec loop = function
626626+ | [] -> ()
627627+ | v :: vs ->
628628+ if vs = [] then (Format.fprintf ppf "@[%a@]" pp_elt v) else
629629+ (Format.fprintf ppf "@[%a@];@ " pp_elt v; loop vs)
630630+ in
631631+ Format.fprintf ppf "@[<1>["; loop c.conf; Format.fprintf ppf "]@]"
632632+end
+96
vendor/opam/topkg/src/topkg_conf.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** Build configuration
77+88+ See {!Topkg.Conf}. *)
99+1010+open Topkg_result
1111+1212+(** {1 Configuration key value converters} *)
1313+1414+type 'a conv
1515+1616+val conv :
1717+ ?docv:string -> (string -> 'a result) -> (Format.formatter -> 'a -> unit) ->
1818+ 'a conv
1919+2020+val conv_with_docv : 'a conv -> docv:string -> 'a conv
2121+val conv_parser : 'a conv -> (string -> 'a result)
2222+val conv_printer : 'a conv -> (Format.formatter -> 'a -> unit)
2323+val conv_docv : 'a conv -> string
2424+2525+val bool : bool conv
2626+val int : int conv
2727+val string : string conv
2828+val fpath : Topkg_fpath.t conv
2929+val some : ?none:string -> 'a conv -> 'a option conv
3030+3131+(** {1 Configuration keys} *)
3232+3333+type 'a key
3434+3535+val key :
3636+ ?docv:string -> ?doc:string -> ?env:string -> string -> 'a conv ->
3737+ absent:'a -> 'a key
3838+3939+val discovered_key :
4040+ ?docv:string -> ?doc:string -> ?env:string -> string -> 'a conv ->
4141+ absent:(unit -> 'a result) -> 'a key
4242+4343+val with_pkg : ?default:bool -> string -> bool key
4444+4545+val pp_keys_cli_opts : Format.formatter -> unit -> unit
4646+4747+(** {1 Build configuration} *)
4848+4949+type t
5050+val empty : t
5151+val value : t -> 'a key -> 'a
5252+val pp_value : t -> Format.formatter -> 'a key -> unit
5353+val dump : Format.formatter -> t -> unit
5454+val of_cli_args :
5555+ pkg_name:string -> build_dir:Topkg_fpath.t -> string list -> t result
5656+5757+val pkg_name : t -> string
5858+val build_dir : t -> Topkg_fpath.t
5959+val vcs : t -> bool
6060+val pinned : t -> bool
6161+val dev_pkg : t -> bool
6262+val jobs : t -> int
6363+6464+type build_context = [`Dev | `Distrib | `Pin ]
6565+val build_context : t -> [`Dev | `Distrib | `Pin ]
6666+val build_tests : t -> bool
6767+6868+val debug : t -> bool
6969+val debugger_support : t -> bool
7070+val profile : t -> bool
7171+val toolchain : t -> string option
7272+7373+(** {1 Tool lookup} *)
7474+7575+type os = [ `Build_os | `Host_os ]
7676+val tool : ?conf:t -> string -> os -> Topkg_cmd.t
7777+7878+(** {1 OCaml configuration} *)
7979+8080+module OCaml : sig
8181+ type conf = t
8282+ type t
8383+ val v : conf -> os -> t
8484+ val find : string -> t -> string option
8585+ val version : t -> int * int * int * string option
8686+ val ext_obj : t -> string
8787+ val ext_asm : t -> string
8888+ val ext_lib : t -> string
8989+ val ext_dll : t -> string
9090+ val ext_exe : t -> string
9191+ val native : t -> bool
9292+ val native_dynlink : t -> bool
9393+ val supports_shared_libraries : t -> bool
9494+ val word_size : t -> int
9595+ val dump : Format.formatter -> t -> unit
9696+end
+151
vendor/opam/topkg/src/topkg_distrib.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+open Topkg_result
77+88+(* Watermarks *)
99+1010+type watermark =
1111+ string *
1212+ [ `String of string
1313+ | `Name
1414+ | `Version
1515+ | `Version_num
1616+ | `Vcs of [ `Commit_id ]
1717+ | `Opam of Topkg_fpath.t option * string * string ]
1818+1919+let opam_fields file =
2020+ (Topkg_opam.File.fields file)
2121+ |> R.reword_error_msg ~replace:true (fun msg -> R.msgf "Watermarks: %s" msg)
2222+ |> Topkg_log.on_error_msg ~level:Topkg_log.Warning ~use:(fun () -> [])
2323+2424+let opam_field =
2525+ let find k m = try Some (List.assoc k m) with Not_found -> None in
2626+ let opam_memo = ref [] in (* memoizes the opam files *)
2727+ let rec opam_field file field = match find file !opam_memo with
2828+ | None ->
2929+ opam_memo := (file, (opam_fields file)) :: !opam_memo;
3030+ opam_field file field
3131+ | Some fields ->
3232+ match find field fields with
3333+ | Some vs -> vs
3434+ | None ->
3535+ Topkg_log.warn
3636+ (fun m -> m "file %s: opam field %S undefined or unsupported"
3737+ file field);
3838+ ["UNDEFINED"]
3939+ in
4040+ opam_field
4141+4242+let vcs_commit_id () =
4343+ (Topkg_vcs.get () >>= fun repo -> Topkg_vcs.head ~dirty:true repo)
4444+ |> R.reword_error_msg ~replace:true
4545+ (fun msg -> R.msgf "Watermarks: VCS commit id determination: %s" msg)
4646+ |> Topkg_log.on_error_msg ~level:Topkg_log.Warning
4747+ ~use:(fun () -> "UNDEFINED")
4848+4949+let define_watermarks ~name ~version ~opam watermarks =
5050+ let define (id, v) =
5151+ let (id, v as def) = match v with
5252+ | `String s -> (id, s)
5353+ | `Version -> (id, version)
5454+ | `Version_num -> (id, Topkg_string.drop_initial_v version)
5555+ | `Name -> (id, name)
5656+ | `Vcs `Commit_id -> (id, vcs_commit_id ())
5757+ | `Opam (file, field, sep) ->
5858+ let file = match file with None -> opam | Some file -> file in
5959+ (id, String.concat sep (opam_field file field))
6060+ in
6161+ Topkg_log.info (fun m -> m "Watermark %s = %S" id v);
6262+ def
6363+ in
6464+ List.map define watermarks
6565+6666+let watermark_file ws file =
6767+ Topkg_os.File.read file >>= fun content ->
6868+ Topkg_os.File.write_subst file ws content >>= fun () ->
6969+ Topkg_log.info (fun m -> m "Watermarked %s" file); Ok ()
7070+7171+let rec watermark_files ws = function
7272+| [] -> Ok ()
7373+| f :: fs -> watermark_file ws f >>= fun () -> watermark_files ws fs
7474+7575+(* Defaults *)
7676+7777+let default_watermarks =
7878+ let space = " " in
7979+ let comma = ", " in
8080+ [ "NAME", `Name;
8181+ "VERSION", `Version;
8282+ "VERSION_NUM", `Version_num;
8383+ "VCS_COMMIT_ID", `Vcs `Commit_id;
8484+ "PKG_MAINTAINER", `Opam (None, "maintainer", comma);
8585+ "PKG_AUTHORS", `Opam (None, "authors", comma);
8686+ "PKG_HOMEPAGE", `Opam (None, "homepage", comma);
8787+ "PKG_ISSUES", `Opam (None, "bug-reports", space);
8888+ "PKG_DOC", `Opam (None, "doc", space);
8989+ "PKG_LICENSE", `Opam (None, "license", comma);
9090+ "PKG_REPO", `Opam (None, "dev-repo", space); ]
9191+9292+let default_files_to_watermark =
9393+ let is_file f =
9494+ Topkg_os.File.exists f |> Topkg_log.on_error_msg ~use:(fun _ -> false)
9595+ in
9696+ let is_binary_ext ext =
9797+ let module Set = Set.Make (String) in
9898+ let exts =
9999+ Set.(empty |>
100100+ add ".eps" |> add ".flv" |> add ".gif" |> add ".ico" |>
101101+ add ".jpeg" |> add ".jpg" |> add ".mov" |> add ".mp3" |>
102102+ add ".mp4" |> add ".otf" |> add ".pdf" |> add ".png" |>
103103+ add ".ps" |> add ".ttf" |> add ".woff")
104104+ in
105105+ Set.mem ext exts
106106+ in
107107+ let keep f = not (is_binary_ext @@ Topkg_fpath.get_ext f) && is_file f in
108108+ fun () ->
109109+ Topkg_vcs.get ()
110110+ >>= fun repo -> Topkg_vcs.tracked_files repo
111111+ >>= fun files -> Ok (List.filter keep files)
112112+113113+let default_massage () = Ok ()
114114+115115+let default_exclude_paths () =
116116+ Ok [".git"; ".gitignore"; ".gitattributes"; ".hg"; ".hgignore"; "build";
117117+ "Makefile"; "_build"]
118118+119119+(* Distribution *)
120120+121121+type t =
122122+ { watermarks : watermark list;
123123+ files_to_watermark : unit -> Topkg_fpath.t list result;
124124+ massage : unit -> unit result;
125125+ exclude_paths : unit -> Topkg_fpath.t list result;
126126+ uri : string option; }
127127+128128+let v
129129+ ?(watermarks = default_watermarks)
130130+ ?(files_to_watermark = default_files_to_watermark)
131131+ ?(massage = fun () -> Ok ())
132132+ ?(exclude_paths = default_exclude_paths)
133133+ ?uri () =
134134+ { watermarks; files_to_watermark; massage; exclude_paths; uri }
135135+136136+let watermarks d = d.watermarks
137137+let files_to_watermark d = d.files_to_watermark
138138+let massage d = d.massage
139139+let exclude_paths d = d.exclude_paths
140140+let uri d = d.uri
141141+let codec =
142142+ let uri = Topkg_codec.(with_kind "uri" @@ option string) in
143143+ let fields =
144144+ let stub () = invalid_arg "not executable outside package definition" in
145145+ (fun d -> d.uri),
146146+ (fun uri ->
147147+ { watermarks = [] (* bad *); files_to_watermark = stub;
148148+ massage = stub; exclude_paths = stub; uri })
149149+ in
150150+ Topkg_codec.version 0 @@
151151+ Topkg_codec.(view ~kind:"distrib" fields uri)
+53
vendor/opam/topkg/src/topkg_distrib.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** {1 Distribution description}
77+88+ See {!section:Topkg.Pkg.distrib}. *)
99+1010+(** {1 Distribution} *)
1111+1212+open Topkg_result
1313+1414+(* Watermarks *)
1515+1616+type watermark =
1717+ string *
1818+ [ `String of string | `Name | `Version | `Version_num | `Vcs of [ `Commit_id ]
1919+ | `Opam of Topkg_fpath.t option * string * string ]
2020+2121+val define_watermarks :
2222+ name:string -> version:string -> opam:Topkg_fpath.t ->
2323+ watermark list -> (string * string) list
2424+2525+val watermark_file : (string * string) list -> Topkg_fpath.t -> unit result
2626+val watermark_files :
2727+ (string * string) list -> Topkg_fpath.t list -> unit result
2828+2929+(* Distribution *)
3030+3131+type t
3232+3333+val v :
3434+ ?watermarks:watermark list ->
3535+ ?files_to_watermark:(unit -> Topkg_fpath.t list result) ->
3636+ ?massage:(unit -> unit result) ->
3737+ ?exclude_paths:(unit -> Topkg_fpath.t list result) ->
3838+ ?uri:string ->
3939+ unit -> t
4040+4141+val watermarks : t -> watermark list
4242+val files_to_watermark : t -> (unit -> Topkg_fpath.t list result)
4343+val massage : t -> (unit -> unit result)
4444+val exclude_paths : t -> (unit -> Topkg_fpath.t list result)
4545+val uri : t -> string option
4646+val codec : t Topkg_codec.t
4747+4848+(* Defaults *)
4949+5050+val default_watermarks : watermark list
5151+val default_files_to_watermark : unit -> Topkg_fpath.t list result
5252+val default_massage : unit -> unit result
5353+val default_exclude_paths : unit -> Topkg_fpath.t list result
+31
vendor/opam/topkg/src/topkg_fexts.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+type ext = [ `Ext of string | `Obj | `Real_clib | `Lib | `Dll | `Exe ]
77+type t = ext list
88+99+let interface = [ `Ext ".mli"; `Ext ".cmi"; `Ext ".cmti"; ]
1010+let cmx = [ `Ext ".cmx" ]
1111+let api = interface @ cmx
1212+let real_c_library = [ `Real_clib ]
1313+let c_library = [ `Lib ]
1414+let c_dll_library = [ `Dll ]
1515+let library = [` Ext ".cma"; `Ext ".cmxa"; `Ext ".cmxs" ] @ c_library
1616+let module_library = (api @ library)
1717+let exe = [ `Exe ]
1818+let ext e = [ `Ext e ]
1919+let exts es = List.map (fun e -> `Ext e) es
2020+2121+let ext_to_string c =
2222+ let ext_obj = Topkg_conf.OCaml.ext_obj c in
2323+ let ext_lib = Topkg_conf.OCaml.ext_lib c in
2424+ let ext_dll = Topkg_conf.OCaml.ext_dll c in
2525+ let ext_exe = Topkg_conf.OCaml.ext_exe c in
2626+ function
2727+ | `Ext s -> s
2828+ | `Obj -> ext_obj
2929+ | `Lib | `Real_clib -> ext_lib
3030+ | `Dll -> ext_dll
3131+ | `Exe -> ext_exe
+27
vendor/opam/topkg/src/topkg_fexts.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** File extensions.
77+88+ See {!Topkg.Exts} for documentation. *)
99+1010+(** {1 File extensions} *)
1111+1212+type ext = [`Ext of string | `Obj | `Real_clib | `Lib | `Dll | `Exe]
1313+1414+type t = ext list
1515+1616+val interface : ext list
1717+val api : ext list
1818+val cmx : ext list
1919+val real_c_library : ext list
2020+val c_library : ext list
2121+val c_dll_library : ext list
2222+val library : ext list
2323+val module_library : ext list
2424+val exe : ext list
2525+val exts : string list -> ext list
2626+val ext : string -> ext list
2727+val ext_to_string : Topkg_conf.OCaml.t -> ext -> string
+49
vendor/opam/topkg/src/topkg_fpath.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+type t = string
77+88+let dir_sep_prefix s =
99+ Topkg_string.is_prefix ~affix:Filename.dir_sep s ||
1010+ (String.length s > 0 && s.[0] = '/')
1111+1212+let dir_sep_suffix s =
1313+ Topkg_string.is_suffix ~affix:Filename.dir_sep s ||
1414+ (String.length s > 0 && s.[String.length s - 1] = '/')
1515+1616+let append =
1717+ fun p q -> match p with
1818+ | "" -> q
1919+ | p ->
2020+ match q with
2121+ | "" -> p
2222+ | q ->
2323+ if dir_sep_prefix q then q else
2424+ if dir_sep_suffix p then (p ^ q) else
2525+ (p ^ "/" ^ q)
2626+2727+let ( // ) = append
2828+2929+let is_dir_path p = match p with
3030+| "." | ".." -> true
3131+| _ ->
3232+ let is_suffix affix = Topkg_string.is_suffix ~affix p in
3333+ List.exists is_suffix ["/"; "/.."; "/."]
3434+3535+let is_file_path p = not (is_dir_path p)
3636+3737+let basename s = Filename.basename s
3838+let dirname s = Filename.dirname s
3939+4040+let last_dot_index s = try Some (String.rindex s '.') with Not_found -> None
4141+let get_ext s = match last_dot_index s with
4242+| None -> ""
4343+| Some i -> Topkg_string.with_index_range ~first:i s
4444+4545+let has_ext e p = Topkg_string.is_suffix ~affix:e p
4646+4747+let rem_ext s = match last_dot_index s with
4848+| None -> s
4949+| Some i -> Topkg_string.with_index_range ~last:(i - 1) s
+24
vendor/opam/topkg/src/topkg_fpath.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** File system paths.
77+88+ See {!Topkg.Fpath}. *)
99+1010+(** {1 File system paths} *)
1111+1212+type t = string
1313+val append : t -> t -> t
1414+val ( // ) : t -> t -> t
1515+1616+val is_dir_path : t -> bool
1717+val is_file_path : t -> bool
1818+1919+val basename : t -> string
2020+val dirname : t -> string
2121+2222+val get_ext : t -> string
2323+val has_ext : string -> t -> bool
2424+val rem_ext : t -> t
+297
vendor/opam/topkg/src/topkg_install.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+open Topkg_result
77+88+type move_scheme =
99+ { field :
1010+ [ `Test of bool * Topkg_fpath.t option * Topkg_cmd.t
1111+ | Topkg_opam.Install.field ];
1212+ auto_bin : bool;
1313+ force : bool;
1414+ built : bool;
1515+ exts : Topkg_fexts.t;
1616+ src : string;
1717+ dst : string;
1818+ debugger_support : bool; (* This a bit hacky, only used by
1919+ OCamlbuild higher-level installs *) }
2020+2121+type t = move_scheme list
2222+2323+let nothing = []
2424+2525+let flatten ls = (* We don't care about order *)
2626+ let rec push acc = function v :: vs -> push (v :: acc) vs | [] -> acc in
2727+ let rec loop acc = function
2828+ | l :: ls -> loop (push acc l) ls
2929+ | [] -> acc
3030+ in
3131+ loop [] ls
3232+3333+let split_ext s = match Topkg_string.cut ~rev:true s ~sep:'.' with
3434+| None -> s, `Ext ""
3535+| Some (name, ext) -> name, `Ext (Topkg_string.strf ".%s" ext)
3636+3737+let bin_drop_exts native = if native then [] else Topkg_fexts.ext ".native"
3838+let lib_drop_exts native native_dynlink supports_shared_libraries =
3939+ match native with
4040+ | false -> Topkg_fexts.(c_library @ exts [".cmx"; ".cmxa"; ".cmxs"])
4141+ | true ->
4242+ match supports_shared_libraries with
4343+ | false -> `Dll :: Topkg_fexts.ext ".cmxs"
4444+ | true ->
4545+ match native_dynlink with
4646+ | false -> Topkg_fexts.ext ".cmxs"
4747+ | true -> []
4848+4949+let to_build ?header c os i =
5050+ let bdir = Topkg_conf.build_dir c in
5151+ let debugger_support = Topkg_conf.debugger_support c in
5252+ let build_tests = Topkg_conf.build_tests c in
5353+ let ocaml_conf = Topkg_conf.OCaml.v c os in
5454+ let native = Topkg_conf.OCaml.native ocaml_conf in
5555+ let native_dylink = Topkg_conf.OCaml.native_dynlink ocaml_conf in
5656+ let supports_shared_libraries =
5757+ Topkg_conf.OCaml.supports_shared_libraries ocaml_conf
5858+ in
5959+ let ext_to_string = Topkg_fexts.ext_to_string ocaml_conf in
6060+ let file_to_str (n, ext) = Topkg_string.strf "%s%s" n (ext_to_string ext) in
6161+ let maybe_build = [ ".cmti"; ".cmt" ] in
6262+ let bin_drops = bin_drop_exts native in
6363+ let lib_drops =
6464+ lib_drop_exts native native_dylink supports_shared_libraries
6565+ in
6666+ let add acc m =
6767+ if m.debugger_support && not debugger_support then acc else
6868+ let mv (targets, moves, tests as acc) ((_, src_ext) as src) dst =
6969+ let drop = not m.force && match m.field with
7070+ | `Bin -> List.exists (( = ) src_ext) bin_drops
7171+ | `Lib | `Lib_root | `Stublibs ->
7272+ List.exists (( = ) src_ext) lib_drops
7373+ | _ -> false
7474+ in
7575+ if drop then (targets, moves, tests) else
7676+ let src = file_to_str src in
7777+ let dst = file_to_str dst in
7878+ let maybe = List.exists (Filename.check_suffix src) maybe_build in
7979+ let targets = if m.built && not maybe then src :: targets else targets in
8080+ let src = if m.built then Topkg_string.strf "%s/%s" bdir src else src in
8181+ match m.field with
8282+ | `Test (run, dir, args) ->
8383+ if not build_tests then acc else
8484+ let test = Topkg_test.v src ~args ~run ~dir in
8585+ (targets, moves, test :: tests)
8686+ | #Topkg_opam.Install.field as field ->
8787+ let move = (field, Topkg_opam.Install.move ~maybe src ~dst) in
8888+ (targets, move :: moves, tests)
8989+ in
9090+ let src, dst =
9191+ if not m.auto_bin then m.src, m.dst else
9292+ ((if native then m.src ^ ".native" else m.src ^ ".byte"),
9393+ m.dst ^ (ext_to_string `Exe))
9494+ in
9595+ if m.exts = [] then mv acc (split_ext src) (split_ext dst) else
9696+ let expand acc ext = mv acc (src, ext) (dst, ext) in
9797+ List.fold_left expand acc m.exts
9898+ in
9999+ let targets, moves, tests = List.fold_left add ([], [], []) (flatten i) in
100100+ let tests = if build_tests then Some tests else None in
101101+ targets, ((`Header header), moves), tests
102102+103103+(* Install fields *)
104104+105105+type field =
106106+ ?force:bool -> ?built:bool -> ?cond:bool -> ?exts:Topkg_fexts.t ->
107107+ ?dst:string -> string -> t
108108+109109+let _field field
110110+ ?(debugger_support = false)
111111+ ?(auto = true) ?(force = false) ?(built = true) ?(cond = true) ?(exts = [])
112112+ ?dst src =
113113+ if not cond then [] else
114114+ let dst = match dst with
115115+ | None -> Topkg_fpath.basename src
116116+ | Some dst ->
117117+ if Topkg_fpath.is_file_path dst then dst else
118118+ dst ^ (Topkg_fpath.basename src)
119119+ in
120120+ [{ field; auto_bin = auto; force; built; exts; src; dst;
121121+ debugger_support; }]
122122+123123+let field field =
124124+ _field ~debugger_support:false ~auto:false field
125125+126126+let field_exec field ?auto ?force ?built ?cond ?exts ?dst src =
127127+ _field field ~debugger_support:false
128128+ ?auto ?force ?built ?cond ?exts ?dst src
129129+130130+let bin = field_exec `Bin
131131+let doc = field `Doc
132132+let etc = field `Etc
133133+let lib = field `Lib
134134+let lib_root = field `Lib_root
135135+let libexec = field_exec `Libexec
136136+let libexec_root = field_exec `Libexec_root
137137+let man = field `Man
138138+let misc = field `Misc
139139+let sbin = field_exec `Sbin
140140+let share = field `Share
141141+let share_root = field `Share_root
142142+let stublibs = field `Stublibs
143143+let toplevel = field `Toplevel
144144+let unknown name = field (`Unknown name)
145145+let test ?(run = true) ?dir ?(args = Topkg_cmd.empty) =
146146+ field_exec (`Test (run, dir, args))
147147+148148+(* OCamlbuild higher-level installs *)
149149+150150+let parse_mllib contents = (* list of module name and path (for dir/Mod) *)
151151+ let lines = Topkg_string.cuts ~sep:'\n' contents in
152152+ let add_mod acc l =
153153+ let path = String.trim @@ match Topkg_string.cut ~sep:'#' l with
154154+ | None -> l
155155+ | Some (p, _ (* comment *)) -> p
156156+ in
157157+ if path = "" then acc else
158158+ let mod_name = Topkg_string.capitalize_ascii @@ Topkg_fpath.basename path in
159159+ (mod_name, path) :: acc
160160+ in
161161+ List.fold_left add_mod [] lines
162162+163163+let field_of_field field = (* hack, recover a field from a field function... *)
164164+ match (List.hd (field "")).field with
165165+ | `Test (run, dir, args) -> assert false
166166+ | #Topkg_opam.Install.field as field -> field
167167+168168+let mllib
169169+ ?(field = lib) ?(cond = true) ?(cma = true) ?(cmxa = true) ?(cmxs = true)
170170+ ?api ?dst_dir mllib
171171+ =
172172+ if not cond then [] else
173173+ let debugger_support_field =
174174+ _field ~debugger_support:true ~auto:false (field_of_field field)
175175+ in
176176+ let lib_dir = Topkg_fpath.dirname mllib in
177177+ let lib_base = Topkg_fpath.rem_ext mllib in
178178+ let dst f = match dst_dir with
179179+ | None -> None
180180+ | Some dir -> Some (Topkg_fpath.append dir (Topkg_fpath.basename f))
181181+ in
182182+ let api mllib_content =
183183+ let mod_names = List.map fst mllib_content in
184184+ match api with
185185+ | None -> mod_names (* all the .mllib modules if unspecified *)
186186+ | Some api ->
187187+ let in_mllib i = List.mem (Topkg_string.capitalize_ascii i) mod_names in
188188+ let api, orphans = List.partition in_mllib api in
189189+ let warn o =
190190+ Topkg_log.warn (fun m -> m "mllib %s: unknown interface %s" mllib o)
191191+ in
192192+ List.iter warn orphans;
193193+ api
194194+ in
195195+ let library =
196196+ let add_if cond v vs = if cond then v :: vs else vs in
197197+ let exts =
198198+ add_if cma (`Ext ".cma") @@ add_if cmxa (`Ext ".cmxa") @@
199199+ add_if cmxs (`Ext ".cmxs") @@ add_if (cmxa || cmxs) `Lib []
200200+ in
201201+ field ?dst:(dst lib_base) ~exts lib_base
202202+ in
203203+ let add_mods acc mllib_content =
204204+ let api = api mllib_content in
205205+ let add_mod acc (m, path) =
206206+ let fname = Topkg_string.uncapitalize_ascii (Topkg_fpath.basename path) in
207207+ let fpath = match Topkg_fpath.dirname path with
208208+ | "." -> Topkg_fpath.append lib_dir fname
209209+ | parent -> Topkg_fpath.(append lib_dir (append parent fname))
210210+ in
211211+ let dst = dst fname in
212212+ let exts, debugger_support_exts = match List.mem m api with
213213+ | true -> Topkg_fexts.api, Topkg_fexts.exts [".ml"; ".cmt"]
214214+ | false ->
215215+ let debugger_support_exts =
216216+ (* Hidden modules are not forced to have an mli this
217217+ information is not explicitely specified at the topkg
218218+ level so we look up the file system. *)
219219+ if Sys.file_exists (fpath ^ ".mli")
220220+ then [".mli"; ".ml"; ".cmti"; ".cmt"]
221221+ else [".ml"; ".cmt"]
222222+ in
223223+ Topkg_fexts.cmx, Topkg_fexts.exts debugger_support_exts
224224+ in
225225+ field ?dst ~exts fpath ::
226226+ debugger_support_field ?dst ~exts:debugger_support_exts fpath ::
227227+ acc
228228+ in
229229+ List.fold_left add_mod acc mllib_content
230230+ in
231231+ begin
232232+ Topkg_os.File.read mllib
233233+ >>= fun contents -> Ok (parse_mllib contents)
234234+ >>= fun mllib_content -> Ok (flatten @@ add_mods [library] mllib_content)
235235+ end
236236+ |> Topkg_log.on_error_msg ~use:(fun () -> [])
237237+238238+let parse_clib contents =
239239+ let lines = Topkg_string.cuts ~sep:'\n' contents in
240240+ let add_obj_path acc l =
241241+ let path = String.trim @@ match Topkg_string.cut ~sep:'#' l with
242242+ | None -> l
243243+ | Some (p, _ (* comment *)) -> p
244244+ in
245245+ if path = "" then acc else path :: acc
246246+ in
247247+ List.fold_left add_obj_path [] lines
248248+249249+let clib
250250+ ?(dllfield = stublibs) ?(libfield = lib) ?(cond = true) ?lib_dst_dir clib
251251+ =
252252+ if not cond then [] else
253253+ let debugger_support_field =
254254+ _field ~debugger_support:true ~auto:false (field_of_field lib)
255255+ in
256256+ let lib_dir = Topkg_fpath.dirname clib in
257257+ let lib_base =
258258+ let base = Topkg_fpath.(basename @@ rem_ext clib) in
259259+ if Topkg_string.is_prefix ~affix:"lib" base
260260+ then Ok (Topkg_string.with_index_range ~first:3 base)
261261+ else R.error_msgf "%s: OCamlbuild .clib file must start with 'lib'" clib
262262+ in
263263+ let lib_dst f = match lib_dst_dir with
264264+ | None -> None
265265+ | Some dir -> Some (Topkg_fpath.append dir (Topkg_fpath.basename f))
266266+ in
267267+ let add_debugger_support cobjs =
268268+ let add_cobj acc path =
269269+ let fname = Topkg_fpath.(rem_ext @@ basename path) in
270270+ let fpath = match Topkg_fpath.dirname path with
271271+ | "." -> Topkg_fpath.append lib_dir fname
272272+ | parent -> Topkg_fpath.(append lib_dir (append parent fname))
273273+ in
274274+ let dst = lib_dst fname in
275275+ debugger_support_field ?dst ~exts:(Topkg_fexts.ext ".c") fpath :: acc
276276+ in
277277+ List.fold_left add_cobj [] cobjs
278278+ in
279279+ begin
280280+ lib_base
281281+ >>= fun lib_base -> Topkg_os.File.read clib
282282+ >>= fun contents ->
283283+ let cobjs = parse_clib contents in
284284+ let lib = Topkg_fpath.append lib_dir ("lib" ^ lib_base) in
285285+ let lib = libfield ~exts:Topkg_fexts.real_c_library lib ?dst:(lib_dst lib)in
286286+ let dll = Topkg_fpath.append lib_dir ("dll" ^ lib_base) in
287287+ let dll = dllfield ~exts:Topkg_fexts.c_dll_library dll in
288288+ Ok (flatten @@ lib :: dll :: add_debugger_support cobjs)
289289+ end
290290+ |> Topkg_log.on_error_msg ~use:(fun () -> [])
291291+292292+(* Dummy codec *)
293293+294294+let codec : t Topkg_codec.t = (* we don't care *)
295295+ let fields = (fun _ -> ()), (fun () -> []) in
296296+ Topkg_codec.version 0 @@
297297+ Topkg_codec.(view ~kind:"install" fields unit)
+52
vendor/opam/topkg/src/topkg_install.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** Package install. *)
77+88+(** {1 Install} *)
99+1010+type t
1111+1212+val nothing : t
1313+val flatten : t list -> t
1414+val to_build :
1515+ ?header:string ->
1616+ Topkg_conf.t ->
1717+ Topkg_conf.os -> t list ->
1818+ (Topkg_fpath.t list * Topkg_opam.Install.t * Topkg_test.t list option)
1919+2020+type field =
2121+ ?force:bool -> ?built:bool -> ?cond:bool -> ?exts:Topkg_fexts.t ->
2222+ ?dst:string -> string -> t
2323+2424+val bin : ?auto:bool -> field
2525+val doc : field
2626+val etc : field
2727+val lib : field
2828+val lib_root : field
2929+val libexec : ?auto:bool -> field
3030+val libexec_root : ?auto:bool -> field
3131+val man : field
3232+val misc : field
3333+val sbin : ?auto:bool -> field
3434+val share : field
3535+val share_root : field
3636+val stublibs : field
3737+val toplevel : field
3838+val unknown : string -> field
3939+4040+val test :
4141+ ?run:bool -> ?dir:Topkg_fpath.t -> ?args:Topkg_cmd.t -> ?auto:bool -> field
4242+4343+val mllib :
4444+ ?field:field -> ?cond:bool -> ?cma:bool -> ?cmxa:bool -> ?cmxs:bool ->
4545+ ?api:string list -> ?dst_dir:Topkg_fpath.t -> Topkg_fpath.t -> t
4646+4747+val clib :
4848+ ?dllfield:field ->
4949+ ?libfield:field ->
5050+ ?cond:bool -> ?lib_dst_dir:Topkg_fpath.t -> Topkg_fpath.t -> t
5151+5252+val codec : t Topkg_codec.t
+87
vendor/opam/topkg/src/topkg_ipc.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+open Topkg_result
77+88+type 'a t =
99+ { cmd : Topkg_cmd.t;
1010+ codec : 'a Topkg_codec.t;
1111+ answer : Topkg_fpath.t; }
1212+1313+let v ?answer cmd codec =
1414+ let answer = match answer with
1515+ | Some a -> a
1616+ | None ->
1717+ (Topkg_os.File.tmp ())
1818+ |> R.reword_error_msg ~replace:true
1919+ (fun m -> R.msgf "Could not create IPC answer file: %s, using stdout" m)
2020+ |> Topkg_log.on_error_msg ~use:(fun () -> Topkg_os.File.dash)
2121+ in
2222+ let cmd = Topkg_cmd.(v answer %% cmd) in
2323+ { cmd; codec; answer }
2424+2525+let cmd ipc = ipc.cmd
2626+let codec ipc = ipc.codec
2727+let answer ipc = ipc.answer
2828+2929+let error_args args =
3030+ R.error_msgf "IPC: %a, unknown arguments"
3131+ Topkg_cmd.dump (Topkg_cmd.of_list args)
3232+3333+(* Package description IPC. Description functions raise Invalid_argument
3434+ at the other end. *)
3535+3636+let pkg () = v Topkg_cmd.(v "pkg") Topkg_pkg.codec
3737+let answer_pkg answer p = Topkg_codec.write answer Topkg_pkg.codec p
3838+3939+(* Run custom lint IPC *)
4040+4141+let lint_custom_codec = Topkg_codec.(option @@ list @@ result_error_msg @@ msg)
4242+let lint_custom () =
4343+ let cmd = Topkg_cmd.(v "lint" % "custom") in
4444+ v cmd lint_custom_codec
4545+4646+let answer_lint_custom answer p =
4747+ let custom_run = match (Topkg_pkg.lint_custom p) with
4848+ | None -> None
4949+ | Some custom -> Some (custom ())
5050+ in
5151+ Topkg_codec.write answer lint_custom_codec custom_run
5252+5353+(* Distrib prepare IPC *)
5454+5555+let distrib_prepared_codec =
5656+ Topkg_codec.version 0 @@
5757+ Topkg_codec.(with_kind "prepared" @@ result_error_msg (list fpath))
5858+5959+let distrib_prepare ~dist_build_dir ~name ~version ~opam ~opam_adds =
6060+ let cmd =
6161+ Topkg_cmd.(v "distrib" % "prepare" %
6262+ "dist-build-dir" % dist_build_dir % "name" % name %
6363+ "version" % version % "opam" % opam % "opam-adds" % opam_adds)
6464+ in
6565+ v cmd distrib_prepared_codec
6666+6767+let answer_distrib_prepare
6868+ answer p ~dist_build_dir ~name ~version ~opam ~opam_adds
6969+ =
7070+ Topkg_codec.write answer distrib_prepared_codec @@
7171+ Topkg_pkg.distrib_prepare p ~dist_build_dir ~name ~version ~opam ~opam_adds
7272+7373+(* IPC answer *)
7474+7575+let write_answer cmd p = match Topkg_cmd.to_list cmd with
7676+| answer :: "pkg" :: [] ->
7777+ answer_pkg answer p
7878+| answer :: "lint" :: "custom" :: [] ->
7979+ answer_lint_custom answer p
8080+| answer :: "distrib" :: "prepare" ::
8181+ "dist-build-dir" :: dist_build_dir :: "name" :: name ::
8282+ "version" :: version :: "opam" :: opam :: "opam-adds" :: opam_adds :: [] ->
8383+ answer_distrib_prepare
8484+ answer p ~dist_build_dir ~name ~version ~opam ~opam_adds
8585+8686+| args ->
8787+ error_args args
+27
vendor/opam/topkg/src/topkg_ipc.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** Topkg interprocess communication.
77+88+ See {!Topkg.Private.Ipc} for documentation. *)
99+1010+open Topkg_result
1111+1212+(** {1 Interprocess communication} *)
1313+1414+type 'a t
1515+1616+val v : ?answer:Topkg_fpath.t -> Topkg_cmd.t -> 'a Topkg_codec.t -> 'a t
1717+val cmd : 'a t -> Topkg_cmd.t
1818+val codec : 'a t -> 'a Topkg_codec.t
1919+val answer : 'a t -> Topkg_fpath.t
2020+2121+val pkg : unit -> Topkg_pkg.t t
2222+val lint_custom : unit -> Topkg_result.R.msg Topkg_result.result list option t
2323+val distrib_prepare :
2424+ dist_build_dir:string -> name:string -> version:string -> opam:string ->
2525+ opam_adds:string -> Topkg_fpath.t list result t
2626+2727+val write_answer : Topkg_cmd.t -> Topkg_pkg.t -> unit Topkg_result.result
+88
vendor/opam/topkg/src/topkg_log.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+open Topkg_result
77+88+type level = App | Error | Warning | Info | Debug
99+1010+let exec = match Array.length Sys.argv with
1111+| 0 -> Filename.basename Sys.executable_name
1212+| n -> Filename.basename Sys.argv.(0)
1313+1414+let _level =
1515+ let default = Some Warning in
1616+ let init =
1717+ try match Sys.getenv "TOPKG_VERBOSITY" with
1818+ | l when Topkg_string.is_prefix ~affix:"quiet" l -> None
1919+ | l when Topkg_string.is_prefix ~affix:"error" l -> Some Error
2020+ | l when Topkg_string.is_prefix ~affix:"warning" l -> Some Warning
2121+ | l when Topkg_string.is_prefix ~affix:"info" l -> Some Info
2222+ | l when Topkg_string.is_prefix ~affix:"debug" l -> Some Debug
2323+ | l ->
2424+ Format.eprintf
2525+ "%s: @[TOPKG_VERBOSITY env var unknown value: %S@]@." exec l;
2626+ default
2727+ with Not_found | Sys_error _ -> default
2828+ in
2929+ ref init
3030+3131+let level () = !_level
3232+let set_level l = _level := l
3333+3434+let level_to_string = function
3535+| None -> "quiet" | Some App -> "app" | Some Error -> "error"
3636+| Some Warning -> "warning" | Some Info -> "info" | Some Debug -> "debug"
3737+3838+let level_of_string = function
3939+| "quiet" -> Ok None
4040+| "app" -> Ok (Some App)
4141+| "error" -> Ok (Some Error)
4242+| "warning" -> Ok (Some Warning)
4343+| "info" -> Ok (Some Info)
4444+| "debug" -> Ok (Some Debug)
4545+| l -> R.error_msgf "%S: unknown log level" l
4646+4747+type 'a msgf =
4848+ (?header:string -> ('a, Format.formatter, unit) format -> 'a) -> unit
4949+5050+let _err_count = ref 0
5151+let err_count () = !_err_count
5252+5353+let _warn_count = ref 0
5454+let warn_count () = !_warn_count
5555+5656+let pp_level_header ppf (h,l) = match h with
5757+| Some h -> Format.fprintf ppf "[%s] " h
5858+| None ->
5959+ Format.pp_print_string ppf begin match l with
6060+ | App -> ""
6161+ | Error -> "[ERROR] "
6262+ | Warning -> "[WARNING] "
6363+ | Info -> "[INFO] "
6464+ | Debug -> "[DEBUG] "
6565+ end
6666+6767+let msg level msgf = match !_level with
6868+| None -> ()
6969+| Some level' when level > level' ->
7070+ if level = Error then incr _err_count else
7171+ if level = Warning then incr _warn_count else ()
7272+| Some _ ->
7373+ (if level = Error then incr _err_count else
7474+ if level = Warning then incr _warn_count else ());
7575+ let pr = if level = App then Format.printf else Format.eprintf in
7676+ msgf @@
7777+ (fun ?header fmt ->
7878+ pr ("%s: %a@[" ^^ fmt ^^ "@]@.") exec pp_level_header (header, level))
7979+8080+let app msgf = msg App msgf
8181+let err msgf = msg Error msgf
8282+let warn msgf = msg Warning msgf
8383+let info msgf = msg Info msgf
8484+let debug msgf = msg Debug msgf
8585+8686+let on_error_msg ?(level = Error) ~use = function
8787+| Ok v -> v
8888+| Error (`Msg e) -> msg level (fun m -> m "%s" e); use ()
+33
vendor/opam/topkg/src/topkg_log.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** Log
77+88+ Abridged [logs]. See {!Topkg.Log} for documentation. *)
99+1010+(** {1 Log} *)
1111+1212+open Topkg_result
1313+1414+type level = App | Error | Warning | Info | Debug
1515+1616+val level : unit -> level option
1717+val set_level : level option -> unit
1818+val level_to_string : level option -> string
1919+val level_of_string : string -> (level option, [`Msg of string]) r
2020+2121+type 'a msgf =
2222+ (?header:string -> ('a, Format.formatter, unit) format -> 'a) -> unit
2323+2424+val msg : level -> 'a msgf -> unit
2525+val app : 'a msgf -> unit
2626+val err : 'a msgf -> unit
2727+val warn : 'a msgf -> unit
2828+val info : 'a msgf -> unit
2929+val debug : 'a msgf -> unit
3030+3131+val on_error_msg : ?level:level -> use:(unit -> 'a) -> 'a result -> 'a
3232+val err_count : unit -> int
3333+val warn_count : unit -> int
+292
vendor/opam/topkg/src/topkg_main.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+open Topkg_result
77+88+(* Help *)
99+1010+let exec = match Array.length Sys.argv with
1111+| 0 -> Filename.basename Sys.executable_name
1212+| n -> Filename.basename Sys.argv.(0)
1313+1414+let usage () = Topkg_string.strf "Usage: %s COMMAND [OPTION]..." exec
1515+let try_help () =
1616+ Topkg_string.strf "%s\nTry `%s --help' for more information." (usage ()) exec
1717+1818+let pp_help ppf () =
1919+ let prf = Format.fprintf in
2020+ prf ppf "@[<v>@,";
2121+ prf ppf "Commands:@,";
2222+ prf ppf " build [OPTION]...@,";
2323+ prf ppf " @[Build the package, see `Build options' below.@]@,";
2424+ prf ppf " test [OPTION]... [TEST]... [-- [ARG]...]@,";
2525+ prf ppf " @[Run all or the given built package tests, \
2626+ see `Test options' below.@]@,";
2727+ prf ppf " clean [OPTION]...@,";
2828+ prf ppf " @[Clean the package build, see `Clean options' below.@]@,";
2929+ prf ppf " help@,";
3030+ prf ppf " @[Show this help.@]@,";
3131+ prf ppf " ipc VERBOSITY [ARG]...@,";
3232+ prf ppf " @[Interprocess communication with topkg care tools.@]@,@,";
3333+ prf ppf "Options:@,";
3434+ prf ppf " -h, -help, --help@,";
3535+ prf ppf " Show this help.@,";
3636+ prf ppf " -q, --quiet@,";
3737+ prf ppf " Be quiet. Takes over -v.@,";
3838+ prf ppf " -v, --verbose (absent=warning or TOPKG_VERBOSITY env)@,";
3939+ prf ppf " Increase verbosity. Repeatable, more than twice is useless.@,";
4040+ prf ppf " --version@,";
4141+ prf ppf " Show topkg's version information.@,@,";
4242+ prf ppf "Build options:@,";
4343+ prf ppf " -d, --dry-run@,";
4444+ prf ppf " @[Do not run build instructions,@ only@ determine@ and@ \
4545+ write@ the@ opam@ install@ file.@]@,";
4646+ prf ppf " -r ARG, --raw ARG (repeatable)@,";
4747+ prf ppf " @[Do not run build instructions or write@ the@ opam@ \
4848+ install@ file,@ only@ invoke@ the build@ system@ with@ \
4949+ the given ARG argument.@]@,@,";
5050+ prf ppf " @[%a@]@,@," Topkg_conf.pp_keys_cli_opts ();
5151+ prf ppf "Test options:@,";
5252+ prf ppf " --build-dir BUILD_DIR (absent=discovered)@,";
5353+ prf ppf " @[Specifies the build directory BUILD_DIR.@]@,";
5454+ prf ppf " -l, --list@,";
5555+ prf ppf " @[Do not run the tests, list them.@]@,@,";
5656+ prf ppf "Clean options:@,";
5757+ prf ppf " --build-dir BUILD_DIR (absent=discovered)@,";
5858+ prf ppf " @[Specifies the build directory BUILD_DIR.@]@,";
5959+ prf ppf " -n NAME, --pkg-name NAME (absent=discovered)@,";
6060+ prf ppf " @[The name NAME of the package (and hence the opam \
6161+ install file).@ If absent provided by the package@ \
6262+ description.@]@,";
6363+ ()
6464+6565+(* Commands *)
6666+6767+let help_cmd pkg =
6868+ let pr = Format.printf in
6969+ let name = Topkg_pkg.name pkg in
7070+ pr "%s's %s - Describes the %s package.@." name exec name;
7171+ pr "%s@." (usage ());
7272+ pr "%a@." pp_help ();
7373+ Ok 0
7474+7575+let version_cmd pkg = print_endline "topkg %%VERSION%%"; Ok 0
7676+7777+let build_cmd pkg kind args =
7878+ let log_conf c =
7979+ Topkg_log.info (fun m -> m "Build configuration:@\n%a" Topkg_conf.dump c)
8080+ in
8181+ let adjust_pkg_to_conf pkg c =
8282+ let name = Topkg_conf.pkg_name c in
8383+ let build_dir = Topkg_conf.build_dir c in
8484+ Topkg_pkg.with_name_and_build_dir pkg ~name ~build_dir
8585+ in
8686+ let pkg_name = Topkg_pkg.name pkg in
8787+ let build_dir = Topkg_pkg.build_dir pkg in
8888+ Topkg_conf.of_cli_args ~pkg_name ~build_dir args
8989+ >>= fun c -> Ok (log_conf c; adjust_pkg_to_conf pkg c)
9090+ >>= fun pkg -> Topkg_pkg.build pkg ~kind c `Host_os
9191+9292+let test_cmd pkg name build_dir list tests args =
9393+ let pkg = Topkg_pkg.with_name_and_build_dir ?name ?build_dir pkg in
9494+ Topkg_pkg.test pkg ~list ~tests ~args
9595+9696+let clean_cmd pkg name build_dir =
9797+ let pkg = Topkg_pkg.with_name_and_build_dir ?name ?build_dir pkg in
9898+ Topkg_pkg.clean pkg `Host_os
9999+100100+let ipc_cmd pkg args =
101101+ Topkg_ipc.write_answer (Topkg_cmd.of_list args) pkg >>= fun () -> Ok 0
102102+103103+let run_cmd pkg cmd args = match cmd with
104104+| `Help -> help_cmd pkg
105105+| `Version -> version_cmd pkg
106106+| `Build kind -> build_cmd pkg kind args
107107+| `Test (pkg_name, bdir, list, tests, args) ->
108108+ test_cmd pkg pkg_name bdir list tests args
109109+| `Clean (pkg_name, bdir) -> clean_cmd pkg pkg_name bdir
110110+| `Ipc -> ipc_cmd pkg args
111111+112112+(* Cli interface *)
113113+114114+let default_verb = Topkg_log.level ()
115115+let incr_verb = function
116116+| Some Topkg_log.Warning -> Some Topkg_log.Info
117117+| Some Topkg_log.Info -> Some Topkg_log.Debug
118118+| v -> v
119119+120120+let is_opt s = Topkg_string.(is_prefix ~affix:"-" s || is_prefix ~affix:"--" s)
121121+122122+let parse_cli_help_version_verbosity args =
123123+ let is_help = function "-h" | "--help" | "-help" -> true | _ -> false in
124124+ let is_verb = function "-v" | "--verbose" -> true | _ -> false in
125125+ let is_quiet = function "-q" | "--quiet" -> true | _ -> false in
126126+ let is_version = function "--version" -> true | _ -> false in
127127+ let rec loop cmd verb acc = function
128128+ | a :: args when is_help a -> loop `Help verb acc args
129129+ | a :: args when is_verb a -> loop cmd (incr_verb verb) acc args
130130+ | a :: args when is_quiet a -> loop cmd None acc args
131131+ | a :: args when is_version a ->
132132+ let cmd = if cmd = `Help then `Help else `Version in
133133+ loop cmd verb acc args
134134+ | ("--" :: _ | [] as rest) -> cmd, verb, List.rev (List.rev_append rest acc)
135135+ | a :: args -> loop cmd verb (a :: acc) args
136136+ in
137137+ match args with
138138+ | "help" :: args -> loop `Help default_verb [] args
139139+ | args -> loop `Cmd default_verb [] args
140140+141141+let parse_build_args args =
142142+ let rec loop dry_run raws acc = function
143143+ | ("-r" | "--raw" as opt) :: args ->
144144+ if args = [] then R.error_msgf "option `%s': missing argument" opt else
145145+ loop dry_run (List.hd args :: raws) acc (List.tl args)
146146+ | ("-d" | "--dry-run") :: args ->
147147+ loop true raws acc args
148148+ | a :: args ->
149149+ loop dry_run raws (a :: acc) args
150150+ | [] ->
151151+ let args = List.rev acc in
152152+ match dry_run, raws with
153153+ | false, [] -> Ok (`Build, args)
154154+ | false, raws -> Ok (`Raw (List.rev raws), args)
155155+ | true, [] -> Ok (`Dry_run, args)
156156+ | true, _ ->
157157+ R.error_msg "option `--dry-run' and `--raw' are mutually exclusive"
158158+ in
159159+ loop false [] [] args
160160+161161+let parse_test_args args =
162162+ let rec loop pkg_name build_dir list tests = function
163163+ | ("--" :: args) ->
164164+ Ok (pkg_name, build_dir, list, List.rev tests,
165165+ Some (Topkg_cmd.of_list args))
166166+ | [] -> Ok (pkg_name, build_dir, list, List.rev tests, None)
167167+ | "--build-dir" :: bdir :: args -> loop pkg_name (Some bdir) list tests args
168168+ | ("-l" | "--list") :: args -> loop pkg_name build_dir true tests args
169169+ | ("-n" | "--pkg-name") :: n :: args ->
170170+ loop (Some n) build_dir list tests args
171171+ | a :: args ->
172172+ if is_opt a then R.error_msgf "unknown option `%s'" a else
173173+ loop pkg_name build_dir list (a :: tests) args
174174+ in
175175+ loop None None false [] args
176176+177177+let parse_clean_args args =
178178+ let rec loop pkg_name build_dir = function
179179+ | "--build-dir" :: bdir :: args -> loop pkg_name (Some bdir) args
180180+ | ("-n" | "--pkg-name") :: n :: args -> loop (Some n) build_dir args
181181+ | [] -> Ok (pkg_name, build_dir)
182182+ | a :: args ->
183183+ R.error_msgf "don't know what to do with `%s'" a
184184+ in
185185+ loop None None args
186186+187187+let parse_ipc_args args =
188188+ begin match args with
189189+ | [] -> R.error_msg "missing verbosity and IPC arguments"
190190+ | verbosity :: args ->
191191+ Topkg_log.level_of_string verbosity
192192+ >>= fun verbosity -> match args with
193193+ | [] -> R.error_msg "no IPC arguments specified"
194194+ | args -> Ok (verbosity, args)
195195+ end
196196+ |> R.reword_error_msg ~replace:true (fun e -> R.msgf "ipc: %s" e)
197197+198198+let parse_cli () =
199199+ let args = List.tl (Array.to_list Sys.argv) in
200200+ begin match args with
201201+ | "ipc" :: args -> (* args may be data so don't interpret anything *)
202202+ parse_ipc_args args >>= fun (verb, args) -> Ok (`Ipc, verb, args)
203203+ | args ->
204204+ match parse_cli_help_version_verbosity args with
205205+ | `Help, _, _ as cmd -> Ok cmd
206206+ | `Version, _, _ as cmd -> Ok cmd
207207+ | `Cmd, verbosity, args ->
208208+ match args with
209209+ | "build" :: args ->
210210+ parse_build_args args >>= fun (kind, args) ->
211211+ Ok (`Build kind, verbosity, args)
212212+ | "test" :: args ->
213213+ parse_test_args args
214214+ >>= fun (pkg_name, bdir, list, tests, args) ->
215215+ Ok (`Test (pkg_name, bdir, list, tests, args), verbosity, [])
216216+ | "clean" :: args ->
217217+ parse_clean_args args >>= fun (pkg_name, bdir) ->
218218+ Ok (`Clean (pkg_name, bdir), verbosity, [])
219219+ | cmd :: _ -> R.error_msgf "Unknown command '%s'" cmd
220220+ | [] -> R.error_msg "No command specified"
221221+ end
222222+ |> R.reword_error_msg ~replace:true (fun e -> R.msgf "%s\n%s" e (try_help ()))
223223+224224+(* Main *)
225225+226226+let check_log ret =
227227+ let msg = format_of_string "Package description has %d %s, see log above." in
228228+ let log kind count =
229229+ if count > 0 then match kind with
230230+ | `Errs -> Topkg_log.err (fun m -> m msg count "error(s)")
231231+ | `Warns -> Topkg_log.warn (fun m -> m msg count "warning(s)")
232232+ in
233233+ let errs = Topkg_log.err_count () in
234234+ let warns = Topkg_log.warn_count () in
235235+ log `Errs errs;
236236+ log `Warns warns;
237237+ Ok (if ret + errs > 0 then 1 else 0)
238238+239239+let setup_log_level level =
240240+ Topkg_log.set_level level;
241241+ Topkg_log.info (fun m -> m "topkg %%VERSION%%, running main");
242242+ Ok ()
243243+244244+let main pkg =
245245+ begin
246246+ parse_cli ()
247247+ >>= fun (cmd, log_level, args) -> setup_log_level log_level
248248+ >>= fun () -> run_cmd pkg cmd args
249249+ >>= fun ret -> match cmd with
250250+ | `Build _ -> (check_log ret)
251251+ | _ -> Ok ret
252252+ end
253253+ |> Topkg_log.on_error_msg ~use:(fun () -> 1)
254254+255255+(* Main execution handling.
256256+257257+ The call to [describe] runs the [main] function unless prevented by
258258+ a previous call to [disable].
259259+260260+ We install an [at_exit] function that checks whether either [main]
261261+ ran or [disable] was called and report an error message if that is
262262+ not the case; this is what happens, for example, in case of syntax
263263+ error in the package description. Unfortunately because of
264264+ http://caml.inria.fr/mantis/view.php?id=7178 we cannot return with
265265+ a non-zero exit code at that point. We could by raising an exception
266266+ but it gets confusing, see http://caml.inria.fr/mantis/view.php?id=7253. *)
267267+268268+let must_run_main = ref true
269269+let disable () = must_run_main := false
270270+271271+let pkg = ref None
272272+let describe
273273+ ?delegate ?readmes ?licenses ?change_logs ?metas ?opams ?lint_files
274274+ ?lint_custom ?distrib ?publish ?build name installs
275275+ =
276276+ match !pkg with
277277+ | Some _ -> invalid_arg "Topkg.Pkg.describe already called once"
278278+ | None ->
279279+ let p =
280280+ Topkg_pkg.v ?delegate ?readmes ?licenses ?change_logs ?metas ?opams
281281+ ?lint_files ?lint_custom ?distrib ?publish ?build name installs
282282+ in
283283+ pkg := Some p;
284284+ if !must_run_main then (must_run_main := false; exit (main p)) else ()
285285+286286+let check_something_useful_happened () =
287287+ if !must_run_main then
288288+ Topkg_log.err (fun m -> m "%a" Topkg_string.pp_text
289289+ "No package description found. A syntax error may have \
290290+ occured or did you forget to call Topkg.Pkg.describe ?")
291291+292292+let () = at_exit check_something_useful_happened
+26
vendor/opam/topkg/src/topkg_main.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** Entry point for [pkg.ml] files. *)
77+88+(** {1 Main} *)
99+1010+open Topkg_result
1111+1212+val describe :
1313+ ?delegate:Topkg_cmd.t ->
1414+ ?readmes:Topkg_pkg.std_file list ->
1515+ ?licenses:Topkg_pkg.std_file list ->
1616+ ?change_logs:Topkg_pkg.std_file list ->
1717+ ?metas:Topkg_pkg.meta_file list ->
1818+ ?opams:Topkg_pkg.opam_file list ->
1919+ ?lint_files:Topkg_fpath.t list option ->
2020+ ?lint_custom:(unit -> R.msg result list) ->
2121+ ?distrib:Topkg_distrib.t ->
2222+ ?publish:Topkg_publish.t ->
2323+ ?build:Topkg_build.t ->
2424+ string -> (Topkg_conf.t -> Topkg_install.t list result) -> unit
2525+2626+val disable : unit -> unit
+114
vendor/opam/topkg/src/topkg_opam.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+open Topkg_result
77+88+(* opam File *)
99+1010+module File = struct
1111+ type t = (string * string list) list
1212+1313+ let codec =
1414+ Topkg_codec.version 0 @@
1515+ Topkg_codec.with_kind "opam fields" @@
1616+ Topkg_codec.(list (pair string (list string)))
1717+1818+ let topkg_cmd = Topkg_cmd.v "topkg"
1919+ let topkg_cmd_available () =
2020+ Topkg_os.Cmd.must_exist topkg_cmd
2121+ |> R.reword_error_msg ~replace:true
2222+ (fun m -> R.msgf "%s. Did you install topkg-care ?" m)
2323+2424+ let ipc_cmd file =
2525+ (* Propagate the log level to the IPC call *)
2626+ let level = Topkg_log.(level_to_string (level ())) in
2727+ let verbosity = Topkg_string.strf "--verbosity=%s" level in
2828+ Topkg_cmd.(v "ipc" % verbosity % "opam-fields" % file)
2929+3030+ let fields file =
3131+ begin
3232+ let cmd = Topkg_cmd.(topkg_cmd %% ipc_cmd file) in
3333+ topkg_cmd_available ()
3434+ >>= fun _ -> Topkg_os.File.must_exist file
3535+ >>= fun _ -> Topkg_os.Cmd.(run_out cmd |> to_string)
3636+ >>= fun s -> (Topkg_codec.dec_result codec s)
3737+ end
3838+ |> R.reword_error_msg ~replace:true
3939+ (fun msg -> R.msgf "opam fields of %s: %s" file msg)
4040+end
4141+4242+(* opam install file *)
4343+4444+module Install = struct
4545+4646+ type field =
4747+ [ `Bin
4848+ | `Doc
4949+ | `Etc
5050+ | `Lib
5151+ | `Lib_root
5252+ | `Libexec
5353+ | `Libexec_root
5454+ | `Man
5555+ | `Misc
5656+ | `Sbin
5757+ | `Share
5858+ | `Share_root
5959+ | `Stublibs
6060+ | `Toplevel
6161+ | `Unknown of string ]
6262+6363+ let field_to_string = function
6464+ | `Bin -> "bin"
6565+ | `Doc -> "doc"
6666+ | `Etc -> "etc"
6767+ | `Lib -> "lib"
6868+ | `Lib_root -> "lib_root"
6969+ | `Libexec -> "libexec"
7070+ | `Libexec_root -> "libexec_root"
7171+ | `Man -> "man"
7272+ | `Misc -> "misc"
7373+ | `Sbin -> "sbin"
7474+ | `Share -> "share"
7575+ | `Share_root -> "share_root"
7676+ | `Stublibs -> "stublibs"
7777+ | `Toplevel -> "toplevel"
7878+ | `Unknown name -> name
7979+8080+ type move = { src : string; dst : string option; maybe : bool; }
8181+8282+ let move ?(maybe = false) ?dst src = { src; dst; maybe }
8383+8484+ type t = [ `Header of string option ] * (field * move) list
8585+8686+ let to_string (`Header h, mvs) =
8787+ let b = Buffer.create 1024 in
8888+ let pr b fmt = Printf.bprintf b fmt in
8989+ let pr_header b = function None -> () | Some h -> pr b "# %s\n\n" h in
9090+ let pr_src b src maybe =
9191+ pr b " \"%s%s\"" (if maybe then "?" else "") src
9292+ in
9393+ let pr_dst b dst = match dst with
9494+ | None -> ()
9595+ | Some dst -> pr b " {\"%s\"}" dst
9696+ in
9797+ let pr_field_end b last = if last <> "" (* not start *) then pr b " ]\n" in
9898+ let pr_field b last field =
9999+ if last = field then pr b "\n" else
100100+ (pr_field_end b last; pr b "%s: [\n" field)
101101+ in
102102+ let pr_move b last (field, { src; dst; maybe }) =
103103+ pr_field b last field;
104104+ pr_src b src maybe;
105105+ pr_dst b dst;
106106+ field
107107+ in
108108+ let sortable (field, mv) = (field_to_string field, mv) in
109109+ let moves = List.sort compare (List.rev_map sortable mvs) in
110110+ pr_header b h;
111111+ let last = List.fold_left (pr_move b) "" moves in
112112+ pr_field_end b last;
113113+ Buffer.contents b
114114+end
+63
vendor/opam/topkg/src/topkg_opam.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** opam helpers.
77+88+ See also {!Topkg.Private.Opam}. *)
99+1010+open Topkg_result
1111+1212+module File : sig
1313+ type t = (string * string list) list
1414+ val codec : t Topkg_codec.t
1515+ val fields : Topkg_fpath.t -> t result
1616+end
1717+1818+(** opam install file.
1919+2020+ A module to generate opam install files.
2121+2222+ {b Reference}.
2323+ {{:http://opam.ocaml.org/doc/manual/dev-manual.html#sec25}
2424+ Syntax and semantics} of opam install files. *)
2525+module Install : sig
2626+2727+ (** {1 opam install files} *)
2828+2929+ type field =
3030+ [ `Bin
3131+ | `Doc
3232+ | `Etc
3333+ | `Lib
3434+ | `Lib_root
3535+ | `Libexec
3636+ | `Libexec_root
3737+ | `Man
3838+ | `Misc
3939+ | `Sbin
4040+ | `Share
4141+ | `Share_root
4242+ | `Stublibs
4343+ | `Toplevel
4444+ | `Unknown of string ]
4545+ (** The type for opam install file fields. *)
4646+4747+ type move
4848+ (** The type for file moves. *)
4949+5050+ val move : ?maybe:bool -> ?dst:Topkg_fpath.t -> Topkg_fpath.t -> move
5151+ (** [move ~maybe ~dst src] moves [src] to [dst], where [dst] is a
5252+ path relative to the directory corresponding to the
5353+ {{!field}field}. If [maybe] is [true] (defaults to [false]),
5454+ then [src] may not exist, otherwise an install error will occur
5555+ if the file doesn't exist. *)
5656+5757+ type t = [ `Header of string option ] * (field * move) list
5858+ (** The type for opam install files. An optional starting header
5959+ comment and a list of field moves. *)
6060+6161+ val to_string : t -> string
6262+ (** [to_string t] is [t] as syntactically valid opam install file. *)
6363+end
+282
vendor/opam/topkg/src/topkg_os.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+open Topkg_result
77+88+(* Environment variables. *)
99+1010+module Env = struct
1111+ let var name = try Some (Sys.getenv name) with Not_found -> None
1212+ let opt_var name ~absent = match var name with None -> absent | Some v -> v
1313+end
1414+1515+(* Directory operations *)
1616+1717+module Dir = struct
1818+1919+ (* Existence *)
2020+2121+ let exists dir =
2222+ try Ok (Sys.(file_exists dir && is_directory dir)) with
2323+ | Sys_error e -> R.error_msgf "%s: %s" dir e
2424+2525+ let must_exist dir = exists dir >>= function
2626+ | true -> Ok dir
2727+ | false -> R.error_msgf "%s: no such directory" dir
2828+2929+ (* Current working directory *)
3030+3131+ let current () = try Ok (Sys.getcwd ()) with Sys_error e -> R.error_msg e
3232+ let set_current d =
3333+ try Ok (Sys.chdir d) with Sys_error e -> R.error_msgf "%s: %s" d e
3434+3535+ let with_current dir f v =
3636+ current ()
3737+ >>= fun original -> set_current dir
3838+ >>= fun () -> let r = f v in set_current original
3939+ >>= fun () -> Ok r
4040+4141+ (* Directory contents *)
4242+4343+ let contents ?(dotfiles = false) ?(rel = false) p =
4444+ try
4545+ let files = Array.to_list @@ Sys.readdir p in
4646+ if rel && dotfiles then Ok files else
4747+ let rec loop acc = function
4848+ | [] -> List.rev acc
4949+ | f :: fs ->
5050+ let acc =
5151+ if not dotfiles && Topkg_string.is_prefix ~affix:"." f then acc else
5252+ if rel then f :: acc else Topkg_fpath.append p f :: acc
5353+ in
5454+ loop acc fs
5555+ in
5656+ Ok (loop [] files)
5757+ with
5858+ | Sys_error e -> R.error_msgf "%s: %s" p e
5959+end
6060+6161+(* File system operations *)
6262+6363+module File = struct
6464+6565+ (* Famous file paths *)
6666+6767+ let null = match Sys.os_type with
6868+ | "Win32" -> "NUL"
6969+ | _ -> "/dev/null"
7070+7171+ let dash = "-"
7272+7373+ (* Existence and deletion *)
7474+7575+ let exists f =
7676+ try Ok (Sys.(file_exists f && not (is_directory f))) with
7777+ | Sys_error e -> R.error_msgf "%s: %s" f e
7878+7979+ let must_exist f = exists f >>= function
8080+ | true -> Ok f
8181+ | false -> R.error_msgf "%s: no such file" f
8282+8383+ let delete ?(must_exist = false) f =
8484+ try
8585+ if not must_exist && not (Sys.file_exists f) then Ok () else
8686+ Ok (Sys.remove f)
8787+ with
8888+ | Sys_error e -> R.error_msgf "%s: %s" f e
8989+9090+ (* Folding over files *)
9191+9292+ let fold ?(skip = fun _ -> false) f acc paths =
9393+ let is_dir d = try Sys.is_directory d with Sys_error _ -> false in
9494+ let readdir d =
9595+ try Array.to_list (Sys.readdir d) with Sys_error _ -> []
9696+ in
9797+ let keep p = not (skip p) in
9898+ let process acc file = f file acc in
9999+ let rec aux f acc = function
100100+ | (d :: ds) :: up ->
101101+ let paths = List.rev_map (Filename.concat d) (readdir d) in
102102+ let paths = List.find_all keep paths in
103103+ let dirs, files = List.partition is_dir paths in
104104+ let acc = List.fold_left process acc files in
105105+ aux f acc (dirs :: ds :: up)
106106+ | [] :: [] -> acc
107107+ | [] :: up -> aux f acc up
108108+ | _ -> assert false
109109+ in
110110+ let paths = List.find_all keep paths in
111111+ let dirs, files = List.partition is_dir paths in
112112+ let acc = List.fold_left process acc files in
113113+ Ok (aux f acc (dirs :: []))
114114+115115+ (* Reading and writing *)
116116+117117+ let with_parent_check op op_name file =
118118+ let err_no_parent op_name file =
119119+ Topkg_string.strf
120120+ "%s: Cannot %s file, parent directory does not exist" file op_name
121121+ in
122122+ (Dir.must_exist (Topkg_fpath.dirname file)
123123+ >>= fun _ -> Ok (op file))
124124+ |> R.reword_error @@ fun _ -> `Msg (err_no_parent op_name file)
125125+126126+ let safe_open_in_bin = with_parent_check open_in_bin "read"
127127+ let safe_open_out_bin = with_parent_check open_out_bin "write"
128128+129129+ let read file =
130130+ try
131131+ let close ic = if file = dash then () else close_in_noerr ic in
132132+ (if file = dash then Ok stdin else safe_open_in_bin file) >>= fun ic ->
133133+ try
134134+ let len = in_channel_length ic in
135135+ let buf = Bytes.create len in
136136+ really_input ic buf 0 len; close ic;
137137+ Ok (Bytes.unsafe_to_string buf)
138138+ with exn -> close ic; raise exn
139139+ with Sys_error e -> R.error_msgf "%s: %s" file e
140140+141141+ let write file s =
142142+ try
143143+ let close oc = if file = dash then () else close_out_noerr oc in
144144+ (if file = dash then Ok stdout else safe_open_out_bin file) >>= fun oc ->
145145+ try output_string oc s; flush oc; close oc; Ok ()
146146+ with exn -> close oc; raise exn
147147+ with Sys_error e -> R.error_msgf "%s: %s" file e
148148+149149+ let write_subst file vars s = (* very ugly mister, too lazy to rewrite *)
150150+ try
151151+ let close oc = if file = dash then () else close_out_noerr oc in
152152+ (if file = dash then Ok stdout else safe_open_out_bin file) >>= fun oc ->
153153+ try
154154+ let start = ref 0 in
155155+ let last = ref 0 in
156156+ let len = String.length s in
157157+ while (!last < len - 4) do
158158+ if not (s.[!last] = '%' && s.[!last + 1] = '%') then incr last else
159159+ begin
160160+ let start_subst = !last in
161161+ let last_id = ref (!last + 2) in
162162+ let stop = ref false in
163163+ while (!last_id < len - 1 && not !stop) do
164164+ if not (s.[!last_id] = '%' && s.[!last_id + 1] = '%') then begin
165165+ if s.[!last_id] <> ' ' then (incr last_id) else
166166+ (stop := true; last := !last_id)
167167+ end else begin
168168+ let id_start = start_subst + 2 in
169169+ let id = String.sub s (id_start) (!last_id - id_start) in
170170+ try
171171+ let subst = List.assoc id vars in
172172+ output oc (Bytes.unsafe_of_string s)
173173+ !start (start_subst - !start);
174174+ output_string oc subst;
175175+ stop := true;
176176+ start := !last_id + 2;
177177+ last := !last_id + 2;
178178+ with Not_found ->
179179+ stop := true;
180180+ last := !last_id
181181+ end
182182+ done;
183183+ (* we exited the loop because we reached eof *)
184184+ if not !stop then last := !last_id
185185+ end
186186+ done;
187187+ output oc (Bytes.unsafe_of_string s) !start (len - !start);
188188+ flush oc;
189189+ close oc;
190190+ Ok ()
191191+ with exn -> close oc; raise exn
192192+ with Sys_error e -> R.error_msgf "%s: %s" file e
193193+194194+ let tmp () =
195195+ try
196196+ let f = Filename.temp_file (Filename.basename Sys.argv.(0)) "topkg" in
197197+ at_exit (fun () -> ignore (delete f)); Ok f
198198+ with Sys_error e -> R.error_msg e
199199+end
200200+201201+(* Running commands *)
202202+203203+module Cmd = struct
204204+205205+ let err_empty_line = "no command, empty command line"
206206+207207+ let line ?stdout ?stderr cmd =
208208+ let strf = Printf.sprintf in
209209+ if Topkg_cmd.is_empty cmd then failwith err_empty_line else
210210+ let cmd = List.rev_map Filename.quote (Topkg_cmd.to_rev_list cmd) in
211211+ let cmd = String.concat " " cmd in
212212+ let redirect fd f = strf " %d>%s" fd (Filename.quote f) in
213213+ let stdout = match stdout with None -> "" | Some f -> redirect 1 f in
214214+ let stderr = match stderr with None -> "" | Some f -> redirect 2 f in
215215+ let win_quote = if Sys.win32 then "\"" else "" in
216216+ strf "%s%s%s%s%s" win_quote cmd stdout stderr win_quote
217217+218218+ let exec ?stdout ?stderr cmd =
219219+ try
220220+ let line = line ?stdout ?stderr cmd in
221221+ Topkg_log.debug (fun m -> m ~header:"EXEC" "@[<1>[%s]@]" line);
222222+ Ok ((), (cmd, `Exited (Sys.command line)))
223223+ with Sys_error e | Failure e -> R.error_msg e
224224+225225+ (* Command existence *)
226226+227227+ let test_cmd = match Sys.os_type with
228228+ | "Win32" -> Topkg_cmd.v "where"
229229+ | _ -> Topkg_cmd.(v "command" % "-v")
230230+231231+ let cmd_bin cmd =
232232+ try List.hd (Topkg_cmd.to_list cmd) with
233233+ | Failure _ -> failwith err_empty_line
234234+235235+ let exists cmd =
236236+ try
237237+ let bin = cmd_bin cmd in
238238+ let cmd = Topkg_cmd.(test_cmd % bin) in
239239+ match exec ~stdout:File.null ~stderr:File.null cmd with
240240+ | Ok (_, (_, `Exited 0)) -> Ok true
241241+ | Ok _ -> Ok false
242242+ | Error _ as e -> e
243243+ with
244244+ Failure e -> R.error_msg e
245245+246246+ let must_exist cmd = exists cmd >>= function
247247+ | false -> R.error_msgf "%s: no such command" (cmd_bin cmd)
248248+ | true -> Ok cmd
249249+250250+ (* Running commands *)
251251+252252+ type run_status = Topkg_cmd.t * [ `Exited of int ]
253253+254254+ let success r = r >>= function
255255+ | (v, (_, `Exited 0)) -> Ok v
256256+ | (v, (cmd, `Exited c)) ->
257257+ R.error_msgf "cmd %a: exited with %d" Topkg_cmd.dump cmd c
258258+259259+ let run ?err:stderr cmd = exec ?stderr cmd |> success
260260+ let run_status ?err:stderr cmd =
261261+ exec ?stderr cmd >>= function ((), (_, st)) -> Ok st
262262+263263+ type run_out = { cmd : Topkg_cmd.t; err : Topkg_fpath.t option }
264264+265265+ let out_string ?(trim = true) o =
266266+ File.tmp ()
267267+ >>= fun file -> exec ?stderr:o.err ~stdout:file o.cmd
268268+ >>= fun ((), st) -> File.read file
269269+ >>= fun out -> Ok ((if trim then String.trim out else out), st)
270270+271271+ let out_lines ?trim o =
272272+ out_string ?trim o >>= function (s, st) ->
273273+ Ok ((if s = "" then [] else Topkg_string.cuts ~sep:'\n' s), st)
274274+275275+ let out_file stdout o = exec ?stderr:o.err ~stdout o.cmd
276276+ let out_stdout o = exec ?stderr:o.err ?stdout:None o.cmd
277277+278278+ let to_string ?trim o = out_string ?trim o |> success
279279+ let to_lines ?trim o = out_lines ?trim o |> success
280280+ let to_file stdout o = out_file stdout o |> success
281281+ let run_out ?err cmd = { cmd; err }
282282+end
+73
vendor/opam/topkg/src/topkg_os.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** OS interaction.
77+88+ Abridged [bos]. See {!Topkg.OS} for documentation. *)
99+1010+(** {1 OS} *)
1111+1212+open Topkg_result
1313+1414+module Env : sig
1515+ val var : string -> string option
1616+ val opt_var : string -> absent:string -> string
1717+end
1818+1919+module File : sig
2020+ val null : Topkg_fpath.t
2121+ val dash : Topkg_fpath.t
2222+2323+ val exists : Topkg_fpath.t -> bool result
2424+ val must_exist : Topkg_fpath.t -> Topkg_fpath.t result
2525+ val delete : ?must_exist:bool -> Topkg_fpath.t -> unit result
2626+2727+ val fold :
2828+ ?skip:(Topkg_fpath.t -> bool) -> (Topkg_fpath.t -> 'a -> 'a) ->
2929+ 'a -> Topkg_fpath.t list -> 'a result
3030+3131+ val read : Topkg_fpath.t -> string result
3232+ val write : Topkg_fpath.t -> string -> unit result
3333+ val write_subst :
3434+ Topkg_fpath.t -> (string * string) list -> string -> unit result
3535+3636+ val tmp : unit -> Topkg_fpath.t result
3737+end
3838+3939+module Dir : sig
4040+ val exists : Topkg_fpath.t -> bool result
4141+ val must_exist : Topkg_fpath.t -> Topkg_fpath.t result
4242+4343+ val current : unit -> Topkg_fpath.t result
4444+ val set_current : Topkg_fpath.t -> unit result
4545+ val with_current : Topkg_fpath.t -> ('a -> 'b) -> 'a -> 'b result
4646+4747+ val contents :
4848+ ?dotfiles:bool -> ?rel:bool -> Topkg_fpath.t -> Topkg_fpath.t list result
4949+end
5050+5151+module Cmd : sig
5252+ val exists : Topkg_cmd.t -> bool result
5353+ val must_exist : Topkg_cmd.t -> Topkg_cmd.t result
5454+5555+ val run : ?err:Topkg_fpath.t -> Topkg_cmd.t -> unit result
5656+ val run_status : ?err:Topkg_fpath.t -> Topkg_cmd.t -> [`Exited of int] result
5757+5858+5959+ type run_status = Topkg_cmd.t * [`Exited of int ]
6060+ val success : ('a * run_status) result -> 'a result
6161+6262+ type run_out
6363+6464+ val out_string : ?trim:bool -> run_out -> (string * run_status) result
6565+ val out_lines : ?trim:bool -> run_out -> (string list * run_status) result
6666+ val out_file : Topkg_fpath.t -> run_out -> (unit * run_status) result
6767+ val out_stdout : run_out -> (unit * run_status) result
6868+6969+ val to_string : ?trim:bool -> run_out -> string result
7070+ val to_lines : ?trim:bool -> run_out -> string list result
7171+ val to_file : Topkg_fpath.t -> run_out -> unit result
7272+ val run_out : ?err:Topkg_fpath.t -> Topkg_cmd.t -> run_out
7373+end
+325
vendor/opam/topkg/src/topkg_pkg.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+open Topkg_result
77+88+type std_file = Topkg_fpath.t * bool
99+let std_file ?(install = true) file = file, install
1010+1111+type meta_file = std_file * bool
1212+let meta_file ?(lint = true) ?install file =
1313+ std_file ?install file, lint
1414+1515+type opam_file = std_file * bool * string list option
1616+let opam_file ?(lint = true) ?(lint_deps_excluding = Some []) ?install file =
1717+ std_file ?install file, lint, lint_deps_excluding
1818+1919+let std_files_installs readmes licenses change_logs metas opams =
2020+ let add field (p, install) acc = if install then field p :: acc else acc in
2121+ List.fold_right (add Topkg_install.doc) readmes @@
2222+ List.fold_right (add Topkg_install.doc) licenses @@
2323+ List.fold_right (add Topkg_install.doc) change_logs @@
2424+ List.fold_right (fun (m, _) -> add Topkg_install.lib m) metas @@
2525+ List.fold_right (fun (o, _, _) -> add Topkg_install.lib o) opams @@
2626+ []
2727+2828+type t =
2929+ { name : string;
3030+ delegate : Topkg_cmd.t option;
3131+ readmes : std_file list;
3232+ licenses : std_file list;
3333+ change_logs : std_file list;
3434+ metas : meta_file list;
3535+ opams : opam_file list;
3636+ lint_files : Topkg_fpath.t list option;
3737+ lint_custom :(unit -> R.msg result list) option;
3838+ distrib : Topkg_distrib.t;
3939+ publish : Topkg_publish.t;
4040+ build : Topkg_build.t;
4141+ installs : Topkg_conf.t -> Topkg_install.t list result; }
4242+4343+let v
4444+ ?delegate
4545+ ?(readmes = [("README.md", true)])
4646+ ?(licenses = [("LICENSE.md", true)])
4747+ ?(change_logs = [("CHANGES.md", true)])
4848+ ?(metas = [meta_file "pkg/META"])
4949+ ?(opams = [opam_file "opam"])
5050+ ?(lint_files = Some [])
5151+ ?lint_custom
5252+ ?(distrib = Topkg_distrib.v ())
5353+ ?(publish = Topkg_publish.v ())
5454+ ?(build = Topkg_build.v ())
5555+ name installs
5656+ =
5757+ { name; delegate; readmes; licenses; change_logs; metas; opams; lint_files;
5858+ lint_custom; distrib; publish; build; installs }
5959+6060+let empty = v "" (fun _ -> Ok [])
6161+let with_name_and_build_dir ?name ?build_dir p =
6262+ let name = match name with None -> p.name | Some n -> n in
6363+ let build = match build_dir with
6464+ | None -> p.build
6565+ | Some build_dir -> Topkg_build.with_dir p.build build_dir
6666+ in
6767+ { p with name; build }
6868+6969+let name p = p.name
7070+let delegate p = p.delegate
7171+let readmes p = List.map fst p.readmes
7272+let change_logs p = List.map fst p.change_logs
7373+let licenses p = List.map fst p.licenses
7474+let distrib p = p.distrib
7575+let build p = p.build
7676+let install p c =
7777+ p.installs c >>= fun installs ->
7878+ let std_files =
7979+ std_files_installs p.readmes p.licenses p.change_logs p.metas p.opams
8080+ in
8181+ Ok (List.rev_append std_files installs)
8282+8383+let std_files p =
8484+ List.map fst p.readmes @ List.map fst p.licenses @
8585+ List.map fst p.change_logs @
8686+ List.(rev_append (rev_map (fun (m, _) -> fst m) p.metas)
8787+ (rev (rev_map (fun (o, _, _) -> fst o) p.opams)))
8888+8989+let build_dir p = Topkg_build.dir p.build
9090+let opam ~name p =
9191+ let has_name (o, _, _) = Topkg_fpath.(basename @@ rem_ext @@ fst o) = name in
9292+ let opams = p.opams in
9393+ match try Some (List.find has_name opams) with Not_found -> None with
9494+ | Some (opam, _, _) -> fst opam
9595+ | None ->
9696+ if name <> p.name then
9797+ Topkg_log.warn
9898+ (fun m -> m "No opam file for %s, using 'opam'" p.name);
9999+ "opam"
100100+101101+let codec =
102102+ let stub _ = invalid_arg "not executable outside package definition" in
103103+ let string_list_option = Topkg_codec.(option @@ list string) in
104104+ let std_file = Topkg_codec.(pair string bool) in
105105+ let meta_file = Topkg_codec.(pair std_file bool) in
106106+ let opam_file = Topkg_codec.(t3 std_file bool (string_list_option)) in
107107+ (* fields *)
108108+ let name = Topkg_codec.(with_kind "name" @@ string) in
109109+ let delegate = Topkg_codec.(with_kind "delegate" @@ option cmd) in
110110+ let readmes = Topkg_codec.(with_kind "readmes" @@ list std_file) in
111111+ let licenses = Topkg_codec.(with_kind "licenses" @@ list std_file) in
112112+ let change_logs = Topkg_codec.(with_kind "change_logs" @@ list std_file) in
113113+ let metas = Topkg_codec.(with_kind "metas" @@ list meta_file) in
114114+ let opams = Topkg_codec.(with_kind "opams" @@ list opam_file) in
115115+ let lint_files = Topkg_codec.(with_kind "lint_files" @@ string_list_option) in
116116+ let lint_custom =
117117+ let kind = "lint_custom" in
118118+ let enc = function None -> "\x00" | Some _ -> "\x01" in
119119+ let dec = function
120120+ | "\x00" -> None | "\x01" -> Some stub | s -> Topkg_codec.err ~kind s in
121121+ Topkg_codec.v ~kind ~enc ~dec
122122+ in
123123+ let distrib = Topkg_distrib.codec in
124124+ let publish = Topkg_publish.codec in
125125+ let build = Topkg_build.codec in
126126+ let fields =
127127+ (fun p -> (p.name, p.delegate, p.readmes, p.licenses, p.change_logs),
128128+ (p.metas, p.opams, p.lint_files, p.lint_custom, p.distrib),
129129+ (p.publish, p.build)),
130130+ (fun ((name, delegate, readmes, licenses, change_logs),
131131+ (metas, opams, lint_files, lint_custom, distrib),
132132+ (publish, build)) ->
133133+ { name; delegate; readmes; licenses; change_logs;
134134+ metas; opams; lint_files; lint_custom; distrib;
135135+ publish; build; installs = stub })
136136+ in
137137+ Topkg_codec.version 0 @@
138138+ Topkg_codec.(view ~kind: "package" fields
139139+ (t3
140140+ (t5 name delegate readmes licenses change_logs)
141141+ (t5 metas opams lint_files lint_custom distrib)
142142+ (t2 publish build)))
143143+(* Distrib *)
144144+145145+let distrib_version_opam_files p ~version ~opam_adds =
146146+ let version = Topkg_string.drop_initial_v version in
147147+ let version_opam_file acc ((file, _), _, _) =
148148+ acc
149149+ >>= fun () -> Topkg_os.File.read file
150150+ >>= fun o -> Ok (Topkg_string.strf "version: \"%s\"\n%s%s"
151151+ version opam_adds o)
152152+ >>= fun o -> Topkg_os.File.write file o
153153+ in
154154+ List.fold_left version_opam_file (Ok ()) p.opams
155155+156156+let distrib_uri p = Topkg_distrib.uri p.distrib
157157+let distrib_prepare p ~dist_build_dir ~name ~version ~opam ~opam_adds =
158158+ let d = distrib p in
159159+ let ws = Topkg_distrib.watermarks d in
160160+ let ws_defs = Topkg_distrib.define_watermarks ws ~name ~version ~opam in
161161+ Topkg_os.Dir.set_current dist_build_dir
162162+ >>= fun () -> Topkg_distrib.files_to_watermark d ()
163163+ >>= fun files -> Topkg_distrib.watermark_files ws_defs files
164164+ >>= fun () -> distrib_version_opam_files p ~version ~opam_adds
165165+ >>= fun () -> Topkg_distrib.massage d ()
166166+ >>= fun () -> Topkg_distrib.exclude_paths d ()
167167+168168+let distrib_prepare_pin p =
169169+ let name = name p in
170170+ let opam = opam ~name p in
171171+ let d = distrib p in
172172+ let ws = Topkg_distrib.watermarks d in
173173+ Topkg_vcs.get ()
174174+ >>= fun repo -> Topkg_vcs.describe ~dirty:true repo
175175+ >>= fun version -> Ok(Topkg_distrib.define_watermarks ws ~name ~version ~opam)
176176+ >>= fun ws_defs -> Topkg_distrib.files_to_watermark d ()
177177+ >>= fun files -> Topkg_distrib.watermark_files ws_defs files
178178+ >>= fun () -> distrib_version_opam_files p ~version ~opam_adds:""
179179+ >>= fun () -> Topkg_distrib.massage d ()
180180+181181+(* Publish *)
182182+183183+let publish_artefacts p = Topkg_publish.artefacts p.publish
184184+185185+(* Test *)
186186+187187+let tests_file p =
188188+ Topkg_fpath.(build_dir p // Topkg_string.strf "_topkg-%s.tests" (name p))
189189+190190+let tests_file_codec = Topkg_codec.(option @@ list Topkg_test.codec)
191191+let write_tests_file p tests =
192192+ Topkg_codec.write (tests_file p) tests_file_codec tests
193193+194194+let tests_run tests ~args =
195195+ let run_test root_dir acc t =
196196+ let exec = Topkg_test.exec t in
197197+ let args = match args with Some args -> args | None -> Topkg_test.args t in
198198+ let cmd = Topkg_cmd.(v Topkg_fpath.(root_dir // exec) %% args) in
199199+ let run () =
200200+ Topkg_log.info (fun m -> m "Running test %s" exec);
201201+ (Topkg_os.Cmd.run_status cmd >>| fun (`Exited c) -> c)
202202+ |> Topkg_log.on_error_msg ~use:(fun _ -> 1)
203203+ in
204204+ let res = match Topkg_test.dir t with
205205+ | None -> Ok (run ())
206206+ | Some dir -> Topkg_os.Dir.with_current dir run ()
207207+ in
208208+ acc + (res |> Topkg_log.on_error_msg ~use:(fun _ -> 1))
209209+ in
210210+ Topkg_os.Dir.current ()
211211+ >>= fun root_dir -> match List.fold_left (run_test root_dir) 0 tests with
212212+ | 0 -> Ok 0
213213+ | n -> Topkg_log.err (fun m -> m "Some tests failed."); Ok 1
214214+215215+let tests_list tests =
216216+ let list_test t =
217217+ let args = Topkg_cmd.to_list (Topkg_test.args t) in
218218+ let args = String.concat " " (List.map Filename.quote args) in
219219+ let exec = Topkg_test.exec t in
220220+ let run = if Topkg_test.run t then "[default]" else "" in
221221+ print_endline (Topkg_string.strf "%s %s %s" exec args run)
222222+ in
223223+ List.iter list_test tests
224224+225225+let tests_select queries tests = match queries with
226226+| [] -> List.filter (fun t -> Topkg_test.run t) tests
227227+| qs ->
228228+ let query q =
229229+ let add_test acc t =
230230+ let exec = Topkg_test.exec t in
231231+ if q = exec then t :: acc else
232232+ let exec = Topkg_fpath.basename exec in
233233+ if q = exec then t :: acc else
234234+ let exec = Topkg_fpath.rem_ext exec in
235235+ if q = exec then t :: acc else acc
236236+ in
237237+ match List.fold_left add_test [] tests with
238238+ | [] -> Topkg_log.warn (fun m -> m "No test matching `%s'" q); []
239239+ | acc -> List.rev acc
240240+ in
241241+ List.flatten (List.map query queries)
242242+243243+let test p ~list ~tests:queries ~args =
244244+ let tests_file = tests_file p in
245245+ Topkg_os.File.exists tests_file >>= function
246246+ | false ->
247247+ R.error_msgf "%s: no such file. Did you forget to build the package ?"
248248+ tests_file
249249+ | true ->
250250+ Topkg_codec.read tests_file tests_file_codec
251251+ >>= function
252252+ | None ->
253253+ R.error_msgf "The tests were not built."
254254+ | Some tests ->
255255+ match list with
256256+ | true -> tests_list tests; Ok 0
257257+ | false ->
258258+ let no_test = format_of_string "No tests to run." in
259259+ match tests_select queries tests with
260260+ | [] when queries = [] -> Topkg_log.app (fun m -> m no_test); Ok 0
261261+ | [] -> Topkg_log.err (fun m -> m no_test); Ok 1
262262+ | tests -> tests_run tests ~args
263263+264264+(* Build *)
265265+266266+let install_file p = p.name ^ ".install"
267267+268268+let write_opam_install_file p install =
269269+ let file = install_file p in
270270+ let install = Topkg_opam.Install.to_string install in
271271+ Topkg_os.File.write file install
272272+ >>| fun () -> Topkg_log.info (fun m -> m "Wrote opam install file %s" file)
273273+274274+let run_build_hook kind hook c =
275275+ (hook c)
276276+ |> R.reword_error_msg ~replace:true
277277+ (fun e -> R.msgf "%s-build hook failed: %s" kind e)
278278+279279+let build p ~kind c os = match kind with
280280+| `Raw args -> (Topkg_build.cmd p.build) c os args >>= fun () -> Ok 0
281281+| `Dry_run | `Build ->
282282+ install p c
283283+ >>= fun is -> Ok (Topkg_install.to_build ~header:p.name c os is)
284284+ >>= fun (targets, install, tests) -> match kind = `Dry_run with
285285+ | true -> write_opam_install_file p install >>= fun () -> Ok 0
286286+ | false ->
287287+ let is_pin = Topkg_conf.build_context c = `Pin in
288288+ let prepare = match Topkg_build.prepare_on_pin p.build && is_pin with
289289+ | false -> Ok ()
290290+ | true ->
291291+ (distrib_prepare_pin p)
292292+ |> R.reword_error_msg ~replace:true
293293+ (fun e -> R.msgf "Pin distribution preparation failed: %s" e)
294294+ in
295295+ prepare
296296+ >>= fun () -> run_build_hook "Pre" (Topkg_build.pre p.build) c
297297+ >>= fun () -> (Topkg_build.cmd p.build) c os targets
298298+ >>= fun () -> write_opam_install_file p install
299299+ >>= fun () -> write_tests_file p tests
300300+ >>= fun () -> run_build_hook "Post" (Topkg_build.post p.build) c
301301+ >>= fun () -> Ok 0
302302+303303+(* Clean *)
304304+305305+let clean p os =
306306+ let file = install_file p in
307307+ let build_dir = build_dir p in
308308+ Topkg_os.File.delete file
309309+ >>= fun () -> Topkg_os.File.delete "pkg.itarget"
310310+ >>= fun () -> (Topkg_build.clean p.build) os ~build_dir
311311+ >>= fun () -> Ok 0
312312+313313+(* Lint *)
314314+315315+let lint_custom p = p.lint_custom
316316+317317+let lint_files p = match p.lint_files with
318318+| None (* disabled *) -> None
319319+| Some fs -> Some (List.rev_append (std_files p) fs)
320320+321321+let lint_metas p =
322322+ List.map (fun ((p, _), lint) -> (p, lint)) p.metas
323323+324324+let lint_opams p =
325325+ List.map (fun ((p, _), lint, lint_deps) -> (p, lint, lint_deps)) p.opams
+89
vendor/opam/topkg/src/topkg_pkg.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** Package descriptions. *)
77+88+(** {1 Package} *)
99+1010+open Topkg_result
1111+1212+type std_file
1313+val std_file : ?install:bool -> Topkg_fpath.t -> std_file
1414+1515+type meta_file
1616+val meta_file : ?lint:bool -> ?install:bool -> Topkg_fpath.t -> meta_file
1717+1818+type opam_file
1919+val opam_file :
2020+ ?lint:bool -> ?lint_deps_excluding:string list option -> ?install:bool ->
2121+ Topkg_fpath.t -> opam_file
2222+2323+type t
2424+2525+val empty : t
2626+val with_name_and_build_dir :
2727+ ?name:string -> ?build_dir:Topkg_fpath.t -> t -> t
2828+2929+val v :
3030+ ?delegate:Topkg_cmd.t ->
3131+ ?readmes:std_file list ->
3232+ ?licenses:std_file list ->
3333+ ?change_logs:std_file list ->
3434+ ?metas:meta_file list ->
3535+ ?opams:opam_file list ->
3636+ ?lint_files:Topkg_fpath.t list option ->
3737+ ?lint_custom:(unit -> R.msg result list) ->
3838+ ?distrib:Topkg_distrib.t ->
3939+ ?publish:Topkg_publish.t ->
4040+ ?build:Topkg_build.t -> string ->
4141+ (Topkg_conf.t -> Topkg_install.t list result) -> t
4242+4343+val name : t -> string
4444+val delegate : t -> Topkg_cmd.t option
4545+val readmes : t -> Topkg_fpath.t list
4646+val change_logs : t -> Topkg_fpath.t list
4747+val licenses : t -> Topkg_fpath.t list
4848+val distrib : t -> Topkg_distrib.t
4949+val build : t -> Topkg_build.t
5050+val install : t -> Topkg_conf.t -> Topkg_install.t list result
5151+val codec : t Topkg_codec.t
5252+5353+(* Derived accessors *)
5454+5555+val build_dir : t -> Topkg_fpath.t
5656+val opam : name:string -> t -> Topkg_fpath.t
5757+5858+(* Distrib *)
5959+6060+val distrib_uri : t -> string option
6161+val distrib_prepare :
6262+ t -> dist_build_dir:Topkg_fpath.t -> name:string -> version:string ->
6363+ opam:Topkg_fpath.t -> opam_adds:string -> Topkg_fpath.t list result
6464+6565+(* Publish *)
6666+6767+val publish_artefacts : t -> [ `Distrib | `Doc | `Alt of string ] list
6868+6969+(* Test *)
7070+7171+val test :
7272+ t -> list:bool -> tests:string list -> args:Topkg_cmd.t option -> int result
7373+7474+(* Build *)
7575+7676+val build :
7777+ t -> kind:[`Build | `Dry_run | `Raw of string list ] ->
7878+ Topkg_conf.t -> Topkg_conf.os -> int result
7979+8080+(* Clean *)
8181+8282+val clean : t -> Topkg_conf.os -> int result
8383+8484+(* Lint *)
8585+8686+val lint_custom : t -> (unit -> R.msg result list) option
8787+val lint_files : t -> Topkg_fpath.t list option
8888+val lint_metas : t -> (Topkg_fpath.t * bool) list
8989+val lint_opams : t -> (Topkg_fpath.t * bool * string list option) list
+31
vendor/opam/topkg/src/topkg_publish.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+open Topkg_result
77+88+type artefact = [`Distrib | `Doc | `Alt of string]
99+type t = { artefacts : artefact list }
1010+1111+let v ?(artefacts = [`Doc; `Distrib]) () = { artefacts }
1212+let artefacts p = p.artefacts
1313+let codec_artefact =
1414+ let tag = function `Distrib -> 0 | `Doc -> 1 | `Alt _ -> 2 in
1515+ let codecs =
1616+ let alt_case =
1717+ ((function `Alt s -> s | _ -> assert false),
1818+ (function s -> `Alt s))
1919+ in
2020+ Topkg_codec.([| const `Distrib; const `Doc; view alt_case string |])
2121+ in
2222+ Topkg_codec.alt ~kind:"artefact" tag codecs
2323+2424+let codec =
2525+ let artefacts = Topkg_codec.(list codec_artefact) in
2626+ let fields =
2727+ (fun p -> p.artefacts),
2828+ (fun artefacts -> { artefacts })
2929+ in
3030+ Topkg_codec.version 0 @@
3131+ Topkg_codec.(view ~kind:"publish" fields artefacts)
+21
vendor/opam/topkg/src/topkg_publish.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** {1 Distribution publication description}
77+88+ See {!section:Topkg.Pkg.publish}. *)
99+1010+(** {1 Distribution} *)
1111+1212+open Topkg_result
1313+1414+(* Publication *)
1515+1616+type artefact = [`Distrib | `Doc | `Alt of string]
1717+type t
1818+1919+val v : ?artefacts:artefact list -> unit -> t
2020+val artefacts : t -> artefact list
2121+val codec : t Topkg_codec.t
+33
vendor/opam/topkg/src/topkg_result.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+let ( >>= ) v f = match v with Ok v -> f v | Error _ as e -> e
77+let ( >>| ) v f = match v with Ok v -> Ok (f v) | Error _ as e -> e
88+99+type ('a, 'b) r = ('a, 'b) result = Ok of 'a | Error of 'b
1010+type 'a result = ('a, [`Msg of string]) r
1111+1212+module R = struct
1313+ type msg = [`Msg of string ]
1414+1515+ let msgf fmt =
1616+ let kmsg _ = `Msg (Format.flush_str_formatter ()) in
1717+ Format.kfprintf kmsg Format.str_formatter fmt
1818+1919+ let reword_error reword = function
2020+ | Ok _ as r -> r
2121+ | Error e -> Error (reword e)
2222+2323+ let error_msg m = Error (`Msg m)
2424+ let error_msgf fmt =
2525+ let kerr _ = Error (`Msg (Format.flush_str_formatter ())) in
2626+ Format.kfprintf kerr Format.str_formatter fmt
2727+2828+ let reword_error_msg ?(replace = false) reword = function
2929+ | Ok _ as r -> r
3030+ | Error (`Msg e) ->
3131+ let (`Msg e' as v) = reword e in
3232+ if replace then Error v else error_msgf "%s\n%s" e e'
3333+end
+29
vendor/opam/topkg/src/topkg_result.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** Results
77+88+ Abbridged [rresult]. See {!section:Topkg.prels} for documention. *)
99+1010+val ( >>= ) : ('a, 'b) result -> ('a -> ('c, 'b) result) -> ('c, 'b) result
1111+val ( >>| ) : ('a, 'b) result -> ('a -> 'c) -> ('c, 'b) result
1212+1313+type ('a, 'b) r = ('a, 'b) result = Ok of 'a | Error of 'b
1414+type 'a result = ('a, [ `Msg of string]) r
1515+1616+module R : sig
1717+ val reword_error : ('b -> 'c) -> ('a, 'b) r -> ('a, 'c) r
1818+1919+ type msg = [ `Msg of string ]
2020+2121+ val msgf : ('a, Format.formatter, unit, [> msg]) format4 -> 'a
2222+2323+ val error_msg : string -> ('b, [> msg]) r
2424+ val error_msgf :
2525+ ('a, Format.formatter, unit, ('b, [> msg]) r) format4 -> 'a
2626+2727+ val reword_error_msg :
2828+ ?replace:bool -> (string -> msg) -> ('a, msg) r -> ('a, [> msg]) r
2929+end
+154
vendor/opam/topkg/src/topkg_string.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+let strf = Format.asprintf
77+88+include String
99+1010+let head s = if s = "" then None else Some s.[0]
1111+1212+(* Predicates *)
1313+1414+let is_prefix ~affix s =
1515+ let len_a = length affix in
1616+ let len_s = length s in
1717+ if len_a > len_s then false else
1818+ let max_idx_a = len_a - 1 in
1919+ let rec loop i =
2020+ if i > max_idx_a then true else
2121+ if unsafe_get affix i <> unsafe_get s i then false else loop (i + 1)
2222+ in
2323+ loop 0
2424+2525+let is_suffix ~affix s =
2626+ let max_idx_a = length affix - 1 in
2727+ let max_idx_s = length s - 1 in
2828+ if max_idx_a > max_idx_s then false else
2929+ let rec loop i =
3030+ if i > max_idx_a then true else
3131+ if unsafe_get affix (max_idx_a - i) <> unsafe_get s (max_idx_s - i)
3232+ then false
3333+ else loop (i + 1)
3434+ in
3535+ loop 0
3636+3737+let for_all sat s =
3838+ let max_idx = length s - 1 in
3939+ let rec loop i =
4040+ if i > max_idx then true else
4141+ if sat (unsafe_get s i) then loop (i + 1) else false
4242+ in
4343+ loop 0
4444+4545+let exists sat s =
4646+ let max_idx = length s - 1 in
4747+ let rec loop i =
4848+ if i > max_idx then false else
4949+ if sat (unsafe_get s i) then true else loop (i + 1)
5050+ in
5151+ loop 0
5252+5353+(* Traversing *)
5454+5555+let find_byte ?(start = 0) c s =
5656+ let max = String.length s - 1 in
5757+ if start > max then None else
5858+ try Some (String.index_from s start c) with Not_found -> None
5959+6060+(* Extracting substrings *)
6161+6262+let with_index_range ?(first = 0) ?last s =
6363+ let max = String.length s - 1 in
6464+ let last = match last with
6565+ | None -> max
6666+ | Some l when l > max -> max
6767+ | Some l -> l
6868+ in
6969+ let first = if first < 0 then 0 else first in
7070+ if first > last then "" else
7171+ String.sub s first (last - first + 1)
7272+7373+let cut ?(rev = false) ~sep s =
7474+ let find_index = if rev then String.rindex else String.index in
7575+ match try Some (find_index s sep) with Not_found -> None with
7676+ | None -> None
7777+ | Some i ->
7878+ Some (String.sub s 0 i, String.sub s (i+1) (String.length s - i - 1))
7979+8080+let cuts ?(empty = true) ~sep s =
8181+ let no_empty = not empty in
8282+ let rec loop acc s = match cut ~sep s with
8383+ | Some (v, vs) -> loop (if no_empty && v = "" then acc else (v :: acc)) vs
8484+ | None -> List.rev (if no_empty && s = "" then acc else (s :: acc))
8585+ in
8686+ loop [] s
8787+8888+(* Version strings *)
8989+9090+let parse_version v =
9191+ let version =
9292+ if is_prefix ~affix:"v" v then with_index_range ~first:1 v else v
9393+ in
9494+ let cut_left_plus_or_tilde s =
9595+ let cut = match String.index_opt s '+', String.index_opt s '~' with
9696+ | None, None -> None
9797+ | (Some _ as i), None | None, (Some _ as i) -> i
9898+ | Some i, Some i' -> Some (if i < i' then i else i')
9999+ in
100100+ match cut with
101101+ | None -> None
102102+ | Some i ->
103103+ Some (with_index_range ~last:(i - 1) s, with_index_range ~first:i s)
104104+ in
105105+ try match cut ~sep:'.' version with
106106+ | None -> None
107107+ | Some (maj, rest) ->
108108+ let maj = int_of_string maj in
109109+ match cut ~sep:'.' rest with
110110+ | None ->
111111+ begin match cut_left_plus_or_tilde rest with
112112+ | None -> Some (maj, int_of_string rest, 0, None)
113113+ | Some (min, i) -> Some (maj, int_of_string min, 0, Some i)
114114+ end
115115+ | Some (min, rest) ->
116116+ let min = int_of_string min in
117117+ begin match cut_left_plus_or_tilde rest with
118118+ | None -> Some (maj, min, int_of_string rest, None)
119119+ | Some (p, i) -> Some (maj, min, int_of_string p, Some i)
120120+ end
121121+ with
122122+ | Failure _ -> None
123123+124124+let drop_initial_v version = match head version with
125125+| Some ('v' | 'V') -> with_index_range ~first:1 version
126126+| None | Some _ -> version
127127+128128+(* Formatters *)
129129+130130+let pp_text ppf s = (* was c&p from Fmt, pp_print_text is 4.02 *)
131131+ let is_nl_or_sp c = c = '\n' || c = ' ' in
132132+ let rec stop_at sat ~start ~max s =
133133+ if start > max then start else
134134+ if sat s.[start] then start else
135135+ stop_at sat ~start:(start + 1) ~max s
136136+ in
137137+ let sub s start stop ~max =
138138+ if start = stop then "" else
139139+ if start = 0 && stop > max then s else
140140+ String.sub s start (stop - start)
141141+ in
142142+ let max = String.length s - 1 in
143143+ let rec loop start s = match stop_at is_nl_or_sp ~start ~max s with
144144+ | stop when stop > max -> Format.pp_print_string ppf (sub s start stop ~max)
145145+ | stop ->
146146+ Format.pp_print_string ppf (sub s start stop ~max);
147147+ begin match s.[stop] with
148148+ | ' ' -> Format.pp_print_space ppf ()
149149+ | '\n' -> Format.pp_force_newline ppf ()
150150+ | _ -> assert false
151151+ end;
152152+ loop (stop + 1) s
153153+ in
154154+ loop 0 s
+34
vendor/opam/topkg/src/topkg_string.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** Strings.
77+88+ See {!Topkg.String} for documentation. *)
99+1010+val strf : ('a, Format.formatter, unit, string) format4 -> 'a
1111+1212+include module type of String
1313+1414+val head : string -> char option
1515+1616+val is_prefix : affix:string -> string -> bool
1717+val is_suffix : affix:string -> string -> bool
1818+val for_all : (char -> bool) -> string -> bool
1919+val exists : (char -> bool) -> string -> bool
2020+2121+val find_byte : ?start:int -> char -> string -> int option
2222+2323+val trim : string -> string
2424+val cut : ?rev:bool -> sep:char -> string -> (string * string) option
2525+val cuts : ?empty:bool -> sep:char -> string -> string list
2626+2727+val with_index_range : ?first:int -> ?last:int -> string -> string
2828+2929+val uppercase_ascii : string -> string
3030+3131+val parse_version : string -> (int * int * int * string option) option
3232+val drop_initial_v : string -> string
3333+3434+val pp_text : Format.formatter -> string -> unit
+22
vendor/opam/topkg/src/topkg_test.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+type t =
77+ { exec : Topkg_fpath.t;
88+ args : Topkg_cmd.t;
99+ run : bool;
1010+ dir : Topkg_fpath.t option; }
1111+1212+let v exec ~args ~run ~dir = { exec; args; run; dir }
1313+let exec t = t.exec
1414+let args t = t.args
1515+let run t = t.run
1616+let dir t = t.dir
1717+let codec =
1818+ let fields =
1919+ (fun t -> (t.exec, t.args, t.run, t.dir)),
2020+ (fun (exec, args, run, dir) -> { exec; args; run; dir})
2121+ in
2222+ Topkg_codec.(view ~kind:"test" fields (t4 fpath cmd bool (option fpath)))
+18
vendor/opam/topkg/src/topkg_test.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** Topkg test description. *)
77+88+type t
99+1010+val v :
1111+ Topkg_fpath.t -> args:Topkg_cmd.t -> run:bool ->
1212+ dir:Topkg_fpath.t option -> t
1313+1414+val exec : t -> Topkg_fpath.t
1515+val args : t -> Topkg_cmd.t
1616+val run : t -> bool
1717+val dir : t -> Topkg_fpath.t option
1818+val codec : t Topkg_codec.t
+389
vendor/opam/topkg/src/topkg_vcs.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+open Topkg_result
77+88+let parse_changes lines =
99+ try
1010+ let parse_line l = match Topkg_string.cut ~sep:' ' l with
1111+ | None -> failwith (Topkg_string.strf "%S: can't parse log line" l)
1212+ | Some cut -> cut
1313+ in
1414+ Ok (List.(rev @@ rev_map parse_line lines))
1515+ with Failure msg -> Error (`Msg msg)
1616+1717+(* Version control system repositories *)
1818+1919+type commit_ish = string
2020+2121+type kind = [ `Git | `Hg ]
2222+2323+let pp_kind ppf = function
2424+| `Git -> Format.pp_print_string ppf "git"
2525+| `Hg -> Format.pp_print_string ppf "hg"
2626+2727+let dirtify id = id ^ "-dirty"
2828+2929+type t = kind * Topkg_cmd.t * Topkg_fpath.t
3030+3131+let git =
3232+ let git = Topkg_cmd.v (Topkg_os.Env.opt_var "TOPKG_GIT" ~absent:"git") in
3333+ lazy (Topkg_os.Cmd.exists git >>= fun exists -> Ok (exists, git))
3434+3535+let hg =
3636+ let hg = Topkg_cmd.v (Topkg_os.Env.opt_var "TOPKG_HG" ~absent:"hg") in
3737+ lazy (Topkg_os.Cmd.exists hg >>= fun exists -> Ok (exists, hg))
3838+3939+let vcs_cmd kind cmd dir = match kind with
4040+| `Git -> Topkg_cmd.(cmd % "--git-dir" % dir)
4141+| `Hg -> Topkg_cmd.(cmd % "--repository" % dir)
4242+4343+let v k cmd ~dir = (k, cmd, dir)
4444+let kind (k, _, _) = k
4545+let dir (_, _, dir) = dir
4646+let cmd (kind, cmd, dir) = vcs_cmd kind cmd dir
4747+4848+(* Git support *)
4949+5050+let git_work_tree (_, _, dir) =
5151+ Topkg_cmd.(v "--work-tree" % Topkg_fpath.dirname dir)
5252+5353+let find_git () = Lazy.force git >>= function
5454+| (false, _) -> Ok None
5555+| (true, git) ->
5656+ let git_dir = Topkg_cmd.(git % "rev-parse" % "--git-dir") in
5757+ Topkg_os.Cmd.(run_out ~err:Topkg_os.File.null git_dir |> out_string)
5858+ >>= function
5959+ | (dir, (_, `Exited 0)) -> Ok (Some (v `Git git ~dir))
6060+ | _ -> Ok None
6161+6262+let err_git cmd c = R.error_msgf "%a exited with %d" Topkg_cmd.dump cmd c
6363+let run_git r args out =
6464+ let git = Topkg_cmd.(cmd r %% args) in
6565+ Topkg_os.Cmd.(run_out git |> out) >>= function
6666+ | (v, (_, `Exited 0)) -> Ok v
6767+ | (_, (_, `Exited c)) -> err_git git c
6868+6969+let git_is_dirty r =
7070+ let status =
7171+ Topkg_cmd.(cmd r %% git_work_tree r % "status" % "--porcelain")
7272+ in
7373+ Topkg_os.Cmd.(run_out ~err:Topkg_os.File.null status |> out_string)
7474+ >>= function
7575+ | ("", (_, `Exited 0)) -> Ok false
7676+ | (_, (_, `Exited 0)) -> Ok true
7777+ | (_, (_, `Exited c)) -> err_git status c
7878+7979+let git_file_is_dirty r file =
8080+ let diff =
8181+ Topkg_cmd.(cmd r %% git_work_tree r % "diff-index" % "--quiet" % "HEAD" %
8282+ p file)
8383+ in
8484+ Topkg_os.Cmd.(run_status ~err:Topkg_os.File.null diff) >>= function
8585+ | `Exited 0 -> Ok false
8686+ | `Exited 1 -> Ok true
8787+ | `Exited c -> err_git diff c
8888+8989+let dirtify_if ~dirty r id = match dirty with
9090+| false -> Ok id
9191+| true ->
9292+ git_is_dirty r >>= fun is_dirty ->
9393+ Ok (if is_dirty then dirtify id else id)
9494+9595+let git_head ~dirty r =
9696+ run_git r Topkg_cmd.(v "rev-parse" % "HEAD") Topkg_os.Cmd.out_string
9797+ >>= fun id -> dirtify_if ~dirty r id
9898+9999+let git_commit_id ~dirty r commit_ish =
100100+ let dirty = dirty && commit_ish = "HEAD" in
101101+ let id = Topkg_cmd.(v "rev-parse" % "--verify" %
102102+ (commit_ish ^ "^{commit}"))
103103+ in
104104+ run_git r id Topkg_os.Cmd.out_string >>= fun id -> dirtify_if ~dirty r id
105105+106106+let git_commit_ptime_s r commit_ish =
107107+ let time = Topkg_cmd.(v "show" % "-s" % "--format=%ct" % commit_ish) in
108108+ run_git r time Topkg_os.Cmd.out_string
109109+ >>= fun ptime -> try Ok (int_of_string ptime) with
110110+ | Failure _ -> R.error_msgf "Could not parse timestamp from %S" ptime
111111+112112+let git_describe ~dirty r commit_ish =
113113+ let dirty = dirty && commit_ish = "HEAD" in
114114+ run_git r
115115+ Topkg_cmd.(git_work_tree r % "describe" % "--always" %%
116116+ on dirty (v "--dirty") %% on (not dirty) (v commit_ish))
117117+ Topkg_os.Cmd.out_string
118118+119119+let git_tags r =
120120+ run_git r Topkg_cmd.(v "tag" % "--list") Topkg_os.Cmd.out_lines
121121+122122+let git_changes r ~after ~until =
123123+ let range =
124124+ if after = "" then until else
125125+ Topkg_string.strf "%s..%s" after until
126126+ in
127127+ let changes = Topkg_cmd.(v "log" % "--oneline" % "--no-decorate" % range) in
128128+ run_git r changes Topkg_os.Cmd.out_lines
129129+ >>= fun commits -> parse_changes commits
130130+131131+let git_tracked_files r ~tree_ish =
132132+ let tracked =
133133+ Topkg_cmd.(git_work_tree r % "ls-tree" % "--name-only" % "-r" % tree_ish)
134134+ in
135135+ run_git r tracked Topkg_os.Cmd.out_lines
136136+137137+let git_clone r ~dir:d =
138138+ let clone = Topkg_cmd.(v "clone" % "--local" % (dir r) % d) in
139139+ run_git r clone Topkg_os.Cmd.out_stdout >>= fun _ -> Ok ()
140140+141141+let git_checkout r ~branch ~commit_ish =
142142+ let branch = match branch with
143143+ | None -> Topkg_cmd.empty
144144+ | Some branch -> Topkg_cmd.(v "-b" % branch)
145145+ in
146146+ run_git r Topkg_cmd.(v "checkout" % "--quiet" %% branch % commit_ish)
147147+ Topkg_os.Cmd.out_string
148148+ >>= fun _ -> Ok ()
149149+150150+let git_commit_files r ~msg files =
151151+ let msg = match msg with
152152+ | None -> Topkg_cmd.empty
153153+ | Some m -> Topkg_cmd.(v "-m" % m)
154154+ in
155155+ let files = Topkg_cmd.(of_list @@ List.map p files) in
156156+ run_git r Topkg_cmd.(v "commit" %% msg %% files) Topkg_os.Cmd.out_stdout
157157+158158+let git_tag r ~force ~sign ~msg ~commit_ish tag =
159159+ let msg = match msg with
160160+ | None -> Topkg_cmd.empty
161161+ | Some m -> Topkg_cmd.(v "-m" % m)
162162+ in
163163+ let flags = Topkg_cmd.(on force (v "-f") %% on sign (v "-s")) in
164164+ run_git r Topkg_cmd.(v "tag" % "-a" %% flags %% msg % tag % commit_ish)
165165+ Topkg_os.Cmd.out_stdout
166166+167167+let git_delete_tag r tag =
168168+ run_git r Topkg_cmd.(v "tag" % "-d" % tag) Topkg_os.Cmd.out_stdout
169169+170170+(* Hg support *)
171171+172172+let hg_rev commit_ish = match commit_ish with "HEAD" -> "tip" | c -> c
173173+174174+let find_hg () = Lazy.force hg >>= function
175175+| (false, _) -> Ok None
176176+| (true, hg) ->
177177+ let hg_root = Topkg_cmd.(hg % "root") in
178178+ Topkg_os.Cmd.(run_out ~err:Topkg_os.File.null hg_root |> out_string)
179179+ >>= function
180180+ | (dir, (_, `Exited 0)) -> Ok (Some (v `Hg hg ~dir))
181181+ | _ -> Ok None
182182+183183+let err_hg cmd c = R.error_msgf "%a exited with %d" Topkg_cmd.dump cmd c
184184+let run_hg r args out =
185185+ let hg = Topkg_cmd.(cmd r %% args) in
186186+ Topkg_os.Cmd.(run_out hg |> out) >>= function
187187+ | (v, (_, `Exited 0)) -> Ok v
188188+ | (_, (_, `Exited c)) -> err_hg hg c
189189+190190+let hg_id r ~rev =
191191+ run_hg r Topkg_cmd.(v "id" % "-i" % "--rev" % rev) Topkg_os.Cmd.out_string
192192+ >>= fun id ->
193193+ let len = String.length id in
194194+ let is_dirty = String.length id > 0 && id.[len - 1] = '+' in
195195+ let id = if is_dirty then String.sub id 0 (len - 1) else id in
196196+ Ok (id, is_dirty)
197197+198198+let hg_is_dirty r =
199199+ hg_id r ~rev:"tip" >>= function (id, is_dirty) -> Ok is_dirty
200200+201201+let hg_file_is_dirty r file =
202202+ run_hg r Topkg_cmd.(v "status" % p file) Topkg_os.Cmd.out_string >>= function
203203+ | "" -> Ok false
204204+ | _ -> Ok true
205205+206206+let hg_head ~dirty r =
207207+ hg_id r ~rev:"tip" >>= function (id, is_dirty) ->
208208+ Ok (if is_dirty && dirty then dirtify id else id)
209209+210210+let hg_commit_id ~dirty r ~rev =
211211+ hg_id r ~rev >>= fun (id, is_dirty) ->
212212+ Ok (if is_dirty && dirty then dirtify id else id)
213213+214214+let hg_commit_ptime_s r ~rev =
215215+ let time = Topkg_cmd.(v "log" % "--template" % "{date(date, \"%s\")}" %
216216+ "--rev" % rev)
217217+ in
218218+ run_hg r time Topkg_os.Cmd.out_string
219219+ >>= fun ptime -> try Ok (int_of_string ptime) with
220220+ | Failure _ -> R.error_msgf "Could not parse timestamp from %S" ptime
221221+222222+let hg_describe ~dirty r ~rev =
223223+ let get_distance s = try Ok (int_of_string s) with
224224+ | Failure _ -> R.error_msgf "%s: Could not parse hg tag distance." (dir r)
225225+ in
226226+ let parent t =
227227+ run_hg r Topkg_cmd.(v "parent" % "--rev" % rev % "--template" % t)
228228+ Topkg_os.Cmd.out_string
229229+ in
230230+ parent "{latesttagdistance}" >>= get_distance
231231+ >>= begin function
232232+ | 1 -> parent "{latesttag}"
233233+ | n -> parent "{latesttag}-{latesttagdistance}-{node|short}"
234234+ end
235235+ >>= fun descr -> match dirty with
236236+ | false -> Ok descr
237237+ | true ->
238238+ hg_id ~rev:"tip" r >>= fun (_, is_dirty) ->
239239+ Ok (if is_dirty then dirtify descr else descr)
240240+241241+let hg_tags r =
242242+ run_hg r Topkg_cmd.(v "tags" % "--quiet" (* sic *)) Topkg_os.Cmd.out_lines
243243+244244+let hg_changes r ~after ~until =
245245+ let rev = Topkg_string.strf "%s::%s" after until in
246246+ let changes =
247247+ Topkg_cmd.(v "log" % "--template" % "{node|short} {desc|firstline}\\n"
248248+ % "--rev" % rev)
249249+ in
250250+ run_hg r changes Topkg_os.Cmd.out_lines
251251+ >>= fun commits -> parse_changes commits
252252+ >>= function
253253+ | [] -> Ok []
254254+ | after :: rest -> Ok (List.rev rest) (* hg order is reverse from git *)
255255+256256+let hg_tracked_files r ~rev =
257257+ run_hg r Topkg_cmd.(v "manifest" % "--rev" % rev) Topkg_os.Cmd.out_lines
258258+259259+let hg_clone r ~dir:d =
260260+ let clone = Topkg_cmd.(v "clone" % (dir r) % d) in
261261+ run_hg r clone Topkg_os.Cmd.out_stdout
262262+263263+let hg_checkout r ~branch ~rev =
264264+ run_hg r Topkg_cmd.(v "update" % "--rev" % rev) Topkg_os.Cmd.out_string
265265+ >>= fun _ -> match branch with
266266+ | None -> Ok ()
267267+ | Some branch ->
268268+ run_hg r Topkg_cmd.(v "branch" % branch) Topkg_os.Cmd.out_string
269269+ >>= fun _ -> Ok ()
270270+271271+let hg_commit_files r ~msg files =
272272+ let msg = match msg with
273273+ | None -> Topkg_cmd.empty
274274+ | Some m -> Topkg_cmd.(v "-m" % m)
275275+ in
276276+ let files = Topkg_cmd.(of_list @@ List.map p files) in
277277+ run_hg r Topkg_cmd.(v "commit" %% msg %% files) Topkg_os.Cmd.out_stdout
278278+279279+let hg_tag r ~force ~sign ~msg ~rev tag =
280280+ if sign then R.error_msgf "Tag signing is not supported by hg" else
281281+ let msg = match msg with
282282+ | None -> Topkg_cmd.empty
283283+ | Some m -> Topkg_cmd.(v "-m" % m)
284284+ in
285285+ run_hg r Topkg_cmd.(v "tag" %% on force (v "-f") %% msg % "--rev" % rev % tag)
286286+ Topkg_os.Cmd.out_stdout
287287+288288+let hg_delete_tag r tag =
289289+ run_hg r Topkg_cmd.(v "tag" % "--remove" % tag) Topkg_os.Cmd.out_stdout
290290+291291+(* Generic VCS support *)
292292+293293+let find ?dir () = match dir with
294294+| None ->
295295+ begin find_git () >>= function
296296+ | Some _ as v -> Ok v
297297+ | None -> find_hg ()
298298+ end
299299+| Some dir ->
300300+ let git_dir = Topkg_fpath.append dir ".git" in
301301+ Topkg_os.Dir.exists git_dir >>= function
302302+ | true ->
303303+ begin Lazy.force git >>= function
304304+ | (_, cmd) -> Ok (Some (v `Git cmd ~dir:git_dir))
305305+ end
306306+ | false ->
307307+ let hg_dir = Topkg_fpath.append dir ".hg" in
308308+ Topkg_os.Dir.exists hg_dir >>= function
309309+ | false -> Ok None
310310+ | true ->
311311+ Lazy.force hg >>= function
312312+ (_, cmd) -> Ok (Some (v `Hg cmd ~dir))
313313+314314+let get ?dir () = find ?dir () >>= function
315315+| Some r -> Ok r
316316+| None ->
317317+ let dir = match dir with
318318+ | None -> Topkg_os.Dir.current ()
319319+ | Some dir -> Ok dir
320320+ in
321321+ dir >>= function dir ->
322322+ R.error_msgf "%s: No VCS repository found" dir
323323+324324+let pp ppf r = Format.fprintf ppf "(%a, %s)" pp_kind (kind r) (dir r)
325325+326326+(* Repository state *)
327327+328328+let is_dirty = function
329329+| (`Git, _, _ as r) -> git_is_dirty r
330330+| (`Hg, _ , _ as r ) -> hg_is_dirty r
331331+332332+let not_dirty r = is_dirty r >>= function
333333+| false -> Ok ()
334334+| true -> R.error_msgf "The VCS repo is dirty, commit or stash your changes."
335335+336336+let file_is_dirty r file = match r with
337337+| (`Git, _, _ as r) -> git_file_is_dirty r file
338338+| (`Hg, _, _ as r ) -> hg_file_is_dirty r file
339339+340340+let head ?(dirty = true) = function
341341+| (`Git, _, _ as r) -> git_head ~dirty r
342342+| (`Hg, _, _ as r) -> hg_head ~dirty r
343343+344344+let commit_id ?(dirty = true) ?(commit_ish = "HEAD") = function
345345+| (`Git, _, _ as r) -> git_commit_id ~dirty r commit_ish
346346+| (`Hg, _, _ as r) -> hg_commit_id ~dirty r ~rev:(hg_rev commit_ish)
347347+348348+let commit_ptime_s ?(commit_ish = "HEAD") = function
349349+| (`Git, _, _ as r) -> git_commit_ptime_s r commit_ish
350350+| (`Hg, _, _ as r) -> hg_commit_ptime_s r ~rev:(hg_rev commit_ish)
351351+352352+let describe ?(dirty = true) ?(commit_ish = "HEAD") = function
353353+| (`Git, _, _ as r) -> git_describe ~dirty r commit_ish
354354+| (`Hg, _, _ as r) -> hg_describe ~dirty r ~rev:(hg_rev commit_ish)
355355+356356+let tags = function
357357+| (`Git, _, _ as r) -> git_tags r
358358+| (`Hg, _, _ as r) -> hg_tags r
359359+360360+let changes ?(until = "HEAD") r ~after = match r with
361361+| (`Git, _, _ as r) -> git_changes r ~after ~until
362362+| (`Hg, _, _ as r) -> hg_changes r ~after:(hg_rev after) ~until:(hg_rev until)
363363+364364+let tracked_files ?(tree_ish = "HEAD") = function
365365+| (`Git, _, _ as r) -> git_tracked_files r ~tree_ish
366366+| (`Hg, _, _ as r) -> hg_tracked_files r ~rev:(hg_rev tree_ish)
367367+368368+(* Operations *)
369369+370370+let clone r ~dir = match r with
371371+| (`Git, _, _ as r) -> git_clone r ~dir
372372+| (`Hg, _, _ as r) -> hg_clone r ~dir
373373+374374+let checkout ?branch r ~commit_ish = match r with
375375+| (`Git, _, _ as r) -> git_checkout r ~branch ~commit_ish
376376+| (`Hg, _, _ as r) -> hg_checkout r ~branch ~rev:(hg_rev commit_ish)
377377+378378+let commit_files ?msg r files = match r with
379379+| (`Git, _, _ as r) -> git_commit_files r ~msg files
380380+| (`Hg, _, _ as r) -> hg_commit_files r ~msg files
381381+382382+let tag ?(force = false) ?(sign = false) ?msg ?(commit_ish = "HEAD") r tag =
383383+ match r with
384384+ | (`Git, _, _ as r) -> git_tag r ~force ~sign ~msg ~commit_ish tag
385385+ | (`Hg, _, _ as r) -> hg_tag r ~force ~sign ~msg ~rev:(hg_rev commit_ish) tag
386386+387387+let delete_tag r tag = match r with
388388+| (`Git, _, _ as r) -> git_delete_tag r tag
389389+| (`Hg, _, _ as r) -> hg_delete_tag r tag
+48
vendor/opam/topkg/src/topkg_vcs.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** VCS repositories.
77+88+ See {!Topkg.Vcs} for documentation. *)
99+1010+(** {1 VCS} *)
1111+1212+open Topkg_result
1313+1414+type kind = [ `Git | `Hg ]
1515+val pp_kind : Format.formatter -> kind -> unit
1616+1717+type commit_ish = string
1818+1919+type t
2020+2121+val kind : t -> kind
2222+val dir : t -> Topkg_fpath.t
2323+val find : ?dir:Topkg_fpath.t -> unit -> t option result
2424+val get : ?dir:Topkg_fpath.t -> unit -> t result
2525+val cmd : t -> Topkg_cmd.t
2626+val pp : Format.formatter -> t -> unit
2727+2828+val is_dirty : t -> bool result
2929+val not_dirty : t -> unit result
3030+val file_is_dirty : t -> Topkg_fpath.t -> bool result
3131+val head : ?dirty:bool -> t -> string result
3232+val commit_id : ?dirty:bool -> ?commit_ish:string -> t -> string result
3333+val commit_ptime_s : ?commit_ish:commit_ish -> t -> int result
3434+val describe : ?dirty:bool -> ?commit_ish:string -> t -> string result
3535+val tags : t -> string list result
3636+val changes :
3737+ ?until:string -> t -> after:string -> (string * string) list result
3838+3939+val tracked_files : ?tree_ish:string -> t -> Topkg_fpath.t list result
4040+4141+val clone : t -> dir:Topkg_fpath.t -> unit result
4242+val checkout : ?branch:string -> t -> commit_ish:string -> unit result
4343+val commit_files : ?msg:string -> t -> Topkg_fpath.t list -> unit result
4444+4545+val delete_tag : t -> string -> unit result
4646+val tag :
4747+ ?force:bool -> ?sign:bool -> ?msg:string -> ?commit_ish:string -> t ->
4848+ string -> unit result
+45
vendor/opam/topkg/test/echo-delegate
···11+#!/usr/bin/env ocaml
22+#use "topfind"
33+#require "bos.setup"
44+#require "fmt"
55+open Bos_setup
66+77+let ok = Ok 0
88+let unsupported = Ok 1
99+1010+let publish = function
1111+| "distrib" :: uri :: name :: version :: msg :: archive :: _ ->
1212+ Fmt.pr "publish distrib %S %S %S %S %S@." uri name version msg archive; ok
1313+| "doc" :: uri :: name :: version :: msg :: docdir :: _ ->
1414+ Fmt.pr "publish doc %S %S %S %S %S@." uri name version msg docdir; ok
1515+| "alt" :: k :: uri :: name :: version :: msg :: archive :: _ ->
1616+ Fmt.pr "publish alt %S %S %S %S %S %S@." k uri name version msg archive; ok
1717+| args ->
1818+ unsupported
1919+2020+let issue = function
2121+| "list" :: uri :: _ -> Fmt.pr "issue list %S@." uri; ok
2222+| "show" :: uri :: id :: _ -> Fmt.pr "issue show %S %S@." uri id; ok
2323+| "open" :: uri :: t :: d :: _ -> Fmt.pr "issue open %S %S %S@." uri t d; ok
2424+| "close" :: uri :: id :: m :: _ -> Fmt.pr "issue close %S %S %S@." uri id m; ok
2525+| args -> unsupported
2626+2727+let request = function
2828+| "publish" :: args -> publish args
2929+| "issue" :: args -> issue args
3030+| args -> unsupported
3131+3232+let main () =
3333+ let doc = "the unsupportive delegate" in
3434+ begin match OS.Arg.(parse ~doc ~pos:string ()) with
3535+ | "ipc" :: verbosity :: req ->
3636+ Logs.level_of_string verbosity
3737+ >>= fun level -> Logs.set_level level; request req
3838+ | "ipc" :: [] ->
3939+ R.error_msg "malformed delegate request, verbosity is missing"
4040+ | args ->
4141+ R.error_msgf "unknown arguments: %s" (String.concat ~sep:" " args)
4242+ end
4343+ |> Logs.on_error_msg ~use:(fun () -> 2)
4444+4545+let () = exit (main ())
+8
vendor/opam/topkg/test/test.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+let () =
77+ let args = String.concat " " (List.tl (Array.to_list Sys.argv)) in
88+ Printf.printf "The test is ok, the arguments are: %s\n" args
+43
vendor/opam/topkg/test/unsupportive-delegate
···11+#!/usr/bin/env ocaml
22+#use "topfind"
33+#require "bos.setup"
44+open Bos_setup
55+66+let unsupported = Ok 1
77+88+let publish = function
99+| "distrib" :: uri :: name :: version :: msg :: archive :: _ ->
1010+ unsupported
1111+| "doc" :: uri :: name :: version :: msg :: docdir :: _ ->
1212+ unsupported
1313+| "alt" :: kind :: uri :: name :: version :: msg :: archive :: _ ->
1414+ unsupported
1515+| args ->
1616+ unsupported
1717+1818+let issue = function
1919+| "list" :: uri :: _ -> unsupported
2020+| "show" :: uri :: id :: _ -> unsupported
2121+| "open" :: uri :: title :: descr :: _ -> unsupported
2222+| "close" :: uri :: id :: msg :: _ -> unsupported
2323+| args -> unsupported
2424+2525+let request = function
2626+| "publish" :: args -> publish args
2727+| "issue" :: args -> issue args
2828+| args -> unsupported
2929+3030+let main () =
3131+ let doc = "the unsupportive delegate" in
3232+ begin match OS.Arg.(parse ~doc ~pos:string ()) with
3333+ | "ipc" :: verbosity :: req ->
3434+ Logs.level_of_string verbosity
3535+ >>= fun level -> Logs.set_level level; request req
3636+ | "ipc" :: [] ->
3737+ R.error_msg "malformed delegate request, verbosity is missing"
3838+ | args ->
3939+ R.error_msgf "unknown arguments: %s" (String.concat ~sep:" " args)
4040+ end
4141+ |> Logs.on_error_msg ~use:(fun () -> 2)
4242+4343+let () = exit (main ())
+53
vendor/opam/topkg/topkg-care.opam
···11+opam-version: "2.0"
22+name: "topkg-care"
33+synopsis: "The transitory OCaml software packager"
44+description: """\
55+**Warning** Topkg is in maintenance mode and should not longer be used.
66+77+Topkg is a packager for distributing OCaml software. It provides an
88+API to describe the files a package installs in a given build
99+configuration and to specify information about the package's
1010+distribution, creation and publication procedures.
1111+1212+The optional topkg-care package provides the `topkg` command line tool
1313+which helps with various aspects of a package's life cycle: creating
1414+and linting a distribution, releasing it on the WWW, publish its
1515+documentation, add it to the OCaml opam repository, etc.
1616+1717+Topkg is distributed under the ISC license and has **no**
1818+dependencies. This is what your packages will need as a *build*
1919+dependency.
2020+2121+Topkg-care is distributed under the ISC license it depends on
2222+[fmt][fmt], [logs][logs], [bos][bos], [cmdliner][cmdliner],
2323+[webbrowser][webbrowser] and `opam-format`.
2424+2525+[fmt]: http://erratique.ch/software/fmt
2626+[logs]: http://erratique.ch/software/logs
2727+[bos]: http://erratique.ch/software/bos
2828+[cmdliner]: http://erratique.ch/software/cmdliner
2929+[webbrowser]: http://erratique.ch/software/webbrowser
3030+3131+Home page: <http://erratique.ch/software/topkg>"""
3232+maintainer: "Daniel Bünzli <daniel.buenzl i@erratique.ch>"
3333+authors: "The topkg programmers"
3434+license: "ISC"
3535+tags: ["packaging" "ocamlbuild" "org:erratique"]
3636+homepage: "https://erratique.ch/software/topkg"
3737+doc: "https://erratique.ch/software/topkg/doc"
3838+bug-reports: "https://github.com/dbuenzli/topkg/issues"
3939+depends: [
4040+ "ocaml" {>= "4.08.0"}
4141+ "ocamlfind" {build & >= "1.6.1"}
4242+ "ocamlbuild"
4343+ "topkg" {= version}
4444+ "fmt" {>= "0.9.0"}
4545+ "logs"
4646+ "bos" {>= "0.2.1"}
4747+ "cmdliner" {>= "1.3.0"}
4848+ "webbrowser"
4949+ "opam-format" {>= "2.0.0"}
5050+]
5151+build: ["ocaml" "pkg/pkg.ml" "build" "--pkg-name" name "--dev-pkg" "%{dev}%"]
5252+dev-repo: "git+https://erratique.ch/repos/topkg.git"
5353+x-maintenance-intent: ["(latest)"]
+46
vendor/opam/topkg/topkg.opam
···11+opam-version: "2.0"
22+name: "topkg"
33+synopsis: "The transitory OCaml software packager"
44+description: """\
55+**Warning** Topkg is in maintenance mode and should not longer be used.
66+77+Topkg is a packager for distributing OCaml software. It provides an
88+API to describe the files a package installs in a given build
99+configuration and to specify information about the package's
1010+distribution, creation and publication procedures.
1111+1212+The optional topkg-care package provides the `topkg` command line tool
1313+which helps with various aspects of a package's life cycle: creating
1414+and linting a distribution, releasing it on the WWW, publish its
1515+documentation, add it to the OCaml opam repository, etc.
1616+1717+Topkg is distributed under the ISC license and has **no**
1818+dependencies. This is what your packages will need as a *build*
1919+dependency.
2020+2121+Topkg-care is distributed under the ISC license it depends on
2222+[fmt][fmt], [logs][logs], [bos][bos], [cmdliner][cmdliner],
2323+[webbrowser][webbrowser] and `opam-format`.
2424+2525+[fmt]: http://erratique.ch/software/fmt
2626+[logs]: http://erratique.ch/software/logs
2727+[bos]: http://erratique.ch/software/bos
2828+[cmdliner]: http://erratique.ch/software/cmdliner
2929+[webbrowser]: http://erratique.ch/software/webbrowser
3030+3131+Home page: <http://erratique.ch/software/topkg>"""
3232+maintainer: "Daniel Bünzli <daniel.buenzl i@erratique.ch>"
3333+authors: "The topkg programmers"
3434+license: "ISC"
3535+tags: ["packaging" "ocamlbuild" "org:erratique"]
3636+homepage: "https://erratique.ch/software/topkg"
3737+doc: "https://erratique.ch/software/topkg/doc"
3838+bug-reports: "https://github.com/dbuenzli/topkg/issues"
3939+depends: [
4040+ "ocaml" {>= "4.08.0"}
4141+ "ocamlfind" {build & >= "1.6.1"}
4242+ "ocamlbuild"
4343+]
4444+build: ["ocaml" "pkg/pkg.ml" "build" "--pkg-name" name "--dev-pkg" "%{dev}%"]
4545+dev-repo: "git+https://erratique.ch/repos/topkg.git"
4646+x-maintenance-intent: ["(latest)"]