My working unpac space for OCaml projects in development
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge opam/patches/topkg

+12571
+6
vendor/opam/topkg/.gitignore
··· 1 + _b0 2 + _build 3 + tmp 4 + *.install 5 + *.native 6 + *.byte
+7
vendor/opam/topkg/.merlin
··· 1 + PKG b0.kit rresult astring fmt fpath logs bos cmdliner 2 + PKG webbrowser webbrowser.cli opam-format 3 + S src 4 + S src-bin 5 + S src-care 6 + S test 7 + B _build/**
+1
vendor/opam/topkg/.ocp-indent
··· 1 + strict_with=always,match_clause=4,strict_else=never
+97
vendor/opam/topkg/B0.ml
··· 1 + open B0_kit.V000 2 + open Result.Syntax 3 + 4 + (* Note we have much more deps than in ocamlbuild here because we don't do 5 + transitive -I. *) 6 + 7 + let opam_core = B0_ocaml.libname "opam-core" 8 + let opam_format = B0_ocaml.libname "opam-format" 9 + let cmdliner = B0_ocaml.libname "cmdliner" 10 + let rresult = B0_ocaml.libname "rresult" 11 + let astring = B0_ocaml.libname "astring" 12 + let fpath = B0_ocaml.libname "fpath" 13 + let fmt = B0_ocaml.libname "fmt" 14 + let fmt_tty = B0_ocaml.libname "fmt.tty" 15 + let fmt_cli = B0_ocaml.libname "fmt.cli" 16 + let logs = B0_ocaml.libname "logs" 17 + let logs_fmt = B0_ocaml.libname "logs.fmt" 18 + let logs_cli = B0_ocaml.libname "logs.cli" 19 + let bos_setup = B0_ocaml.libname "bos.setup" 20 + let webbrowser = B0_ocaml.libname "webbrowser" 21 + let webbrowser_cli = B0_ocaml.libname "webbrowser.cli" 22 + 23 + let topkg = B0_ocaml.libname "topkg" 24 + let topkg_care = B0_ocaml.libname "topkg.care" 25 + 26 + (* Libraries *) 27 + 28 + let topkg_lib = 29 + B0_ocaml.lib topkg ~name:"topkg-lib" ~srcs:[`Dir ~/"src"] 30 + 31 + let care_requires requires = 32 + opam_core :: opam_format:: cmdliner:: rresult :: astring :: fpath :: fmt :: 33 + fmt_tty :: fmt_cli :: logs :: logs_fmt :: logs_cli:: bos_setup :: 34 + webbrowser :: webbrowser_cli :: topkg :: requires 35 + 36 + let topkg_care_lib = 37 + let srcs = [`Dir ~/"src-care"] in 38 + B0_ocaml.lib topkg_care ~srcs ~requires:(care_requires []) 39 + 40 + (* Tools *) 41 + 42 + let topkg_tool = 43 + let srcs = [`Dir ~/"src-bin"; `X ~/"src-bin/toy_github_delegate.ml" ] in 44 + B0_ocaml.exe ~public:true "topkg" ~srcs ~requires:(care_requires [topkg_care]) 45 + 46 + let toy_github_delegate = 47 + let srcs = [`File ~/"src-bin/toy_github_delegate.ml"] in 48 + let requires = [cmdliner; fpath; rresult; fmt_tty; astring; bos_setup; topkg; 49 + topkg_care] 50 + in 51 + B0_ocaml.exe ~public:true "toy-github-topkg-delegate" ~srcs ~requires 52 + 53 + 54 + (* Packs *) 55 + 56 + let base_metadata = 57 + B0_meta.empty 58 + |> ~~ B0_meta.authors ["The topkg programmers"] 59 + |> ~~ B0_meta.maintainers ["Daniel Bünzli <daniel.buenzl i@erratique.ch>"] 60 + |> ~~ B0_meta.homepage "https://erratique.ch/software/topkg" 61 + |> ~~ B0_meta.online_doc "https://erratique.ch/software/topkg/doc" 62 + |> ~~ B0_meta.licenses ["ISC"] 63 + |> ~~ B0_meta.repo "git+https://erratique.ch/repos/topkg.git" 64 + |> ~~ B0_meta.issues "https://github.com/dbuenzli/topkg/issues" 65 + |> ~~ B0_meta.description_tags ["packaging"; "ocamlbuild"; "org:erratique"] 66 + |> B0_meta.tag B0_opam.tag 67 + |> ~~ B0_opam.build 68 + {|[["ocaml" "pkg/pkg.ml" "build" "--pkg-name" name 69 + "--dev-pkg" "%{dev}%"]]|} 70 + let topkg = 71 + let meta = 72 + base_metadata 73 + |> ~~ B0_opam.depends 74 + [ "ocaml", {|>= "4.08.0"|}; 75 + "ocamlfind", {|build & >= "1.6.1"|}; 76 + "ocamlbuild", ""; ] 77 + in 78 + B0_pack.make "topkg" ~doc:"topkg package" ~meta ~locked:true [] 79 + 80 + let topkg_care = 81 + let meta = 82 + base_metadata 83 + |> ~~ B0_release.src_archive_name (B0_pack.basename topkg) 84 + |> ~~ B0_opam.depends 85 + [ "ocaml", {|>= "4.08.0"|}; 86 + "ocamlfind", {|build & >= "1.6.1"|}; 87 + "ocamlbuild", ""; 88 + "topkg", {|= version|}; 89 + "fmt", {|>= "0.9.0"|}; 90 + "logs", ""; 91 + "bos", {|>= "0.2.1"|}; 92 + "cmdliner", {|>= "1.3.0"|}; 93 + "webbrowser", ""; 94 + "opam-format", {|>= "2.0.0"|}; 95 + ] 96 + in 97 + B0_pack.make "topkg-care" ~doc:"topkg-care package" ~meta ~locked:true []
vendor/opam/topkg/BRZO

This is a binary file and will not be displayed.

+298
vendor/opam/topkg/CHANGES.md
··· 1 + v1.1.1 2025-11-03 Zagreb 2 + ------------------------- 3 + 4 + - Support for large package builds. On build, generate a `pkg.itarget` 5 + at the root with the files to build and invoke `ocamlbuild` with 6 + `pkg.otarget` rather than the explicit list of files. The 7 + `pkg.itarget` file can be ignored by your VCS, it is generated on 8 + the fly on each build. As a side effect, improves error reporting on 9 + build failures. 10 + 11 + v1.1.0 2025-07-23 Zagreb 12 + ------------------------ 13 + 14 + - Change the default of `Topkg.Conf.debugger_support` from `false` to 15 + `true`. This enables the install of `.ml` and `.cmt` files by 16 + default which is expected for a good experience on `ocaml.org`. The 17 + previous behaviour can be recovered by setting 18 + `TOPKG_CONF_DEBUGGER_SUPPORT=false` in your environment (#143). 19 + 20 + - Fix install of hidden library modules when `Topkg.Conf.debugger_support` is 21 + `true`: 22 + 1. When the hidden module has an `.mli`, the `.cmi` files was being installed 23 + instead of the `.cmti` and the `.mli` was missing (#143). 24 + 2. When the hidden module had no `.mli` the installation would fail 25 + by requiring one. 26 + 27 + v1.0.8 2025-03-10 La Forclaz (VS) 28 + --------------------------------- 29 + 30 + - `topkg-care`: handle `fmt` and `cmdliner` deprecations. 31 + - Require OCaml >= 4.08 32 + 33 + v1.0.7 2022-01-20 La Forclaz (VS) 34 + --------------------------------- 35 + 36 + - Fix install of C stubs in byte code only installations. 37 + Thanks to @kit-ty-kate for the investigation (#140). 38 + 39 + v1.0.6 2022-11-04 Zagreb 40 + ------------------------ 41 + 42 + - Fix native dynlink detection on OCaml 5.0 and thus `cmxs` file 43 + installation. Thanks to @kit-ty-kate for the report and the patch. 44 + 45 + v1.0.5 2022-01-28 La Forclaz (VS) 46 + --------------------------------- 47 + 48 + - `Topkg.String.parse_version`. Support for the new OCaml 49 + version string format (https://github.com/ocaml/ocaml/pull/9712) 50 + - Switch from `opam config var` to `opam var`. 51 + - Fix compilation for next version of `cmdliner`. 52 + 53 + v1.0.4 2021-10-04 Zagreb 54 + ------------------------- 55 + 56 + - Remove mentions of `Result.result` in the code base. We got the 57 + dependency indirectly through `bos` and the latest version of the 58 + latter no longer depends on it. 59 + 60 + v1.0.3 2020-09-19 Zagreb 61 + ------------------------ 62 + 63 + - Fix for OCaml 4.12. The M.() notation is evil. 64 + Thanks to Florian Angeletti for the fix. 65 + 66 + v1.0.2 2020-07-30 Zagreb 67 + ------------------------ 68 + 69 + - Support OCaml configurations without shared library support 70 + (#136). Thanks to Antonio Nuno Monteiro for the help. 71 + 72 + v1.0.1 2019-07-19 Zagreb 73 + ------------------------ 74 + 75 + - Require OCaml 4.03 and handle various stdlib deprecations. 76 + - Drop `result` dependency. 77 + - Fix an upcoming `Fmt` incompability. 78 + - Addition of `synopsis` and `description` fields to the 79 + opam file generated for the tarball. 80 + 81 + v1.0.0 2018-10-14 Zagreb 82 + ------------------------ 83 + 84 + - Support (only) for opam-publish v2.0.0. The `--pkg-opam-dir` option 85 + of `topkg opam` to indicate the old-style opam package directory to 86 + submit is renamed to `--opam-publish-file` to indicate the opam file 87 + to submit. 88 + - Add `topkg opam publish` command, alias of `topkg opam submit`. 89 + - Toy github delegate: make curl follow redirects. Before obscure 90 + failures would result when repos got moved around (#120). Thanks 91 + to Richard Mortier for the report. 92 + - Fix infinite loop in `Topkg.OS.File.write_subst`. This could result 93 + in `topkg distrib` never finishing (#128). Thanks to Christophe 94 + Troestler for reporting and Jérémie Dimino for the patch. 95 + - Add `.ps` and `.eps` files to default watermarking excludes. 96 + Thanks Christophe Troestler for the suggestion (#128). 97 + - Use `command -v` rather than `type` to check for tool existence. 98 + Thanks to Hannes Mehnert for the patch. 99 + 100 + v0.9.1 2017-10-16 Zagreb 101 + ------------------------ 102 + 103 + - Make `topkg build` return a non-zero exit code when the build 104 + fails. Thanks to Etienne Millon for the patch. 105 + - Improve `topkg doc` for `jbuilder` users. Thanks to Thomas 106 + Gazagnaire for the patch. 107 + - `ocamlbuild` users: default to parallel builds. This can 108 + be controlled via the `--jobs` command line argument. Thanks 109 + to Edwin Török for the patch. 110 + 111 + v0.9.0 La Forclaz (VS) 112 + ---------------------- 113 + 114 + - Deprecate `--pinned` in favor of `--dev-pkg` in the `build` command. 115 + The semantics is the same and with opam < 2.0 it should still be set 116 + to `"%{pinned}%"`. With opam >= 2.0 it should be set to `"%{dev}%"` 117 + this allows to infer the correct build context for (non-pinned) VCS 118 + packages (#79). 119 + - Improve `ocamlbuild` cross-compilation support. Adds the 120 + `Conf.toolchain` configuration key. If specified on the command line 121 + or via the `TOPKG_CONF_TOOLCHAIN` environment variable, its value is 122 + used with the `-toolchain` option introduced in `ocamlbuild` 0.11.0 123 + in default build command `Pkg.build_cmd`. If unspecified the default 124 + build command is left unchanged. Thanks to whitequark for the patch. 125 + - Add the `--raw ARG` repeteable option to the `build` command. Allows 126 + to skip package build instructions and opam install file generation 127 + to simply invoke the package build command with the `ARG` argument. 128 + - `topkg doc` (ocamlbuild specific). Build the documentation using 129 + the package `build` command and `--raw` arguments. Avoids problems 130 + encountered by packages that use ocamlbuild plugins (#80). 131 + - Add the `cma`, `cmxa` and `cmxs` optional arguments to `Pkg.mllib`. 132 + These allow to precisely specify what you (don't) want to build. They 133 + all default to `true`. Thanks to Stephen Dolan for the suggestion. 134 + - Add `Pkg.lib_root` and `Pkg.libexec_root` install fields. Warning 135 + these are opam 2.0 only fields. 136 + - Change `test` command for multi-opam packages by mirroring the way 137 + the `build` command works. The `--pkg-name` or `-n` option specifies 138 + the package's test to run or list. If unspecified the default 139 + package is tested, before `pkg/pkg.ml test` would list and run 140 + the last built package. This means that if you have `pkg/pkg.ml 141 + build -n PKG && pkg/pkg test` invocations you need to turn them into 142 + `pkg/pkg.ml build -n PKG && pkg/pkg test -n PKG`. 143 + - Fix `topkg run`, do not run `.so` and `.cmxs` files. 144 + - Fix changelog parsing. Subsections of an entry were not being properly 145 + parsed (#103). 146 + - Fix `topkg opam pkg`'s `url` file generation for github users which 147 + have `dev-repo:` with opam "version control bound" uris (#106). 148 + - Depends at least on `cmdliner.1.0.0` and `opam-format` (`opam-lib` 149 + is out). 150 + 151 + v0.8.1 2016-11-02 Zagreb 152 + ------------------------ 153 + 154 + - Add `Pkg.{nothing,flatten}`. Thanks to David Kaloper Meršinjak for the 155 + suggestion and the patch. 156 + 157 + 158 + v0.8.0 2016-10-31 Zagreb 159 + ------------------------ 160 + 161 + - Add `Conf.debugger_support` a configuration key to inform to the 162 + build system it should build and install build artefacts for 163 + debuggers. Packages using `Pkg.{mlib,clib}` descriptions will handle 164 + this automatically. The key can be set globally in a switch via the 165 + `TOPKG_CONF_DEBUGGER_SUPPORT` environment variable (#77). 166 + - Add `Pkg.{ocb_tag,ocb_bool_tag,ocb_bool_tags}` to easily extend 167 + `ocamlbuild` invocations according to the build configuration (#78). Thanks 168 + to David Kaloper Meršinjak for the idea and the patch. 169 + - Add `Exts.interface` for installing `mli` only compilation units (#74). 170 + - `Pkg.mllib` description. Correct support for mllib which have subpaths (#75). 171 + - Documentation generation. Fix support in the presence of `ocamlbuild` 172 + plugins (#80). Thanks to David Kaloper Meršinjak for the report 173 + and the patch. 174 + - Documentation generation. If there is no `doc/style.css` but `odig` is 175 + installed, use its `ocamldoc` stylesheet. This allows to see how it will 176 + be rendered by `odig` and avoids maintaining stylesheets in repos. 177 + 178 + v0.7.9 2016-09-21 Zagreb 179 + ------------------------ 180 + 181 + - Better package parsing in `ocamlbuild` _tags files. Thanks 182 + to Thomas Gazagnaire for the report. 183 + - Remove references to internal names in the API. 184 + 185 + v0.7.8 2016-08-09 Zagreb 186 + ------------------------ 187 + 188 + - Add a `--profile` configuration key. Thanks to David Kaloper 189 + Meršinjak for the patch. 190 + - Add `OS.File.write_subst`. Allows clients to substitute watermark 191 + like variables in hooks. 192 + - Add `Pkg.{build,clean}_cmd`. Allows clients to extend the default 193 + build system invocation. 194 + - Be more quiet on package builds (log the `.install` file 195 + written message at info level). 196 + - Remove Topkg_care.Browser. Depend on the `webbrowser` package 197 + instead. 198 + 199 + v0.7.7 2016-07-13 Cambridge (UK) 200 + -------------------------------- 201 + 202 + - Test description, allow to specify a working directory 203 + for the test via the `?dir` optional argument. Thanks 204 + to Thomas Gazagnaire for suggesting. 205 + - Fix behaviour of pinned distribution watermarking when 206 + git repo is not at the root directory of the package and 207 + `--vcs true` is forced. 208 + - Fix pkg/pkg.ml's main's ignoring `TOPKG_VERBOSITY`'s value. 209 + - Toy GitHub delegate: fix log verbosity propagation. 210 + - Fix `Vcs.is_dirty` to detect untracked files. Thanks to Hannes Mehnert. 211 + - Pager, do not try to discover if `TERM` variable is undefined. 212 + - Add `topkg run` to easily run built executables. 213 + 214 + v0.7.6 2016-07-01 Cambridge (UK) 215 + -------------------------------- 216 + 217 + - Add `pkg/pkg.ml clean` command. Removes the opam install file 218 + and performs an effect that can be specified via `clean` in the 219 + `Pkg.build` description. The `topkg clean` command now simply forwards 220 + to this command. 221 + - Change the signature of the build command `cmd` in the `Pkg.build` 222 + specification. This is an API breaking change but does not affect any 223 + published package. See #53 for details. 224 + - Add `Conf.OCaml.word_size`. Reports the bit word size for 225 + the programs that are produced by a given compiler. 226 + - Build configuration key parsing. Fail hard on any error instead 227 + of warn and continue (#56). Thanks to Thomas Gazagnaire for suggesting 228 + the previous idea was a terrible one. 229 + - Add a `--debug` configuration key. Defaults to `true` or the value 230 + of the `TOPKG_CONF_DEBUG` environment variable. The default build 231 + system invocation is changed to enable save of debugging information 232 + in build artefacts if they key is `true`. The key is generally not 233 + meant to be specified by packagers so that the policy can changed in 234 + bulk over topkg packages (#54). 235 + - `Pkg.files_to_watermark` default function. Make sure only files are 236 + returned (#58). Fixes problems with symlinks to directories in git 237 + checkout. Thanks to Thomas Gazagnaire for reporting. 238 + - Improve error message of some `Topkg.OS` functions (#57). 239 + - Remove deprecated `--installer` configuration key. 240 + - `topkg lint` fix regression in error opam lint report. 241 + 242 + v0.7.5 2016-06-22 Cambridge (UK) 243 + -------------------------------- 244 + 245 + - `topkg doc` add short option `-d` for `--dev`. 246 + - Fix `Pkg.mllib`, module list was lowercased rather than uncapitalized. 247 + 248 + v0.7.4 2016-06-17 Cambridge (UK) 249 + -------------------------------- 250 + 251 + - Add test description and run support. New `topkg test` command. 252 + - Add distribution publication description support. Allows to define the 253 + set of default publication artefacts in the package description. The cli 254 + syntax of `topkg publish` for alternative artefacts changes from 255 + `alt KIND` to `alt-KIND`. 256 + - Distributed (and thus installed) opam files are now properly 257 + versioned via the `version:` field. 258 + - Improve tarball reproducibility across systems by not relying on the 259 + VCS checkout state for determining the read and write rights (#43). 260 + - opam package submission: use the `opam-publish` submit message 261 + to append the release notes to the submission. 262 + - Toy GitHub delegate: improve user authentication by trying to steal 263 + an existing opam-publish token. 264 + - Toy GitHub delegate: improve package documentation publication. Thanks 265 + to Thomas Gazagnaire for the patches. 266 + - Error message and IPC logging level propagation improvements. Thanks to 267 + Thomas Gazagnaire for the help. 268 + 269 + v0.7.3 2016-06-12 Cambridge (UK) 270 + -------------------------------- 271 + 272 + - Change pin build detection (#44). This changes opam build 273 + instruction for packages. Substitute `"--installer" "true"` by 274 + `"--pinned" "%{pinned}%"` in build instructions. The 275 + `--installer` option is deprecated and has no effect. 276 + 277 + v0.7.2 2016-06-02 Cambridge (UK) 278 + -------------------------------- 279 + 280 + - `Pkg.describe`, allow multiple readme, change log and license files. 281 + The optional arguments `readme`, `change_log` and `license` become 282 + `readmes`, `change_logs`, `licenses` with the same default. When 283 + topkg needs to act on a change log (e.g. `topkg log`) or readme 284 + (e.g. `topkg opam descr`), it acts on the first element of 285 + `change_log` and/or `readmes`. 286 + 287 + - Fix `Conf.vcs` discovery to only look for a git 288 + directory in the build directory (#42). 289 + 290 + v0.7.1 2016-05-26 Arbaz (VS) 291 + ---------------------------- 292 + 293 + - Improve Windows support. Thanks to Andreas Hauptmann for the help. 294 + 295 + v0.7.0 2016-05-22 La Forclaz (VS) 296 + --------------------------------- 297 + 298 + First release.
+39
vendor/opam/topkg/DEVEL.md
··· 1 + Package structure 2 + ----------------- 3 + 4 + This repo is the base of two opam packages: 5 + 6 + - `topkg`, with opam file [topkg.opam](topkg.opam) 7 + - `topkg-care` with opam file [topkg-care.opam](topkg-care.opam) 8 + 9 + Both share the same [pkg/pkg.ml](pkg/pkg.ml) file. `topkg` simply 10 + builds the `Topkg` library and `topkg-care` builds the `Topkg_care` 11 + library and the `topkg` command line tool. The distinction is made in 12 + the `pkg.ml` file based on the package name passed to the build 13 + instructions. 14 + 15 + The reason for this structure is that while both `Topkg` and `Topkg_care` 16 + could be distributed together and `Topkg_care` be simply build 17 + depending on optional dependencies being present, this would have a 18 + fatal flaw: `topkg` cannot be used for any of `Topkg_care`'s 19 + dependencies. Since we want to use `topkg` for these dependencies 20 + aswell, this structure allows to cut the dependency cycle that would 21 + occur. 22 + 23 + So if you want to develop `topkg` you should do a: 24 + 25 + ``` 26 + opam pin add -kgit topkg topkg#master 27 + opam pin add -kgit topkg-care topkg#master 28 + ``` 29 + 30 + Changing the Topkg API 31 + ---------------------- 32 + 33 + Is a *very* delicate thing to do as it could break packages. Here's an 34 + invocation that can be used to reinstall packages that build depend on 35 + `topkg`: 36 + 37 + ``` 38 + opam reinstall $(opam list -s --installed --depends-on topkg) 39 + ```
+13
vendor/opam/topkg/LICENSE.md
··· 1 + Copyright (c) 2016 Daniel C. Bünzli 2 + 3 + Permission to use, copy, modify, and/or distribute this software for any 4 + purpose with or without fee is hereby granted, provided that the above 5 + copyright notice and this permission notice appear in all copies. 6 + 7 + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+55
vendor/opam/topkg/README.md
··· 1 + Topkg — The transitory OCaml software packager 2 + ============================================== 3 + 4 + **Warning** Topkg is in maintenance mode and should not longer be used. 5 + 6 + Topkg is a packager for distributing OCaml software. It provides an 7 + API to describe the files a package installs in a given build 8 + configuration and to specify information about the package's 9 + distribution, creation and publication procedures. 10 + 11 + The optional topkg-care package provides the `topkg` command line tool 12 + which helps with various aspects of a package's life cycle: creating 13 + and linting a distribution, releasing it on the WWW, publish its 14 + documentation, add it to the OCaml opam repository, etc. 15 + 16 + Topkg is distributed under the ISC license and has **no** 17 + dependencies. This is what your packages will need as a *build* 18 + dependency. 19 + 20 + Topkg-care is distributed under the ISC license it depends on 21 + [fmt][fmt], [logs][logs], [bos][bos], [cmdliner][cmdliner], 22 + [webbrowser][webbrowser] and `opam-format`. 23 + 24 + [fmt]: http://erratique.ch/software/fmt 25 + [logs]: http://erratique.ch/software/logs 26 + [bos]: http://erratique.ch/software/bos 27 + [cmdliner]: http://erratique.ch/software/cmdliner 28 + [webbrowser]: http://erratique.ch/software/webbrowser 29 + 30 + Home page: <http://erratique.ch/software/topkg> 31 + 32 + ## Installation 33 + 34 + Topkg and topkg-care can be installed with `opam`: 35 + 36 + opam install topkg # All you need for your packages 37 + opam install topkg-care # topkg binary, takes care of your packages 38 + 39 + If you don't use `opam` consult the [`topkg.opam`](topkg.opam) and 40 + [`topkg-care.opam`](topkg-care.opam) files for build instructions. 41 + 42 + ## Documentation 43 + 44 + A basic introduction and API reference is automatically generated by 45 + `ocamldoc` from the interfaces. It can be consulted [online][doc]. 46 + 47 + The `topkg` command line tool is extensively documented in man pages 48 + available through it's help system. Type: 49 + 50 + ``` 51 + topkg help release # for help about releasing your package 52 + topkg help # for more help 53 + ``` 54 + 55 + [doc]: http://erratique.ch/software/topkg/doc
+11
vendor/opam/topkg/_tags
··· 1 + true : bin_annot, safe_string 2 + 3 + <_b0> : -traverse 4 + <src> : include 5 + 6 + <src-{care,bin}> : include 7 + <src-{care,bin}/*> : package(opam-format cmdliner fmt.cli logs.cli bos.setup), \ 8 + package(webbrowser webbrowser.cli) 9 + 10 + <test> : include 11 + <support> : include
+8
vendor/opam/topkg/doc/index-topkg-care.mld
··· 1 + {0 Topkg {%html: <span class="version">%%VERSION%%</span>%}} 2 + 3 + {b Warning.} Topkg is in maintenance mode and should not longer be 4 + used. 5 + 6 + {1:library_topkg_care Library [topkg.care]} 7 + 8 + {!modules: Topkg_care }
+8
vendor/opam/topkg/doc/index-topkg.mld
··· 1 + {0 Topkg {%html: <span class="version">%%VERSION%%</span>%}} 2 + 3 + {b Warning.} Topkg is in maintenance mode and should not longer be 4 + used. 5 + 6 + {1:library_topkg Library [topkg]} 7 + 8 + {!modules: Topkg}
+12
vendor/opam/topkg/doc/index.mld
··· 1 + {0 Topkg {%html: <span class="version">%%VERSION%%</span>%}} 2 + 3 + {b Warning.} Topkg is in maintenance mode and should not longer be 4 + used. 5 + 6 + {1:library_topkg Library [topkg]} 7 + 8 + {!modules: Topkg} 9 + 10 + {1:library_topkg_care Library [topkg.care]} 11 + 12 + {!modules: Topkg_care }
+19
vendor/opam/topkg/pkg/META
··· 1 + description = "The transitory OCaml software packager" 2 + version = "%%VERSION_NUM%%" 3 + requires = "" 4 + archive(byte) = "topkg.cma" 5 + archive(native) = "topkg.cmxa" 6 + plugin(byte) = "topkg.cma" 7 + plugin(native) = "topkg.cmxs" 8 + 9 + package "care" ( 10 + directory = "../topkg-care" 11 + description = "Topkg package care tools" 12 + version = "%%VERSION_NUM%%" 13 + requires = "topkg opam-format cmdliner bos.setup" 14 + archive(byte) = "topkg_care.cma" 15 + archive(native) = "topkg_care.cmxa" 16 + plugin(byte) = "topkg_care.cma" 17 + plugin(native) = "topkg_care.cmxs" 18 + exists_if = "topkg_care.cma" 19 + )
+57
vendor/opam/topkg/pkg/pkg.ml
··· 1 + #!/usr/bin/env ocaml 2 + #use "topfind" 3 + 4 + (* Bootstrap from source, note #mod_use is 4.01 *) 5 + #directory "src" 6 + #mod_use "topkg_result.ml" 7 + #mod_use "topkg_string.ml" 8 + #mod_use "topkg_log.ml" 9 + #mod_use "topkg_fpath.ml" 10 + #mod_use "topkg_cmd.ml" 11 + #mod_use "topkg_os.ml" 12 + #mod_use "topkg_vcs.ml" 13 + #mod_use "topkg_codec.ml" 14 + #mod_use "topkg_conf.ml" 15 + #mod_use "topkg_fexts.ml" 16 + #mod_use "topkg_opam.ml" 17 + #mod_use "topkg_test.ml" 18 + #mod_use "topkg_install.ml" 19 + #mod_use "topkg_build.ml" 20 + #mod_use "topkg_distrib.ml" 21 + #mod_use "topkg_publish.ml" 22 + #mod_use "topkg_pkg.ml" 23 + #mod_use "topkg_ipc.ml" 24 + #mod_use "topkg_main.ml" 25 + #mod_use "topkg.ml" 26 + 27 + open Topkg 28 + 29 + let () = 30 + let metas = [ Pkg.meta_file ~install:false "pkg/META" ] in 31 + let opams = 32 + let install = false in 33 + let not_topkg_deps = 34 + Some ["fmt"; "logs"; "bos"; "cmdliner"; "webbrowser"; "opam-format"] 35 + in 36 + [ Pkg.opam_file ~install "topkg.opam" ~lint_deps_excluding:not_topkg_deps; 37 + Pkg.opam_file ~install "topkg-care.opam" ] 38 + in 39 + Pkg.describe ~metas ~opams "topkg" @@ fun c -> 40 + match (* bootstrap, Conf doesn't work, eqs *) Topkg_conf.pkg_name c with 41 + | "topkg" -> 42 + Ok [ Pkg.lib "pkg/META"; 43 + Pkg.lib "topkg.opam" ~dst:"opam"; 44 + Pkg.mllib ~api:["Topkg"] "src/topkg.mllib"; 45 + Pkg.doc "doc/index-topkg.mld" ~dst:"odoc-pages/index.mld"; 46 + Pkg.test "test/test"; ] 47 + | "topkg-care" -> 48 + Ok [ Pkg.lib "topkg-care.opam" ~dst:"opam"; 49 + Pkg.mllib ~api:["Topkg_care"] "src-care/topkg_care.mllib"; 50 + Pkg.bin "src-bin/topkg_bin" ~dst:"topkg"; 51 + Pkg.bin "src-bin/toy_github_delegate" 52 + ~dst:"toy-github-topkg-delegate"; 53 + Pkg.doc "doc/index-topkg-care.mld" ~dst:"odoc-pages/index.mld"; 54 + Pkg.doc "test/unsupportive-delegate"; 55 + Pkg.doc "test/echo-delegate" ] 56 + | other -> 57 + R.error_msgf "unknown package name: %s" other
+40
vendor/opam/topkg/src-bin/bistro.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + open Bos_setup 7 + 8 + let bistro () = 9 + begin 10 + let verb = Cmd.(v "--verbosity" % Logs.(level_to_string (level ()))) in 11 + let topkg = Cmd.(v "topkg") in 12 + OS.Cmd.run Cmd.(topkg % "distrib" %% verb) 13 + >>= fun () -> OS.Cmd.run Cmd.(topkg % "publish" %% verb) 14 + >>= fun () -> OS.Cmd.run Cmd.(topkg % "opam" %% verb % "pkg") 15 + >>= fun () -> OS.Cmd.run Cmd.(topkg % "opam" %% verb % "submit") 16 + >>= fun () -> Ok 0 17 + end 18 + |> Cli.handle_error 19 + 20 + (* Command line interface *) 21 + 22 + open Cmdliner 23 + 24 + let doc = "For when you are in a hurry or need to go for a drink" 25 + let sdocs = Manpage.s_common_options 26 + let exits = Cli.exits 27 + let man_xrefs = [ `Main; `Cmd "distrib"; `Cmd "publish"; `Cmd "opam" ] 28 + let man = 29 + [ `S Manpage.s_description; 30 + `P "The $(tname) command (quick in Russian) is equivalent to invoke:"; 31 + `Pre "\ 32 + topkg distrib # Create the distribution archive 33 + topkg publish # Publish it on the WWW with its documentation 34 + topkg opam pkg # Create an opam package 35 + topkg opam submit # Submit it to OCaml's opam repository"; 36 + `P "See topkg-release(7) for more information."; ] 37 + 38 + let cmd = 39 + Cmd.v (Cmd.info "bistro" ~doc ~sdocs ~exits ~man ~man_xrefs) @@ 40 + Term.(const bistro $ Cli.setup)
+8
vendor/opam/topkg/src-bin/bistro.mli
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** The [bistro] command. *) 7 + 8 + val cmd : int Cmdliner.Cmd.t
+127
vendor/opam/topkg/src-bin/browse.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + open Bos_setup 7 + 8 + (* Targets *) 9 + 10 + let opam_doc_field = 11 + "doc", `Opam "doc", "doc opam file field" 12 + 13 + let opam_homepage_field = 14 + "homepage", `Opam "homepage", "homepage opam file field" 15 + 16 + let opam_issues_field = 17 + "issues", `Opam "bug-reports", "bug-reports opam file field" 18 + 19 + let opam_repo_field = 20 + "repo", `Opam "dev-repo", "dev-repo opam file field" 21 + 22 + let topkg_api = 23 + "topkg-api", `Uri "%%PKG_DOC%%", "topkg's API docs" 24 + 25 + let ocaml_man = 26 + "ocaml-man", `Uri "http://caml.inria.fr/pub/docs/manual-ocaml/", 27 + "OCaml manual" 28 + 29 + let ocaml_issues = 30 + "ocaml-issues", `Uri "http://caml.inria.fr/mantis/", "OCaml issue tracker" 31 + 32 + let ocamlbuild_man = 33 + "ocamlbuild-man", 34 + `Uri "https://github.com/ocaml/ocamlbuild/blob/master/manual/manual.adoc", 35 + "OCamlbuild manual" 36 + 37 + let opam_man = 38 + "opam-man", `Uri "http://opam.ocaml.org/doc/Manual.html", "opam manual" 39 + 40 + let packages = 41 + "packages", `Uri "http://opam.ocaml.org/packages/", "OCaml opam repository" 42 + 43 + let planet = 44 + "planet", `Uri "https://ocaml.org/community/planet/", "OCaml Planet" 45 + 46 + let temptation = 47 + "temptation", 48 + `Uri "https://www.\x2568\x2561\x2573\x256B\x2565\x256C\x256C.org", "" 49 + 50 + let caml_list = 51 + "caml-list", `Uri "http://news.gmane.org/gmane.comp.lang.caml.inria", 52 + "Main OCaml mailing list" 53 + 54 + let weekly_news = 55 + "weekly-news", `Uri "http://alan.petitepomme.net/cwn/", "OCaml Weekly News" 56 + 57 + let targets = 58 + [ opam_doc_field; opam_homepage_field; opam_issues_field; opam_repo_field; 59 + topkg_api; ocaml_man; ocaml_issues; ocamlbuild_man; opam_man; packages; 60 + planet; temptation; caml_list; weekly_news; ] 61 + 62 + let parse_target, max_target_len = 63 + let add (acc, len) (t, v, _) = (t, v) :: acc, max len (String.length t) in 64 + let index, max = List.fold_left add ([], 0) targets in 65 + (* This gives us trie lookup *) 66 + Cmdliner.Arg.conv_parser (Cmdliner.Arg.enum index), max 67 + 68 + (* opam field uris *) 69 + 70 + let opam_field_uri opam field = 71 + Topkg_care.Opam.File.fields opam 72 + >>= fun fields -> match String.Map.find field fields with 73 + | Some (uri :: _) -> Ok uri 74 + | Some [] -> R.error_msgf "%a: field %s is empty" Fpath.pp opam field 75 + | None -> R.error_msgf "%a: field %s is undefined" Fpath.pp opam field 76 + 77 + (* Browse command *) 78 + 79 + let browse () pkg_file opam browser prefix background target = 80 + begin 81 + let uri = match parse_target target with 82 + | Ok (`Uri uri) -> Ok uri 83 + | Ok (`Opam field) -> 84 + let pkg = Topkg_care.Pkg.v ?opam pkg_file in 85 + Topkg_care.Pkg.opam pkg >>= fun opam -> opam_field_uri opam field 86 + | Error msg -> 87 + let uri_prefixes = ["http://"; "https://"; "file://"] in 88 + if List.exists (fun p -> String.is_prefix ~affix:p target) uri_prefixes 89 + then Ok target 90 + else Error msg 91 + in 92 + uri 93 + >>= fun uri -> Webbrowser.reload ~background ~prefix ?browser uri 94 + >>= fun () -> Ok 0 95 + end 96 + |> Cli.handle_error 97 + 98 + (* Command line interface *) 99 + 100 + open Cmdliner 101 + 102 + let target = 103 + let doc = "Target to browse, see above for the list of targets." in 104 + Arg.(value & pos 0 string "homepage" & info [] ~doc ~docv:"TARGET or URI") 105 + 106 + let doc = "Browse the package's WWW links" 107 + let sdocs = Manpage.s_common_options 108 + let exits = Cli.exits 109 + let man_xrefs = [ `Main ] 110 + let man = 111 + let target acc (t, _, doc) = 112 + if doc = "" then acc else 113 + let pad = String.v ~len:(max_target_len - String.length t) (fun _ -> ' ') in 114 + `Pre (strf "%s$(b,%s) %s" pad t doc) :: `Noblank :: acc 115 + in 116 + [ `S Manpage.s_description; 117 + `P "The $(tname) command opens or reloads URIs mentioned in the 118 + opam file in a WWW browser. A few other useful logical target are 119 + provided and arbitrary file, http or https schemed URIs can also 120 + be specified as the target."; 121 + `Blocks (List.(tl @@ rev @@ fold_left target [] targets)); ] 122 + 123 + let cmd = 124 + Cmd.v (Cmd.info "browse" ~doc ~sdocs ~exits ~man ~man_xrefs) @@ 125 + Term.(const browse $ Cli.setup $ Cli.pkg_file $ Cli.opam $ 126 + Webbrowser_cli.browser $ Webbrowser_cli.prefix $ 127 + Webbrowser_cli.background $ target)
+8
vendor/opam/topkg/src-bin/browse.mli
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** The [browse] command. *) 7 + 8 + val cmd : int Cmdliner.Cmd.t
+104
vendor/opam/topkg/src-bin/build.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + open Bos_setup 7 + 8 + let build_args pkg_name build_dir dry_run raws tests debug args = 9 + let on_some_use_opt opt to_arg = function 10 + | None -> Cmd.empty 11 + | Some value -> Cmd.(v opt % to_arg value) 12 + in 13 + let verb = Cli.propagate_verbosity_to_pkg_file () in 14 + let pkg_name = on_some_use_opt "--pkg-name" (fun x -> x) pkg_name in 15 + let build_dir = on_some_use_opt "--build-dir" Cmd.p build_dir in 16 + let dry_run = if dry_run then Cmd.(v "--dry-run") else Cmd.empty in 17 + let raws = Cmd.of_list ~slip:"--raw" raws in 18 + let tests = on_some_use_opt "--tests" String.of_bool tests in 19 + let debug = on_some_use_opt "--debug" String.of_bool debug in 20 + Cmd.(verb %% dry_run %% raws %% pkg_name %% build_dir %% tests %% debug %% 21 + Cmd.of_list args) 22 + 23 + let build () pkg_file pkg_name build_dir dry_run raws tests debug args = 24 + let pkg = Topkg_care.Pkg.v pkg_file in 25 + let args = build_args pkg_name build_dir dry_run raws tests debug args in 26 + let out = OS.Cmd.out_stdout in 27 + begin 28 + OS.Dir.current () 29 + >>= fun dir -> Topkg_care.Pkg.build pkg ~dir ~args ~out 30 + >>| (function ((), (_, `Exited 0)) -> 0 | _ -> 1) 31 + end 32 + |> Cli.handle_error 33 + 34 + (* Command line interface *) 35 + 36 + open Cmdliner 37 + 38 + let args = 39 + let doc = "Build configuration. Needs to be specified after a -- token 40 + so that the command line options do not get interpreted by 41 + $(b,topkg build) itself." 42 + in 43 + Arg.(value & pos_all string [] & info [] ~doc ~docv:"BUILD_CONF") 44 + 45 + let pkg_name = 46 + let doc = "The name $(docv) of the package (and of the opam install file). 47 + This is equivalent to specify the same option after the -- token. 48 + If absent provided by the package description." 49 + in 50 + let docv = "PKG_NAME" in 51 + Arg.(value & opt (some string) None & info ["n"; "pkg-name"] ~doc ~docv) 52 + 53 + let build_dir = 54 + let doc = "Specifies the build directory $(docv). This is equivalent to 55 + specify the same option after the -- token. If absent, provided 56 + by the package description." 57 + in 58 + let docv = "BUILD_DIR" in 59 + Arg.(value & opt (some Cli.path_arg) None & info ["build-dir"] ~doc ~docv) 60 + 61 + let dry_run = 62 + let doc = "Do not run build instructions, only determine and write the opam 63 + install file. This is equivalent to specify the same option after 64 + the -- token." 65 + in 66 + Arg.(value & flag & info ["d"; "dry-run"] ~doc) 67 + 68 + let raws = 69 + let doc = "Do not run build instructions or write the opam install file, only 70 + invoke the build system with the given $(docv) argument." 71 + in 72 + Arg.(value & opt_all string [] & info ["r"; "raw"] ~doc ~docv:"ARG") 73 + 74 + let tests = 75 + let doc = "Specifies whether tests should be built. If absent depends on the 76 + build context, true for development and false otherwise. This is 77 + equivalent to specify the same option after the -- token." 78 + in 79 + Arg.(value & opt (some bool) None & info ["tests"] ~doc ~docv:"BOOL") 80 + 81 + let debug = 82 + let doc = "Debug build. Specifies if debugging information should be 83 + saved in build artefacts. This is equivalent to specify the 84 + same option after the -- token." 85 + in 86 + let env = Cmd.Env.info "TOPKG_CONF_DEBUG" in 87 + Arg.(value & opt (some bool) None & info ["debug"] ~env ~doc ~docv:"BOOL") 88 + 89 + let doc = "Build the package" 90 + let sdocs = Manpage.s_common_options 91 + let exits = Cmd.Exit.info 1 ~doc:"on build failure." :: Cli.exits 92 + let man_xrefs = [ `Main ] 93 + let man = 94 + [ `S Manpage.s_synopsis; 95 + `P "$(mname) $(tname) [$(i,OPTION)]... [-- $(i,BUILD_CONF)...]"; 96 + `S Manpage.s_description; 97 + `P "The $(tname) command builds the package. This is equivalent to 98 + invoke:"; 99 + `Pre "ocaml ./pkg/pkg.ml build $(i,BUILD_CONF)..."; ] 100 + 101 + let cmd = 102 + Cmd.v (Cmd.info "build" ~doc ~sdocs ~exits ~man ~man_xrefs) @@ 103 + Term.(const build $ Cli.setup $ Cli.pkg_file $ pkg_name $ build_dir $ 104 + dry_run $ raws $ tests $ debug $ args)
+8
vendor/opam/topkg/src-bin/build.mli
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** The [build] command. *) 7 + 8 + val cmd : int Cmdliner.Cmd.t
+49
vendor/opam/topkg/src-bin/clean.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + open Bos_setup 7 + 8 + (* Command *) 9 + 10 + let clean_args name build_dir = 11 + let on_some_use_opt opt to_arg = function 12 + | None -> Cmd.empty 13 + | Some value -> Cmd.(v opt % to_arg value) 14 + in 15 + let verb = Cli.propagate_verbosity_to_pkg_file () in 16 + let build_dir = on_some_use_opt "--build-dir" Cmd.p build_dir in 17 + let name = on_some_use_opt "--pkg-name" (fun n -> n) name in 18 + Cmd.(verb %% name %% build_dir) 19 + 20 + let clean () pkg_file name build_dir = 21 + let pkg = Topkg_care.Pkg.v ?build_dir ?name pkg_file in 22 + let args = clean_args name build_dir in 23 + let out = OS.Cmd.out_stdout in 24 + begin 25 + OS.Dir.current () 26 + >>= fun dir -> Topkg_care.Pkg.clean pkg ~dir ~args ~out 27 + >>| (function ((), (_, `Exited 0)) -> 0 | _ -> 1) 28 + end 29 + |> Cli.handle_error 30 + 31 + (* Command line interface *) 32 + 33 + open Cmdliner 34 + 35 + let doc = "Clean the package's build" 36 + let sdocs = Manpage.s_common_options 37 + let exits = Cmd.Exit.info 1 ~doc:"on clean failure." :: Cli.exits 38 + let man_xrefs = [`Main; `Cmd "build"] 39 + let man = 40 + [ `S Manpage.s_synopsis; 41 + `P "$(mname) $(tname) [$(i,OPTION)]..."; 42 + `S Manpage.s_description; 43 + `P "The $(tname) command deletes the package's build and its opam 44 + install file. This is equivalent to invoke:"; 45 + `Pre "ocaml ./pkg/pkg.ml clean";] 46 + 47 + let cmd = 48 + Cmd.v (Cmd.info "clean" ~doc ~sdocs ~exits ~man ~man_xrefs) @@ 49 + Term.(const clean $ Cli.setup $ Cli.pkg_file $ Cli.pkg_name $ Cli.build_dir)
+8
vendor/opam/topkg/src-bin/clean.mli
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** The [clean] command. *) 7 + 8 + val cmd : int Cmdliner.Cmd.t
+176
vendor/opam/topkg/src-bin/cli.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + open Bos_setup 7 + open Cmdliner 8 + 9 + (* Converters and arguments *) 10 + 11 + let path_arg = Arg.conv Fpath.(of_string, pp) 12 + 13 + let pkg_file = 14 + let doc = "Use $(docv) as the package description file." in 15 + let docv = "FILE" in 16 + Arg.(value & opt path_arg (Fpath.v "pkg/pkg.ml") & 17 + info ["pkg-file"] ~docs:Manpage.s_common_options ~doc ~docv) 18 + 19 + let pkg_name = 20 + let doc = "The name $(docv) of the opam package. If absent provided 21 + by the package description." 22 + in 23 + let docv = "PKG_NAME" in 24 + Arg.(value & opt (some string) None & info ["n"; "pkg-name"] ~doc ~docv) 25 + 26 + let opam = 27 + let doc = "The opam file to use. If absent uses the default opam file 28 + mentioned in the package description." 29 + in 30 + let docv = "FILE" in 31 + Arg.(value & opt (some path_arg) None & info ["opam"] ~doc ~docv) 32 + 33 + let dist_name = 34 + let doc = "The name $(docv) of the package to use for the package 35 + distribution. If absent, provided by the package description." 36 + in 37 + let docv = "NAME" in 38 + Arg.(value & opt (some string) None & info ["dist-name"] ~doc ~docv) 39 + 40 + let dist_version = 41 + let doc = "The version string to use for the package distribution. 42 + If absent, provided by the VCS tag description of the 43 + HEAD commit." 44 + in 45 + let docv = "VERSION" in 46 + Arg.(value & opt (some string) None & info ["dist-version"] ~doc ~docv) 47 + 48 + let dist_file = 49 + let doc = "The package distribution archive. If absent the file 50 + $(i,BUILD_DIR)/$(i,NAME)-$(i,VERSION).tbz (see options 51 + $(b,--build-dir), $(b,--dist-name) and $(b,--dist-version))." 52 + in 53 + let docv = "FILE" in 54 + Arg.(value & opt (some path_arg) None & info ["dist-file"] ~doc ~docv) 55 + 56 + let dist_opam = 57 + let doc = "opam file to use for the distribution. If absent uses the opam 58 + file mentioned in the package description that corresponds to 59 + the distribution package name $(i,NAME) (see option 60 + $(b,--dist-name))." 61 + in 62 + let docv = "FILE" in 63 + Arg.(value & opt (some path_arg) None & info ["dist-opam"] ~doc ~docv) 64 + 65 + let dist_uri = 66 + let doc = "The distribution archive URI on the WWW. If absent, provided by the 67 + package description." 68 + in 69 + let docv = "URI" in 70 + Arg.(value & opt (some string) None & info ["dist-uri"] ~doc ~docv) 71 + 72 + let readme = 73 + let doc = "The readme to use. If absent, provided by the package 74 + description." 75 + in 76 + let docv = "FILE" in 77 + Arg.(value & opt (some path_arg) None & info ["readme"] ~doc ~docv) 78 + 79 + let change_log = 80 + let doc = "The change log to use. If absent, provided by the package 81 + description." 82 + in 83 + let docv = "FILE" in 84 + Arg.(value & opt (some path_arg) None & info ["change-log"] ~doc ~docv) 85 + 86 + let delegate = 87 + let doc = "The delegate tool $(docv) to use. If absent, see topkg-delegate(7) 88 + for the lookup procedure." 89 + in 90 + let docv = "TOOL" in 91 + let to_cmd = function None -> None | Some s -> Some (Bos.Cmd.v s) in 92 + Term.(const to_cmd $ 93 + Arg.(value & opt (some string) None & info ["delegate"] ~doc ~docv)) 94 + 95 + let build_dir = 96 + let doc = "Specifies the build directory $(docv). If absent, provided by the 97 + package description." 98 + in 99 + let docv = "BUILD_DIR" in 100 + Arg.(value & opt (some path_arg) None & info ["build-dir"] ~doc ~docv) 101 + 102 + let publish_msg = 103 + let doc = "The publication message $(docv). Defaults to the change 104 + log of the last version (see $(b,topkg log -l))." 105 + in 106 + let docv = "MSG" in 107 + Arg.(value & opt (some string) None & info ["m"; "message"] ~doc ~docv) 108 + 109 + (* Terms *) 110 + 111 + let logs_to_topkg_log_level = function 112 + | None -> None 113 + | Some Logs.App -> Some (Topkg.Log.App) 114 + | Some Logs.Error -> Some (Topkg.Log.Error) 115 + | Some Logs.Warning -> Some (Topkg.Log.Warning) 116 + | Some Logs.Info -> Some (Topkg.Log.Info) 117 + | Some Logs.Debug -> Some (Topkg.Log.Debug) 118 + 119 + let setup style_renderer log_level cwd = 120 + Fmt_tty.setup_std_outputs ?style_renderer (); 121 + Topkg.Log.set_level (logs_to_topkg_log_level log_level); 122 + Logs.set_level log_level; 123 + Logs.set_reporter (Logs_fmt.reporter ~app:Fmt.stdout ()); 124 + Logs.info (fun m -> m "topkg %%VERSION%% running"); 125 + match cwd with 126 + | None -> `Ok () 127 + | Some dir -> 128 + match OS.Dir.set_current dir with 129 + | Ok () -> `Ok () 130 + | Error (`Msg m) -> `Error (false, m) (* use cmdliner evaluation error *) 131 + 132 + let setup = 133 + let style_renderer = 134 + let env = Cmd.Env.info "TOPKG_COLOR" in 135 + Fmt_cli.style_renderer ~docs:Manpage.s_common_options ~env () 136 + in 137 + let log_level = 138 + let env = Cmd.Env.info "TOPKG_VERBOSITY" in 139 + Logs_cli.level ~docs:Manpage.s_common_options ~env () 140 + in 141 + let cwd = 142 + let doc = "Change to directory $(docv) before doing anything." in 143 + let docv = "DIR" in 144 + Arg.(value & opt (some path_arg) None & info ["C"; "pkg-dir"] 145 + ~docs:Manpage.s_common_options ~doc ~docv) 146 + in 147 + Term.(ret (const setup $ style_renderer $ log_level $ cwd)) 148 + 149 + (* Verbosity propagation. *) 150 + 151 + let propagate_verbosity_to_pkg_file () = match Logs.level () with 152 + | None -> Bos.Cmd.(v "-q") 153 + | Some Logs.Info -> Bos.Cmd.(v "-v") 154 + | Some Logs.Debug -> Bos.Cmd.(v "-v" % "-v") 155 + | Some _ -> Bos.Cmd.empty 156 + 157 + (* Error handling *) 158 + 159 + let warn_if_vcs_dirty msg = 160 + Topkg.Vcs.get () 161 + >>= fun repo -> Topkg.Vcs.is_dirty repo 162 + >>= function 163 + | false -> Ok () 164 + | true -> 165 + Logs.warn 166 + (fun m -> m "The repo is %a. %a" Topkg_care.Pp.dirty () Fmt.text msg); 167 + Ok () 168 + 169 + let handle_error = function 170 + | Ok 0 -> if Logs.err_count () > 0 then 3 else 0 171 + | Ok n -> n 172 + | Error _ as r -> Logs.on_error_msg ~use:(fun _ -> 3) r 173 + 174 + let exits = 175 + Cmd.Exit.info 3 ~doc:"on indiscriminate errors reported on stderr." :: 176 + Cmd.Exit.defaults
+82
vendor/opam/topkg/src-bin/cli.mli
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** {!Cmdliner} and common definitions for commands. *) 7 + 8 + open Cmdliner 9 + open Rresult 10 + 11 + (** {1 Converters and options} *) 12 + 13 + val path_arg : Fpath.t Arg.conv 14 + (** [path_arg] is a path argument converter. *) 15 + 16 + val pkg_file : Fpath.t Term.t 17 + (** A [--pkg-file] option to specify the package description file to use. *) 18 + 19 + val pkg_name : string option Term.t 20 + (** A [--pkg-name] option to specify the opam package name. *) 21 + 22 + val opam : Fpath.t option Term.t 23 + (** An [--opam] option for defining an opam file. *) 24 + 25 + val dist_name : string option Term.t 26 + (** A [--dist-name] option to define the package name of the distribution. *) 27 + 28 + val dist_version : string option Term.t 29 + (** A [--dist-version] option to define the package version. *) 30 + 31 + val dist_file : Fpath.t option Term.t 32 + (** A [--dist-file] option to define the distribution archive file. *) 33 + 34 + val dist_uri : string option Term.t 35 + (** A [--dist-uri] option to define the distribution archive URI on the WWW. *) 36 + 37 + val dist_opam : Fpath.t option Term.t 38 + (** An [--dist-opam] option to define the opam file. *) 39 + 40 + val readme : Fpath.t option Term.t 41 + (** A [--readme] option to define the readme. *) 42 + 43 + val change_log : Fpath.t option Term.t 44 + (** A [--change-log] option to define the change log. *) 45 + 46 + val opam : Fpath.t option Term.t 47 + (** An [--opam] option to define an opam file. *) 48 + 49 + val delegate : Bos.Cmd.t option Term.t 50 + (** A [--delegate] option to define the delegate. *) 51 + 52 + val build_dir : Fpath.t option Term.t 53 + (** A [--build-dir] option to define the build directory. *) 54 + 55 + val publish_msg : string option Term.t 56 + (** A [--msg] option to define a publication message. *) 57 + 58 + (** {1 Terms} *) 59 + 60 + val setup : unit Term.t 61 + (** [setup env] defines a basic setup common to all commands. The 62 + setup does, by side effect, set {!Logs} log verbosity, adjusts 63 + colored output and sets the current working directory. *) 64 + 65 + (** {1 Verbosity propagation} *) 66 + 67 + val propagate_verbosity_to_pkg_file : unit -> Bos.Cmd.t 68 + (** [propagate_verbosity_to_pkg_file ()] is 69 + a command line fragment that has the option to propagate 70 + the current log verbosity to an invocation of the package 71 + description. *) 72 + 73 + (** {1 Warnings and errors} *) 74 + 75 + val warn_if_vcs_dirty : string -> (unit, R.msg) result 76 + (** [warn_if_vcs_dirty msg] warns with [msg] if the VCS is dirty. *) 77 + 78 + val handle_error : (int, R.msg) result -> int 79 + (** [handle_error r] is [r]'s result or logs [r]'s error and returns [3]. *) 80 + 81 + val exits : Cmd.Exit.info list 82 + (** [exits] is are the exit codes common to all commands. *)
+164
vendor/opam/topkg/src-bin/distrib.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + open Bos_setup 7 + 8 + let lint_distrib pkg ~dir = 9 + Logs.app (fun m -> m "@.Linting distrib in %a" Fpath.pp dir); 10 + Topkg_care.Pkg.lint pkg ~dir Topkg_care.Pkg.lint_all 11 + 12 + let build_distrib pkg ~dir skip_tests = 13 + Logs.app (fun m -> m "@.Building package in %a" Fpath.pp dir); 14 + let tests = if skip_tests then Cmd.empty else Cmd.(v "--tests" % "true") in 15 + let args = Cmd.(v "--dev-pkg" % "false" % "--vcs" % "false" %% tests) in 16 + let out = OS.Cmd.out_string in 17 + Topkg_care.Pkg.build pkg ~dir ~args ~out >>= function 18 + | (_, (_, `Exited 0)) -> 19 + Logs.app (fun m -> m "%a package builds" Topkg_care.Pp.status `Ok); Ok 0 20 + | (stdout, _) -> 21 + Logs.app (fun m -> m "%s@\n%a package builds" 22 + stdout Topkg_care.Pp.status `Fail); Ok 1 23 + 24 + let test_distrib pkg ~dir = 25 + Logs.app (fun m -> m "@.Running package tests in %a" Fpath.pp dir); 26 + let out = OS.Cmd.out_string in 27 + Topkg_care.Pkg.test pkg ~dir ~args:Cmd.empty ~out >>= function 28 + | (_, (_, `Exited 0)) -> 29 + Logs.app (fun m -> m "%a package tests" 30 + Topkg_care.Pp.status `Ok); Ok 0 31 + | (stdout, _) -> 32 + Logs.app (fun m -> m "%s@\n%a package tests" 33 + stdout Topkg_care.Pp.status `Fail); Ok 1 34 + 35 + let check_archive pkg ar ~skip_lint ~skip_build ~skip_tests = 36 + Topkg_care.Archive.untbz ~clean:true ar 37 + >>= fun dir -> (if skip_lint then Ok 0 else lint_distrib pkg ~dir) 38 + >>= fun c0 -> (if skip_build then Ok 0 else build_distrib pkg ~dir skip_tests) 39 + >>= fun c1 -> (if skip_tests || skip_build then Ok 0 else 40 + test_distrib pkg ~dir) 41 + >>= fun c2 -> match c0 + c1 + c2 with 42 + | 0 -> OS.Dir.delete ~recurse:true dir >>= fun () -> Ok 0 43 + | n -> Ok 1 44 + 45 + let warn_if_vcs_dirty ()= 46 + Cli.warn_if_vcs_dirty "The distribution archive may be inconsistent." 47 + 48 + let log_footprint pkg archive = 49 + Topkg_care.Pkg.name pkg 50 + >>= fun name -> Topkg_care.Pkg.version pkg 51 + >>= fun version -> Topkg.Vcs.get () 52 + >>= fun repo -> Topkg.Vcs.commit_id repo ~dirty:false ~commit_ish:"HEAD" 53 + >>= fun commit_ish -> 54 + Logs.app 55 + (fun m -> m "@.@[<v>@[Distribution for %a@ %a@]@,@[Commit %a@]@,\ 56 + @[Archive %a@]@]" 57 + Topkg_care.Pp.name name Topkg_care.Pp.version version 58 + Topkg_care.Pp.commit commit_ish Topkg_care.Pp.path archive); 59 + Ok () 60 + 61 + let log_wrote_archive ar = 62 + Logs.app (fun m -> m "Wrote archive %a" Topkg_care.Pp.path ar); Ok () 63 + 64 + let distrib 65 + () pkg_file opam build_dir name version keep_dir skip_lint skip_build 66 + skip_tests 67 + = 68 + begin 69 + let pkg = Topkg_care.Pkg.v ?name ?version ?build_dir ?opam pkg_file in 70 + Topkg_care.Pkg.distrib_archive pkg ~keep_dir 71 + >>= fun ar -> log_wrote_archive ar 72 + >>= fun () -> check_archive pkg ar ~skip_lint ~skip_build ~skip_tests 73 + >>= fun errs -> log_footprint pkg ar 74 + >>= fun () -> warn_if_vcs_dirty () 75 + >>= fun () -> Ok errs 76 + end 77 + |> Cli.handle_error 78 + 79 + (* Command line interface *) 80 + 81 + open Cmdliner 82 + 83 + let keep_build_dir = 84 + let doc = "Keep the distribution build directory after successful archival." 85 + in 86 + Arg.(value & flag & info ["keep-build-dir"] ~doc) 87 + 88 + let skip_lint = 89 + let doc = "Do not lint the archive distribution." in 90 + Arg.(value & flag & info ["skip-lint"] ~doc) 91 + 92 + let skip_build = 93 + let doc = "Do not try to build the package from the archive." in 94 + Arg.(value & flag & info ["skip-build"] ~doc) 95 + 96 + let skip_tests = 97 + let doc = "Do not try to build and run the package tests from the archive. 98 + Implied by $(b,--skip-build)." 99 + in 100 + Arg.(value & flag & info ["skip-tests"] ~doc) 101 + 102 + let doc = "Create a package distribution archive" 103 + let sdocs = Manpage.s_common_options 104 + let exits = Cli.exits 105 + let envs = 106 + [ Cmd.Env.info "TOPKG_BZIP2" ~doc:"The $(b,bzip2) tool to use to compress the 107 + archive. Gets the archive on stdin and must output the result on 108 + standard out."; 109 + Cmd.Env.info "TOPKG_TAR" ~doc:"The $(b,tar) tool to use to unarchive a tbz 110 + archive (archive creation itself is handled by topkg)."; ] 111 + 112 + let man_xrefs = [ `Main ] 113 + let man = 114 + [ `S Manpage.s_description; 115 + `P "The $(tname) command creates a package distribution 116 + archive in the build directory of the package. The generated 117 + archive should be bit-wise reproducible. There are however a few 118 + caveats, see the section about this further down."; 119 + `P "More detailed information about the archive creation process and its 120 + customization can be found in topkg's API documentation."; 121 + `P "Once the archive is created it is unpacked in the build directory, 122 + linted and the package is built using the package description 123 + contained in the archive. The build will use the default package 124 + configuration so it may fail in the current environment 125 + without this necessarily implying an actual problem with the 126 + distribution; one should still worry about it though. 127 + These checks can be prevented by using the $(b,--skip-lint) and 128 + $(b,--skip-build) options."; 129 + `S "REPRODUCIBLE DISTRIBUTION ARCHIVES"; 130 + `P "Given the package name, the HEAD commit identifier 131 + and the version string, the $(tname) command should always 132 + generate the same archive."; 133 + `P "More precisely, files are added to the archive using a well 134 + defined order on path names. Their file permissions are either 135 + 0o775 for directories and files that are executable for the user 136 + in the HEAD repository checkout or 0o664 for those that are not. 137 + Their modification times are set to the commit date (note that if 138 + git is used, git-log(1) shows the author date which may not 139 + coincide). No other file metadata is recorded."; 140 + `P "This should ensure that the resulting archive is bit-wise 141 + identical regardless of the context in which it is 142 + created. However this may fail for one or more of the 143 + following reasons:"; 144 + `I ("Non-reproducible distribution massage", "The package 145 + distribution massaging hook relies on external factors 146 + that are not captured by the source repository checkout. 147 + For example external data files, environment variables, etc."); 148 + `I ("File paths with non US-ASCII characters", 149 + "If these paths are encoded in UTF-8, different file systems 150 + may return the paths with different Unicode normalization 151 + forms which could yield different byte serializations in the 152 + archive (note that this could be lifted at the cost of a 153 + dependency on Uunf)."); 154 + `I ("The bzip2 utility", "The archive is compressed using the bzip2 utility. 155 + Reproducibility relies on bzip2 to be a reproducible function 156 + across platforms."); 157 + `I ("Topkg changes", "Topkg could change its distribution procedure in 158 + the future, for example to correct bugs."); ] 159 + 160 + let cmd = 161 + Cmd.v (Cmd.info "distrib" ~doc ~sdocs ~exits ~envs ~man ~man_xrefs) @@ 162 + Term.(const distrib $ Cli.setup $ Cli.pkg_file $ Cli.dist_opam $ 163 + Cli.build_dir $ Cli.dist_name $ Cli.dist_version $ keep_build_dir $ 164 + skip_lint $ skip_build $ skip_tests)
+8
vendor/opam/topkg/src-bin/distrib.mli
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** The [distrib] command. *) 7 + 8 + val cmd : int Cmdliner.Cmd.t
+131
vendor/opam/topkg/src-bin/doc.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + open Bos_setup 7 + 8 + let unixy_path p = 9 + (* ocamlbuild doesn't like Windows paths it seems. Try to do our best here. *) 10 + let volume, p = Fpath.split_volume p in 11 + volume ^ (String.concat ~sep:"/" (Fpath.segs p)) 12 + 13 + let copy_assets src_dir dst_dir = 14 + let copy_asset dst_dir file = match Fpath.get_ext file with 15 + | ".css" | ".svg" | ".svgz" | ".png" | ".jpeg" | ".gif" | ".woff" | ".ttf" 16 + | ".otf" | ".eot" -> 17 + begin OS.File.exists file >>= function 18 + | false -> Ok () 19 + | true -> 20 + OS.File.read file 21 + >>= fun cont -> OS.File.write Fpath.(dst_dir / filename file) cont 22 + end 23 + |> Logs.on_error_msg ~use:(fun () -> ()) 24 + | _ -> () 25 + in 26 + OS.Dir.exists src_dir >>= function 27 + | false -> Ok () 28 + | true -> 29 + OS.Dir.contents src_dir 30 + >>= fun files -> List.iter (copy_asset dst_dir) files; Ok () 31 + 32 + let copy_odig_css doc_dir dst_dir = 33 + OS.File.exists Fpath.(doc_dir / "style.css") >>= function 34 + | true -> Ok () 35 + | false -> 36 + let get_odig_etc = Cmd.(v "opam" % "var" % "odig:etc") in 37 + OS.Cmd.(run_out get_odig_etc |> to_string) >>= function 38 + | "#undefined" (* no comment *) -> Ok () 39 + | etcdir -> 40 + Fpath.of_string etcdir >>= fun etcdir -> 41 + OS.File.read Fpath.(etcdir / "ocamldoc.css") 42 + >>= fun cont -> OS.File.write Fpath.(dst_dir / "style.css") cont 43 + 44 + let doc_build_args pkg_name build_dir dev target = 45 + let verb = Cli.propagate_verbosity_to_pkg_file () in 46 + let pkg_name = Cmd.(v "--pkg-name" % pkg_name) in 47 + let build_dir = Cmd.(v "--build-dir" % Cmd.p build_dir) in 48 + let target = unixy_path target in 49 + let doc_flags = ["-docflags"; "-colorize-code,-charset,utf-8"; target ] in 50 + let raws = Cmd.of_list ~slip:"--raw" doc_flags in 51 + Cmd.(verb %% pkg_name %% build_dir %% raws) 52 + 53 + let build_doc pkg pkg_name build_dir dev = 54 + let out = OS.Cmd.to_stdout in 55 + let doc_dir = Fpath.v "doc" in 56 + let odocl = Fpath.(doc_dir / (if dev then "dev.odocl" else "api.odocl")) in 57 + Ok Fpath.(set_ext ".docdir" odocl / "index.html") 58 + >>= fun target -> Ok (doc_build_args pkg_name build_dir dev target) 59 + >>= fun args -> OS.Dir.current () 60 + >>= fun dir -> Topkg_care.Pkg.build pkg ~dir ~args ~out 61 + >>= fun () -> Ok Fpath.(build_dir // parent target) 62 + >>= fun dst_dir -> copy_assets doc_dir dst_dir 63 + >>= fun () -> copy_odig_css doc_dir dst_dir 64 + >>= fun () -> Ok dst_dir 65 + 66 + let browser_reload reload ~background ~browser dir = 67 + OS.Dir.current () 68 + >>= fun cwd -> Ok Fpath.(cwd // dir) 69 + >>= fun abs_dir -> match not (reload || background) with 70 + | true -> Ok abs_dir 71 + | false -> 72 + let uri = strf "file://%s" Fpath.(to_string abs_dir) in 73 + Webbrowser.reload ~background ~prefix:true ?browser uri 74 + >>= fun () -> Ok abs_dir 75 + 76 + let doc_cmd () pkg_file name build_dir dev reload background browser = 77 + begin 78 + let pkg = Topkg_care.Pkg.v ?build_dir ?name pkg_file in 79 + Topkg_care.Pkg.name pkg 80 + >>= fun pkg_name -> Topkg_care.Pkg.build_dir pkg 81 + >>= fun build_dir -> build_doc pkg pkg_name build_dir dev 82 + >>= fun docdir -> browser_reload reload ~background ~browser docdir 83 + >>= fun abs_docdir -> 84 + Logs.app (fun m -> 85 + m "Generated %s doc in %a" 86 + (if dev then "dev" else "API") Topkg_care.Pp.path abs_docdir); 87 + Ok 0 88 + end 89 + |> Cli.handle_error 90 + 91 + (* Command line interface *) 92 + 93 + open Cmdliner 94 + 95 + let reload_browser = 96 + let doc = "Open an URI of the documentation directory or reload an 97 + existing browser tab that holds a sub-page of the documentation." 98 + in 99 + Arg.(value & flag & info ["r"; "reload-browser"] ~doc) 100 + 101 + let dev = 102 + let doc = "Build the development documentation." in 103 + Arg.(value & flag & info ["d"; "dev"] ~doc) 104 + 105 + let doc = "Build the package's API documentation" 106 + let sdocs = Manpage.s_common_options 107 + let exits = Cli.exits 108 + let man_xrefs = [ `Main ] 109 + let man = 110 + [ `S Manpage.s_description; 111 + `P "The $(tname) command builds the package's API documentation. Use 112 + the option $(b,-r) to open or refresh the documentation in 113 + a WWW browser (see $(b,--browser) for details)."; 114 + `P "$(b,WARNING.) The way this command works is at the 115 + moment very ad-hoc and ocamlbuild specific. It will 116 + change in the future."; 117 + `P "Current support relies on having a doc/ directory at the root of the 118 + distribution. The ocamlbuild file doc/api.odocl defines the API 119 + documentation and the doc/dev.odocl the development documentation. 120 + The directory can also hold CSS, PNG, JPEG, GIF, SVG, WOFF, TTF, OTF 121 + files that are copied over to the generated documentation directory."; 122 + `P "The package's build system is invoked via `--raw` with the 123 + ocamlbuild documentation targets."; 124 + `P "If the doc/ directory has no doc/style.css file but odig(1) is 125 + installed, its ocamldoc stylesheet is used." ] 126 + 127 + let cmd = 128 + Cmd.v (Cmd.info "doc" ~doc ~sdocs ~exits ~man ~man_xrefs) @@ 129 + Term.(const doc_cmd $ Cli.setup $ Cli.pkg_file $ Cli.pkg_name $ Cli.build_dir 130 + $ dev $ reload_browser $ Webbrowser_cli.background $ 131 + Webbrowser_cli.browser)
+8
vendor/opam/topkg/src-bin/doc.mli
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** The [doc] command. *) 7 + 8 + val cmd : int Cmdliner.Cmd.t
+378
vendor/opam/topkg/src-bin/help.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + let topkg_manual = "Topkg manual" 7 + let version = "%%VERSION%%" 8 + 9 + (* Help manuals *) 10 + 11 + open Cmdliner 12 + 13 + let see_also ~cmds = 14 + let cmds = (Astring.String.concat ~sep:"(1), " ("topkg" :: cmds)) ^ "(1)" in 15 + [ `S Manpage.s_see_also; `P cmds ] 16 + 17 + let release = 18 + ("TOPKG-RELEASE", 7, "", version, topkg_manual), 19 + [ `S Manpage.s_name; 20 + `P "topkg-release - How to release a (topkg) package"; 21 + `S Manpage.s_description; 22 + `P "The basic release script is the following. Each step is 23 + refined and explained with more details below."; 24 + `Pre "\ 25 + topkg browse issues # Review remaining outstanding issues 26 + topkg status # Review the changes since last version 27 + topkg log edit # Write the release notes 28 + topkg log commit # Commit the release notes 29 + topkg tag # Tag the distribution with a version 30 + topkg distrib # Create the distribution archive 31 + topkg publish # Publish it on the WWW with its documentation 32 + topkg opam pkg # Create an opam package 33 + topkg opam submit # Submit it to OCaml's opam repository"; 34 + `P "The last four steps can be performed via a single invocation 35 + to topkg-bistro(1)."; 36 + `S "BASIC CHECKS"; 37 + `P "First have a look at the outstanding issues the package may have 38 + by checking the issue tracker."; 39 + `Pre "topkg browse issues"; 40 + `P "If the package's delegate supports issue tracker interaction 41 + (see topkg-delegate(7)), you can consult them directly in the 42 + terminal with:"; 43 + `Pre "topkg issue list"; 44 + `P "Basic checks are performed on the distribution archive when it is 45 + created, but save time by catching errors early. Hence test that 46 + your source repository lints and that it builds in the current build 47 + environment and that the package tests pass."; 48 + `Pre "\ 49 + topkg lint 50 + topkg build # Check out the generated opam install file too 51 + topkg test"; 52 + `S "WRITE THE RELEASE NOTES"; 53 + `P "Carefully write the release notes in the package's change log, these 54 + are essential time savers for users of the package. It may help to 55 + consult the list of changes that were committed since the last VCS 56 + version tag with:"; 57 + `Pre "topkg status"; 58 + `P "You can then write the release notes and commit them to the VCS with:"; 59 + `Pre "\ 60 + topkg log edit 61 + topkg log commit"; 62 + `P "The next step is simplified if the change log follows a certain 63 + format, see topkg-log(1) for details."; 64 + `P "The last two commands mentioned perform no magic, it is entirely up 65 + to you to use them or not. The first one simply opens the change log 66 + of the package in your \\$EDITOR and the second one commits it to 67 + your VCS with a dull, canned, commit message."; 68 + `S "VCS TAG THE RELEASE"; 69 + `P "Here again topkg provides a magic-less command that will simply 70 + extract the latest version tag from the package's change log 71 + and tag the VCS HEAD commit with it:"; 72 + `Pre "topkg tag"; 73 + `P "This will only work if the change log follows a certain format, 74 + see tokpg-log(1) for details. You can check the extracted tag is 75 + the one you wish before with:"; 76 + `Pre "topkg log -t"; 77 + `P "If you do not want to rely on topkg's broken extraction algorithms 78 + just specify it on the command line:"; 79 + `Pre "topkg tag v1.0.1"; 80 + `P "And if you really think topkg does a bad job at this, simply 81 + use your VCS directly to tag a release."; 82 + `S "CREATE THE DISTRIBUTION ARCHIVE AND PUBLISH IT"; 83 + `P "Now that the release is tagged in your VCS, generate a distribution 84 + archive for it in the build directory with:"; 85 + `Pre "topkg distrib"; 86 + `P "This uses the source tree of the HEAD commit for creating a 87 + distribution in the build directory. The distribution version 88 + string is the VCS tag description (e.g. git-describe(1)) of 89 + the HEAD commit. Alternatively it can be specified on the command 90 + line."; 91 + `P "If everything went well you can now publish the distribution and 92 + its documentation on the WWW. The exact actions that happen here 93 + depend on the package's delegate, see topkg-delegate(7) for more 94 + details."; 95 + `Pre "topkg publish"; 96 + `P "The distribution is now public. It may already have been picked up 97 + by other systems hence do not try to alter the archive and 98 + republish it with a different bit-stream after that point (if 99 + you are tempted to do this please consider taking a functional 100 + programming course). At worst 410 the archive from 101 + the WWW. But in most cases, if there is a problem with the 102 + archive, simply leave it there and publish a new one with an 103 + increased patch version number."; 104 + `S "SUBMIT TO OCAML'S OPAM REPOSITORY"; 105 + `P "The following steps still need the distribution archive created in 106 + the preceeding step to be in the build directory. If that's no 107 + longer the case but nothing moved in your VCS, you can simply 108 + invoke $(b,topkg distrib), it should produce a bit-wise identical 109 + archive. If the VCS moved checkout the distribution commit to 110 + regenerate the archive or provide, in the subsequent commands, 111 + the archive manually via the $(b,--dist-file) option, see 112 + topkg-opam(1) for details."; 113 + `P "To add the package to OCaml's opam repository, start by creating an 114 + opam package description in the build directory with:"; 115 + `Pre "topkg opam pkg"; 116 + `P "then simply submit it to the opam repository with:"; 117 + `Pre "topkg opam submit"; 118 + `P "The latter does nothing more than invoking opam-publish-submit(1) on 119 + the package description generated earlier."; 120 + `P "Congratulations. You are done. Ditch your computer."; 121 + `S "TROUBLESHOOTING"; 122 + `P "Here are a few troubleshooting scenarios and possible resolution."; 123 + `I ("Before publishing", 124 + "Anything that happens before the $(b,topkg publish) step, 125 + like a failing $(b,topkg distrib), is easy to resolve. Delete the 126 + version tag of your VCS, a $(b,topkg tag -d) will do, add 127 + some commits, adjust your release notes and start over."); 128 + `I ("opam submission build failure", 129 + "If the build failure is due to a missing constraint, follow the 130 + instruction of the next item to correct the opam file. If the failure 131 + is due to a defect in the distribution archive, call it a day and 132 + start over with a patch version release that corrects the problem. 133 + Do not try to reuse the version string of the failed release, other 134 + systems may already have picked up the broken archive."); 135 + `I ("opam repository maintainer and robot complaints", 136 + "These pesky but loved maintainers and robots... If they 137 + complain about certain aspects of your opam submission, you can either 138 + try to correct it manually from the opam package description found 139 + in the build directory and reinvoke $(b,topkg opam submit) or edit 140 + the opam file of the source repository and regenerate the opam Package 141 + description with $(b,topkg opam pkg) and the $(b,--pkg-opam) 142 + option. Note that if the VCS moved meanwhile you may have to use 143 + the various command line options of topkg-opam(1) to make sure 144 + you point to the right package version and distribution archive. 145 + In either case you should be aware that there will be a mismatch 146 + between the opam file in the distribution archive and the one 147 + you submitted to the opam repository. If this happens to be a 148 + problem, start over with a new patch version release."); 149 + `Blocks (see_also ~cmds:[]); ] 150 + 151 + let delegate = 152 + ("TOPKG-DELEGATE", 7, "", version, topkg_manual), 153 + [ `S Manpage.s_name; 154 + `P "topkg-delegate - The topkg delegate"; 155 + `S Manpage.s_description; 156 + `P "The delegate of a package is a program invoked by topkg to perform 157 + actions that are difficult or not desirable to standardize within 158 + topkg itself, namely:"; 159 + `I ("$(b,topkg publish)", 160 + "Publish distribution archives and derived artefacts."); 161 + `I ("$(b,topkg issue)", "Interact with the package's issue tracker."); 162 + `P "A sample delegate is provided at the end of this man page."; 163 + `S "DELEGATE LOOKUP PROCEDURE"; 164 + `P "The delegate used by a package is defined by the first match in the 165 + following lookup procedure."; 166 + `I ("1. Command line", "Specified with the $(b,--delegate) option on 167 + the command line."); 168 + `I ("2. Package description.", "Specified in the package description file, 169 + see topkg's API documentation."); 170 + `I ("3. Environment variable.", "Specified in the TOPKG_DELEGATE 171 + environment variable."); 172 + `I ("4. Homepage derived discovery.", "Consult the 'homepage' field of the 173 + package's opam file, extract the second-level domain of the URI as 174 + \\$NAME and uses the tool $(b,\\$NAME-topkg-delegate) iff it exists 175 + in the executable search path. For example if the homepage is 176 + http://www.example.org/mypackage, an existing 177 + $(b,example-topkg-delegate) tool will be used."); 178 + `I ("5. Transitory toy github fallback.", "If the previous step yields 179 + $(b,github-topkg-delegate) but that it doesn't exist in the 180 + executable search path. The $(b,toy-github-topkg-delegate) tool 181 + distributed with topkg-care is used. This tool will disappear in 182 + the future whenever a good github delegate emerges. Consult 183 + $(b,toy-github-topkg-delegate --help) for more information."); 184 + `S "DELEGATE PROTOCOL"; 185 + `P "The delegate is invoked by $(b,topkg) with a request in order to 186 + finish its own execution. This means that the delegate takes over 187 + $(b,topkg)'s standard channels and is in charge until the end of 188 + execution (except on errors, see below)."; 189 + `P "The delegate always gets information as command line arguments with 190 + file paths arguments given as absolute paths. The first argument is 191 + always 'ipc' and is followed by a verbosity parameter:"; 192 + `P "my-topkg-delegate ipc $(i,VERB) $(i,ARG) ..."; 193 + `P "$(i,VERB) will be either `quiet', `error', `warning', `info' or 194 + `debug' and the delegate must adjust its logging level appropriately. 195 + The remaining arguments are the request, see below for requests 196 + made by $(b,topkg)."; 197 + `P "The delegate must always exit with one of the following exit codes:"; 198 + `I ("0", "The request is successful."); `Noblank; 199 + `I ("1", "The request is unsupported."); `Noblank; 200 + `I ("2", "The request errored."); 201 + `P "Exit 0 must be returned iff the request could be fulfilled according 202 + to its semantics."; 203 + `P "Exit 1 must be returned iff the request arguments cannot be 204 + understood or if the request is not implemented by the delegate. 205 + In this case the delegate must produce no output."; 206 + `P "Exit 2 must be returned iff the request could not be fulfilled 207 + according to its semantics. In this case it is the delegate's duty 208 + to provide good error messages for diagnosis on standard output"; 209 + `P "In both non-zero exit codes, it is not the delegate's duty to 210 + try to save request data. In these cases $(b,topkg) will take over 211 + again in order to prevent user input data loss. This 212 + occurs for example on issue creation, so that the issue 213 + description the user may have input interactively is not 214 + lost but \"saved\" to standard output."; 215 + `S "PUBLISH DELEGATION"; 216 + `P "Publish delegation requests have the form:"; 217 + `P "publish $(i,ACTION) $(i,ARG)..."; 218 + `P "The following actions are currently defined."; 219 + `I ("publish distrib $(i,DISTRIB_URI) $(i,NAME) $(i,VERSION) 220 + $(i,MSG) $(i,ARCHIVE)", 221 + "Publish the distribution archive file $(i,ARCHIVE) for the package 222 + named $(i,NAME) at version $(i,VERSION) with publication 223 + message $(i,MSG). See topkg API's documentation 224 + for information about the value of $(i,DISTRIB_URI)."); 225 + `I ("publish doc $(i,DOC_URI) $(i,NAME) $(i,VERSION) $(i,MSG) $(i,DOCDIR)", 226 + "Publish the documentation directory $(i,DOCDIR) for the package 227 + named $(i,NAME) at version $(i,VERSION) with publication message 228 + $(i,MSG). $(i,DOC_URI) has the value of the doc field of the 229 + package's opam file."); 230 + `I ("publish alt $(i,DISTRIB_URI) $(i,KIND) $(i,NAME) $(i,VERSION) 231 + $(i,MSG) $(i,ARCHIVE)", 232 + "Alternative publication artefact named $(i,KIND). The semantics 233 + of the action is left to the delegate. The request arguments 234 + are the same as those of the distrib action."); 235 + `S "ISSUE DELEGATION"; 236 + `P "Issue delegation requests have the form:"; 237 + `P "issue $(i,ACTION) $(i,ISSUES_URI) $(i,ARG) ..."; 238 + `P "with $(i,ISSUES_URI) the value of the bug-reports field of the 239 + package's opam file or \"\" if there is no such field."; 240 + `P "The following actions are currently defined."; 241 + `I ("issue list $(i,ISSUES_URI)", 242 + "List open issues on standard output. Each issue should be on its 243 + own line with the format '$(i,ID) $(i,TITLE)'."); 244 + `I ("issue show $(i,ISSUES_URI) $(i,ID)", 245 + "Show details about issue $(i,ID) on standard output."); 246 + `I ("issue open $(i,ISSUES_URI) $(i,TITLE) $(i,MSG)", 247 + "Create an issue with title $(i,TITLE) and description $(i,MSG) 248 + (may be an empty string). If the request is successful the 249 + delegate should communicate the resulting issue identifier in some 250 + way on standard output."); 251 + `I ("issue close $(i,ID) $(i,MSG)", 252 + "Close issue $(i,ID) with closing message $(i,MSG)."); 253 + `S "SAMPLE UNSUPPORTIVE DELEGATE"; 254 + `P "This delegate script can be used as a blueprint. All requests 255 + are simply unsupported."; 256 + `Pre "\ 257 + #!/usr/bin/env ocaml 258 + #use \"topfind\" 259 + #require \"bos.setup\" 260 + open Bos_setup 261 + 262 + let unsupported = Ok 1 263 + 264 + let publish = function 265 + | \"distrib\" :: uri :: name :: version :: msg :: archive :: _ -> 266 + unsupported 267 + | \"doc\" :: uri :: name :: version :: msg :: docdir :: _ -> 268 + unsupported 269 + | \"alt\" :: kind :: uri :: name :: version :: msg :: archive :: _ -> 270 + unsupported 271 + | args -> 272 + unsupported 273 + 274 + let issue = function 275 + | \"list\" :: uri :: _ -> unsupported 276 + | \"show\" :: uri :: id :: _ -> unsupported 277 + | \"open\" :: uri :: title :: descr :: _ -> unsupported 278 + | \"close\" :: uri :: id :: msg :: _ -> unsupported 279 + | args -> unsupported 280 + 281 + let request = function 282 + | \"publish\" :: args -> publish args 283 + | \"issue\" :: args -> issue args 284 + | args -> unsupported 285 + 286 + let main () = 287 + let doc = \"the unsupportive delegate\" in 288 + begin match OS.Arg.(parse ~doc ~pos:string ()) with 289 + | \"ipc\" :: verbosity :: req -> 290 + Logs.level_of_string verbosity 291 + >>= fun level -> Logs.set_level level; request req 292 + | \"ipc\" :: [] -> 293 + R.error_msg \"malformed delegate request, verbosity is missing\" 294 + | args -> 295 + R.error_msgf \"unknown arguments: %s\" (String.concat ~sep:\" \" args) 296 + end 297 + |> Logs.on_error_msg ~use:(fun () -> 2) 298 + 299 + let () = exit (main ()) 300 + "; 301 + `Blocks (see_also ~cmds:["topkg-issue"; "topkg-publish"]); ] 302 + 303 + let troubleshoot = 304 + ("TOPKG-TROUBLESHOOT", 7, "", version, topkg_manual), 305 + [ `S Manpage.s_name; 306 + `P "topkg-troubleshoot - A few troubleshooting tips"; 307 + `S Manpage.s_description; 308 + `P "If you get into trouble try the following to get a better undersanding 309 + of what is happening."; 310 + `S "ASK FOR MORE LOGGING"; 311 + `P "Invoke $(b,topkg) with $(b,-v), $(b,-v -v), or use the 312 + TOPKG_VERBOSITY environment variable; see the $(b,--verbosity) 313 + option."; 314 + `P "Messages comming from the $(b,topkg) tool are prefixed 315 + by 'topkg:' while those comming from the package description are 316 + prefixed by its base name, usually 'pkg.ml:'."; 317 + `S "DEBUG THE GENERATED OPAM INSTALL FILE"; 318 + `P "To debug the generated opam install file according to the build 319 + configuration you don't need to build the package. Use the 320 + $(b,--dry-run) (or $(b,-d)) option and add a little bit of logging to 321 + output the build configuration that was determined:"; 322 + `Pre "pkg/pkg.ml build -d -v [OPTION]...";`Noblank; 323 + `Pre "topkg build -d -v [OPTION]... # mostly equivalent"; 324 + `S "DEBUG DEV PACKAGE INSTALLS"; 325 + `P "If you need more information about what happens when dev packages 326 + are installed (VCS pins or VCS packages) in opam, for example the 327 + actual watermark values, invoke opam as follows:"; 328 + `P "TOPKG_VERBOSITY=debug opam upgrade mypkg -v"; 329 + `S "RELEASE PROCESS TROUBLES"; 330 + `P "See the TROUBLESHOOTING section of topkg-release(7)."; 331 + `Blocks (see_also ~cmds:[]) ] 332 + 333 + (* Help command *) 334 + 335 + let pages = 336 + [ "release", release; 337 + "delegate", delegate; 338 + "troubleshoot", troubleshoot; ] 339 + 340 + let help man_format topic commands = match topic with 341 + | None -> `Help (man_format, None) 342 + | Some topic -> 343 + let topics = "topics" :: commands @ (List.map fst pages) in 344 + let topics = List.sort compare topics in 345 + let conv = 346 + Cmdliner.Arg.(conv_parser (enum (List.rev_map (fun s -> (s, s)) topics))) 347 + in 348 + match conv topic with 349 + | Error (`Msg e) -> `Error (false, e) 350 + | Ok t when List.mem t commands -> `Help (man_format, Some t) 351 + | Ok t when t = "topics" -> 352 + Fmt.pr "@[<v>%a@]@." Fmt.(list string) topics; 353 + `Ok 0 354 + | Ok t -> 355 + let man = try List.assoc t pages with Not_found -> assert false in 356 + Fmt.pr "%a" (Cmdliner.Manpage.print man_format) man; 357 + `Ok 0 358 + 359 + (* Command line interface *) 360 + 361 + open Cmdliner 362 + 363 + let topic = 364 + let doc = "The topic to get help on, `topics' lists the topic." in 365 + Arg.(value & pos 0 (some string) None & info [] ~docv:"TOPIC" ~doc) 366 + 367 + let doc = "Show help about topkg" 368 + let sdocs = Manpage.s_common_options 369 + let exits = Cli.exits 370 + let man_xrefs = [`Main] 371 + let man = 372 + [ `S Manpage.s_description; 373 + `P "The $(tname) command shows help about $(mname)."; 374 + `P "Use `topics' as $(i,TOPIC) to get a list of topics." ] 375 + 376 + let cmd = 377 + Cmd.v (Cmd.info "help" ~doc ~exits ~man ~man_xrefs) @@ 378 + Term.(ret (const help $ Arg.man_format $ topic $ Term.choice_names))
+8
vendor/opam/topkg/src-bin/help.mli
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** The [help] command. *) 7 + 8 + val cmd : int Cmdliner.Cmd.t
+50
vendor/opam/topkg/src-bin/ipc.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + open Bos_setup 7 + 8 + let opam_fields file = 9 + begin 10 + Logs.info begin fun m -> 11 + m ~header:"IPC" "opam fields of %s with cwd %a" file 12 + (R.pp ~ok:Fpath.pp ~error:R.pp_msg) (OS.Dir.current ()) 13 + end; 14 + Fpath.of_string file 15 + >>= fun file -> Topkg_care.Opam.File.fields file 16 + >>= fun fs -> Ok (String.Map.bindings fs) 17 + >>= fun fs -> Ok (Topkg.Private.(Codec.enc Opam.File.codec fs)) 18 + >>= fun enc -> OS.File.(write dash enc) 19 + end 20 + |> R.reword_error_msg ~replace:true 21 + (fun msg -> R.msgf "ipc opam-fields: %s" msg) 22 + 23 + let ipc_answer = function 24 + | ["opam-fields"; file] -> opam_fields file 25 + | args -> R.error_msgf "ipc: unknown arguments %a" Cmd.dump (Cmd.of_list args) 26 + 27 + let ipc () args = match ipc_answer args with 28 + | Ok () -> `Ok 0 29 + | Error (`Msg msg) -> `Error (false, msg) 30 + 31 + (* Command line interface *) 32 + 33 + open Cmdliner 34 + 35 + let args = 36 + let doc = "IPC call arguments" in 37 + Arg.(value (pos_all string [] & info [] ~doc ~docv:"ARG")) 38 + 39 + let doc = "Interprocess communication with package description files" 40 + let sdocs = Manpage.s_common_options 41 + let exits = Cli.exits 42 + let man_xrefs = [`Main] 43 + let man = 44 + [ `S Manpage.s_description; 45 + `P "The $(tname) command is used by package description files. It is 46 + undocumented." ] 47 + 48 + let cmd = 49 + Cmd.v (Cmd.info "ipc" ~doc ~sdocs ~exits ~man ~man_xrefs) @@ 50 + Term.(ret (const ipc $ Cli.setup $ args))
+8
vendor/opam/topkg/src-bin/ipc.mli
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** The [ipc] command. *) 7 + 8 + val cmd : int Cmdliner.Cmd.t
+145
vendor/opam/topkg/src-bin/issue.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + open Bos_setup 7 + 8 + let get_id = function 9 + | Some id -> Ok id 10 + | None -> R.error_msgf "No issue ID specified" 11 + 12 + let get_issue_msg ~info = function 13 + | Some "" -> Ok None 14 + | Some msg -> Ok (Some (String.cuts ~sep:"\n" msg)) 15 + | None -> 16 + let is_msg s = not (String.is_prefix ~affix:"#" s) in 17 + let rec rem_white_prefix = function 18 + | l :: ls when String.for_all Char.Ascii.is_white l -> rem_white_prefix ls 19 + | ls -> ls 20 + in 21 + OS.File.tmp "topkg-issue-msg-%s" 22 + >>= fun f -> OS.File.write f info 23 + >>= fun () -> Topkg_care.Text.edit_file f 24 + >>= function 25 + | 0 -> 26 + OS.File.read f >>= fun m -> 27 + let msg = List.filter is_msg (String.cuts ~sep:"\n" m) in 28 + begin match rem_white_prefix msg with 29 + | [] -> Ok None 30 + | lines -> Ok (Some lines) 31 + end 32 + | n -> 33 + Logs.err (fun m -> m "Editor exited with non-zero error code."); 34 + Ok None 35 + 36 + (* Actions *) 37 + 38 + let issue_show pkg ~id = 39 + get_id id >>= fun id -> Topkg_care.Delegate.issue_show pkg ~id 40 + 41 + let issue_open pkg msg = 42 + let open_info = 43 + "\n\ 44 + # Please enter an issue description. The first non-blank line will be\n\ 45 + # the issue title and the rest the issue description. Lines starting\n\ 46 + # with '#' will be ignored. An empty description aborts the action." 47 + in 48 + get_issue_msg ~info:open_info msg >>= function 49 + | None -> 50 + Logs.app (fun m -> m "Open issue aborted due to empty issue message."); 51 + Ok (); 52 + | Some lines -> 53 + let title, body = match lines with 54 + | title :: body -> title, String.(trim @@ concat ~sep:"\n" body) 55 + | [] -> assert false 56 + in 57 + Topkg_care.Delegate.issue_open pkg ~title ~body 58 + 59 + let issue_close pkg ~id msg = 60 + let close_info = 61 + "\n\ 62 + # Please enter a closing message. Lines starting with '#' will\n\ 63 + # be ignored. An empty message aborts the action." 64 + in 65 + get_id id 66 + >>= fun id -> get_issue_msg ~info:close_info msg 67 + >>= function 68 + | None -> 69 + Logs.app 70 + (fun m -> m "Close issue %s aborted due to empty issue message." id); 71 + Ok () 72 + | Some lines -> 73 + let msg = String.(trim @@ concat ~sep:"\n" lines) in 74 + Topkg_care.Delegate.issue_close pkg ~id ~msg 75 + 76 + (* Command *) 77 + 78 + let issue () pkg_file opam delegate action id msg = 79 + begin 80 + let pkg = Topkg_care.Pkg.v ?opam ?delegate pkg_file in 81 + let ret = match action with 82 + | `List -> Topkg_care.Delegate.issue_list pkg 83 + | `Show -> issue_show pkg ~id 84 + | `Open -> issue_open pkg msg 85 + | `Close -> issue_close pkg ~id msg 86 + in 87 + ret >>= fun () -> Ok 0 88 + end 89 + |> Cli.handle_error 90 + 91 + (* Command line interface *) 92 + 93 + open Cmdliner 94 + 95 + let action = 96 + let action = ["list",`List; "show",`Show; "open",`Open; "close",`Close;] in 97 + let doc = strf "The action to perform. $(docv) must be one of %s." 98 + (Arg.doc_alts_enum action) 99 + in 100 + let cmd = Arg.enum action in 101 + Arg.(value & pos 0 cmd `List & info [] ~doc ~docv:"ACTION") 102 + 103 + let id = 104 + let doc = "An issue ID of the package's issue tracker." in 105 + Arg.(value & pos 1 (some string) None & info [] ~doc ~docv:"ID") 106 + 107 + let msg = 108 + let doc = "For $(b,open) and $(b,close), $(docv) is the issue message. 109 + Prevents the interactive prompt for the message." 110 + in 111 + let docv = "MSG" in 112 + Arg.(value & opt (some string) None & info ["m"; "message"] ~doc ~docv) 113 + 114 + let doc = "Interact with the package's issue tracker" 115 + let sdocs = Manpage.s_common_options 116 + let exits = Cli.exits 117 + let envs = 118 + [ Cmd.Env.info "EDITOR" ~doc:"The editor used to edit issue messages."; 119 + Cmd.Env.info "TOPKG_DELEGATE" ~doc:"The package delegate to use, see 120 + topkg-delegate(7)." ] 121 + 122 + let man_xrefs = [ `Main ] 123 + let man = 124 + [ `S Manpage.s_synopsis; 125 + `P "$(mname) $(tname) [$(i,OPTION)]... [$(i,ACTION)]..."; 126 + `S Manpage.s_description; 127 + `P "The $(tname) command interacts with the package's issue 128 + tracker via the package delegate. See topkg-delegate(7) for more 129 + details."; 130 + `P "To consult the issues in a WWW browser invoke 131 + $(b,topkg browse issues), no delegate is needed for this."; 132 + `S "ACTIONS"; 133 + `I ("$(b,list) (default)", 134 + "List open issues."); 135 + `I ("$(b,show) $(i,ID)", 136 + "Show information about issue $(i,ID)."); 137 + `I ("$(b,open)", 138 + "Open a new issue."); 139 + `I ("$(b,close) $(i,ID)", 140 + "Close issue $(i,ID).") ] 141 + 142 + let cmd = 143 + Cmd.v (Cmd.info "issue" ~doc ~sdocs ~exits ~envs ~man ~man_xrefs) @@ 144 + Term.(const issue $ Cli.setup $ Cli.pkg_file $ Cli.opam $ Cli.delegate $ 145 + action $ id $ msg)
+8
vendor/opam/topkg/src-bin/issue.mli
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** The [issue] command. *) 7 + 8 + val cmd : int Cmdliner.Cmd.t
+54
vendor/opam/topkg/src-bin/lint.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + open Bos_setup 7 + 8 + let lint () pkg_file ignore_pkg lints = 9 + begin 10 + let pkg = Topkg_care.Pkg.v pkg_file in 11 + OS.Dir.current () 12 + >>= fun dir -> Topkg_care.Pkg.lint ~ignore_pkg pkg ~dir lints 13 + end 14 + |> Cli.handle_error 15 + 16 + (* Command line interface *) 17 + 18 + open Cmdliner 19 + 20 + let lints = 21 + let test = [ "custom", `Custom; 22 + "std-files", `Std_files; 23 + "meta", `Meta; 24 + "opam", `Opam; 25 + "deps", `Deps; ] 26 + in 27 + let doc = strf "Test to perform. $(docv) must be one of %s. If unspecified 28 + all tests are performed." (Arg.doc_alts_enum test) 29 + in 30 + let test = Arg.enum test in 31 + let docv = "TEST" in 32 + Arg.(value & pos_all test Topkg_care.Pkg.lint_all & info [] ~doc ~docv) 33 + 34 + let ignore_pkg = 35 + let doc = "Ignore package description file." in 36 + Arg.(value & flag & info ["i"; "ignore-pkg"] ~doc) 37 + 38 + let doc = "Check package distribution consistency and conventions" 39 + let sdocs = Manpage.s_common_options 40 + let exits = Cmd.Exit.info 1 ~doc:"on lint failure" :: Cli.exits 41 + let man_xrefs = [`Main; `Cmd "distrib"] 42 + let man = 43 + [ `S Manpage.s_description; 44 + `P "The $(tname) command makes tests on a package distribution or 45 + source repository. It checks that standard files exist, that 46 + ocamlfind META files pass the ocamlfind lint test, that opam package 47 + files pass the opam lint test and that the opam dependencies are 48 + consistent with those of the build system."; 49 + `P "Linting is automatically performed on distribution generation, see 50 + topkg-distrib(1) for more details." ] 51 + 52 + let cmd = 53 + Cmd.v (Cmd.info "lint" ~doc ~sdocs ~exits ~man ~man_xrefs) 54 + Term.(const lint $ Cli.setup $ Cli.pkg_file $ ignore_pkg $ lints)
+8
vendor/opam/topkg/src-bin/lint.mli
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** The [lint] command. *) 7 + 8 + val cmd : int Cmdliner.Cmd.t
+126
vendor/opam/topkg/src-bin/log.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + open Bos_setup 7 + 8 + (* Actions *) 9 + 10 + let show change_log last last_version no_pager = 11 + let text = 12 + if not (last || last_version) then OS.File.read change_log else 13 + (Topkg_care.Text.change_log_file_last_entry change_log 14 + >>= fun (v, (h, t)) -> Ok (if last_version then v else strf "%s\n%s" h t)) 15 + in 16 + text 17 + >>= fun text -> Topkg_care.Text.find_pager ~don't:(no_pager || last_version) 18 + >>= function 19 + | None -> Logs.app (fun m -> m "%s" text); Ok () 20 + | Some pager -> OS.Cmd.(in_string text |> run_in pager) 21 + 22 + let commit change_log = 23 + let change_log = Fpath.to_string change_log in 24 + Topkg.Vcs.get () 25 + >>= fun repo -> Topkg.Vcs.file_is_dirty repo change_log 26 + >>= function 27 + | true -> Topkg.Vcs.commit_files repo ~msg:"Update change log." [change_log] 28 + | false -> 29 + Logs.app (fun m -> m "No changes to commit in %s" change_log); Ok () 30 + 31 + (* Command *) 32 + 33 + let log () pkg_file change_log action last last_version no_pager = 34 + begin 35 + let pkg = Topkg_care.Pkg.v ?change_log pkg_file in 36 + Topkg_care.Pkg.change_log pkg 37 + >>= fun change_log -> match action with 38 + | `Show -> show change_log last last_version no_pager >>= fun () -> Ok 0 39 + | `Edit -> Topkg_care.Text.edit_file change_log 40 + | `Commit -> commit change_log >>= fun () -> Ok 0 41 + end 42 + |> Cli.handle_error 43 + 44 + (* Command line interface *) 45 + 46 + open Cmdliner 47 + 48 + let action = 49 + let action = [ "show", `Show; "edit", `Edit; "commit", `Commit] in 50 + let doc = strf "The action to perform. $(docv) must be one of %s." 51 + (Arg.doc_alts_enum action) 52 + in 53 + let cmd = Arg.enum action in 54 + Arg.(value & pos 0 cmd `Show & info [] ~doc ~docv:"ACTION") 55 + 56 + let no_pager = 57 + let doc = "Do not pipe the output into a pager. This automatically 58 + happens if the TERM environment variable is 'dumb' or undefined." 59 + in 60 + Arg.(value & flag & info ["no-pager"] ~doc) 61 + 62 + let last = 63 + let doc = "Show only the change log of the last version. Extracted as the 64 + first marked up section of the change log." 65 + in 66 + Arg.(value & flag & info ["l"; "last"] ~doc) 67 + 68 + let last_version = 69 + let doc = "Show only the version string of the last version. Extracted as 70 + the first token of the title of the first marked up section of 71 + the change log. Implies $(b,--no-pager)."; 72 + in 73 + Arg.(value & flag & info ["t"; "last-version"] ~doc) 74 + 75 + let doc = "Show and edit the package's change log" 76 + let sdocs = Manpage.s_common_options 77 + let exits = Cli.exits 78 + let envs = 79 + [ Cmd.Env.info "EDITOR" ~doc:"The editor used to edit the change log."; 80 + Cmd.Env.info "PAGER" ~doc:"The pager used to consult the change log."; 81 + Cmd.Env.info "TERM" ~doc:"See option $(b,--no-pager)." ] 82 + 83 + let man_xrefs = [ `Main; `Cmd "publish"; `Cmd "tag"; `Cmd "opam"; ] 84 + let man = 85 + [ `S Manpage.s_description; 86 + `P "The $(tname) command shows, edits and commits 87 + the package's change log."; 88 + `S "CHANGE LOG FORMAT"; 89 + `P "To be able to extract the version and changes of the last distribution, 90 + a well defined change log format is assumed. Not abiding to the 91 + format is not catastrophic but may hinder or derail some facilities 92 + provided by topkg."; 93 + `P "The format assumes that the change log is written either in Markdown 94 + (default or .md extension) or Asciidoc (.asciidoc or .adoc extension). 95 + A change log is a list of marked up sections. A section is 96 + a header of any level until the next header at the same level or 97 + the end of file. For example here are two Markdown sections:"; 98 + `Pre "\ 99 + v2.0.0 100 + ------ 101 + ### New features 102 + etc. 103 + ### Breaking changes 104 + etc. 105 + 106 + v1.6.0 1995-09-12 107 + ----------------- 108 + etc."; 109 + `P "The first marked up section in the file is taken as being the 110 + change log for the last distribution; use $(b,topkg log -l) 111 + to check that it is parsed correctly. This is used by topkg-publish(1) 112 + and topkg-opam(1) to enrich distribution publication."; 113 + `P "The first token of the first section header title is taken as being the 114 + version string of the distribution; use $(b,topkg log -t) to check 115 + that it is parsed correctly. It is used by topkg-tag(1) to tag the 116 + source repository."; 117 + `S "ACTIONS"; 118 + `I ("$(b,show) (default)", "shows the package's change log."); 119 + `I ("$(b,edit)", "edit the package's change log."); 120 + `I ("$(b,commit)", "commit changes made to the package's change log to the 121 + VCS.") ] 122 + 123 + let cmd = 124 + Cmd.v (Cmd.info "log" ~doc ~sdocs ~exits ~envs ~man) @@ 125 + Term.(const log $ Cli.setup $ Cli.pkg_file $ Cli.change_log $ action $ 126 + last $ last_version $ no_pager)
+8
vendor/opam/topkg/src-bin/log.mli
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** The [log] command. *) 7 + 8 + val cmd : int Cmdliner.Cmd.t
+200
vendor/opam/topkg/src-bin/opam.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + open Bos_setup 7 + 8 + let get_opam_publish_file p opam_publish_file = match opam_publish_file with 9 + | Some file -> Ok file 10 + | None -> 11 + Topkg_care.Pkg.build_dir p 12 + >>= fun bdir -> Topkg_care.Pkg.distrib_filename ~opam:true p 13 + >>= fun fname -> Ok Fpath.(bdir // fname + ".opam") 14 + 15 + let descr pkg = 16 + Topkg_care.Pkg.opam_descr pkg >>= fun (descr, _) -> 17 + Logs.app (fun m -> m "%s" (Topkg_care.Opam.Descr.to_string descr)); 18 + Ok 0 19 + 20 + let pkg pkg dist_pkg opam_publish_file = 21 + let log_pkg dst = 22 + Logs.app (fun m -> m "Wrote opam package %a" Topkg_care.Pp.path dst) 23 + in 24 + let warn_if_vcs_dirty () = 25 + Cli.warn_if_vcs_dirty "The opam package may be inconsistent with the \ 26 + distribution." 27 + in 28 + let opam_file_content opam (descr, from_opam) url = 29 + let descr = 30 + if from_opam then "" else 31 + (Topkg_care.Opam.Descr.to_opam_fields descr ^ "\n") 32 + in 33 + strf "%s\n%s%s" opam descr (Topkg_care.Opam.Url.to_opam_section url) 34 + in 35 + get_opam_publish_file pkg opam_publish_file 36 + >>= fun dst -> Topkg_care.Pkg.opam pkg 37 + >>= fun opam -> OS.File.read opam 38 + >>= fun opam -> Topkg_care.Pkg.opam_descr pkg 39 + >>= fun descr -> Topkg_care.Pkg.distrib_file dist_pkg 40 + >>= fun distrib_file -> Topkg_care.Pkg.distrib_uri dist_pkg 41 + >>= fun uri -> Topkg_care.Opam.Url.with_distrib_file ~uri distrib_file 42 + >>= fun url -> OS.File.write dst (opam_file_content opam descr url) 43 + >>= fun () -> log_pkg dst; warn_if_vcs_dirty () 44 + >>= fun () -> 45 + Ok 0 46 + 47 + let submit pkg opam_pkg_dst = 48 + Topkg_care.Opam.ensure_publish () 49 + >>= fun () -> get_opam_publish_file pkg opam_pkg_dst 50 + >>= fun opam_file -> OS.File.exists opam_file 51 + >>= function 52 + | false -> 53 + Logs.err (fun m -> m "Package@ file %a@ does@ not@ exist. Did@ you@ \ 54 + forget@ to@ invoke 'topkg opam pkg' ?" 55 + Fpath.pp opam_file); 56 + Ok 1 57 + | true -> 58 + Logs.app (fun m -> m "Publishing %a" Topkg_care.Pp.path opam_file); 59 + Topkg_care.Pkg.publish_msg pkg 60 + >>= fun msg -> Topkg_care.Opam.submit ~opam_file ~msg () 61 + >>= fun () -> Ok 0 62 + 63 + let field pkg field = match field with 64 + | None -> Logs.err (fun m -> m "Missing FIELD positional argument"); Ok 1 65 + | Some field -> 66 + Topkg_care.Pkg.opam_field pkg field 67 + >>= function 68 + | Some v -> Logs.app (fun m -> m "%s" (String.concat ~sep:" " v)); Ok 0 69 + | None -> 70 + Topkg_care.Pkg.opam pkg >>= fun opam -> 71 + Logs.err (fun m -> m "%a: field %s is undefined" Fpath.pp opam field); 72 + Ok 1 73 + 74 + (* Command *) 75 + 76 + let opam () pkg_file build_dir 77 + dist_name dist_version dist_opam dist_uri dist_file 78 + opam_publish_file pkg_name pkg_version pkg_opam pkg_descr 79 + readme change_log publish_msg action field_name 80 + = 81 + let p = 82 + Topkg_care.Pkg.v 83 + ?build_dir ?name:pkg_name ?version:pkg_version ?opam:pkg_opam 84 + ?opam_descr:pkg_descr ?readme ?change_log ?publish_msg pkg_file 85 + in 86 + begin match action with 87 + | `Descr -> descr p 88 + | `Pkg -> 89 + let dist_p = 90 + Topkg_care.Pkg.v 91 + ?build_dir ?name:dist_name ?version:dist_version ?opam:dist_opam 92 + ?distrib_uri:dist_uri ?distrib_file:dist_file ?readme ?change_log 93 + ?publish_msg pkg_file 94 + in 95 + pkg p dist_p opam_publish_file 96 + | `Submit -> submit p opam_publish_file 97 + | `Field -> field p field_name 98 + end 99 + |> Cli.handle_error 100 + 101 + (* Command line interface *) 102 + 103 + open Cmdliner 104 + 105 + let action = 106 + let action = [ "descr", `Descr; "pkg", `Pkg; "submit", `Submit; 107 + "publish", `Submit; "field", `Field ] 108 + in 109 + let doc = strf "The action to perform. $(docv) must be one of %s." 110 + (Arg.doc_alts_enum action) 111 + in 112 + let action = Arg.enum action in 113 + Arg.(required & pos 0 (some action) None & info [] ~doc ~docv:"ACTION") 114 + 115 + let field = 116 + let doc = "the field to output ($(b,field) action)" in 117 + Arg.(value & pos 1 (some string) None & info [] ~doc ~docv:"FIELD") 118 + 119 + let opam_publish_file = 120 + let doc = "The file to use to publish the opam package. If absent the 121 + file $(i,BUILD_DIR)/$(i,PKG_NAME).$(i,PKG_VERSION).opam in the 122 + build directory is used (see options $(b,--build-dir), 123 + $(b,--pkg-name) and $(b,--pkg-version))" 124 + in 125 + let docv = "FILE" in 126 + Arg.(value & opt (some Cli.path_arg) None & info ["opam-publish-file"] 127 + ~doc ~docv) 128 + 129 + let pkg_version = 130 + let doc = "The version string $(docv) of the opam package. If absent provided 131 + provided by the VCS tag description of the HEAD commit." 132 + in 133 + let docv = "PKG_NAME" in 134 + Arg.(value & opt (some string) None & info ["pkg-version"] ~doc ~docv) 135 + 136 + let pkg_opam = 137 + let doc = "The opam file to use for the opam package. If absent uses the 138 + opam file mentioned in the package description that corresponds 139 + to the opam package name $(i,PKG_NAME) (see option 140 + $(b,--pkg-name))" 141 + in 142 + let docv = "FILE" in 143 + Arg.(value & opt (some Cli.path_arg) None & info ["pkg-opam"] ~doc ~docv) 144 + 145 + let pkg_descr = 146 + let doc = "The opam descr file to use for the opam package. If absent 147 + and the opam file has synopsis and description fields this 148 + is used for the description. It absent and there are no such 149 + fields in the opam file and the opam file name 150 + (see $(b,--pkg-opam)) has a `.opam` 151 + extension, uses an existing file with the same path but a `.descr` 152 + extension. If the opam file name is `opam` uses a `descr` 153 + file in the same directory. If these files are not found 154 + a description is extracted from the the readme (see 155 + option $(b,--readme)) as follow: the first marked up 156 + section of the readme is extracted, its title is parsed 157 + according to the pattern '\\$(NAME) \\$(SEP) \\$(SYNOPSIS)', 158 + the body of the section is the long description. A few 159 + lines are filtered out: lines that start with either 160 + 'Home page:', 'Contact:' or '%%VERSION'." 161 + in 162 + let docv = "FILE" in 163 + Arg.(value & opt (some Cli.path_arg) None & info ["pkg-descr"] ~doc ~docv) 164 + 165 + let doc = "Interaction with opam and the OCaml opam repository" 166 + let sdocs = Manpage.s_common_options 167 + let envs = 168 + [ Cmd.Env.info "TOPKG_OPAM_PUBLISH" ~doc:"The $(b,opam-publish) tool to use 169 + to submit packages." ] 170 + 171 + let man_xrefs = [`Main; `Cmd "distrib" ] 172 + let man = 173 + [ `S Manpage.s_synopsis; 174 + `P "$(mname) $(tname) [$(i,OPTION)]... $(i,ACTION)"; 175 + `S Manpage.s_description; 176 + `P "The $(tname) command provides a few actions to interact with 177 + opam and the OCaml opam repository."; 178 + `S "ACTIONS"; 179 + `I ("$(b,descr)", 180 + "extract and print an opam descr file. This is used by the 181 + $(b,pkg) action. See the $(b,--pkg-descr) option for details."); 182 + `I ("$(b,pkg)", 183 + "create an opam package description for a distribution. 184 + The action needs a distribution archive to operate, see 185 + topkg-distrib(1) or the $(b,--dist-file) option."); 186 + `I ("$(b,submit) or $(b,publish)", 187 + "submits a package created with the action $(b,pkg) the OCaml 188 + opam repository. Requires the $(b,opam-publish) tool to be 189 + installed."); 190 + `I ("$(b,field) $(i,FIELD)", 191 + "outputs the field $(i,FIELD) of the package's opam file."); ] 192 + 193 + let cmd = 194 + Cmd.v (Cmd.info "opam" ~doc ~sdocs ~envs ~man ~man_xrefs) @@ 195 + Term.(const opam $ Cli.setup $ Cli.pkg_file $ Cli.build_dir $ 196 + Cli.dist_name $ Cli.dist_version $ Cli.dist_opam $ 197 + Cli.dist_uri $ Cli.dist_file $ 198 + opam_publish_file $ Cli.pkg_name $ pkg_version $ pkg_opam $ 199 + pkg_descr $ Cli.readme $ Cli.change_log $ Cli.publish_msg $ 200 + action $ field)
+8
vendor/opam/topkg/src-bin/opam.mli
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** The [opam] command. *) 7 + 8 + val cmd : int Cmdliner.Cmd.t
+125
vendor/opam/topkg/src-bin/publish.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + open Bos_setup 7 + 8 + let absolute path = OS.Dir.current () >>| fun cwd -> Fpath.(cwd // path) 9 + 10 + let gen_doc dir = 11 + let do_doc () = 12 + OS.Cmd.run Cmd.(v "topkg" % "doc") 13 + >>| fun () -> Fpath.(dir / "_build" / "doc" / "api.docdir") 14 + in 15 + R.join @@ OS.Dir.with_current dir do_doc () 16 + 17 + let publish_doc pkg = 18 + Topkg_care.Pkg.distrib_file pkg 19 + >>= fun distrib_file -> Topkg_care.Pkg.publish_msg pkg 20 + >>= fun msg -> Topkg_care.Archive.untbz ~clean:true distrib_file 21 + >>= fun dir -> gen_doc dir 22 + >>= fun docdir -> absolute docdir 23 + >>= fun docdir -> Topkg_care.Delegate.publish_doc pkg ~msg ~docdir 24 + 25 + let publish_distrib pkg = 26 + Topkg_care.Pkg.distrib_file pkg 27 + >>= fun distrib_file -> Topkg_care.Pkg.publish_msg pkg 28 + >>= fun msg -> absolute distrib_file 29 + >>= fun archive -> Topkg_care.Delegate.publish_distrib pkg ~msg ~archive 30 + 31 + let publish_alt pkg kind = 32 + Topkg_care.Pkg.distrib_file pkg 33 + >>= fun distrib_file -> Topkg_care.Pkg.publish_msg pkg 34 + >>= fun msg -> absolute distrib_file 35 + >>= fun archive -> Topkg_care.Delegate.publish_alt pkg ~kind ~msg ~archive 36 + 37 + let publish () 38 + pkg_file build_dir name version opam delegate change_log distrib_uri 39 + distrib_file publish_msg publish_artefacts 40 + = 41 + begin 42 + let publish_artefacts = match publish_artefacts with 43 + | [] -> None 44 + | v -> Some v 45 + in 46 + let pkg = Topkg_care.Pkg.v ?name ?version ?build_dir ?opam ?delegate 47 + ?change_log ?distrib_uri ?distrib_file ?publish_msg 48 + ?publish_artefacts pkg_file 49 + in 50 + let publish_artefact acc artefact = 51 + acc >>= fun acc -> match artefact with 52 + | `Doc -> publish_doc pkg 53 + | `Distrib -> publish_distrib pkg 54 + | `Alt kind -> publish_alt pkg kind 55 + in 56 + Topkg_care.Pkg.publish_artefacts pkg 57 + >>= fun todo -> List.fold_left publish_artefact (Ok ()) todo 58 + >>= fun () -> Ok 0 59 + end 60 + |> Cli.handle_error 61 + 62 + (* Command line interface *) 63 + 64 + open Cmdliner 65 + 66 + let artefacts = 67 + let alt_prefix = "alt-" in 68 + let parser = function 69 + | "do" | "doc" -> Ok `Doc 70 + | "di" | "dis" | "dist" | "distr" | "distri" | "distrib" -> Ok `Distrib 71 + | s when String.is_prefix ~affix:alt_prefix s -> 72 + begin match String.(with_range ~first:(length alt_prefix) s) with 73 + | "" -> Error ("`alt-' alternative artefact kind is missing") 74 + | kind -> Ok (`Alt kind) 75 + end 76 + | s -> Error (strf "`%s' unknown publication artefact" s) 77 + in 78 + let printer ppf = function 79 + | `Doc -> Fmt.string ppf "doc" 80 + | `Distrib -> Fmt.string ppf "distrib" 81 + | `Alt a -> Fmt.pf ppf "alt-%s" a 82 + in 83 + let artefact = Arg.conv' (parser, printer) in 84 + let doc = strf "The artefact to publish. $(docv) must be one of `doc`, 85 + `distrib` or `alt-$(i,KIND)`. If absent, the set of 86 + default publication artefacts is determined by the 87 + package description." 88 + in 89 + Arg.(value & pos_all artefact [] & info [] ~doc ~docv:"ARTEFACT") 90 + 91 + let doc = "Publish package distribution archives and derived artefacts" 92 + let sdocs = Manpage.s_common_options 93 + let exits = Cli.exits 94 + let envs = 95 + [ Cmd.Env.info "TOPKG_DELEGATE" ~doc:"The package delegate to use, see 96 + topkg-delegate(7)."; ] 97 + 98 + let man_xrefs = [`Main; `Cmd "distrib" ] 99 + let man = 100 + [ `S Manpage.s_synopsis; 101 + `P "$(mname) $(tname) [$(i,OPTION)]... [$(i,ARTEFACT)]..."; 102 + `S Manpage.s_description; 103 + `P "The $(tname) command publishes package distribution archives 104 + and other artefacts via the package delegate. See topkg-delegate(7) for 105 + more details."; 106 + `P "Artefact publication always relies on a distribution archive having 107 + been generated before with topkg-distrib(1)."; 108 + `S "ARTEFACTS"; 109 + `I ("$(b,distrib)", 110 + "Publishes a distribution archive on the WWW."); 111 + `I ("$(b,doc)", 112 + "Publishes the documentation of a distribution archive on the WWW."); 113 + `I ("$(b,alt)-$(i,KIND)", 114 + "Publishes the alternative artefact of kind $(i,KIND) of 115 + a distribution archive. The semantics of alternative artefacts 116 + is left to the delegate, it could be anything, an email, 117 + a pointless tweet, a feed entry etc. See topkg-delegate(7) for 118 + more details."); ] 119 + 120 + let cmd = 121 + Cmd.v (Cmd.info "publish" ~doc ~sdocs ~exits ~envs ~man ~man_xrefs) @@ 122 + Term.(const publish $ Cli.setup $ Cli.pkg_file $ Cli.build_dir $ 123 + Cli.dist_name $ Cli.dist_version $ Cli.dist_opam $ 124 + Cli.delegate $ Cli.change_log $ Cli.dist_uri $ Cli.dist_file $ 125 + Cli.publish_msg $ artefacts)
+8
vendor/opam/topkg/src-bin/publish.mli
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** The [publish] command. *) 7 + 8 + val cmd : int Cmdliner.Cmd.t
+103
vendor/opam/topkg/src-bin/run.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + open Bos_setup 7 + 8 + let pp_exec = Fmt.(quote string) 9 + 10 + let blacklist = [ ".so"; ".cmxs" ] (* Don't try to run these kind of files *) 11 + 12 + let exec_match exec p = 13 + OS.File.exists p >>= function 14 + | false -> Ok false 15 + | true -> 16 + OS.Path.Mode.get p >>= fun mode -> 17 + if mode land 0o111 = 0 then Ok false else 18 + let p_base, ext = Fpath.split_ext p in 19 + let p_base = Fpath.to_string p_base in 20 + let p = Fpath.to_string p in 21 + Ok (not (List.mem ext blacklist) && 22 + (String.is_suffix ~affix:exec p_base || 23 + String.is_suffix ~affix:exec p)) 24 + 25 + let find_exec exec dir = 26 + let ambiguous l = 27 + R.error_msgf "Ambiguous matches for %a, could match any of %a" 28 + pp_exec exec Fmt.(list ~sep:(any ", ") Fpath.pp) l 29 + in 30 + OS.Dir.exists dir >>= function 31 + | false -> R.error_msgf "Build directory %a does not exist" Fpath.pp dir 32 + | true -> 33 + let elements = `Sat (exec_match exec) in 34 + OS.Dir.fold_contents ~elements (fun p ps -> p :: ps) [] dir 35 + >>= function 36 + | [p0] -> Ok p0 37 + | [p0; p1] as l -> 38 + let p0, ext0 = Fpath.split_ext p0 in 39 + let p1, ext1 = Fpath.split_ext p1 in 40 + if Fpath.equal p0 p1 && ext0 = ".native" || ext1 = ".native" 41 + then Ok (Fpath.add_ext ".native" p0) 42 + else ambiguous l 43 + | [] -> 44 + R.error_msgf "No matches for %a in build directory %a" 45 + Fmt.(quote string) exec Fpath.pp dir 46 + | l -> 47 + ambiguous l 48 + 49 + let run () pkg_file build_dir exec args = 50 + let pkg = Topkg_care.Pkg.v pkg_file ?build_dir in 51 + begin 52 + Topkg_care.Pkg.build_dir pkg 53 + >>= fun build_dir -> find_exec exec build_dir 54 + >>= fun exec -> Ok Cmd.(v (p exec) %% of_list args) 55 + >>= fun cmd -> OS.Cmd.run_status cmd 56 + >>= function 57 + | `Exited 0 -> Ok 0 58 + | status -> 59 + Logs.err (fun m -> m "run %a %a" 60 + Cmd.dump cmd OS.Cmd.pp_status status); 61 + Ok 1 62 + end 63 + |> Cli.handle_error 64 + 65 + (* Command line interface *) 66 + 67 + open Cmdliner 68 + 69 + let args = 70 + let doc = "Arguments given to the executable. If options are being 71 + passed, needs to be specified after a -- token so that the 72 + command line options do not get interpreted by the $(tname) 73 + command itself." 74 + in 75 + Arg.(value & pos_right 0 string [] & info [] ~doc ~docv:"ARG") 76 + 77 + let exec = 78 + let doc = "Executable name or path suffix, with or without its 79 + extension. If multiple executable match the specification 80 + the command errors except if two paths match and differ 81 + only by their .byte and .native file extension. In the latter 82 + case the .native path is used." 83 + in 84 + let docv = "EXEC" in 85 + Arg.(required & pos 0 (some string) None & info [] ~doc ~docv) 86 + 87 + let doc = "Run built executables" 88 + let sdocs = Manpage.s_common_options 89 + let exits = Cmd.Exit.info 1 ~doc:"on run non-zero status exit." :: Cli.exits 90 + let man_xrefs = [ `Main ] 91 + let man = 92 + [ `S Manpage.s_synopsis; 93 + `P "$(mname) $(tname) [$(i,OPTION)]... $(i,EXEC) \ 94 + [-- [$(i,ARG)]...]]"; 95 + `S Manpage.s_description; 96 + `P "The $(tname) command runs executable files found 97 + in the build directory."; 98 + `P "$(b,WARNING) The way this command works is subject to change 99 + in the future." ] 100 + 101 + let cmd = 102 + Cmd.v (Cmd.info "run" ~doc ~sdocs ~exits ~man ~man_xrefs) @@ 103 + Term.(const run $ Cli.setup $ Cli.pkg_file $ Cli.build_dir $ exec $ args)
+8
vendor/opam/topkg/src-bin/run.mli
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** The [run] command. *) 7 + 8 + val cmd : int Cmdliner.Cmd.t
+87
vendor/opam/topkg/src-bin/status.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + open Bos_setup 7 + 8 + let pp_since ppf = function 9 + | "" -> () 10 + | v -> Fmt.pf ppf " since %a" Topkg_care.Pp.version v 11 + 12 + let pp_dirty ppf = function 13 + | false -> () 14 + | true -> Fmt.pf ppf "The repository is %a.@," Topkg_care.Pp.dirty () 15 + 16 + let pp_commit ppf (id, log) = 17 + Fmt.pf ppf "%a %s" Topkg_care.Pp.commit id log 18 + 19 + let pp_status ppf (dirty, version, changes) = match changes with 20 + | [] when not dirty -> Fmt.pf ppf "@[<v>No changes%a@]" pp_since version 21 + | changes -> 22 + Fmt.pf ppf "@[<v>Changes%a:@,%a%a@]" 23 + pp_since version pp_dirty dirty (Fmt.list pp_commit) changes 24 + 25 + let find_latest_version_tag repo = 26 + let rev_compare v v' = -1 * compare v v' in 27 + let parse_tag acc t = match Topkg.String.parse_version t with 28 + | None -> acc 29 + | Some v -> (v, t) :: acc 30 + in 31 + Topkg.Vcs.tags repo >>| fun tags -> 32 + match List.(sort rev_compare (fold_left parse_tag [] tags)) with 33 + | (_, latest) :: _ -> Some latest 34 + | [] -> None 35 + 36 + let find_after repo = function 37 + | Some after -> Ok after 38 + | None -> 39 + find_latest_version_tag repo >>| function 40 + | None -> 41 + Logs.info (fun m -> m "No VCS version tag found."); "" 42 + | Some tag -> 43 + Logs.info (fun m -> m "Latest VCS version tag found: %s" tag); tag 44 + 45 + let status () _ after until = 46 + begin 47 + Topkg.Vcs.get () 48 + >>= fun repo -> Topkg.Vcs.is_dirty repo 49 + >>= fun dirty -> find_after repo after 50 + >>= fun after -> Topkg.Vcs.changes repo ~after ~until 51 + >>= fun changes -> 52 + Logs.app (fun m -> m "%a" pp_status (dirty, after, changes)); 53 + Ok (if dirty || changes <> [] then 0 else 1) 54 + end 55 + |> Cli.handle_error 56 + 57 + (* Command line interface *) 58 + 59 + open Cmdliner 60 + 61 + let after = 62 + let doc = "Commit-ish $(docv) after which commits are considered. 63 + Default is the latest VCS version tag of the form [v]X.Y.Z[+info]." 64 + in 65 + Arg.(value & opt (some string) None & info ["after"] ~doc ~docv:"COMMIT-ISH") 66 + 67 + let until = 68 + let doc = "Commit-ish $(docv) until which commits are considered." in 69 + let docv = "COMMIT-ISH" in 70 + Arg.(value & opt string "HEAD" & info ["until"] ~doc ~docv) 71 + 72 + let doc = "List commits to publish in the next distribution" 73 + let sdocs = Manpage.s_common_options 74 + let exits = 75 + (Cmd.Exit.info 0 ~doc:"changes have been detected.") :: 76 + (Cmd.Exit.info 1 ~doc:"no changes have been detected.") :: 77 + Cmd.Exit.defaults 78 + 79 + let man_xrefs = [ `Main ] 80 + let man = 81 + [ `S Manpage.s_description; 82 + `P "The $(tname) command consults the package's VCS and outputs the 83 + list of commits that define the changes for the next distribution." ] 84 + 85 + let cmd = 86 + Cmd.v (Cmd.info "status" ~doc ~sdocs ~exits ~man ~man_xrefs) @@ 87 + Term.(const status $ Cli.setup $ Cli.pkg_file $ after $ until)
+8
vendor/opam/topkg/src-bin/status.mli
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** The [status] command. *) 7 + 8 + val cmd : int Cmdliner.Cmd.t
+80
vendor/opam/topkg/src-bin/tag.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + open Bos_setup 7 + 8 + let extract_version change_log = 9 + Topkg_care.Text.change_log_file_last_entry change_log 10 + >>= fun (version, _) -> Ok version 11 + 12 + let vcs_tag tag ~commit_ish ~force ~sign ~delete ~msg = 13 + let msg = match msg with None -> strf "Distribution %s" tag | Some m -> m in 14 + Topkg.Vcs.get () 15 + >>= fun repo -> match delete with 16 + | true -> Topkg.Vcs.delete_tag repo tag 17 + | false -> 18 + Topkg.Vcs.tag repo ~force ~sign ~msg ~commit_ish tag >>| fun () -> 19 + Logs.app (fun m -> m "Tagged version %a" Topkg_care.Pp.version tag) 20 + 21 + let tag () pkg_file change_log tag commit_ish force sign delete msg = 22 + begin 23 + let pkg = Topkg_care.Pkg.v ?change_log pkg_file in 24 + let tag = match tag with 25 + | Some t -> Ok t 26 + | None -> Topkg_care.Pkg.change_log pkg >>= fun cl -> extract_version cl 27 + in 28 + tag 29 + >>= fun tag -> vcs_tag tag ~commit_ish ~force ~sign ~delete ~msg 30 + >>= fun () -> Ok 0 31 + end 32 + |> Cli.handle_error 33 + 34 + (* Command line interface *) 35 + 36 + open Cmdliner 37 + 38 + let version = 39 + let doc = "The version tag to use. If absent, automatically extracted 40 + from the package's change log; see topkg-log(1) for details." 41 + in 42 + Arg.(value & pos 0 (some string) None & info [] ~doc ~docv:"VERSION") 43 + 44 + let commit = 45 + let doc = "Commit-ish $(docv) to tag." in 46 + Arg.(value & opt string "HEAD" & info ["commit"] ~doc ~docv:"COMMIT-ISH") 47 + 48 + let msg = 49 + let doc = "Commit message for the tag. If absent, the message 50 + 'Distribution $(i,VERSION)' is used." 51 + in 52 + Arg.(value & opt (some string) None & info ["m"; "message"] ~doc ~docv:"MSG") 53 + 54 + let sign = 55 + let doc = "Sign the tag using the VCS's default signing key." in 56 + Arg.(value & flag & info ["s"; "sign"] ~doc) 57 + 58 + let force = 59 + let doc = "If the tag exists, replace it rather than fail." in 60 + Arg.(value & flag & info ["f"; "force"] ~doc) 61 + 62 + let delete = 63 + let doc = "Delete the specified tag rather than create it." in 64 + Arg.(value & flag & info ["d"; "delete"] ~doc) 65 + 66 + let doc = "Tag the package's source repository with a version" 67 + let sdocs = Manpage.s_common_options 68 + let exits = Cli.exits 69 + let man_xrefs = [ `Main; `Cmd "log" ] 70 + let man = 71 + [ `S Manpage.s_description; 72 + `P "The $(tname) command tags the package's VCS HEAD commit with a 73 + version. If the version is not specified on the command line it is 74 + automatically extracted from the package's change log; use 75 + $(b,topkg log -t) to check the extracted value." ] 76 + 77 + let cmd = 78 + Cmd.v (Cmd.info "tag" ~doc ~sdocs ~exits ~man ~man_xrefs) @@ 79 + Term.(const tag $ Cli.setup $ Cli.pkg_file $ Cli.change_log $ 80 + version $ commit $ force $ sign $ delete $ msg)
+8
vendor/opam/topkg/src-bin/tag.mli
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** The [tag] command. *) 7 + 8 + val cmd : int Cmdliner.Cmd.t
+81
vendor/opam/topkg/src-bin/test.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + open Bos_setup 7 + 8 + let test_args name build_dir list args = 9 + let on_some_use_opt opt to_arg = function 10 + | None -> Cmd.empty 11 + | Some value -> Cmd.(v opt % to_arg value) 12 + in 13 + let verb = Cli.propagate_verbosity_to_pkg_file () in 14 + let build_dir = on_some_use_opt "--build-dir" Cmd.p build_dir in 15 + let list = if list then Cmd.(v "--list") else Cmd.empty in 16 + let name = on_some_use_opt "--pkg-name" (fun n -> n) name in 17 + Cmd.(verb %% name %% list %% build_dir %% Cmd.of_list args) 18 + 19 + let test () pkg_file pkg_name build_dir list args = 20 + let pkg = Topkg_care.Pkg.v pkg_file in 21 + let args = test_args pkg_name build_dir list args in 22 + let out = OS.Cmd.out_stdout in 23 + begin 24 + OS.Dir.current () 25 + >>= fun dir -> Topkg_care.Pkg.test pkg ~dir ~args ~out 26 + >>| (function ((), (_, `Exited 0)) -> 0 | _ -> 1) 27 + end 28 + |> Cli.handle_error 29 + 30 + (* Command line interface *) 31 + 32 + open Cmdliner 33 + 34 + let args = 35 + let doc = "Tests and arguments to the tests. If options are being 36 + passed, needs to be specified after a -- token so that the 37 + command line options do not get interpreted by $(b,topkg test) 38 + itself. If arguments need to be specified for the test itself a 39 + second -- token is needed." 40 + in 41 + Arg.(value & pos_all string [] & info [] ~doc ~docv:"[TEST]... [-- ARG...]") 42 + 43 + let build_dir = 44 + let doc = "Specifies the build directory $(docv). If absent, provided 45 + by the package description. This is equivalent to specify 46 + the same option after the first -- token" 47 + in 48 + let docv = "BUILD_DIR" in 49 + Arg.(value & opt (some Cli.path_arg) None & info ["build-dir"] ~doc ~docv) 50 + 51 + let list = 52 + let doc = "Do not run the tests, list them. This is equivalent to 53 + specify the same option after the first -- token." 54 + in 55 + Arg.(value & flag & info ["l"; "list"] ~doc) 56 + 57 + let doc = "Run built package tests" 58 + let sdocs = Manpage.s_common_options 59 + let exits = Cmd.Exit.info 1 ~doc:"on test failure." :: Cli.exits 60 + let man_xrefs = [ `Main ] 61 + let man = 62 + [ `S Manpage.s_synopsis; 63 + `P "$(mname) $(tname) [$(i,OPTION)]... [-- [$(i,TEST)]... \ 64 + [-- [$(i,ARG)]...]]"; 65 + `S Manpage.s_description; 66 + `P "The $(tname) command runs the tests that were built by 67 + topkg-build(1). This is equivalent to invoke:"; 68 + `Pre "ocaml ./pkg/pkg.ml test [$(i,TEST)]... [-- [$(i,ARG)]...]"; 69 + `P "The value for $(i,TEST) can be a full path to the test executable 70 + or simply the basename of the test executable with or without the file 71 + extension. The option $(b,--list) lists the tests that were built."; 72 + `P "Note that if you want to pass command line arguments to a test you 73 + need to specify the token -- twice. For example to pass 'arg' to a 74 + test 'mytest' use one of the following invocation:"; 75 + `Pre "topkg test -- mytest -- arg"; `Noblank; 76 + `Pre "topkg test mytest -- -- arg" ] 77 + 78 + let cmd = 79 + Cmd.v (Cmd.info "test" ~doc ~sdocs ~exits ~man ~man_xrefs) @@ 80 + Term.(const test $ Cli.setup $ Cli.pkg_file $ Cli.pkg_name $ build_dir $ 81 + list $ args)
+8
vendor/opam/topkg/src-bin/test.mli
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** The [test] command. *) 7 + 8 + val cmd : int Cmdliner.Cmd.t
+44
vendor/opam/topkg/src-bin/topkg_bin.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + open Cmdliner 7 + 8 + let cmds = 9 + [ Bistro.cmd; Browse.cmd; Build.cmd; Clean.cmd; Distrib.cmd; Doc.cmd; 10 + Help.cmd; Ipc.cmd; Issue.cmd; Lint.cmd; Log.cmd; Opam.cmd; 11 + Publish.cmd; Run.cmd; Status.cmd; Tag.cmd; Test.cmd; ] 12 + 13 + let main () = `Help (`Pager, None) 14 + 15 + (* Command line interface *) 16 + 17 + let doc = "Topkg package care" 18 + let sdocs = Manpage.s_common_options 19 + let exits = Cli.exits 20 + let man = 21 + [ `S Manpage.s_description; 22 + `P "$(mname) takes care of topkg packages."; 23 + `P "Use '$(mname) help release' for help to release a package."; 24 + `Noblank; 25 + `P "Use '$(mname) help delegate' for help about the topkg delegate."; 26 + `Noblank; 27 + `P "Use '$(mname) help troubleshoot' for a few troubleshooting tips."; 28 + `Noblank; 29 + `P "Use '$(mname) help $(i,COMMAND)' for help about $(i,COMMAND)."; 30 + `S Manpage.s_bugs; 31 + `P "Report them, see $(i,%%PKG_HOMEPAGE%%) for contact information."; 32 + `S Manpage.s_authors; 33 + `P "Daniel C. Buenzli, $(i,http://erratique.ch)"; ] 34 + 35 + let main = 36 + let default = Term.(ret (const main $ Cli.setup)) in 37 + let info = Cmd.info "topkg" ~version:"%%VERSION%%" ~doc ~sdocs ~exits ~man in 38 + Cmd.group ~default info cmds 39 + 40 + let main () = 41 + Topkg.Private.disable_main (); 42 + Cmd.eval' main 43 + 44 + let () = if !Sys.interactive then () else exit (main ())
+327
vendor/opam/topkg/src-bin/toy_github_delegate.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + open Bos_setup 7 + 8 + (* Publish documentation *) 9 + 10 + let repo_docdir_owner_repo_and_path_from_doc_uri uri = 11 + (* Parses the $PATH of $SCHEME://$HOST/$REPO/$PATH *) 12 + let uri_error uri = 13 + R.msgf "Could not derive publication directory $PATH from opam doc \ 14 + field value %a; expected the pattern \ 15 + $SCHEME://$OWNER.github.io/$REPO/$PATH" String.dump uri 16 + in 17 + match Topkg_care.Text.split_uri ~rel:true uri with 18 + | None -> Error (uri_error uri) 19 + | Some (_, host, path) -> 20 + if path = "" then Error (uri_error uri) else 21 + (match String.cut ~sep:"." host with 22 + | Some (owner, g) when String.equal g "github.io" -> Ok owner 23 + | _ -> Error (uri_error uri)) 24 + >>= fun owner -> 25 + match String.cut ~sep:"/" path with 26 + | None -> Error (uri_error uri) 27 + | Some (repo, "") -> Ok (owner, repo, Fpath.v ".") 28 + | Some (repo, path) -> 29 + (Fpath.of_string path >>| fun p -> owner, repo, Fpath.rem_empty_seg p) 30 + |> R.reword_error_msg (fun _ -> uri_error uri) 31 + 32 + let publish_doc_gh_pages uri name version docdir = 33 + Fpath.of_string docdir 34 + >>= fun docdir -> repo_docdir_owner_repo_and_path_from_doc_uri uri 35 + >>= fun (owner, repo, dir) -> 36 + let remote = strf "git@@github.com:%s/%s.git" owner repo in 37 + let git_for_repo r = Cmd.of_list (Topkg.Cmd.to_list @@ Topkg.Vcs.cmd r) in 38 + let create_empty_gh_pages git = 39 + let msg = "Initial commit by topkg." in 40 + let create () = 41 + OS.Cmd.run Cmd.(v "git" % "init") 42 + >>= fun () -> Topkg.Vcs.get () 43 + >>= fun repo -> Ok (git_for_repo repo) 44 + >>= fun git -> OS.Cmd.run Cmd.(git % "checkout" % "--orphan" % "gh-pages") 45 + >>= fun () -> OS.File.write (Fpath.v "README") "" (* need some file *) 46 + >>= fun () -> OS.Cmd.run Cmd.(git % "add" % "README") 47 + >>= fun () -> OS.Cmd.run Cmd.(git % "commit" % "README" % "-m" % msg) 48 + in 49 + OS.Dir.with_tmp "gh-pages-%s.tmp" (fun dir () -> 50 + OS.Dir.with_current dir create () |> R.join 51 + >>= fun () -> OS.Cmd.run Cmd.(git % "fetch" % Fpath.to_string dir 52 + % "gh-pages") 53 + ) () |> R.join 54 + in 55 + Topkg.Vcs.get () 56 + >>= fun repo -> Ok (git_for_repo repo) 57 + >>= fun git -> 58 + (match OS.Cmd.run Cmd.(git % "fetch" % remote % "gh-pages") with 59 + | Ok () -> Ok () 60 + | Error _ -> create_empty_gh_pages git) 61 + >>= fun () -> (OS.Cmd.run_out Cmd.(git % "rev-parse" % "FETCH_HEAD") 62 + |> OS.Cmd.to_string) 63 + >>= fun id -> OS.Cmd.run Cmd.(git % "branch" % "-f" % "gh-pages" % id) 64 + >>= fun () -> Topkg_care.Delegate.publish_in_git_branch ~remote 65 + ~branch:"gh-pages" ~name ~version ~docdir ~dir 66 + >>= fun () -> Ok 0 67 + 68 + (* Publish releases *) 69 + 70 + let repo_and_owner_of_uri uri = 71 + let uri_error uri = 72 + R.msgf "Could not derive owner and repo from opam dev-repo \ 73 + field value %a; expected the pattern \ 74 + $SCHEME://$HOST/$OWNER/$REPO[.$EXT][/$DIR]" String.dump uri 75 + in 76 + match Topkg_care.Text.split_uri ~rel:true uri with 77 + | None -> Error (uri_error uri) 78 + | Some (_, _, path) -> 79 + if path = "" then Error (uri_error uri) else 80 + match String.cut ~sep:"/" path with 81 + | None -> Error (uri_error uri) 82 + | Some (owner, path) -> 83 + let repo = match String.cut ~sep:"/" path with 84 + | None -> path 85 + | Some (repo, path) -> repo 86 + in 87 + begin 88 + Fpath.of_string repo 89 + >>= fun repo -> Ok (owner, Fpath.(to_string @@ rem_ext repo)) 90 + end 91 + |> R.reword_error_msg (fun _ -> uri_error uri) 92 + 93 + let steal_opam_publish_github_auth () = 94 + let opam = Cmd.(v "opam") in 95 + let publish = Fpath.v "plugins/opam-publish" in 96 + OS.Cmd.exists opam >>= function 97 + | false -> Ok None 98 + | true -> 99 + OS.Cmd.(run_out Cmd.(opam % "var" % "root") |> to_string) 100 + >>= fun root -> Fpath.of_string root 101 + >>= fun root -> OS.Path.query Fpath.(root // publish / "$(user).token") 102 + >>= function 103 + | [] -> Ok None 104 + | (file, defs) :: _ -> 105 + OS.File.read file >>= fun token -> 106 + Ok (Some (strf "%s:%s" (String.Map.get "user" defs) token)) 107 + 108 + let github_auth ~owner = 109 + match 110 + steal_opam_publish_github_auth () 111 + |> Logs.on_error_msg ~use:(fun _ -> None) 112 + with 113 + | Some auth -> auth 114 + | None -> OS.Env.(value "TOPKG_GITHUB_AUTH" string ~absent:owner) 115 + 116 + let create_release_json version msg = 117 + let escape_for_json s = 118 + let len = String.length s in 119 + let max = len - 1 in 120 + let rec escaped_len i l = 121 + if i > max then l else 122 + match String.get s i with 123 + | '\\' | '\"' | '\n' | '\r' | '\t' -> escaped_len (i + 1) (l + 2) 124 + | _ -> escaped_len (i + 1) (l + 1) 125 + in 126 + let escaped_len = escaped_len 0 0 in 127 + if escaped_len = len then s else 128 + let b = Bytes.create escaped_len in 129 + let rec loop i k = 130 + if i > max then Bytes.unsafe_to_string b else 131 + match String.get s i with 132 + | ('\\' | '\"' | '\n' | '\r' | '\t' as c) -> 133 + Bytes.set b k '\\'; 134 + let c = match c with 135 + | '\\' -> '\\' | '\"' -> '\"' | '\n' -> 'n' | '\r' -> 'r' 136 + | '\t' -> 't' 137 + | _ -> assert false 138 + in 139 + Bytes.set b (k + 1) c; loop (i + 1) (k + 2) 140 + | c -> 141 + Bytes.set b k c; loop (i + 1) (k + 1) 142 + in 143 + loop 0 0 144 + in 145 + strf "{ \"tag_name\" : \"%s\", \ 146 + \"body\" : \"%s\" }" (escape_for_json version) (escape_for_json msg) 147 + 148 + let run_with_auth auth curl = 149 + let auth = strf "-u %s" auth in 150 + OS.Cmd.(in_string auth |> run_io curl) 151 + 152 + let curl_create_release curl version msg owner repo = 153 + let parse_release_id resp = (* FIXME this is retired. *) 154 + let headers = String.cuts ~sep:"\r\n" resp in 155 + try 156 + let not_slash c = not (Char.equal '/' c) in 157 + let loc = List.find (String.is_prefix ~affix:"Location:") headers in 158 + let id = String.take ~rev:true ~sat:not_slash loc in 159 + match String.to_int id with 160 + | None -> R.error_msgf "Could not parse id from location header %S" loc 161 + | Some id -> Ok id 162 + with Not_found -> 163 + R.error_msgf "Could not find release id in response:\n%s." 164 + (String.concat ~sep:"\n" headers) 165 + in 166 + let data = create_release_json version msg in 167 + let uri = strf "https://api.github.com/repos/%s/%s/releases" owner repo in 168 + let auth = github_auth ~owner in 169 + let cmd = Cmd.(curl % "-D" % "-" % "--data" % data % uri) in 170 + run_with_auth auth cmd |> OS.Cmd.to_string ~trim:false 171 + >>= parse_release_id 172 + 173 + let curl_upload_archive curl archive owner repo release_id = 174 + let uri = 175 + (* FIXME upload URI prefix should be taken from release creation 176 + response *) 177 + strf "https://uploads.github.com/repos/%s/%s/releases/%d/assets?name=%s" 178 + owner repo release_id (Fpath.filename archive) 179 + in 180 + let auth = github_auth ~owner in 181 + let data = Cmd.(v "--data-binary" % strf "@@%s" (Fpath.to_string archive)) in 182 + let ctype = Cmd.(v "-H" % "Content-Type:application/x-tar") in 183 + let cmd = Cmd.(curl %% ctype %% data % uri) in 184 + OS.Cmd.(run_with_auth auth cmd |> to_stdout) 185 + 186 + let publish_distrib uri name version msg archive = 187 + let git_for_repo r = Cmd.of_list (Topkg.Cmd.to_list @@ Topkg.Vcs.cmd r) in 188 + let curl = Cmd.(v "curl" % "-L" % "-s" % "-S" % "-K" % "-") in 189 + Fpath.of_string archive 190 + >>= fun archive -> OS.Cmd.must_exist curl 191 + >>= fun curl -> Topkg.Vcs.get () 192 + >>= fun repo -> Ok (git_for_repo repo) 193 + >>= fun git -> OS.Cmd.run Cmd.(git % "push" % "--force" % "--tags") 194 + >>= fun () -> repo_and_owner_of_uri uri 195 + >>= fun (owner, repo) -> curl_create_release curl version msg owner repo 196 + >>= fun id -> curl_upload_archive curl archive owner repo id 197 + >>= fun () -> Ok 0 198 + 199 + (* Publish delegations *) 200 + 201 + let unsupported = Ok 1 202 + 203 + let publish = function 204 + | "distrib" :: uri :: name :: version :: msg :: archive :: _ -> 205 + publish_distrib uri name version msg archive 206 + | "doc" :: uri :: name :: version :: msg :: docdir :: _ -> 207 + publish_doc_gh_pages uri name version docdir 208 + | "alt" :: kind :: uri :: name :: version :: msg :: archive :: _ -> 209 + unsupported 210 + | args -> 211 + unsupported 212 + 213 + (* Issue delegations *) 214 + 215 + let issue = function 216 + | "list" :: uri :: _ -> unsupported 217 + | "show" :: uri :: id :: _ -> unsupported 218 + | "open" :: uri :: title :: descr :: _ -> unsupported 219 + | "close" :: uri :: id :: msg :: _ -> unsupported 220 + | args -> unsupported 221 + 222 + (* Delegation requests *) 223 + 224 + let request = function 225 + | "publish" :: args -> publish args 226 + | "issue" :: args -> issue args 227 + | args -> unsupported 228 + 229 + (* Delegate tool commands *) 230 + 231 + let ipc_cmd args = 232 + begin match args with 233 + | verbosity :: req -> 234 + Logs.level_of_string verbosity >>= fun logs_level -> 235 + Topkg.Log.level_of_string verbosity >>= fun topkg_level -> 236 + Topkg.Log.set_level topkg_level; 237 + Logs.set_level logs_level; 238 + request req 239 + | [] -> 240 + R.error_msg "malformed delegate request, verbosity is missing" 241 + end 242 + |> Logs.on_error_msg ~use:(fun () -> 2) 243 + 244 + let main_cmd () = `Help (`Pager, None) 245 + 246 + (* Cli interface *) 247 + 248 + open Cmdliner 249 + 250 + let ipc_cmd = 251 + let doc = "Delegate request IPCs" in 252 + let man = 253 + [ `S "DESCRIPTION"; 254 + `P "The $(tname) command implements the topkg delegate protocol. 255 + See topkg-delegate(7) and $(mname) $(b,--help) for more 256 + information." ] 257 + in 258 + let args = 259 + let doc = "IPC call arguments" in 260 + Arg.(value (pos_all string [] & info [] ~doc ~docv:"ARG")) 261 + in 262 + Cmd.v (Cmd.info "ipc" ~doc ~man) @@ 263 + Term.(const ipc_cmd $ args) 264 + 265 + let main_cmd = 266 + let doc = "Topkg's toy GitHub delegate" in 267 + let envs = 268 + [ Cmd.Env.info "TOPKG_GITHUB_AUTH" ~doc:"GitHub authentication data, see 269 + the section GITHUB AUTHENTICATION for details." ] 270 + in 271 + let man_xrefs = [ `Tool "topkg" ] in 272 + let man = 273 + [ `S "DESCRIPTION"; 274 + `P "$(mname) is a toy topkg delegate for GitHub. It will disappear 275 + once a decent GitHub delegate emerges. For more 276 + information about topkg delegates, see topkg-delegate(7)."; 277 + `P "This delegate only supports the following delegations:"; 278 + `I ("$(b,topkg publish doc)", 279 + "Commits and pushes the documentation to the gh-pages of the 280 + source repository. The publication directory PATH in the branch is 281 + determined by matching the opam 'doc' field against the 282 + pattern SCHEME://OWNER.github.io/REPO/PATH."); 283 + `I ("$(b,topkg publish distrib)", 284 + "This requires curl(1). Creates a GitHub release with the 285 + version and publication message given to the delegate and 286 + uploads the distribution archive as a release artefact. This 287 + requires GitHub authentication, see section GITHUB AUTHENTICATION 288 + for details. Also bear in mind that error reporting 289 + (e.g. if the release already exists) is made of raw JSON 290 + responses and thus very user-unfriendly."); 291 + `S "GITHUB AUTHENTICATION"; 292 + `P "This being a toy delegate, you get toy authentication. Here 293 + are the steps, in order, that are tried to authenticate you on 294 + GitHub."; 295 + `I ("1. opam-publish token stealing.", 296 + "If you have already used opam-publish, an authorization token 297 + was generated for it that is keept in 298 + \\$(opam config var root)/plugins/opam-publish/\\$(user).token. If 299 + such a file exists, \\$(user) and the corresponding token will 300 + be used for authentication."); 301 + `I ("2. Environment variable.", 302 + "You scan specify the user and the password or token using 303 + the TOPKG_GITHUB_AUTH environment variable with a username:token 304 + value, see $(i,https://developer.github.com/v3/auth/)."); 305 + `I ("3. Cli prompt.", 306 + "As a last resort the username used for authentication is 307 + the name of the GitHub owner of the repo (determined from 308 + the $(i,DISTRIB_URI) URI, itself determined from the 'dev-repo' 309 + field of the opam file, see topkg-delegate(7) and topkg's API 310 + documentation for more details); in this case your GitHub 311 + password will be prompted twice on the command line by curl (ugh).")] 312 + in 313 + let version = "%%VERSION%%" in 314 + let default = Term.(ret (const main_cmd $ const ())) in 315 + let info = 316 + Cmd.info "toy-github-topkg-delegate" ~version ~doc ~envs ~man ~man_xrefs 317 + in 318 + Cmd.group ~default info [ipc_cmd] 319 + 320 + let main () = 321 + Topkg.Private.disable_main (); 322 + match Cmd.eval_value main_cmd with 323 + | Error _ -> 3 324 + | Ok (`Ok ret) -> ret 325 + | Ok _ -> 0 326 + 327 + let () = if !Sys.interactive then () else exit (main ())
+13
vendor/opam/topkg/src-care/topkg_care.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + module Text = Topkg_care_text 7 + module Pp = Topkg_care_text.Pp 8 + module Opam = Topkg_care_opam 9 + module OCamlbuild = Topkg_care_ocamlbuild 10 + module OCamlfind = Topkg_care_ocamlfind 11 + module Archive = Topkg_care_archive 12 + module Pkg = Topkg_care_pkg 13 + module Delegate = Topkg_care_delegate
+487
vendor/opam/topkg/src-care/topkg_care.mli
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** Topkg package care. 7 + 8 + Tools to help the package developer in the life cycle of the 9 + package. Most of these tools can be invoked directly from the 10 + command line via the [topkg] binary installed by the [topkg-care] 11 + package. 12 + 13 + {b WARNING.} Do not use this API in your package description file, use 14 + only {!Topkg}. This API was not thoroughly designed, is not stable and 15 + may change even between minor versions of [topkg]. Use at your own 16 + risk. *) 17 + 18 + open Bos_setup 19 + 20 + (** {1 Helpers} *) 21 + 22 + (** Text processing helpers. *) 23 + module Text : sig 24 + 25 + (** {1 Marked-up text files} 26 + 27 + {b Warning.} Some of the following functions are not serious and can 28 + break on certain valid inputs in all sorts of fashion. To understand 29 + breakage bear in mind that they operate line-wise. *) 30 + 31 + type flavour = [ `Markdown | `Asciidoc ] 32 + (** The type for text document formats. *) 33 + 34 + val flavour_of_fpath : Fpath.t -> flavour option 35 + (** [flavour_of_fpath p] determines a flavour according to the 36 + extension of [p] as follows: 37 + {ul 38 + {- [Some `Markdown] for [.md]} 39 + {- [Some `Asciidoc] for [.asciidoc] or [.adoc]} 40 + {- [None] otherwise}} *) 41 + 42 + val head : ?flavour:flavour -> string -> (string * string) option 43 + (** [head ~flavour text] extracts the {e head} of the document [text] of 44 + flavour [flavour] (defaults to [`Markdown]). 45 + 46 + The head is defined as follows: 47 + {ul 48 + {- Anything before the first header is discarded.} 49 + {- The first header is kept in the first component} 50 + {- Everything that follows until the next header of the same or greater 51 + level is kept discarding trailing blank lines.}} *) 52 + 53 + val header_title : ?flavour:flavour -> string -> string 54 + (** [header_title ~flavour text] extract the title of a header [text] 55 + of flavour [flavour] (defaults to [`Markdown]). *) 56 + 57 + (** {1 Toy change log parsing} *) 58 + 59 + val change_log_last_entry : 60 + ?flavour:flavour -> string -> (string * (string * string)) option 61 + (** [change_log_last_version ~flavour text] tries to parse the last 62 + change log entry of [text] (i.e. the {!head} of [text]) into 63 + [Some (version, (header, text))], where [(header,text)] is the 64 + result of {!head} and [version] a version number extracted from 65 + [header] (see [topkg-log(2)] for details). *) 66 + 67 + val change_log_file_last_entry : 68 + Fpath.t -> ((string * (string * string)), R.msg) result 69 + (** [change_log_file_last_entry file] tries to parse the last 70 + change log entry of the file [file] using {!flavour_of_fpath} and 71 + and {!change_log_last_entry}. *) 72 + 73 + (** {1 Toy URI parsing} *) 74 + 75 + val split_uri : ?rel:bool -> string -> (string * string * string) option 76 + (** [split_uri uri] splits [uri] into a triple [(scheme, host, path)]. If 77 + [rel] is [true] (defaults to [false]), a leading ["/"] in [path] is 78 + removed. *) 79 + 80 + (** {1 Edit and page text} *) 81 + 82 + val edit_file : Fpath.t -> (int, R.msg) result 83 + (** [edit_file f] invokes the tool mentioned in the [EDITOR] 84 + environment variable with [f] and returns the exit code of 85 + the program. *) 86 + 87 + val find_pager : don't:bool -> (Cmd.t option, R.msg) result 88 + (** [find ~no_pager] is an optional pager command. If [don't] is 89 + [true] returns [None]. Otherwise first consults the [PAGER] environment 90 + variable, then tries [less] or [more] in that order. If the [TERM] 91 + environment variable is ["dumb"] or undefined unconditionaly returns 92 + [None]. *) 93 + end 94 + 95 + (** Pretty printers. *) 96 + module Pp : sig 97 + 98 + (** {1 Pretty printers} *) 99 + 100 + val name : string Fmt.t 101 + (** [name] formats a package name. *) 102 + 103 + val version : string Fmt.t 104 + (** [version] formats a package version. *) 105 + 106 + val commit : string Fmt.t 107 + (** [commit] formats a commit-ish. *) 108 + 109 + val dirty : unit Fmt.t 110 + (** [dirty] formats a "dirty" string. *) 111 + 112 + val path : Fpath.t Fmt.t 113 + (** [path] formats a bold path *) 114 + 115 + val status : [`Ok | `Fail] Fmt.t 116 + (** [status] formats a result status. *) 117 + end 118 + 119 + (** [opam] helpers. *) 120 + module Opam : sig 121 + 122 + (** {1:cmd Command} *) 123 + 124 + val cmd : Cmd.t 125 + (** [cmd] is a command for [opam] looked up using 126 + {!Topkg.Conf.tool}[ "opam" `Host_os]. *) 127 + 128 + (** {1:publish Publish} *) 129 + 130 + val ensure_publish : unit -> (unit, R.msg) result 131 + (** [ensure_publish ()] makes sure [opam-publish] is in the executable 132 + search PATH. *) 133 + 134 + val submit : ?msg:string -> opam_file:Fpath.t -> unit -> (unit, R.msg) result 135 + (** [submit ~opam_file] submits the opam file [opam_file] with [opam-publish] 136 + and submission message [msg] (if any) to the OCaml opam repository. *) 137 + 138 + (** {1:pkgs Packages} *) 139 + 140 + val ocaml_base_packages : String.set 141 + (** [ocaml_base_packages] are the base opam packages distributed 142 + with OCaml: ["base-bigarray"], ["base-bytes"], ["base-threads"], 143 + ["base-unix"]. *) 144 + 145 + (** {1:file Files} *) 146 + 147 + (** opam files *) 148 + module File : sig 149 + 150 + (** {1:file opam file} *) 151 + 152 + val field_names : String.set 153 + (** [field_names] is the maximal domain of the map returned by 154 + {!fields}, excluding extension fields (not yet supported by 155 + [opam-lib] 1.2.2). *) 156 + 157 + val fields : Fpath.t -> ((string list) String.map , R.msg) result 158 + (** [fields f] returns a simplified model of the fields of the opam 159 + file [f]. The domain of the result is included in 160 + {!field_names}. Note that the [depends:] and [depopts:] fields 161 + are returned without version constraints. *) 162 + 163 + (** {1:deps Dependencies} *) 164 + 165 + val deps : ?opts:bool -> (string list) String.map -> String.set 166 + (** [deps ~opts fields] returns the packages mentioned in the [depends:] 167 + fields, if [opts] is [true] (default) those from [depopts:] are added 168 + aswell. *) 169 + end 170 + 171 + (** [descr] files. *) 172 + module Descr : sig 173 + 174 + (** {1:descr Descr file} *) 175 + 176 + type t = string * string 177 + (** The type for opam [descr] files, the package synopsis and the 178 + description. *) 179 + 180 + val of_string : string -> (t, R.msg) result 181 + (** [of_string s] is a description from the string [s]. *) 182 + 183 + val to_string : t -> string 184 + (** [to_string d] is [d] as a string. *) 185 + 186 + val to_opam_fields : t -> string 187 + (** [to_opam_fields d] is [d] as synopsis and description opam v2 188 + fields. *) 189 + 190 + val of_readme : 191 + ?flavour:Text.flavour -> string -> (t, R.msg) result 192 + (** [of_readme r] extracts an opam description file from a readme [r] 193 + with a certain structure. *) 194 + 195 + val of_readme_file : Fpath.t -> (t, R.msg) result 196 + (** [of_readme_file f] extracts an opam description file from 197 + a readme file [f] using {!Text.flavour_of_fpath} and 198 + {!of_readme}. *) 199 + end 200 + 201 + (** [url] files. *) 202 + module Url : sig 203 + 204 + (** {1:url Url file} *) 205 + 206 + type t 207 + (** The type for url file contents. *) 208 + 209 + val v : uri:string -> checksum:string -> t 210 + (** [v ~uri ~checksum] is an URL file for URI [uri] with 211 + checksum [checksum]. *) 212 + 213 + val with_distrib_file : uri:string -> Fpath.t -> (t, R.msg) result 214 + (** [with_distrib_file ~uri f] is an URL file for URI [uri] with 215 + the checksum of file [f]. *) 216 + 217 + val to_opam_section : t -> string 218 + (** [to_opam_section u] is [u] as an opam v2 url section. *) 219 + end 220 + end 221 + 222 + (** [ocamlbuild] helpers. *) 223 + module OCamlbuild : sig 224 + 225 + (** {1:cmd Command} *) 226 + 227 + val cmd : Cmd.t 228 + (** [cmd] is a command for [ocamlbuild] looked up using 229 + {!Topkg.Conf.tool}[ "ocamlbuild" `Host_os]. *) 230 + 231 + (** {1 Packages} *) 232 + 233 + val package_tags : ?roots:bool -> Fpath.t -> (String.set, R.msg) result 234 + (** [packages ~roots f] is the set of packages identifiers 235 + mentioned by the 236 + {{:https://github.com/ocaml/ocamlbuild/blob/master/manual/manual.adoc#17-findlib-based-packages} 237 + package tags} of the [_tags] file [f]. If [roots] is [true] 238 + (defaults to [false]) only root packages, i.e. the identifier 239 + before the first ['.'], are in the set. 240 + 241 + {b Warning.} This is a very dumb parsing that simply looks up 242 + for all ["package($ID)"] and ["package($ID0[, ]*$ID1...)"] 243 + patterns in the [_tags] file. *) 244 + end 245 + 246 + (** [ocamlfind] helpers. *) 247 + module OCamlfind : sig 248 + 249 + (** {1:cmd Command} *) 250 + 251 + val cmd : Cmd.t 252 + (** [cmd] is a command for [ocamlfind] looked up using 253 + {!Topkg.Conf.tool}[ "ocamlfind" `Host_os]. *) 254 + 255 + (** {1 Packages} *) 256 + 257 + val base_packages : String.set 258 + (** [base_packages] are the OCamlfind packages that are 259 + distributed with OCaml. *) 260 + end 261 + 262 + (** Archive file creation. *) 263 + module Archive : sig 264 + 265 + (** {1 Ustar archives} *) 266 + 267 + val tar : 268 + Fpath.t -> exclude_paths:Fpath.set -> root:Fpath.t -> mtime:int -> 269 + (string, R.msg) result 270 + (** [tar dir ~exclude_paths ~root ~mtime] is a (us)tar archive that 271 + contains the file hierarchy [dir] except the relative 272 + hierarchies present in [exclude_paths]. In the archive, members 273 + of [dir] are rerooted at [root] and sorted according to 274 + {!Fpath.compare}. They have their modification time set to 275 + [mtime] and their file permissions are [0o775] for directories 276 + and files executable by the user and [0o664] for other files. No other 277 + file metadata is preserved. 278 + 279 + {b Note.} This is a pure OCaml implementation, no [tar] tool is needed. *) 280 + 281 + (** {1 Bzip2 compression and unarchiving} *) 282 + 283 + val ensure_bzip2 : unit -> (unit, R.msg) result 284 + (** [ensure_bzip2 ()] makes sure the [bzip2] utility is available. *) 285 + 286 + val bzip2 : string -> dst:Fpath.t -> (unit, R.msg) result 287 + (** [bzip2 s dst] compresses [s] to [dst] using bzip2. *) 288 + 289 + val ensure_tar : unit -> (unit, R.msg) result 290 + (** [ensure_tar ()] makes sure the [tar] utility is available. *) 291 + 292 + val untbz : ?clean:bool -> Fpath.t -> (Fpath.t, R.msg) result 293 + (** [untbz ~clean ar] untars the tar bzip2 archive [ar] in the same 294 + directory as [ar] and returns a base directory for [ar]. If [clean] is 295 + [true] (defaults to [false]) first delete the base directory if it 296 + exists. *) 297 + end 298 + 299 + (** {1 Package care} *) 300 + 301 + (** Package description. *) 302 + module Pkg : sig 303 + 304 + (** {1 Packages} *) 305 + 306 + type t 307 + (** The type for package descriptions. *) 308 + 309 + val v : 310 + ?name:string -> 311 + ?version:string -> 312 + ?delegate:Cmd.t -> 313 + ?build_dir:Fpath.t -> 314 + ?opam:Fpath.t -> 315 + ?opam_descr:Fpath.t -> 316 + ?readme:Fpath.t -> 317 + ?change_log:Fpath.t -> 318 + ?license:Fpath.t -> 319 + ?distrib_uri:string -> 320 + ?distrib_file:Fpath.t -> 321 + ?publish_msg:string -> 322 + ?publish_artefacts:[ `Distrib | `Doc | `Alt of string] list -> 323 + Fpath.t -> t 324 + (** [v pkg_file] is a package from description file [pkg_file] which 325 + is loaded only if needed. The optional parameters allow to 326 + override [pkg_file]'s definition. *) 327 + 328 + val pkg_file : t -> Fpath.t 329 + (** [pkg_file p] is [p]'s description file. *) 330 + 331 + val name : t -> (string, R.msg) result 332 + (** [name p] is [p]'s name. *) 333 + 334 + val version : t -> (string, R.msg) result 335 + (** [version p] is [p]'s version string.*) 336 + 337 + val delegate : t -> (Cmd.t, R.msg) result 338 + (** [delegate p] is [p]'s delegate. *) 339 + 340 + val build_dir : t -> (Fpath.t, R.msg) result 341 + (** [build_dir p] is [p]'s build directory. *) 342 + 343 + val opam : t -> (Fpath.t, R.msg) result 344 + (** [opam p] is [p]'s opam file. *) 345 + 346 + val opam_descr : t -> (Opam.Descr.t * bool, R.msg) result 347 + (** [opam_descr p] is [p]'s opam description. The boolean indicates 348 + if the description was found in the opam file itself. *) 349 + 350 + val opam_field : t -> string -> (string list option, R.msg) result 351 + (** [opam_field p f] looks up field [f] of [p]'s opam file. *) 352 + 353 + val opam_fields : t -> (string list String.map, R.msg) result 354 + (** [opam_fields p] are [p]'s opam file fields. *) 355 + 356 + val readmes : t -> (Fpath.t list, R.msg) result 357 + (** [readmes p] are [p]'s readme files. *) 358 + 359 + val readme : t -> (Fpath.t, R.msg) result 360 + (** [readme p] is the first element of [readmes p]. *) 361 + 362 + val change_logs : t -> (Fpath.t list, R.msg) result 363 + (** [change_logs p] are [p]'s change logs. *) 364 + 365 + val change_log : t -> (Fpath.t, R.msg) result 366 + (** [change_log p] is the first element of [change_logs p]. *) 367 + 368 + val licenses : t -> (Fpath.t list, R.msg) result 369 + (** [licenses p] are [p]'s license files. *) 370 + 371 + val distrib_uri : ?raw:bool -> t -> (string, R.msg) result 372 + (** [distrib_uri p] is [p]'s distribution URI. If [raw] is [true] 373 + defaults to [false], [p]'s raw URI distribution pattern is returned. *) 374 + 375 + val distrib_file : t -> (Fpath.t, R.msg) result 376 + (** [distrib_file p] is [p]'s distribution archive. *) 377 + 378 + val publish_msg : t -> (string, R.msg) result 379 + (** [publish_msg p] is [p]'s distribution publication message. *) 380 + 381 + (** {1 Test} *) 382 + 383 + val test : 384 + t -> dir:Fpath.t -> args:Cmd.t -> 385 + out:(OS.Cmd.run_out -> ('a, R.msg) result) -> ('a, R.msg) result 386 + 387 + (** {1 Build} *) 388 + 389 + val build : 390 + t -> dir:Fpath.t -> args:Cmd.t -> 391 + out:(OS.Cmd.run_out -> ('a, R.msg) result) -> ('a, R.msg) result 392 + 393 + (** {1 Clean} *) 394 + 395 + val clean : 396 + t -> dir:Fpath.t -> args:Cmd.t -> 397 + out:(OS.Cmd.run_out -> ('a, R.msg) result) -> ('a, R.msg) result 398 + 399 + (** {1 Distribution} *) 400 + 401 + val distrib_archive : t -> keep_dir:bool -> (Fpath.t, R.msg) result 402 + (** [distrib_archive p ~keep_dir] creates a distribution archive 403 + for [p] and returns its path. If [keep_dir] is [true] the 404 + repository checkout used to create the distribution archive 405 + is kept in the build directory. *) 406 + 407 + val distrib_filename : ?opam:bool -> t -> (Fpath.t, R.msg) result 408 + (** [distrib_filename ~opam p] is a distribution filename for [p]. 409 + If [opam] is [true] (defaults to [false]), the name follows 410 + opam's naming conventions. *) 411 + 412 + val publish_artefacts : t -> 413 + ([ `Distrib | `Doc | `Alt of string ] list, R.msg) result 414 + (** [publish_artefacts p] are [p]'s publication artefacts. *) 415 + 416 + (** {1 Lint} *) 417 + 418 + type lint = [ `Custom | `Std_files | `Meta | `Opam | `Deps ] 419 + (** The type for lints. *) 420 + 421 + val lint_all : lint list 422 + (** [lint_all] is a list with all lint values. *) 423 + 424 + val lint : 425 + ?ignore_pkg:bool -> t -> dir:Fpath.t -> lint list -> 426 + (int, R.msg) result 427 + (** [distrib ~ignore_pkg p ~dir lints] performs the lints 428 + mentioned in [lints] in a directory [dir] on the package [p]. 429 + If [ignore_pkg] is [true] [p]'s definitions are ignored. *) 430 + end 431 + 432 + (** Package delegate. *) 433 + module Delegate : sig 434 + 435 + (** {1 Publish} *) 436 + 437 + val publish_distrib : 438 + Pkg.t -> msg:string -> archive:Fpath.t -> (unit, R.msg) result 439 + (** [publish_distrib p ~msg ~archive] publishes the distribution 440 + archive [archive] of package [p] with publication message [msg]. *) 441 + 442 + val publish_doc : 443 + Pkg.t -> msg:string -> docdir:Fpath.t -> (unit, R.msg) result 444 + (** [publish_distrib p ~msg ~docdir] publishes the documentation 445 + directory [docdir] of package [p] with publication message [msg]. *) 446 + 447 + val publish_alt : 448 + Pkg.t -> kind:string -> msg:string -> archive:Fpath.t -> 449 + (unit, R.msg) result 450 + (** [publish_alt p ~kind ~msg ~archive] publishes the 451 + artefact [kind] for distribution archive [archive] of package [p] 452 + with publication message [msg]. *) 453 + 454 + (** {2 Helpers} *) 455 + 456 + val publish_in_git_branch : 457 + remote:string -> branch:string -> 458 + name:string -> version:string -> docdir:Fpath.t -> 459 + dir:Fpath.t -> (unit, R.msg) result 460 + (** [publish_in_git_branch ~remote ~branch ~name ~version ~docdir ~dir] 461 + publishes the documentation directory [docdir] of a package 462 + named [name] at version [version] by replacing the [dir] 463 + sub-directory of the branch [branch] of the current working 464 + directory git repository (use ["."] to copy the docdir at the 465 + root directory of the branch) and pushes the branch to [remote]. 466 + 467 + {b Note.} The publication procedure first checkouts the 468 + [gh-pages] in a temporary clone located in the {!Fpath.parent} 469 + directory of [docdir]. The [branch] branch of this clone is then 470 + pushed to the current working git repository, whose [branch] 471 + branch is then pushed to the [remote] repository. *) 472 + 473 + (** {1 Issues} *) 474 + 475 + val issue_list : Pkg.t -> (unit, R.msg) result 476 + (** [issue_list p] outputs the issue list on stdout. *) 477 + 478 + val issue_show : Pkg.t -> id:string -> (unit, R.msg) result 479 + (** [issue_show p ~id] outputs information about issue [id] on stdout. *) 480 + 481 + val issue_open : Pkg.t -> title:string -> body:string -> (unit, R.msg) result 482 + (** [issue_open p ~title ~body] create a new issue with title [title] 483 + and description body [body]. *) 484 + 485 + val issue_close : Pkg.t -> id:string -> msg:string -> (unit, R.msg) result 486 + (** [issue_close p ~id ~msg] closes issue [id] with message [msg]. *) 487 + end
+9
vendor/opam/topkg/src-care/topkg_care.mllib
··· 1 + Topkg_care 2 + Topkg_care_archive 3 + Topkg_care_delegate 4 + Topkg_care_ipc 5 + Topkg_care_ocamlbuild 6 + Topkg_care_ocamlfind 7 + Topkg_care_opam 8 + Topkg_care_pkg 9 + Topkg_care_text
+152
vendor/opam/topkg/src-care/topkg_care_archive.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + open Bos_setup 7 + 8 + (* Ustar archives *) 9 + 10 + module Tar = struct 11 + 12 + type ptime = int 13 + type t = string list 14 + 15 + let empty = [] 16 + 17 + (* Header. 18 + 19 + See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/\ 20 + pax.html#tag_20_92_13_06 *) 21 + 22 + let to_unix_path_string = 23 + if Fpath.dir_sep = "/" then Fpath.to_string else 24 + fun f -> String.concat ~sep:"/" (Fpath.segs f) 25 + 26 + let set_filename h f = 27 + let s = to_unix_path_string f in 28 + match String.length s with 29 + | n when n <= 100 -> Bytes.blit_string s 0 h 0 (String.length s) 30 + | n -> 31 + try match String.cut ~rev:true ~sep:"/" s with 32 + | None -> raise Exit 33 + | Some (p, n) -> 34 + (* This could be made more clever by trying to find 35 + the slash nearest to the half string position. *) 36 + if String.length p > 155 || String.length n > 100 then raise Exit; 37 + Bytes.blit_string n 0 h 0 (String.length n); 38 + Bytes.blit_string p 0 h 345 (String.length p); 39 + with 40 + | Exit -> failwith (strf "%a: file name too long" Fpath.pp f) 41 + 42 + let set_string off h s = Bytes.blit_string s 0 h off (String.length s) 43 + let set_octal field off len (* terminating NULL included *) h n = 44 + let octal = Printf.sprintf "%0*o" (len - 1) n in 45 + if String.length octal < len 46 + then Bytes.blit_string octal 0 h off (String.length octal) else 47 + failwith (strf "field %s: can't encode %d in %d-digit octal number" 48 + field (len - 1) n) 49 + 50 + let header_checksum h = 51 + let len = Bytes.length h in 52 + let rec loop acc i = 53 + if i > len then acc else 54 + loop (acc + (Char.to_int @@ Bytes.unsafe_get h i)) (i + 1) 55 + in 56 + loop 0 0 57 + 58 + let header fname mode mtime size typeflag = 59 + try 60 + let h = Bytes.make 512 '\x00' in 61 + set_filename h fname; 62 + set_octal "mode" 100 8 h mode; 63 + set_octal "owner" 108 8 h 0; 64 + set_octal "group" 116 8 h 0; 65 + set_octal "size" 124 12 h size; 66 + set_octal "mtime" 136 12 h mtime; 67 + set_string 148 h " "; (* Checksum *) 68 + set_string 156 h typeflag; 69 + set_string 257 h "ustar"; 70 + set_string 263 h "00"; 71 + set_octal "devmajor" 329 8 h 0; 72 + set_octal "devminor" 329 8 h 0; 73 + let c = header_checksum h in 74 + set_octal "checksum" 148 9 (* not NULL terminated *) h c; 75 + Ok (Bytes.unsafe_to_string h) 76 + with Failure msg -> R.error_msg msg 77 + 78 + (* Files *) 79 + 80 + let padding content = match String.length content mod 512 with 81 + | 0 -> "" 82 + | n -> Bytes.unsafe_to_string (Bytes.make (512 - n) '\x00') 83 + 84 + let add t fname ~mode ~mtime kind = 85 + let typeflag, size, data = match kind with 86 + | `Dir -> "5", 0, [] 87 + | `File cont -> "0", String.length cont, [cont; padding cont] 88 + in 89 + header fname mode mtime size typeflag 90 + >>| fun header -> List.rev_append data (header :: t) 91 + 92 + (* Encode *) 93 + 94 + let to_string t = 95 + let end_of_file = Bytes.unsafe_to_string (Bytes.make 1024 '\x00') in 96 + String.concat (List.rev (end_of_file :: t)) 97 + end 98 + 99 + let path_set_of_dir dir ~exclude_paths = 100 + let add_prefix p acc = Fpath.(Set.add (dir // p) acc) in 101 + let exclude_paths = Fpath.Set.(fold add_prefix exclude_paths empty) in 102 + let not_excluded p = Ok (not (Fpath.Set.mem p exclude_paths)) in 103 + let traverse = `Sat not_excluded in 104 + let elements = `Sat not_excluded in 105 + let err _ e = e in 106 + OS.Dir.fold_contents ~dotfiles:true ~err ~elements ~traverse 107 + Fpath.Set.add Fpath.Set.empty dir 108 + 109 + let tar dir ~exclude_paths ~root ~mtime = 110 + let tar_add file tar = 111 + let fname = match Fpath.rem_prefix dir file with 112 + | None -> assert false 113 + | Some file -> Fpath.(root // file) 114 + in 115 + Logs.info (fun m -> m "Archiving %a" Fpath.pp fname); 116 + tar 117 + >>= fun tar -> OS.Dir.exists file 118 + >>= function 119 + | true -> Tar.add tar fname ~mode:0o775 ~mtime `Dir 120 + | false -> 121 + OS.Path.Mode.get file 122 + >>= fun mode -> OS.File.read file 123 + >>= fun contents -> 124 + let mode = if 0o100 land mode > 0 then 0o775 else 0o664 in 125 + Tar.add tar fname ~mode ~mtime (`File contents) 126 + in 127 + path_set_of_dir dir ~exclude_paths 128 + >>= fun fset -> Fpath.Set.fold tar_add fset (Ok Tar.empty) 129 + >>| fun tar -> Tar.to_string tar 130 + 131 + (* Bzip2 compression and unarchiving *) 132 + 133 + let bzip2_cmd = OS.Env.(value "TOPKG_BZIP2" cmd ~absent:(Cmd.v "bzip2")) 134 + let ensure_bzip2 () = OS.Cmd.must_exist bzip2_cmd >>| fun _ -> () 135 + let bzip2 s ~dst = OS.Cmd.(in_string s |> run_io bzip2_cmd |> to_file dst) 136 + 137 + let tar_cmd = OS.Env.(value "TOPKG_TAR" cmd ~absent:(Cmd.v "tar")) 138 + let ensure_tar () = OS.Cmd.must_exist tar_cmd >>| fun _ -> () 139 + let untbz ?(clean = false) ar = 140 + let clean_dir dir = OS.Dir.exists dir >>= function 141 + | true when clean -> OS.Dir.delete ~recurse:true dir 142 + | _ -> Ok () 143 + in 144 + let archive_dir, ar = Fpath.split_base ar in 145 + let unarchive ar = 146 + let dir = Fpath.rem_ext ar in 147 + OS.Cmd.must_exist tar_cmd 148 + >>= fun cmd -> clean_dir dir 149 + >>= fun () -> OS.Cmd.run Cmd.(tar_cmd % "-xjf" % p ar) 150 + >>= fun () -> Ok Fpath.(archive_dir // dir) 151 + in 152 + R.join @@ OS.Dir.with_current archive_dir unarchive ar
+51
vendor/opam/topkg/src-care/topkg_care_archive.mli
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** Archive creation. 7 + 8 + See {!Topkg_care.Archive}. *) 9 + 10 + open Bos_setup 11 + 12 + (** {1 Ustar archives} *) 13 + 14 + (** ustar encoder. 15 + 16 + {b References}. 17 + {{:http://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html#tag_20_92_13_06}ustar Interchange Format} in POSIX 1003.1, 2013. *) 18 + module Tar : sig 19 + 20 + (** {1 Ustar encoder} *) 21 + 22 + type ptime = int 23 + (** The type for POSIX times in seconds since the epoch. *) 24 + 25 + type t 26 + (** The type for ustar archives. *) 27 + 28 + val empty : t 29 + (** [empty] is the empty ustar archive. *) 30 + 31 + val add : 32 + t -> Fpath.t -> mode:int -> mtime:ptime -> [`Dir | `File of string ] -> 33 + (t, R.msg) result 34 + (** [add a f mode mtime kind] adds to archive [a] an element of 35 + type [kind] with file path [f], permission mode [mode] and modificaton 36 + time [mtime]. *) 37 + 38 + val to_string : t -> string 39 + (** [to_string a] is the byte serialization of the archive [a]. *) 40 + end 41 + 42 + val tar : 43 + Fpath.t -> exclude_paths:Fpath.set -> root:Fpath.t -> mtime:int -> 44 + (string, R.msg) result 45 + 46 + (** {1 Bzip2 compression and unarchiving} *) 47 + 48 + val ensure_bzip2 : unit -> (unit, R.msg) result 49 + val bzip2 : string -> dst:Fpath.t -> (unit, R.msg) result 50 + val ensure_tar : unit -> (unit, R.msg) result 51 + val untbz : ?clean:bool -> Fpath.t -> (Fpath.t, R.msg) result
+148
vendor/opam/topkg/src-care/topkg_care_delegate.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + open Bos_setup 7 + 8 + (* Running the delegate *) 9 + 10 + let run_delegate pkg args = 11 + let verbosity = Logs.level_to_string (Logs.level ()) in 12 + Topkg_care_pkg.delegate pkg 13 + >>= fun del -> Ok Cmd.(del % "ipc" % verbosity %% args) 14 + >>= fun cmd -> OS.Cmd.run_status cmd 15 + >>= function 16 + | `Exited 0 -> Ok () 17 + | `Exited 1 -> 18 + R.error_msgf "Action unsupported by delegate %a" Cmd.pp del 19 + | (`Exited n | `Signaled n) -> 20 + R.error_msgf "Delegate %a errored with %d" Cmd.pp del n 21 + 22 + (* Publish request *) 23 + 24 + let publish_distrib p ~msg ~archive = 25 + Topkg_care_pkg.name p 26 + >>= fun name -> Topkg_care_pkg.version p 27 + >>= fun version -> Topkg_care_pkg.distrib_uri p 28 + >>= fun distrib_uri -> 29 + run_delegate p Cmd.(v "publish" % "distrib" % distrib_uri % 30 + name % version % msg % p archive) 31 + 32 + let publish_doc p ~msg ~docdir = 33 + let doc_uri p = Topkg_care_pkg.opam_field_hd p "doc" >>= function 34 + | None -> Ok "" 35 + | Some uri -> Ok uri 36 + in 37 + Topkg_care_pkg.name p 38 + >>= fun name -> Topkg_care_pkg.version p 39 + >>= fun version -> doc_uri p 40 + >>= fun doc_uri -> 41 + run_delegate p Cmd.(v "publish" % "doc" % doc_uri % name % version % msg % 42 + p docdir) 43 + 44 + let publish_alt p ~kind ~msg ~archive = 45 + Topkg_care_pkg.name p 46 + >>= fun name -> Topkg_care_pkg.version p 47 + >>= fun version -> Topkg_care_pkg.distrib_uri p 48 + >>= fun distrib_uri -> 49 + run_delegate p Cmd.(v "publish" % "alt" % distrib_uri % kind % 50 + name % version % msg % p archive) 51 + 52 + let publish_in_git_branch ~remote ~branch ~name ~version ~docdir ~dir = 53 + let pp_distrib ppf (name, version) = 54 + Fmt.pf ppf "%a %a" 55 + Topkg_care_text.Pp.name name Topkg_care_text.Pp.version version 56 + in 57 + let log_publish_result msg distrib dir = 58 + Logs.app (fun m -> m "%s %a@ in@ directory@ %a@ of@ gh-pages@ branch" 59 + msg pp_distrib distrib Fpath.pp dir) 60 + in 61 + let cp src dst = 62 + let dst_is_root = Fpath.is_current_dir dst in 63 + let src = 64 + if dst_is_root then Fpath.to_dir_path src else Fpath.rem_empty_seg src 65 + in 66 + (* FIXME we lost Windows friends here, fix bos #30 *) 67 + OS.Cmd.run Cmd.(v "cp" % "-R" % p src % p dst) 68 + in 69 + let delete dir = 70 + if not (Fpath.is_current_dir dir) then OS.Dir.delete ~recurse:true dir else 71 + let delete acc p = acc >>= fun () -> OS.Path.delete ~recurse:true p in 72 + let gitdir = Fpath.v ".git" in 73 + let not_git p = not (Fpath.equal p gitdir) in 74 + OS.Dir.contents dir 75 + >>= fun files -> List.fold_left delete (Ok ()) (List.filter not_git files) 76 + in 77 + let git_for_repo r = Cmd.of_list (Topkg.Cmd.to_list @@ Topkg.Vcs.cmd r) in 78 + let replace_dir_and_push docdir dir = 79 + let msg = strf "Update %s doc to %s." name version in 80 + Topkg.Vcs.get () 81 + >>= fun repo -> Ok (git_for_repo repo) 82 + >>= fun git -> OS.Cmd.run Cmd.(git % "checkout" % branch) 83 + >>= fun () -> delete dir 84 + >>= fun () -> cp docdir dir 85 + >>= fun () -> Topkg.Vcs.is_dirty repo 86 + >>= function 87 + | false -> Ok false 88 + | true -> 89 + OS.Cmd.run Cmd.(git % "add" % p dir) 90 + >>= fun () -> OS.Cmd.run Cmd.(git % "commit" % "-m" % msg) 91 + >>= fun () -> OS.Cmd.run Cmd.(git % "push") 92 + >>= fun () -> Ok true 93 + in 94 + if not (Fpath.is_rooted ~root:Fpath.(v ".") dir) 95 + then 96 + R.error_msgf "%a directory is not rooted in the repository or not relative" 97 + Fpath.pp dir 98 + else 99 + let clonedir = Fpath.(parent docdir / strf "%s-%s.pubdoc" name version) in 100 + OS.Dir.delete ~recurse:true clonedir 101 + >>= fun () -> Topkg.Vcs.get () 102 + >>= fun repo -> Topkg.Vcs.clone repo ~dir:(Fpath.to_string clonedir) 103 + >>= fun () -> OS.Dir.with_current clonedir (replace_dir_and_push docdir) dir 104 + >>= fun res -> res 105 + >>= function 106 + | false (* no changes *) -> 107 + log_publish_result "No documentation changes for" (name, version) dir; 108 + Ok () 109 + | true -> 110 + let push_spec = strf "%s:%s" branch branch in 111 + Ok (git_for_repo repo) >>= fun git -> 112 + OS.Cmd.run Cmd.(git % "push" % remote % push_spec) 113 + >>= fun () -> OS.Dir.delete ~recurse:true clonedir 114 + >>= fun () -> 115 + log_publish_result "Published documentation for" (name, version) dir; 116 + Ok () 117 + 118 + (* Issue requests *) 119 + 120 + let issues_uri p = Topkg_care_pkg.opam_field p "dev-repo" >>| function 121 + | None | Some [] -> "" 122 + | Some (u :: _) -> u 123 + 124 + let issue_list p = 125 + issues_uri p >>= fun issues_uri -> 126 + run_delegate p Cmd.(v "issue" % "list" % issues_uri) 127 + 128 + let issue_show p ~id = 129 + issues_uri p >>= fun issues_uri -> 130 + run_delegate p Cmd.(v "issue" % "show" % issues_uri % "id") 131 + 132 + let issue_open p ~title ~body = 133 + issues_uri p >>= fun issues_uri -> 134 + match run_delegate p Cmd.(v "issue" % "open" % issues_uri % title % body) with 135 + | Ok _ as v -> v 136 + | Error _ as e -> 137 + let pp_body ppf = function "" -> () | body -> Fmt.pf ppf "@,@,%s" body in 138 + Logs.app (fun m -> m "@[<v>Your open issue message was:@,---@,%s%a@]" 139 + title pp_body body); 140 + e 141 + 142 + let issue_close p ~id ~msg = 143 + issues_uri p >>= fun issues_uri -> 144 + match run_delegate p Cmd.(v "issue" % "close" % issues_uri % id % msg) with 145 + | Ok _ as v -> v 146 + | Error _ as e -> 147 + Logs.app (fun m -> m "@[<v>Your closing message was:@,---@,%s@]" msg); 148 + e
+40
vendor/opam/topkg/src-care/topkg_care_delegate.mli
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** Package delegate. 7 + 8 + See {!Topkg_care.Delegate} for documentation. *) 9 + 10 + open Bos_setup 11 + 12 + (** {1 Publish} *) 13 + 14 + val publish_distrib : 15 + Topkg_care_pkg.t -> msg:string -> archive:Fpath.t -> 16 + (unit, R.msg) result 17 + 18 + val publish_doc : 19 + Topkg_care_pkg.t -> msg:string -> docdir:Fpath.t -> 20 + (unit, R.msg) result 21 + 22 + val publish_alt : 23 + Topkg_care_pkg.t -> kind:string -> msg:string -> archive:Fpath.t -> 24 + (unit, R.msg) result 25 + 26 + val publish_in_git_branch : 27 + remote:string -> branch:string -> 28 + name:string -> version:string -> docdir:Fpath.t -> 29 + dir:Fpath.t -> (unit, R.msg) result 30 + 31 + (** {1 Delegate} *) 32 + 33 + val issue_list : Topkg_care_pkg.t -> (unit, R.msg) result 34 + val issue_show : Topkg_care_pkg.t -> id:string -> (unit, R.msg) result 35 + 36 + val issue_open : 37 + Topkg_care_pkg.t -> title:string -> body:string -> (unit, R.msg) result 38 + 39 + val issue_close : 40 + Topkg_care_pkg.t -> id:string -> msg:string -> (unit, R.msg) result
+33
vendor/opam/topkg/src-care/topkg_care_ipc.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + open Bos_setup 7 + 8 + let ocaml = 9 + Cmd.of_list @@ Topkg.Cmd.to_list @@ Topkg.Conf.tool "ocaml" `Build_os 10 + 11 + let pkg_must_exist pkg_file = match OS.File.must_exist pkg_file with 12 + | Ok _ -> Ok pkg_file 13 + | Error _ -> 14 + let p = match OS.Dir.current () with 15 + | Error _ -> (* ignore *) pkg_file 16 + | Ok dir -> Fpath.(dir // pkg_file) 17 + in 18 + R.error_msgf "Not a package: no file %a" Fpath.pp p 19 + 20 + let ask ~pkg_file ipc = 21 + let codec = Topkg.Private.Ipc.codec ipc in 22 + let verbosity = Logs.(level_to_string (level ())) in 23 + let ipc_cmd = Cmd.of_list @@ Topkg.Cmd.to_list @@ Topkg.Private.Ipc.cmd ipc in 24 + let cmd = Cmd.(ocaml % p pkg_file % "ipc" % verbosity %% ipc_cmd) in 25 + pkg_must_exist pkg_file >>= fun pkg_file -> 26 + begin 27 + OS.Cmd.run cmd 28 + >>= fun () -> Fpath.of_string (Topkg.Private.Ipc.answer ipc) 29 + >>= fun answer -> OS.File.read answer 30 + >>= fun data -> Topkg.Private.Codec.dec_result codec data 31 + end 32 + |> R.reword_error_msg 33 + (fun _ -> R.msgf "Failed to load package description %a" Fpath.pp pkg_file)
+18
vendor/opam/topkg/src-care/topkg_care_ipc.mli
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** IPC with package description files *) 7 + 8 + (** {1 Asking packages} *) 9 + 10 + open Bos_setup 11 + 12 + val ocaml : Cmd.t 13 + (** [ocaml] is a command for [ocaml] looked up using 14 + {!Topkg.Conf.tool}[ "ocaml" `Build_os]. *) 15 + 16 + val ask : pkg_file:Fpath.t -> 'a Topkg.Private.Ipc.t -> ('a, R.msg) result 17 + (** [ask pkg_file ipc] performs the IPC [ipc] with the package description 18 + file [pkg_file] using the interpreter {!ocaml}. *)
+33
vendor/opam/topkg/src-care/topkg_care_ocamlbuild.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + open Bos_setup 7 + 8 + let cmd = 9 + Cmd.of_list @@ Topkg.Cmd.to_list @@ Topkg.Conf.tool "ocamlbuild" `Host_os 10 + 11 + let find_packages ~roots s = 12 + let package = String.sub "package(" in 13 + let not_rpar c = not (Char.equal ')' c) in 14 + let not_dot c = not (Char.equal '.' c) in 15 + let is_comma c = Char.equal ',' c in 16 + let is_sep c = Char.Ascii.is_white c || is_comma c in 17 + let rec loop acc s = match String.Sub.find_sub ~sub:package s with 18 + | None -> acc 19 + | Some s -> 20 + let rest = String.Sub.(extend (stop s)) in 21 + let ids, rest = String.Sub.span ~sat:not_rpar rest in 22 + let ids = String.Sub.fields ~empty:false ~is_sep ids in 23 + let add_id acc id = 24 + let id = if roots then String.Sub.take ~sat:not_dot id else id in 25 + String.Set.add (String.Sub.to_string id) acc 26 + in 27 + let acc = List.fold_left add_id acc ids in 28 + loop acc (String.Sub.tail rest) 29 + in 30 + loop String.Set.empty (String.sub s) 31 + 32 + let package_tags ?(roots = false) file = 33 + OS.File.read file >>= fun contents -> Ok (find_packages ~roots contents)
+15
vendor/opam/topkg/src-care/topkg_care_ocamlbuild.mli
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** [ocamlbuild] helpers. 7 + 8 + See {!Topkg_care.OCamlbuild}. *) 9 + 10 + (** {1 OCamlbuild} *) 11 + 12 + open Bos_setup 13 + 14 + val cmd : Cmd.t 15 + val package_tags : ?roots:bool -> Fpath.t -> (String.set, R.msg) result
+13
vendor/opam/topkg/src-care/topkg_care_ocamlfind.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + open Bos_setup 7 + 8 + let cmd = 9 + Cmd.of_list @@ Topkg.Cmd.to_list @@ Topkg.Conf.tool "ocamlfind" `Host_os 10 + 11 + let base_packages = String.Set.of_list 12 + [ "bigarray"; "bytes"; "compiler-libs"; "dynlink"; "graphics"; "num"; 13 + "ocamldoc"; "stdlib"; "str"; "threads"; "unix" ]
+15
vendor/opam/topkg/src-care/topkg_care_ocamlfind.mli
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** [ocamlfind] helpers. 7 + 8 + See {!Topkg_care.OCamlfind}. *) 9 + 10 + (** {1 OCamlfind} *) 11 + 12 + open Bos_setup 13 + 14 + val cmd : Cmd.t 15 + val base_packages : String.set
+184
vendor/opam/topkg/src-care/topkg_care_opam.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + open Bos_setup 7 + 8 + (* Command *) 9 + 10 + let cmd = 11 + Cmd.of_list @@ Topkg.Cmd.to_list @@ Topkg.Conf.tool "opam" `Host_os 12 + 13 + (* Publish *) 14 + 15 + let publish = 16 + let absent = Cmd.(v "opam-publish") in 17 + OS.Env.(value "TOPKG_OPAM_PUBLISH" cmd ~absent) 18 + 19 + let ensure_publish () = 20 + OS.Cmd.must_exist publish >>= fun cmd -> 21 + OS.Cmd.run_out Cmd.(publish % "--version") |> OS.Cmd.out_string 22 + >>= fun (version, _) -> match Topkg.String.parse_version version with 23 + | None -> R.error_msgf "Could not determine the version of opam-publish" 24 + | Some (m, _, _, _) when m < 2 -> 25 + R.error_msgf "topkg needs at least opam-publish 2.0.0" 26 + | Some _ -> Ok () 27 + 28 + let submit ?msg ~opam_file () = 29 + let msg = match msg with 30 + | None -> Ok (Cmd.empty) 31 + | Some msg -> 32 + OS.File.tmp "topkg-opam-submit-msg-%s" 33 + >>= fun m -> OS.File.write m msg 34 + >>= fun () -> Ok Cmd.(v "--msg-file" % p m) 35 + in 36 + msg >>= fun msg -> OS.Cmd.run Cmd.(publish %% msg % p opam_file) 37 + 38 + (* Packages *) 39 + 40 + let ocaml_base_packages = String.Set.of_list 41 + [ "base-bigarray"; "base-bytes"; "base-threads"; "base-unix"; ] 42 + 43 + (* Files *) 44 + 45 + module File = struct 46 + 47 + (* Try to compose with the OpamFile.OPAM API *) 48 + 49 + let id x = x 50 + let list f = fun v -> [f v] 51 + let field name field conv = 52 + name, fun acc o -> String.Map.add name (conv (field o)) acc 53 + 54 + let opt_field name field conv = 55 + name, fun acc o -> match field o with 56 + | None -> acc 57 + | Some v -> String.Map.add name (conv v) acc 58 + 59 + let deps_conv d = 60 + let add_pkg acc (n, _) = OpamPackage.Name.to_string n :: acc in 61 + OpamFormula.fold_left add_pkg [] d 62 + 63 + let fields = [ 64 + opt_field "name" OpamFile.OPAM.name_opt (list OpamPackage.Name.to_string); 65 + opt_field "version" OpamFile.OPAM.version_opt 66 + (list OpamPackage.Version.to_string); 67 + field "opam-version" OpamFile.OPAM.opam_version 68 + (list OpamVersion.to_string); 69 + field "available" OpamFile.OPAM.available (list OpamFilter.to_string); 70 + field "maintainer" OpamFile.OPAM.maintainer id; 71 + field "homepage" OpamFile.OPAM.homepage id; 72 + field "authors" OpamFile.OPAM.author id; 73 + field "license" OpamFile.OPAM.license id; 74 + field "doc" OpamFile.OPAM.doc id; 75 + field "tags" OpamFile.OPAM.tags id; 76 + field "bug-reports" OpamFile.OPAM.bug_reports id; 77 + opt_field "dev-repo" OpamFile.OPAM.dev_repo (list OpamUrl.to_string); 78 + field "depends" OpamFile.OPAM.depends deps_conv; 79 + field "depopts" OpamFile.OPAM.depopts deps_conv; 80 + opt_field "synopsis" OpamFile.OPAM.synopsis (list id); 81 + opt_field "description" OpamFile.OPAM.descr_body (list id); 82 + ] 83 + 84 + let field_names = 85 + let add acc (name, field) = String.Set.add name acc in 86 + List.fold_left add String.Set.empty fields 87 + 88 + let fields file = 89 + let parse file = 90 + let file = OpamFilename.of_string (Fpath.to_string file) in 91 + let opam = OpamFile.OPAM.read (OpamFile.make file) in 92 + let known_fields = 93 + let add_field acc (_, field) = field acc opam in 94 + List.fold_left add_field String.Map.empty fields 95 + in 96 + (* FIXME add OpamFile.OPAM.extensions when supported *) 97 + known_fields 98 + in 99 + Logs.info (fun m -> m "Parsing opam file %a" Fpath.pp file); 100 + try Ok (parse file) with 101 + | exn -> 102 + (* Apparently in at least opam-lib 1.2.2, the error will be logged 103 + on stdout. *) 104 + R.error_msgf "%a: could not parse opam file" Fpath.pp file 105 + 106 + let deps ?(opts = true) fields = 107 + let deps = match String.Map.find "depends" fields with 108 + | None -> [] | Some deps -> deps 109 + in 110 + let dep_opts = 111 + if not opts then [] else 112 + match String.Map.find "depopts" fields with 113 + | None -> [] | Some deps -> deps 114 + in 115 + String.Set.of_list (List.rev_append dep_opts deps) 116 + end 117 + 118 + module Descr = struct 119 + type t = string * string 120 + 121 + let of_string s = match String.cuts ~sep:"\n" s with 122 + | [] -> R.error_msgf "Cannot extract opam descr." 123 + | synopsis :: descr -> Ok (synopsis, String.concat ~sep:"\n" descr) 124 + 125 + let to_string (synopsis, descr) = strf "%s\n%s" synopsis descr 126 + let to_opam_fields (synopsis, descr) = 127 + strf "synopsis: \"\"\"%s\"\"\"\ndescription: \"\"\"\\\n%s\"\"\"" 128 + synopsis descr 129 + 130 + let of_readme ?flavour r = 131 + let parse_synopsis l = 132 + let error l = R.error_msgf "%S: can't extract opam synopsis" l in 133 + let ok s = Ok String.(Ascii.capitalize @@ String.Sub.to_string s) in 134 + let not_white c = not (Char.Ascii.is_white c) in 135 + let skip_non_white l = String.Sub.drop ~sat:not_white l in 136 + let skip_white l = String.Sub.drop ~sat:Char.Ascii.is_white l in 137 + let start = 138 + String.sub l |> skip_white |> skip_non_white |> skip_white 139 + in 140 + match String.Sub.head start with 141 + | None -> error l 142 + | Some c when Char.Ascii.is_letter c -> ok start 143 + | Some c -> (* Try to skip a separator. *) 144 + let start = start |> skip_non_white |> skip_white in 145 + match String.Sub.head start with 146 + | None -> error l 147 + | Some _ -> ok start 148 + in 149 + let drop_line l = 150 + String.is_prefix ~affix:"Home page:" l || 151 + String.is_prefix ~affix:"Homepage:" l || 152 + String.is_prefix ~affix:"Contact:" l || 153 + String.is_prefix ~affix:"%%VERSION" l 154 + in 155 + let keep_line l = not (drop_line l) in 156 + match Topkg_care_text.head ?flavour r with 157 + | None -> R.error_msgf "Could not extract opam description." 158 + | Some (title, text) -> 159 + let sep = "\n" in 160 + let title = Topkg_care_text.header_title ?flavour title in 161 + parse_synopsis title 162 + >>= fun synopsis -> Ok (String.cuts ~sep text) 163 + >>= fun text -> Ok (List.filter keep_line text) 164 + >>= fun text -> Ok (synopsis, String.concat ~sep text) 165 + 166 + let of_readme_file file = 167 + let flavour = Topkg_care_text.flavour_of_fpath file in 168 + (OS.File.read file 169 + >>= fun text -> of_readme ?flavour text) 170 + |> R.reword_error_msg ~replace:true 171 + (fun m -> R.msgf "%a: %s" Fpath.pp file m) 172 + end 173 + 174 + module Url = struct 175 + type t = string 176 + let v ~uri ~checksum = strf "archive: \"%s\"\nchecksum: \"%s\"" uri checksum 177 + let with_distrib_file ~uri distrib_file = 178 + try 179 + let checksum = Digest.(to_hex @@ file (Fpath.to_string distrib_file)) in 180 + Ok (v ~uri ~checksum) 181 + with Failure msg | Sys_error msg -> R.error_msg msg 182 + 183 + let to_opam_section u = strf "url {\n%s\n}" u 184 + end
+40
vendor/opam/topkg/src-care/topkg_care_opam.mli
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** opam interaction. 7 + 8 + See {!Topkg_care.Opam}. *) 9 + 10 + (** {1 opam} *) 11 + 12 + open Bos_setup 13 + 14 + val cmd : Cmd.t 15 + val ensure_publish : unit -> (unit, R.msg) result 16 + val submit : ?msg:string -> opam_file:Fpath.t -> unit -> (unit, R.msg) result 17 + val ocaml_base_packages : String.set 18 + 19 + module File : sig 20 + val field_names : String.set 21 + val fields : Fpath.t -> ((string list) String.map , R.msg) result 22 + val deps : ?opts:bool -> (string list) String.map -> String.set 23 + end 24 + 25 + module Descr : sig 26 + type t = string * string 27 + val of_string : string -> (t, R.msg) result 28 + val to_string : t -> string 29 + val to_opam_fields : t -> string 30 + val of_readme : 31 + ?flavour:Topkg_care_text.flavour -> string -> (t, R.msg) result 32 + val of_readme_file : Fpath.t -> (t, R.msg) result 33 + end 34 + 35 + module Url : sig 36 + type t = string 37 + val v : uri:string -> checksum:string -> t 38 + val with_distrib_file : uri:string -> Fpath.t -> (t, R.msg) result 39 + val to_opam_section : t -> string 40 + end
+564
vendor/opam/topkg/src-care/topkg_care_pkg.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + open Bos_setup 7 + 8 + (* Misc *) 9 + 10 + let path_set_of_strings ps = 11 + try 12 + let add_excl acc p = match Fpath.of_string p with 13 + | Error (`Msg msg) -> failwith msg 14 + | Ok p -> Fpath.Set.add p acc 15 + in 16 + Ok (List.fold_left add_excl Fpath.Set.empty ps) 17 + with 18 + Failure msg -> R.error_msg msg 19 + 20 + let rec path_list_of_strings ps = 21 + let rec loop acc = function 22 + | [] -> Ok (List.rev acc) 23 + | p :: ps -> 24 + match Fpath.of_string p with 25 + | Error _ as e -> e 26 + | Ok p -> loop (p :: acc) ps 27 + in 28 + loop [] ps 29 + 30 + let uri_sld uri = match Topkg_care_text.split_uri uri with 31 + | None -> None 32 + | Some (_, host, _) -> 33 + match List.rev (String.cuts ~sep:"." host) with 34 + | fst :: snd :: _ -> Some snd 35 + | _ -> None 36 + 37 + let uri_append u s = match String.head ~rev:true u with 38 + | None -> s 39 + | Some '/' -> strf "%s%s" u s 40 + | Some _ -> strf "%s/%s" u s 41 + 42 + let chop_ext u = match String.cut ~rev:true ~sep:"." u with 43 + | None -> u 44 + | Some (u, _) -> u 45 + 46 + let chop_git_prefix u = match String.cut ~sep:"git+" u with 47 + | Some ("", uri) -> uri 48 + | _ -> u 49 + 50 + (* Package *) 51 + 52 + type t = 53 + { name : string option; 54 + version : string option; 55 + delegate : Cmd.t option; 56 + build_dir : Fpath.t option; 57 + opam : Fpath.t option; 58 + opam_descr : Fpath.t option; 59 + opam_fields : (string list String.map, R.msg) result Lazy.t; 60 + readmes : Fpath.t list option; 61 + change_logs : Fpath.t list option; 62 + licenses : Fpath.t list option; 63 + distrib_uri : string option; 64 + distrib_file : Fpath.t option; 65 + publish_msg : string option; 66 + publish_artefacts : [ `Distrib | `Doc | `Alt of string ] list option; 67 + pkg_file : Fpath.t; 68 + pkg : (Topkg.Private.Pkg.t, R.msg) result Lazy.t; } 69 + 70 + let empty p = { p with pkg = lazy (Ok (Topkg.Private.Pkg.empty)) } 71 + let pkg p = Lazy.force p.pkg 72 + 73 + let opam_fields p = Lazy.force p.opam_fields 74 + let opam_field p f = opam_fields p >>| fun fields -> String.Map.find f fields 75 + let opam_field_hd p f = opam_field p f >>| function 76 + | None | Some [] -> None 77 + | Some (v :: _) -> Some v 78 + 79 + let opam_homepage_sld p = opam_field_hd p "homepage" >>| function 80 + | None -> None 81 + | Some uri -> match uri_sld uri with None -> None | Some sld -> Some (uri, sld) 82 + 83 + let pkg_file p = p.pkg_file 84 + 85 + let name p = match p.name with 86 + | Some n -> Ok n 87 + | None -> pkg p >>| Topkg.Private.Pkg.name 88 + 89 + let version p = match p.version with 90 + | Some v -> Ok v 91 + | None -> Topkg.Vcs.get () >>= fun r -> Topkg.Vcs.describe ~dirty:false r 92 + 93 + let delegate p = 94 + let not_found () = 95 + R.error_msg "No package delegate found. \ 96 + Try `topkg help delegate` for more information." 97 + in 98 + match p.delegate with 99 + | Some cmd -> Ok cmd 100 + | None -> 101 + pkg p >>| Topkg.Private.Pkg.delegate >>= function 102 + | Some cmd -> Ok (Cmd.of_list @@ Topkg.Cmd.to_list cmd) 103 + | None -> 104 + match OS.Env.(value "TOPKG_DELEGATE" (some cmd) ~absent:None) with 105 + | Some cmd -> Ok cmd 106 + | None -> 107 + opam_homepage_sld p >>= function 108 + | None -> not_found () 109 + | Some (_, sld) -> 110 + let exec = strf "%s-topkg-delegate" sld in 111 + let cmd = Cmd.v exec in 112 + OS.Cmd.exists cmd >>= function 113 + | true -> Ok cmd 114 + | false -> 115 + if exec <> "github-topkg-delegate" 116 + then not_found () 117 + else Ok (Cmd.v "toy-github-topkg-delegate") 118 + 119 + let build_dir p = match p.build_dir with 120 + | Some b -> Ok b 121 + | None -> pkg p >>| Topkg.Private.Pkg.build_dir >>= Fpath.of_string 122 + 123 + let readmes p = match p.readmes with 124 + | Some f -> Ok f 125 + | None -> pkg p >>| Topkg.Private.Pkg.readmes >>= path_list_of_strings 126 + 127 + let readme p = readmes p >>= function 128 + | [] -> R.error_msgf "No readme file specified in the package description" 129 + | r :: _ -> Ok r 130 + 131 + let opam p = match p.opam with 132 + | Some f -> Ok f 133 + | None -> 134 + name p 135 + >>= fun name -> pkg p 136 + >>| Topkg.Private.Pkg.opam ~name >>= Fpath.of_string 137 + 138 + let opam_field_descr p = 139 + opam_field p "synopsis" >>= function 140 + | None -> Ok None 141 + | Some syn -> 142 + opam_field p "description" >>= function 143 + | None -> Ok None 144 + | Some descr -> Ok (Some (List.hd syn, List.hd descr)) 145 + 146 + let opam_descr p = 147 + let descr_file_for_opam opam = 148 + if Fpath.has_ext ".opam" opam then Fpath.(rem_ext opam + ".descr") else 149 + Fpath.(parent opam / "descr") 150 + in 151 + let read f = 152 + OS.File.read f 153 + >>= fun c -> Topkg_care_opam.Descr.of_string c 154 + >>| fun d -> d, false 155 + in 156 + match p.opam_descr with 157 + | Some f -> read f 158 + | None -> 159 + opam_field_descr p >>= function 160 + | Some descr -> Ok (descr, true) 161 + | None -> 162 + opam p 163 + >>= fun opam -> Ok (descr_file_for_opam opam) 164 + >>= fun descr_file -> OS.File.exists descr_file 165 + >>= function 166 + | true -> 167 + Logs.info (fun m -> m "Found opam descr file %a" 168 + Fpath.pp descr_file); 169 + read descr_file 170 + | false -> 171 + readme p 172 + >>= fun readme -> 173 + Logs.info 174 + (fun m -> m "Extracting opam descr from %a" Fpath.pp readme); 175 + Topkg_care_opam.Descr.of_readme_file readme 176 + >>| fun d -> d, false 177 + 178 + let change_logs p = match p.change_logs with 179 + | Some f -> Ok f 180 + | None -> pkg p >>| Topkg.Private.Pkg.change_logs >>= path_list_of_strings 181 + 182 + let change_log p = change_logs p >>= function 183 + | [] -> R.error_msgf "No change log specified in the package description." 184 + | l :: _ -> Ok l 185 + 186 + let licenses p = match p.licenses with 187 + | Some f -> Ok f 188 + | None -> pkg p >>| Topkg.Private.Pkg.licenses >>= path_list_of_strings 189 + 190 + let distrib_uri ?(raw = false) p = 191 + let subst_uri p uri = 192 + uri 193 + >>= fun uri -> name p 194 + >>= fun name -> version p 195 + >>= fun version -> Ok (Topkg.String.drop_initial_v version) 196 + >>= fun version_num -> 197 + let defs = String.Map.(empty 198 + |> add "NAME" name |> add "VERSION" version 199 + |> add "VERSION_NUM" version_num) 200 + in 201 + Pat.of_string uri >>| fun pat -> Pat.format defs pat 202 + in 203 + let not_found () = 204 + R.error_msg "no distribution URI found, see topkg's API documentation." 205 + in 206 + let uri = match p.distrib_uri with 207 + | Some u -> Ok u 208 + | None -> 209 + pkg p 210 + >>= fun pkg -> match Topkg.Private.Pkg.distrib_uri pkg with 211 + | Some u -> Ok u 212 + | None -> 213 + opam_homepage_sld p >>= function 214 + | None -> not_found () 215 + | Some (uri, sld) -> 216 + if sld <> "github" 217 + then (Ok (uri_append uri "releases/$(NAME)-$(VERSION_NUM).tbz")) 218 + else 219 + opam_field_hd p "dev-repo">>= function 220 + | None -> not_found () 221 + | Some dev_repo -> 222 + Ok (uri_append (chop_git_prefix (chop_ext dev_repo)) 223 + "releases/download/$(VERSION)/$(NAME)-$(VERSION_NUM).tbz") 224 + in 225 + if raw then uri else subst_uri p uri 226 + 227 + let distrib_filename ?(opam = false) p = 228 + let sep = if opam then '.' else '-' in 229 + name p 230 + >>= fun name -> version p 231 + >>= fun version -> Ok (Topkg.String.drop_initial_v version) 232 + >>= fun version_num -> Fpath.of_string (strf "%s%c%s" name sep version_num) 233 + 234 + let distrib_archive_path p = 235 + build_dir p 236 + >>= fun build_dir -> distrib_filename ~opam:false p 237 + >>| fun b -> Fpath.(build_dir // b + ".tbz") 238 + 239 + let distrib_file p = match p.distrib_file with 240 + | Some f -> Ok f 241 + | None -> 242 + (distrib_archive_path p 243 + >>= fun f -> OS.File.must_exist f) 244 + |> R.reword_error_msg 245 + (fun _ -> R.msgf "Did you forget to call 'topkg distrib' ?") 246 + 247 + let publish_msg p = match p.publish_msg with 248 + | Some msg -> Ok msg 249 + | None -> 250 + change_log p 251 + >>= fun change_log -> Topkg_care_text.change_log_file_last_entry change_log 252 + >>= fun (_, (h, txt)) -> Ok (strf "%s\n%s" h txt) 253 + 254 + let publish_artefacts p = match p.publish_artefacts with 255 + | Some arts -> Ok arts 256 + | None -> pkg p >>| Topkg.Private.Pkg.publish_artefacts 257 + 258 + let v 259 + ?name ?version ?delegate ?build_dir ?opam:opam_file ?opam_descr 260 + ?readme ?change_log ?license ?distrib_uri ?distrib_file ?publish_msg 261 + ?publish_artefacts 262 + pkg_file 263 + = 264 + let readmes = match readme with Some r -> Some [r] | None -> None in 265 + let change_logs = match change_log with Some c -> Some [c] | None -> None in 266 + let licenses = match license with Some l -> Some [l] | None -> None in 267 + let pkg = lazy (Topkg_care_ipc.ask ~pkg_file (Topkg.Private.Ipc.pkg ())) in 268 + let rec opam_fields = lazy (opam p >>= fun o -> Topkg_care_opam.File.fields o) 269 + and p = 270 + { name; version; delegate; build_dir; opam = opam_file; opam_descr; 271 + opam_fields; readmes; change_logs; licenses; distrib_uri; distrib_file; 272 + publish_msg; publish_artefacts; pkg_file; pkg } 273 + in 274 + p 275 + 276 + (* Distrib *) 277 + 278 + let distrib_prepare_ipc p ~dist_build_dir ~name ~version ~opam ~opam_adds = 279 + let dist_build_dir = Fpath.to_string dist_build_dir in 280 + let opam = Fpath.to_string opam in 281 + R.join @@ Topkg_care_ipc.ask ~pkg_file:p.pkg_file 282 + (Topkg.Private.Ipc.distrib_prepare ~dist_build_dir ~name ~version ~opam 283 + ~opam_adds) 284 + 285 + let distrib_archive p ~keep_dir = 286 + Topkg_care_archive.ensure_bzip2 () 287 + >>= fun () -> name p 288 + >>= fun name -> build_dir p 289 + >>= fun build_dir -> version p 290 + >>= fun version -> opam p 291 + >>= fun opam -> distrib_filename p 292 + >>= fun root -> Ok Fpath.(build_dir // root + ".build") 293 + >>= fun dist_build_dir -> OS.Dir.delete ~recurse:true dist_build_dir 294 + >>= fun () -> Topkg_vcs.get () 295 + >>= fun repo -> Topkg_vcs.commit_id repo ~dirty:false 296 + >>= fun head -> Topkg_vcs.commit_ptime_s repo ~commit_ish:head 297 + >>= fun mtime -> Topkg_vcs.clone repo ~dir:(Fpath.to_string dist_build_dir) 298 + >>= fun () -> Topkg_vcs.get ~dir:(Fpath.to_string dist_build_dir) () 299 + >>= fun clone -> Ok (Topkg_string.strf "topkg-dist-%s" head) 300 + >>= fun branch -> Topkg_vcs.checkout clone ~branch ~commit_ish:head 301 + >>= fun () -> opam_descr p 302 + >>= fun (descr, from_opam) -> 303 + let opam_adds = 304 + if from_opam then "" else 305 + (Topkg_care_opam.Descr.to_opam_fields descr ^ "\n") 306 + in 307 + distrib_prepare_ipc p ~dist_build_dir ~name ~version ~opam ~opam_adds 308 + >>= fun exclude_paths -> path_set_of_strings exclude_paths 309 + >>= fun exclude_paths -> 310 + Topkg_care_archive.tar dist_build_dir ~exclude_paths ~root ~mtime 311 + >>= fun tar -> distrib_archive_path p 312 + >>= fun archive -> Topkg_care_archive.bzip2 tar ~dst:archive 313 + >>= fun () -> 314 + (if keep_dir then Ok () else OS.Dir.delete ~recurse:true dist_build_dir) 315 + >>= fun () -> Ok archive 316 + 317 + (* Test & build *) 318 + 319 + let run p ~dir cmd ~args ~out = 320 + let pkg_file = p.pkg_file in 321 + let cmd = Cmd.(v "ocaml" % p pkg_file % cmd %% args) in 322 + let run () = OS.Cmd.run_out cmd |> out in 323 + R.join @@ OS.Dir.with_current dir run () 324 + 325 + let test p ~dir ~args ~out = run p ~dir "test" ~args ~out 326 + let build p ~dir ~args ~out = run p ~dir "build" ~args ~out 327 + let clean p ~dir ~args ~out = run p ~dir "clean" ~args ~out 328 + 329 + (* Lint *) 330 + 331 + let pp_path = Topkg_care_text.Pp.path 332 + let pp_status = Topkg_care_text.Pp.status 333 + 334 + let lint_log msg = Logs.info (fun m -> m ~header:"LINT" "%a" Fmt.text msg); 0 335 + let lint_disabled test = 336 + Logs.info (fun m -> m ~header:"LINT" "Package@ disabled@ %a." Fmt.text test); 337 + 0 338 + 339 + let lint_custom p = 340 + let report errs res = 341 + let status, msg, errs = match res with 342 + | Ok (`Msg msg) -> `Ok, msg, errs 343 + | Error (`Msg msg) -> `Fail, msg, errs + 1 344 + in 345 + Logs.app (fun m -> m "%a @[%a@]" pp_status status Fmt.text msg); 346 + errs 347 + in 348 + begin 349 + let pkg_file = p.pkg_file in 350 + pkg p >>= fun p -> match Topkg.Private.Pkg.lint_custom p with 351 + | None -> Ok (lint_disabled "custom linting") 352 + | _ -> 353 + Topkg_care_ipc.ask ~pkg_file (Topkg.Private.Ipc.lint_custom ()) 354 + >>= function 355 + | None -> assert false 356 + | Some results -> Ok (List.fold_left report 0 results) 357 + end 358 + |> Logs.on_error_msg ~use:(fun () -> 1) 359 + 360 + let lint_std_files p = 361 + let lint_exists file errs = 362 + let report exists = 363 + let status, errs = if exists then `Ok, errs else `Fail, errs + 1 in 364 + Logs.app (fun m -> 365 + m "%a @[File %a@ is@ present.@]" pp_status status pp_path file); 366 + errs 367 + in 368 + (OS.File.exists file >>= fun exists -> Ok (report exists)) 369 + |> Logs.on_error_msg ~use:(fun () -> errs + 1) 370 + in 371 + begin 372 + pkg p >>= fun p -> match Topkg.Private.Pkg.lint_files p with 373 + | None -> Ok (lint_disabled "standard files linting") 374 + | Some files -> 375 + path_set_of_strings files 376 + >>= fun files -> Ok (Fpath.Set.fold lint_exists files 0) 377 + end 378 + |> Logs.on_error_msg ~use:(fun () -> 1) 379 + 380 + let lint_file_with_cmd file_kind ~cmd file errs handle_exit = 381 + let run_linter cmd file ~exists = 382 + if not exists then Ok (`Fail (strf "%a: No such file" Fpath.pp file)) else 383 + OS.Cmd.(run_out ~err:err_run_out Cmd.(cmd % p file) |> out_string) 384 + >>| fun (out, status) -> handle_exit (snd status) out 385 + in 386 + begin 387 + OS.File.exists file 388 + >>= fun exists -> run_linter cmd file ~exists 389 + >>| function 390 + | `Ok -> 391 + Logs.app 392 + (fun m -> m "%a @[lint@ %s %a.@]" 393 + pp_status `Ok file_kind pp_path file); 394 + errs 395 + | `Fail msgs -> 396 + Logs.app 397 + (fun m -> m "%a @[<v>@[lint@ %s %a:@]@,@[%a messages:@]@,%a@]" 398 + pp_status `Fail file_kind pp_path file Cmd.pp cmd Fmt.lines msgs); 399 + errs + 1 400 + end 401 + |> Logs.on_error_msg ~use:(fun () -> errs + 1) 402 + 403 + let lint_metas p = 404 + begin 405 + pkg p 406 + >>= fun p -> match Topkg.Private.Pkg.lint_metas p with 407 + | [] -> Ok (lint_log "No ocamlfind META file to lint") 408 + | metas -> 409 + let cmd = Cmd.(Topkg_care_ocamlfind.cmd % "lint") in 410 + let handle_exit status out = match status with 411 + | `Exited 0 -> `Ok 412 + | _ -> `Fail out 413 + in 414 + let lint errs (f, lint) = 415 + begin 416 + Fpath.of_string f >>| fun f -> 417 + if not lint 418 + then lint_log (strf "ocamlfind META lint disabled for %a" Fpath.pp f) 419 + else lint_file_with_cmd "META file" ~cmd f errs handle_exit 420 + end 421 + |> Logs.on_error_msg ~use:(fun () -> errs + 1) 422 + in 423 + Ok (List.fold_left lint 0 metas) 424 + end 425 + |> Logs.on_error_msg ~use:(fun () -> 1) 426 + 427 + let lint_opams p = 428 + begin 429 + pkg p >>= fun p -> match Topkg.Private.Pkg.lint_opams p with 430 + | [] -> Ok (lint_log "No opam file to lint") 431 + | opams -> 432 + (* We first run opam lint with -s and if there's something beyond 433 + 5 we rerun it without it for the error messages. It's ugly 434 + since 5 will still but opam lint's cli is broken. *) 435 + let cmd = Cmd.(Topkg_care_opam.cmd % "lint") in 436 + let handle_exit file status out = match status, out with 437 + | `Exited 0, 438 + ("" | "5" (* dirname version vs opam file version *)) -> `Ok 439 + | _ -> 440 + let err = OS.Cmd.err_run_out in 441 + match OS.Cmd.(run_out ~err Cmd.(cmd % p file) |> out_string) with 442 + | Ok (out, _) -> `Fail out 443 + | Error (`Msg out) -> `Fail out 444 + in 445 + let cmd = Cmd.(cmd % "-s") in 446 + let lint errs (f, lint, _) = 447 + begin 448 + Fpath.of_string f >>| fun f -> 449 + if not lint 450 + then lint_log (strf "opam file lint disabled for %a" Fpath.pp f) 451 + else lint_file_with_cmd "opam file" ~cmd f errs (handle_exit f) 452 + end 453 + |> Logs.on_error_msg ~use:(fun () -> errs + 1) 454 + in 455 + Ok (List.fold_left lint 0 opams) 456 + end 457 + |> Logs.on_error_msg ~use:(fun () -> 1) 458 + 459 + let lint_deps_default_excludes = 460 + let exclude = ["ocamlfind"; "ocamlbuild"; "topkg"; "ocaml"] in 461 + Topkg_care_ocamlfind.base_packages 462 + |> String.Set.union Topkg_care_opam.ocaml_base_packages 463 + |> String.Set.union (String.Set.of_list exclude) 464 + 465 + let lint_opam_deps errs (opam, _, exclude) = 466 + let pp_deps_mismatches ppf (opam_only, tags_only) = 467 + let pp_miss present absent ppf id = 468 + Fmt.pf ppf "@[%a: %a present but %a absent@]" 469 + Fmt.(styled `Underline string) id 470 + Fmt.(styled `Green string) present 471 + Fmt.(styled `Red string) absent 472 + in 473 + let sep = 474 + if String.Set.(is_empty opam_only || is_empty tags_only) 475 + then Fmt.nop else Fmt.cut 476 + in 477 + Fmt.pf ppf "@[<v>%a%a%a@]" 478 + (String.Set.pp (pp_miss "opam" "_tags")) opam_only sep () 479 + (String.Set.pp (pp_miss "_tags" "opam")) tags_only 480 + in 481 + let lint_deps ~exclude ~opam ~tags = 482 + begin 483 + Topkg_care_ocamlbuild.package_tags ~roots:true tags 484 + >>= fun tags -> Topkg_care_opam.File.fields opam 485 + >>| fun fields -> 486 + let exclude = String.Set.union exclude lint_deps_default_excludes in 487 + let keep id = 488 + not (String.Set.mem id exclude) && 489 + not (String.is_prefix ~affix:"conf-" id) 490 + in 491 + let opam_deps = Topkg_care_opam.File.deps ~opts:true fields in 492 + let opam = String.Set.filter keep opam_deps in 493 + let tags = String.Set.filter keep tags in 494 + let opam_only = String.Set.diff opam tags in 495 + let tags_only = String.Set.diff tags opam in 496 + if String.Set.is_empty opam_only && String.Set.is_empty tags_only 497 + then None 498 + else Some (opam_only, tags_only) 499 + end 500 + |> R.reword_error_msg ~replace:true 501 + (fun msg -> R.msgf "could not lint dependencies: %s" msg) 502 + in 503 + begin 504 + Fpath.of_string opam 505 + >>= fun opam -> match exclude with 506 + | None -> 507 + Ok (lint_disabled @@ 508 + Fmt.(str_like 509 + stdout "opam dependency linting for %a" pp_path opam)) 510 + | Some exclude -> 511 + let tags = Fpath.v "_tags" in 512 + let exclude = String.Set.of_list exclude in 513 + lint_deps ~exclude ~opam ~tags 514 + >>| function 515 + | None -> 516 + Logs.app (fun m -> m "%a @[opam file %a@ and %a dependency check." 517 + pp_status `Ok pp_path opam pp_path tags); 518 + errs 519 + | Some miss -> 520 + Logs.app 521 + (fun m -> m "%a @[<v>@[File %a and %a dependency check:@]@,%a@]" 522 + pp_status `Fail pp_path opam pp_path tags 523 + pp_deps_mismatches miss); 524 + errs + 1 525 + end 526 + |> Logs.on_error_msg ~use:(fun () -> errs + 1) 527 + 528 + let lint_deps p = 529 + begin 530 + pkg p >>= fun p -> match Topkg.Private.Pkg.lint_opams p with 531 + | [] -> Ok (lint_log "No opam file to dependency lint") 532 + | opams -> Ok (List.fold_left lint_opam_deps 0 opams) 533 + end 534 + |> Logs.on_error_msg ~use:(fun () -> 1) 535 + 536 + type lint = [ `Custom | `Std_files | `Meta | `Opam | `Deps ] 537 + 538 + let lints = 539 + [`Custom, lint_custom; 540 + `Std_files, lint_std_files; 541 + `Meta, lint_metas; 542 + `Opam, lint_opams; 543 + `Deps, lint_deps ] 544 + 545 + let lint_all = List.map fst lints 546 + 547 + let lint ?(ignore_pkg = false) p ~dir todo = 548 + let lint pkg = 549 + let do_lint acc (l, f) = acc + if List.mem l todo then f pkg else 0 in 550 + match List.fold_left do_lint 0 lints with 551 + | 0 -> 552 + Logs.app (fun m -> m "%a lint@ %a %a" 553 + pp_status `Ok pp_path dir 554 + Fmt.(styled `Green (any "success")) ()); 0 555 + | n -> 556 + Logs.app (fun m -> m "%a lint@ %a@ %a:@ %d@ errors." 557 + pp_status `Fail pp_path dir 558 + Fmt.(styled `Red (any "failure")) () n); 1 559 + in 560 + let p = 561 + if ignore_pkg then Ok (empty p) else 562 + pkg p >>| fun _ -> p (* verify the pkg_file exists *) 563 + in 564 + p >>= fun p -> OS.Dir.with_current dir lint p
+81
vendor/opam/topkg/src-care/topkg_care_pkg.mli
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** Package descriptions. 7 + 8 + See {!Topkg_care.Pkg} *) 9 + 10 + open Bos_setup 11 + 12 + (** {1 Package} *) 13 + 14 + type t 15 + 16 + val v : 17 + ?name:string -> 18 + ?version:string -> 19 + ?delegate:Cmd.t -> 20 + ?build_dir:Fpath.t -> 21 + ?opam:Fpath.t -> 22 + ?opam_descr:Fpath.t -> 23 + ?readme:Fpath.t -> 24 + ?change_log:Fpath.t -> 25 + ?license:Fpath.t -> 26 + ?distrib_uri:string -> 27 + ?distrib_file:Fpath.t -> 28 + ?publish_msg:string -> 29 + ?publish_artefacts:[ `Distrib | `Doc | `Alt of string] list -> 30 + Fpath.t -> t 31 + 32 + val pkg_file : t -> Fpath.t 33 + val name : t -> (string, R.msg) result 34 + val version : t -> (string, R.msg) result 35 + val delegate : t -> (Cmd.t, R.msg) result 36 + val build_dir : t -> (Fpath.t, R.msg) result 37 + val opam : t -> (Fpath.t, R.msg) result 38 + val opam_field : t -> string -> (string list option, R.msg) result 39 + val opam_field_hd : t -> string -> (string option, R.msg) result 40 + val opam_fields : t -> (string list String.map, R.msg) result 41 + val opam_descr : t -> (Topkg_care_opam.Descr.t * bool, R.msg) result 42 + val readmes : t -> (Fpath.t list, R.msg) result 43 + val readme : t -> (Fpath.t, R.msg) result 44 + val change_logs : t -> (Fpath.t list, R.msg) result 45 + val change_log : t -> (Fpath.t, R.msg) result 46 + val licenses : t -> (Fpath.t list, R.msg) result 47 + val distrib_uri : ?raw:bool -> t -> (string, R.msg) result 48 + val distrib_file : t -> (Fpath.t, R.msg) result 49 + val publish_msg : t -> (string, R.msg) result 50 + val publish_artefacts : t -> 51 + ([ `Distrib | `Doc | `Alt of string] list, R.msg) result 52 + 53 + (** {1 Test} *) 54 + 55 + val test : 56 + t -> dir:Fpath.t -> args:Cmd.t -> 57 + out:(OS.Cmd.run_out -> ('a, R.msg) result) -> ('a, R.msg) result 58 + 59 + (** {1 Build} *) 60 + 61 + val build : 62 + t -> dir:Fpath.t -> args:Cmd.t -> 63 + out:(OS.Cmd.run_out -> ('a, R.msg) result) -> ('a, R.msg) result 64 + 65 + (** {1 Clean} *) 66 + 67 + val clean : 68 + t -> dir:Fpath.t -> args:Cmd.t -> 69 + out:(OS.Cmd.run_out -> ('a, R.msg) result) -> ('a, R.msg) result 70 + 71 + (** {1 Distrib} *) 72 + 73 + val distrib_filename : ?opam:bool -> t -> (Fpath.t, R.msg) result 74 + val distrib_archive : t -> keep_dir:bool -> (Fpath.t, R.msg) result 75 + 76 + (** {1 Lint} *) 77 + 78 + type lint = [ `Custom | `Std_files | `Meta | `Opam | `Deps ] 79 + val lint_all : lint list 80 + val lint : 81 + ?ignore_pkg:bool -> t -> dir:Fpath.t -> lint list -> (int, R.msg) result
+166
vendor/opam/topkg/src-care/topkg_care_text.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + open Bos_setup 7 + 8 + type flavour = [ `Markdown | `Asciidoc ] 9 + 10 + let flavour_of_fpath f = match String.Ascii.lowercase (Fpath.get_ext f) with 11 + | ".md" -> Some `Markdown 12 + | ".asciidoc" | ".adoc" -> Some `Asciidoc 13 + | _ -> None 14 + 15 + let rec drop_blanks = function "" :: ls -> drop_blanks ls | ls -> ls 16 + let last_line = function [] -> None | l :: rev_ls -> Some l 17 + 18 + (* Detecting headers *) 19 + 20 + let simple_header hchar l before rest = 21 + match String.(length @@ take ~sat:(Char.equal hchar) l) with 22 + | 0 -> None 23 + | n -> Some (n, l, before, rest) 24 + 25 + let underline_header n uchar l before rest = 26 + let is_underline_header uchar l = 27 + String.(length @@ take ~sat:(Char.equal uchar) l) >= 2 28 + in 29 + if not (is_underline_header uchar l) then None else 30 + match last_line before with 31 + | None -> None 32 + | Some t -> Some (n, strf "%s\n%s" t l, List.tl before, rest) 33 + 34 + let rec find_markdown_header before = function 35 + | [] -> None 36 + | l :: ls -> 37 + match simple_header '#' l before ls with 38 + | Some _ as h -> h 39 + | None -> 40 + match underline_header 1 '=' l before ls with 41 + | Some _ as h -> h 42 + | None -> 43 + match underline_header 2 '-' l before ls with 44 + | Some _ as h -> h 45 + | None -> find_markdown_header (l :: before) ls 46 + 47 + let rec find_asciidoc_header before = function 48 + | [] -> None 49 + | l :: ls -> 50 + match simple_header '=' l before ls with 51 + | Some _ as h -> h 52 + | None -> 53 + match underline_header 1 '-' l before ls with 54 + | Some _ as h -> h 55 + | None -> 56 + match underline_header 2 '~' l before ls with 57 + | Some _ as h -> h 58 + | None -> 59 + match underline_header 3 '^' l before ls with 60 + | Some _ as h -> h 61 + | None -> 62 + match underline_header 4 '+' l before ls with 63 + | Some _ as h -> h 64 + | None -> find_asciidoc_header (l :: before) ls 65 + 66 + let head find_header text = 67 + let lines = String.cuts ~sep:"\n" text in 68 + let ret h acc = 69 + let contents = String.concat ~sep:"\n" (List.rev @@ drop_blanks acc) in 70 + Some (h, contents) 71 + in 72 + match find_header [] lines with 73 + | None -> None 74 + | Some (n, first, _ (* discard *), rest) -> 75 + let rec loop acc rest = match find_header acc rest with 76 + | None -> ret first (List.rev_append rest acc) 77 + | Some (n', h, before, rest) -> 78 + if n' > n then loop (h :: before) rest else 79 + ret first before 80 + in 81 + loop [] rest 82 + 83 + let head ?(flavour = `Markdown) text = match flavour with 84 + | `Markdown -> head find_markdown_header text 85 + | `Asciidoc -> head find_asciidoc_header text 86 + 87 + let header_title ?(flavour = `Markdown) h = match String.cuts ~sep:"\n" h with 88 + | [h] -> 89 + begin match flavour with 90 + | `Markdown -> String.(trim @@ drop ~sat:(Char.equal '#') h) 91 + | `Asciidoc -> String.(trim @@ drop ~sat:(Char.equal '=') h) 92 + end 93 + | h :: _ -> h (* underline headers *) 94 + | [] -> assert false 95 + 96 + (* Toy change log parsing *) 97 + 98 + let change_log_last_entry ?flavour text = match head ?flavour text with 99 + | None -> None 100 + | Some (h, changes) -> 101 + let title = header_title ?flavour h in 102 + match String.take ~sat:Char.Ascii.is_graphic title with 103 + | "" -> Logs.app (fun m -> m "%S %S" h changes); None 104 + | version -> Some (version, (h, changes)) 105 + 106 + let change_log_file_last_entry file = 107 + let flavour = flavour_of_fpath file in 108 + OS.File.read file 109 + >>= fun text -> match change_log_last_entry ?flavour text with 110 + | None -> R.error_msgf "%a: Could not parse change log." Fpath.pp file 111 + | Some (version, (header, changes)) -> Ok (version, (header, changes)) 112 + 113 + (* Toy URI parsing *) 114 + 115 + let split_uri ?(rel = false) uri = match String.(cut ~sep:"//" (trim uri)) with 116 + | None -> None 117 + | Some (scheme, rest) -> 118 + match String.cut ~sep:"/" rest with 119 + | None -> Some (scheme, rest, "") 120 + | Some (host, path) -> 121 + let path = if rel then path else "/" ^ path in 122 + Some (scheme, host, path) 123 + 124 + (* Edit and page text *) 125 + 126 + let find_pager ~don't = 127 + if don't then Ok None else 128 + match OS.Env.var "TERM" with 129 + | Some "dumb" | None -> Ok None 130 + | _ -> 131 + let add_env v cmds = match OS.Env.(value v (some cmd) ~absent:None) with 132 + | None -> cmds 133 + | Some cmd -> cmd :: cmds 134 + in 135 + let cmds = [Cmd.v "less"; Cmd.v "more" ] in 136 + let cmds = add_env "PAGER" cmds in 137 + let rec loop = function 138 + | [] -> Ok None 139 + | cmd :: cmds -> 140 + OS.Cmd.exists cmd >>= function 141 + | true -> Ok (Some cmd) 142 + | false -> loop cmds 143 + in 144 + loop cmds 145 + 146 + let edit_file f = match OS.Env.(value "EDITOR" (some cmd) ~absent:None) with 147 + | None -> R.error_msg "EDITOR environment variable undefined." 148 + | Some editor -> 149 + OS.Cmd.exists editor >>= function 150 + | false -> R.error_msgf "Editor %a not in search path" Cmd.pp editor 151 + | true -> 152 + OS.Cmd.(run_status Cmd.(editor % p f)) >>= function 153 + | `Exited n | `Signaled n -> Ok n 154 + 155 + (* Pretty-printers. *) 156 + 157 + module Pp = struct 158 + let name = Fmt.(styled `Bold string) 159 + let version = Fmt.(styled `Cyan string) 160 + let commit = Fmt.(styled `Yellow string) 161 + let dirty = Fmt.(styled `Red (any "dirty")) 162 + let path = Fmt.(styled `Bold Fpath.pp) 163 + let status ppf = function 164 + | `Ok -> Fmt.(brackets @@ styled `Green (any " OK ")) ppf () 165 + | `Fail -> Fmt.(brackets @@ styled `Red (any "FAIL")) ppf () 166 + end
+38
vendor/opam/topkg/src-care/topkg_care_text.mli
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** Text processing helpers. 7 + 8 + See {!Topkg_care.Text}. *) 9 + 10 + (** {1 Text} *) 11 + 12 + open Bos_setup 13 + 14 + type flavour = [ `Markdown | `Asciidoc ] 15 + 16 + val flavour_of_fpath : Fpath.t -> flavour option 17 + val head : ?flavour:flavour -> string -> (string * string) option 18 + val header_title : ?flavour:flavour -> string -> string 19 + 20 + val change_log_last_entry : 21 + ?flavour:flavour -> string -> (string * (string * string)) option 22 + 23 + val change_log_file_last_entry : 24 + Fpath.t -> ((string * (string * string)), R.msg) result 25 + 26 + val split_uri : ?rel:bool -> string -> (string * string * string) option 27 + 28 + val find_pager : don't:bool -> (Cmd.t option, R.msg) result 29 + val edit_file : Fpath.t -> (int, R.msg) result 30 + 31 + module Pp : sig 32 + val name : string Fmt.t 33 + val version : string Fmt.t 34 + val commit : string Fmt.t 35 + val dirty : unit Fmt.t 36 + val path : Fpath.t Fmt.t 37 + val status : [`Ok | `Fail] Fmt.t 38 + end
+103
vendor/opam/topkg/src/topkg.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (* Preliminaries *) 7 + 8 + include Topkg_result 9 + 10 + let strf = Topkg_string.strf 11 + module String = Topkg_string 12 + 13 + type fpath = string 14 + module Fpath = Topkg_fpath 15 + 16 + module Cmd = Topkg_cmd 17 + module Log = Topkg_log 18 + module OS = Topkg_os 19 + module Vcs = Topkg_vcs 20 + 21 + (* Package description *) 22 + 23 + module Conf = Topkg_conf 24 + module Exts = Topkg_fexts 25 + module Pkg = struct 26 + 27 + (* Install *) 28 + 29 + type install = Topkg_install.t 30 + type field = Topkg_install.field 31 + type exec_field = ?auto:bool -> field 32 + 33 + let nothing = Topkg_install.nothing 34 + let flatten = Topkg_install.flatten 35 + let bin = Topkg_install.bin 36 + let doc = Topkg_install.doc 37 + let etc = Topkg_install.etc 38 + let lib = Topkg_install.lib 39 + let lib_root = Topkg_install.lib_root 40 + let libexec = Topkg_install.libexec 41 + let libexec_root = Topkg_install.libexec_root 42 + let man = Topkg_install.man 43 + let misc = Topkg_install.misc 44 + let sbin = Topkg_install.sbin 45 + let share = Topkg_install.share 46 + let share_root = Topkg_install.share_root 47 + let stublibs = Topkg_install.stublibs 48 + let toplevel = Topkg_install.toplevel 49 + let unknown = Topkg_install.unknown 50 + let test = Topkg_install.test 51 + 52 + let mllib = Topkg_install.mllib 53 + let clib = Topkg_install.clib 54 + 55 + (* Distrib *) 56 + 57 + type watermark = Topkg_distrib.watermark 58 + type distrib = Topkg_distrib.t 59 + 60 + let distrib = Topkg_distrib.v 61 + let watermarks = Topkg_distrib.default_watermarks 62 + let files_to_watermark = Topkg_distrib.default_files_to_watermark 63 + let massage = Topkg_distrib.default_massage 64 + let exclude_paths = Topkg_distrib.default_exclude_paths 65 + 66 + (* Publish *) 67 + 68 + type publish = Topkg_publish.t 69 + let publish = Topkg_publish.v 70 + 71 + (* Build *) 72 + 73 + type build = Topkg_build.t 74 + let build = Topkg_build.v 75 + let build_cmd = Topkg_build.build_cmd 76 + let clean_cmd = Topkg_build.clean_cmd 77 + let ocb_tag = Topkg_build.ocb_tag 78 + let ocb_bool_tag = Topkg_build.ocb_bool_tag 79 + let ocb_bool_tags = Topkg_build.ocb_bool_tags 80 + 81 + (* Package *) 82 + 83 + type std_file = Topkg_pkg.std_file 84 + let std_file = Topkg_pkg.std_file 85 + 86 + type meta_file = Topkg_pkg.meta_file 87 + let meta_file = Topkg_pkg.meta_file 88 + 89 + type opam_file = Topkg_pkg.opam_file 90 + let opam_file = Topkg_pkg.opam_file 91 + 92 + (* Describe *) 93 + 94 + let describe = Topkg_main.describe 95 + end 96 + 97 + module Private = struct 98 + let disable_main = Topkg_main.disable 99 + module Codec = Topkg_codec 100 + module Pkg = Topkg_pkg 101 + module Ipc = Topkg_ipc 102 + module Opam = Topkg_opam 103 + end
+2522
vendor/opam/topkg/src/topkg.mli
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** The transitory OCaml package builder. 7 + 8 + See the {{!basics}basics} and the {{!menagerie}menagerie} of [pkg.ml] 9 + files. 10 + 11 + {e %%VERSION%% - {{:%%PKG_HOMEPAGE%% }homepage}} *) 12 + 13 + (** {1:prels Preliminaries} 14 + 15 + In the most simple cases you won't need this, jump directly to the 16 + {{!basics}basics} or {{!Pkg}package description API}. *) 17 + 18 + val ( >>= ) : ('a, 'b) result -> ('a -> ('c, 'b) result) -> ('c, 'b) result 19 + (** [r >>= f] is [f v] if [r = Ok v] and [r] if [r = Error _]. *) 20 + 21 + val ( >>| ) : ('a, 'b) result -> ('a -> 'c) -> ('c, 'b) result 22 + (** [r >>| f] is [Ok (f v)] if [r = Ok v] and [r] if [r = Error _]. *) 23 + 24 + (** This definition re-export [result]'s constructors so that an 25 + [open Topkg] gets them in scope. *) 26 + type ('a, 'b) r = ('a, 'b) result = Ok of 'a | Error of 'b 27 + 28 + type 'a result = ('a, [ `Msg of string]) r 29 + (** The type for topkg results. *) 30 + 31 + (** Result value combinators. *) 32 + module R : sig 33 + 34 + (** {1:err Errors} *) 35 + 36 + val reword_error : ('b -> 'c) -> ('a, 'b) r -> ('a, 'c) r 37 + (** [reword_error reword r] is: 38 + {ul 39 + {- [r] if [r = Ok v]} 40 + {- [Error (reword e)] if [r = Error e]}} *) 41 + 42 + (** {1:errmsg Error messages} *) 43 + 44 + type msg = [ `Msg of string ] 45 + (** The type for (error) messages. *) 46 + 47 + val error_msg : string -> ('b, [> msg]) r 48 + (** [error_msg s] is [Error (`Msg s)]. *) 49 + 50 + val error_msgf : 51 + ('a, Format.formatter, unit, ('b, [> msg]) r) format4 -> 'a 52 + (** [error_msgf fmt ...] is an error formatted according to [fmt]. *) 53 + 54 + val reword_error_msg : 55 + ?replace:bool -> (string -> msg) -> ('a, msg) r -> ('a, [> msg]) r 56 + (** [reword_error_msg ~replace reword r] is like {!reword_error} except 57 + if [replace] is [false] (default), the result of [reword old_msg] is 58 + concatened, on a new line to the old message. *) 59 + end 60 + 61 + val strf : ('a, Format.formatter, unit, string) format4 -> 'a 62 + (** [strf] is [Printf.asprintf]. *) 63 + 64 + (** Strings. *) 65 + module String : sig 66 + 67 + (** {1:adds String additions} *) 68 + 69 + include module type of String 70 + 71 + val head : string -> char option 72 + (** [head s] if [Some s.[0]] if [s <> ""] and [None] otherwise. *) 73 + 74 + (** {1:preds Predicates} *) 75 + 76 + val is_prefix : affix:string -> string -> bool 77 + (** [is_prefix ~affix s] is [true] iff [affix.[i] = s.[i]] for 78 + all indices [i] of [affix]. *) 79 + 80 + val is_suffix : affix:string -> string -> bool 81 + (** [is_suffix ~affix s] is true iff [affix.[n - i] = s.[m - i]] for all 82 + indices [i] of [affix] with [n = String.length affix - 1] and [m = 83 + String.length s - 1]. *) 84 + 85 + val for_all : (char -> bool) -> string -> bool 86 + (** [for_all p s] is [true] iff for all indices [i] of [s], [p s.[i] 87 + = true]. *) 88 + 89 + val exists : (char -> bool) -> string -> bool 90 + (** [exists p s] is [true] iff there exists an index [i] of [s] with 91 + [p s.[i] = true]. *) 92 + 93 + (** {1:subs Extracting substrings} *) 94 + 95 + val with_index_range : ?first:int -> ?last:int -> string -> string 96 + (** [with_index_range ~first ~last s] are the consecutive bytes of [s] 97 + whose indices exist in the range \[[first];[last]\]. 98 + 99 + [first] defaults to [0] and last to [String.length s - 1]. 100 + 101 + Note that both [first] and [last] can be any integer. If 102 + [first > last] the interval is empty and the empty string is 103 + returned. *) 104 + 105 + val cut : ?rev:bool -> sep:char -> string -> (string * string) option 106 + (** [cut ~sep s] is either the pair [Some (l,r)] of the two 107 + (possibly empty) substrings of [s] that are delimited by the 108 + first match of the separator character [sep] or [None] if 109 + [sep] can't be matched in [s]. Matching starts from the 110 + beginning of [s] ([rev] is [false], default) or the end ([rev] 111 + is [true]). 112 + 113 + The invariant [l ^ (String.make 1 sep) ^ r = s] holds. *) 114 + 115 + val cuts : ?empty:bool -> sep:char -> string -> string list 116 + (** [cuts ~sep s] is the list of all substring of [s] that are delimited by 117 + matches of [sep]. Empty substrings are ommited in the list if 118 + [empty] is [falsee] (defaults to [true]). The invariant 119 + [String.concat (String.make 1 sep) (split ~sep s) = s] holds. *) 120 + 121 + (** {1:vers Parsing version strings} *) 122 + 123 + val parse_version : string -> (int * int * int * string option) option 124 + (** [parse_version] parses version strings of the form: 125 + {[ 126 + "[v]major.minor[.patchlevel][(+|~)additional-info]" 127 + ]} 128 + into [(major, minor, patch, (+|~)additional_info)] tuples. *) 129 + 130 + val drop_initial_v : string -> string 131 + (** [drop_initial_v s] drops a leading ['v'] or ['V'] from [s]. *) 132 + end 133 + 134 + type fpath = string 135 + (** The type for file system paths. *) 136 + 137 + (** File system paths. 138 + 139 + {b Note.} Only use ["/"] as a directory separator, even on 140 + Windows platforms. *) 141 + module Fpath : sig 142 + 143 + (** {1:fpath File system paths} *) 144 + 145 + type t = fpath 146 + (** The type for file system paths. *) 147 + 148 + val append : t -> t -> t 149 + (** [append p q] appends [q] to [p] as follows: 150 + {ul 151 + {- If [q] is absolute then [q] is returned} 152 + {- Otherwise appends [q]'s segments to [p] using a ["/"] if needed.}} *) 153 + 154 + val ( // ) : t -> t -> t 155 + (** [p // q] is [append p q]. *) 156 + 157 + val is_dir_path : t -> bool 158 + (** [is_dir_path p] is [true] iff [p] represents a directory. This means 159 + that [p] is [.], [..] or ends with [/], [/..] or [/.]. *) 160 + 161 + val is_file_path : t -> bool 162 + (** [is_file_path p] is [not (is_dir_path true)]. *) 163 + 164 + val basename : t -> string 165 + (** [basename p] is [p]'s basename, the last non empty segment of [p]. *) 166 + 167 + val dirname : t -> string 168 + (** [dirname p] is [p]'s dirname, [p] without its last non empty segment. *) 169 + 170 + (** {1:exts File extensions} *) 171 + 172 + val get_ext : t -> string 173 + (** [get_ext p] is [p]'s filename extension (including the ['.']) or 174 + the empty string if there is no extension *) 175 + 176 + val has_ext : string -> t -> bool 177 + (** [has_ext e p] is [true] iff [e] is a suffix of [p]. *) 178 + 179 + val rem_ext : t -> t 180 + (** [rem_ext p] is [p] without its filename extension. *) 181 + 182 + end 183 + 184 + (** Command lines. 185 + 186 + Both command lines and command line fragments using the same are 187 + represented with the same {{!t}type}. 188 + 189 + When a command line is {{!section:OS.Cmd.run}run}, the first 190 + element of the line defines the program name and each other 191 + element is an argument that is passed {e as is} in the 192 + program's [argv] array: no shell interpretation or any form of 193 + argument quoting and/or concatenation occurs. *) 194 + module Cmd : sig 195 + 196 + (** {1:lines Command line fragments} *) 197 + 198 + type t 199 + (** The type for command line fragments. *) 200 + 201 + val v : string -> t 202 + (** [v cmd] is a new command line (or command line fragment) 203 + whose first argument is [cmd]. *) 204 + 205 + val empty : t 206 + (** [empty] is an empty command line. *) 207 + 208 + val is_empty : t -> bool 209 + (** [is_empty l] is [true] iff [l] is empty. *) 210 + 211 + val ( % ) : t -> string -> t 212 + (** [l % arg] adds [arg] to the command line [l]. *) 213 + 214 + val ( %% ) : t -> t -> t 215 + (** [l %% frag] appends the line fragment [frag] to [l]. *) 216 + 217 + val add_arg : t -> string -> t 218 + (** [add_arg l arg] is [l % arg]. *) 219 + 220 + val add_args : t -> t -> t 221 + (** [add_args l frag] is [l %% frag]. *) 222 + 223 + val on : bool -> t -> t 224 + (** [on bool line] is [line] if [bool] is [true] and {!empty} 225 + otherwise. *) 226 + 227 + val p : fpath -> string 228 + (** [p] is [(fun f -> f)]. *) 229 + 230 + (** {1:predicates Predicates and comparison} *) 231 + 232 + val equal : t -> t -> bool 233 + (** [equal l l'] is [true] iff [l] and [l'] are litterally equal. *) 234 + 235 + val compare : t -> t -> int 236 + (** [compare l l'] is a total order on command lines. *) 237 + 238 + (** {1:convert Conversions and pretty printing} *) 239 + 240 + val to_list : t -> string list 241 + (** [to_list l] is [l] as a list of strings. *) 242 + 243 + val of_list : ?slip:string -> string list -> t 244 + (** [of_list ?slip l] is a command line from the list of arguments 245 + [l]. If [slip] is specified it is added on the command line 246 + before each element of [l]. *) 247 + 248 + val dump : Format.formatter -> t -> unit 249 + (** [dump ppf cmd] formats an unspecified representation of [cmd] on 250 + [ppf]. *) 251 + end 252 + 253 + (** Topkg log. *) 254 + module Log : sig 255 + 256 + (** {1:level Reporting levels} *) 257 + 258 + (** The type for reporting levels. *) 259 + type level = App | Error | Warning | Info | Debug 260 + 261 + val level : unit -> level option 262 + (** [level ()] is the current reporting level. *) 263 + 264 + val set_level : level option -> unit 265 + (** [set_level l] sets the current reporting level to [l]. *) 266 + 267 + val level_to_string : level option -> string 268 + (** [level_to_string l] converts [l] to an unspecified human-readable 269 + US-ASCII string that can be parsed back by {!level_of_string}. *) 270 + 271 + val level_of_string : string -> (level option, [`Msg of string]) r 272 + (** [level_of_string s] parses the representation of {!level_to_string} 273 + from [s]. *) 274 + 275 + (** {1:logf Log functions} *) 276 + 277 + type 'a msgf = 278 + (?header:string -> ('a, Format.formatter, unit) format -> 'a) -> unit 279 + 280 + val msg : level -> 'a msgf -> unit 281 + (** [msg l (fun m -> m fmt ...)] logs with level [l] a message formatted 282 + with [fmt]. *) 283 + 284 + val app : 'a msgf -> unit 285 + (** [app] is msg [App]. *) 286 + 287 + val err : 'a msgf -> unit 288 + (** [err] is [msg Error]. *) 289 + 290 + val warn : 'a msgf -> unit 291 + (** [err] is [msg Warning]. *) 292 + 293 + val info : 'a msgf -> unit 294 + (** [err] is [msg Info]. *) 295 + 296 + val debug : 'a msgf -> unit 297 + (** [err] is [msg Debug]. *) 298 + 299 + val on_error_msg : ?level:level -> use:(unit -> 'a) -> 'a result -> 'a 300 + (** [on_error_msg ~level r] is: 301 + {ul 302 + {- [v] if [r = Ok v]} 303 + {- [use e] if [r = Error (`Msg e)]. As a side effect [e] is logged 304 + with level [level] (defaults to [Error]).}} *) 305 + 306 + (** {1:monitoring Monitoring} *) 307 + 308 + val err_count : unit -> int 309 + (** [err_count ()] is the number of messages logged with level [Error]. *) 310 + 311 + val warn_count : unit -> int 312 + (** [warn_count ()] is the number of messages logged with level [Warning]. *) 313 + end 314 + 315 + (** OS interaction. *) 316 + module OS : sig 317 + 318 + (** {1:os OS} *) 319 + 320 + (** Environment variables *) 321 + module Env : sig 322 + 323 + (** {1:vars Variables} *) 324 + 325 + val var : string -> string option 326 + (** [var name] is the value of the environment variable [name], if 327 + defined. *) 328 + 329 + val opt_var : string -> absent:string -> string 330 + (** [opt_var name ~absent] is the value of the optionally defined 331 + environment variable [name], if defined, and [absent] if undefined. *) 332 + end 333 + 334 + (** File operations. *) 335 + module File : sig 336 + 337 + (** {1:paths Famous file paths} *) 338 + 339 + val null : fpath 340 + (** [null] represents a file on the OS that discards all writes 341 + and returns end of file on reads. *) 342 + 343 + val dash : fpath 344 + (** [dash] is ["-"]. This value is is used by {!read} and {!write} 345 + to respectively denote [stdin] and [stdout]. *) 346 + 347 + (** {1:exdel Existence and deletion} *) 348 + 349 + val exists : fpath -> bool result 350 + (** [exists file] is [true] if [file] is a regular in the file 351 + system and false otherwise. Symbolic links are followed. *) 352 + 353 + val must_exist : fpath -> fpath result 354 + (** [must_exist file] is [file] if [file] is a regular file in the 355 + file system and an error otherwise. Symbolic links are followed. *) 356 + 357 + val delete : ?must_exist:bool -> fpath -> unit result 358 + (** [delete ~must_exist file] deletes file [file]. If [must_exist] 359 + is [true] (defaults to [false]) an error is returned if [file] 360 + doesn't exist. *) 361 + 362 + (** {1:fold Folding over file hierarchies} *) 363 + 364 + val fold : 365 + ?skip:(fpath -> bool) -> (fpath -> 'a -> 'a) -> 'a -> 366 + fpath list -> 'a result 367 + (** [fold_files skip f acc paths] folds [f] over the {e files} 368 + found in the file hierarchies starting at [paths]. Files 369 + and directories [p] for which [skip p] is [true] are skipped. 370 + [skip] defaults to [(fun _ -> false)]. *) 371 + 372 + (** {1:rw Reading and writing} *) 373 + 374 + val read : fpath -> string result 375 + (** [read file] is [file]'s contents. If [file] is {!dash} reads 376 + from {!stdin} and the channel is not closed when 377 + the function returns. *) 378 + 379 + val write : fpath -> string -> unit result 380 + (** [write file content] writes [content] to [file]. If [file] is {!dash} 381 + writes to {!stdout} and flushes but doesn't close the channel 382 + when the function returns. *) 383 + 384 + val write_subst : fpath -> (string * string) list -> string -> unit result 385 + (** [write_subst file vars content] is like {!write} except any occurence 386 + of a string of the form ["%%ID%%"] in [content] is replaced by the 387 + value of [List.assoc "ID" vars], if any. *) 388 + 389 + (** {1:tmpfiles Temporary files} *) 390 + 391 + val tmp : unit -> fpath result 392 + (** [tmp ()] creates a temporary file and returns its name. The file 393 + is destroyed at the end of program execution. *) 394 + end 395 + 396 + (** Directory operations. *) 397 + module Dir : sig 398 + 399 + (** {1:exists Existence and contents} *) 400 + 401 + val exists : fpath -> bool result 402 + (** [exists dir] is [true] if directory [dir] exists in the file 403 + system. Symbolic links are followed. *) 404 + 405 + val must_exist : fpath -> fpath result 406 + (** [must_exist dir] is [dir] if [file] is a regular file in the 407 + file system and an error otherwise. Symbolic links are followed. *) 408 + 409 + val contents : ?dotfiles:bool -> ?rel:bool -> fpath -> fpath list result 410 + (** [contents ~dotfiles ~rel dir] is the list of directories and filse 411 + in [dir]. If [rel] is [true] (defaults to [false]) the resulting 412 + paths are relative to [dir], otherwise they have [dir] prepended. 413 + If [dotfiles] is [false] (default) elements that start with a [.] 414 + are omitted. *) 415 + 416 + (** {1:current Current working directory} *) 417 + 418 + val current : unit -> fpath result 419 + (** [current ()] is the current working directory. *) 420 + 421 + val set_current : fpath -> unit result 422 + (** [set_current dir] sets the current working directory to [dir]. *) 423 + 424 + val with_current : fpath -> ('a -> 'b) -> 'a -> 'b result 425 + (** [with_current dir f v] is [f v] with the current working directory 426 + bound to [dir]. After the function returns the current working 427 + directory is back to its initial value. *) 428 + end 429 + 430 + (** Running commands. *) 431 + module Cmd : sig 432 + 433 + (** {1:exists Command existence} *) 434 + 435 + val exists : Cmd.t -> bool result 436 + (** [exists cmd] is [true] if the executable of [cmd] can be found 437 + in the path and [false] otherwise. *) 438 + 439 + val must_exist : Cmd.t -> Cmd.t result 440 + (** [must_exist cmd] is [cmd] if the executable of [cmd] can be found 441 + in the path and an error otherwise. *) 442 + 443 + (** {1:run Running commands} *) 444 + 445 + val run : ?err:fpath -> Cmd.t -> unit result 446 + (** [run cmd] runs the command [cmd]. [std{i,o,err}] are connected 447 + to the invoking process' standard channels. If [err] is specified 448 + [stderr] is redirected to the given file (e.g. {!File.null}). *) 449 + 450 + val run_status : ?err:fpath -> Cmd.t -> [`Exited of int] result 451 + (** [run_status cmd] is like {!run}, but doesn't error on non-zero 452 + exit status. *) 453 + 454 + (** {1:stdout Capturing standard output} *) 455 + 456 + type run_status = Cmd.t * [`Exited of int ] 457 + (** The type for run statuses, the command that was run and the run 458 + status. *) 459 + 460 + val success : ('a * run_status) result -> 'a result 461 + (** [success r] is: 462 + {ul 463 + {- [Ok v] if [r = Ok (v, (_, `Exited 0))]} 464 + {- [Error _] otherwise. Non [`Exited 0] statuses are turned into 465 + an error message.}} *) 466 + 467 + type run_out 468 + (** The type for representing the standard output of a command run. *) 469 + 470 + val out_string : ?trim:bool -> run_out -> (string * run_status) result 471 + (** [out_string ~trim o] captures the standard output [o] as a [string]. 472 + If [trim] is [true] (default) the result is passed through 473 + {!String.trim}. *) 474 + 475 + val out_lines : ?trim:bool -> run_out -> (string list * run_status) result 476 + (** [out_lines ~trim] is like {!out_string} but the result is 477 + {{!String.cuts}cut} on newlines (['\n']). *) 478 + 479 + val out_file : fpath -> run_out -> (unit * run_status) result 480 + (** [out_file f o] writes the standard output [o] to file [f]. *) 481 + 482 + val out_stdout : run_out -> (unit * run_status) result 483 + (** [out_stdout o] redirects the standard output [o] to the current 484 + process standard output. *) 485 + 486 + val to_string : ?trim:bool -> run_out -> string result 487 + (** [to_string] is [(out_string ?trim o |> success)]. *) 488 + 489 + val to_lines : ?trim:bool -> run_out -> string list result 490 + (** [to_lines ?trim o] is [(out_string ?trim o |> success)]. *) 491 + 492 + val to_file : fpath -> run_out -> unit result 493 + (** [to_file f o] is [(out_file f o |> success)] *) 494 + 495 + val run_out : ?err:fpath -> Cmd.t -> run_out 496 + (** [run_out cmd] represents the standard output of the command run [cmd]. 497 + [std{i,err}] are connected to the invoking prcoess stream and standard 498 + output can be consumed with {!to_string}, {!to_lines} or {!to_file}. 499 + If [err] is specified [stderr] is redirected to the given file. *) 500 + end 501 + end 502 + 503 + (** Version control system repositories. *) 504 + module Vcs : sig 505 + 506 + (** {1:vcsops Version control system repositories} *) 507 + 508 + type kind = [ `Git | `Hg ] 509 + (** The type for version control systems (VCS). *) 510 + 511 + val pp_kind : Format.formatter -> kind -> unit 512 + (** [pp_kind ppf k] prints an unspecified representation of [k] on [ppf]. *) 513 + 514 + type commit_ish = string 515 + (** The type for symbols resolving to a commit. The module uses 516 + ["HEAD"] for specifying the current checkout; use 517 + this symbol even if the underlying VCS is [`Hg]. *) 518 + 519 + type t 520 + (** The type for version control systems repositories. *) 521 + 522 + val kind : t -> kind 523 + (** [kind r] is [r]'s VCS kind. *) 524 + 525 + val dir : t -> fpath 526 + (** [dir r] is [r]'s repository directory. *) 527 + 528 + val cmd : t -> Cmd.t 529 + (** [cmd r] is the base VCS command to use to act on [r]. 530 + 531 + {b Warning} Prefer the functions below to remain VCS independent. *) 532 + 533 + val find : ?dir:fpath -> unit -> t option result 534 + (** [find ~dir ()] looks for a VCS repository in working directory [dir] 535 + (not the repository directory like [.git], default is guessed 536 + automatically). *) 537 + 538 + val get : ?dir:fpath -> unit -> t result 539 + (** [get] is like {!find} but returns an error if no VCS was found. *) 540 + 541 + val pp : Format.formatter -> t -> unit 542 + (** [pp ppf r] prints an unspecified representation of [r] on [ppf]. *) 543 + 544 + (** {1:state Repository state} *) 545 + 546 + val is_dirty : t -> bool result 547 + (** [is_dirty r] is [Ok true] iff the working tree of [r] has uncommited 548 + changes. *) 549 + 550 + val not_dirty : t -> unit result 551 + (** [not_dirty] is [Ok ()] iff the working directory of [r] is not dirty and 552 + an error that enjoins to stash or commit otherwise. *) 553 + 554 + val file_is_dirty : t -> fpath -> bool result 555 + (** [file_id_dirty r f] is [Ok true] iff [f] has uncommited changes. *) 556 + 557 + val head : ?dirty:bool -> t -> string result 558 + (** [head ~dirty r] is the HEAD commit identifier of the repository 559 + [r]. If [dirty] is [true] (default), and indicator is appended to 560 + the commit identifier if the working tree of [r] {!is_dirty}. *) 561 + 562 + val commit_id : ?dirty:bool -> ?commit_ish:commit_ish -> t -> string result 563 + (** [commit_id ~dirty ~commit_ish r] is the object name (identifier) 564 + of [commit_ish] (defaults to ["HEAD"]). If [commit_ish] is 565 + ["HEAD"] and [dirty] is [true] (default) and indicator is 566 + appended to the identifier if the working tree is dirty. *) 567 + 568 + val commit_ptime_s : ?commit_ish:commit_ish -> t -> int result 569 + (** [commit_ptime_s t ~commit_ish] is the POSIX time in seconds 570 + of commit [commit_ish] (defaults to ["HEAD"]) of repository [r]. *) 571 + 572 + val describe : ?dirty:bool -> ?commit_ish:commit_ish -> t -> string result 573 + (** [describe ~dirty ~commit_ish r] identifies [commit_ish] (defaults to 574 + ["HEAD"]) using tags from the repository [r]. If [commit_ish] is 575 + ["HEAD"] and [dirty] is [true] (default) an indicator is appended to 576 + the identifier if the working tree is dirty. *) 577 + 578 + val tags : t -> string list result 579 + (** [tags r] is the list of tags in the repo [r]. *) 580 + 581 + val changes : 582 + ?until:commit_ish -> t -> after:commit_ish -> (string * string) list result 583 + (** [changes r ~after ~until] is the list of commits with their 584 + one-line message from commit-ish [after] to commit-ish [until] 585 + (defaults to ["HEAD"]). *) 586 + 587 + val tracked_files : ?tree_ish:string -> t -> fpath list result 588 + (** [tracked_files ~tree_ish r] are the files tracked by the tree object 589 + [tree_ish] (defaults to ["HEAD"]). *) 590 + 591 + (** {1:ops Repository operations} *) 592 + 593 + val clone : t -> dir:fpath -> unit result 594 + (** [clone r ~dir] clones [r] in directory [dir]. *) 595 + 596 + val checkout : ?branch:string -> t -> commit_ish:commit_ish -> unit result 597 + (** [checkout r ~branch commit_ish] checks out [commit_ish]. Checks out 598 + in a new branch [branch] if provided. *) 599 + 600 + val commit_files : ?msg:string -> t -> fpath list -> unit result 601 + (** [commit_files r ~msg files] commits the file [files] with message 602 + [msg] (if unspecified the VCS should prompt). *) 603 + 604 + val tag : 605 + ?force:bool -> ?sign:bool -> ?msg:string -> ?commit_ish:string -> t -> 606 + string -> unit result 607 + (** [tag r ~force ~sign ~msg ~commit_ish t] tags [commit_ish] with [t] 608 + and message [msg] (if unspecified the VCS should prompt). if 609 + [sign] is [true] (defaults to [false]) signs the tag ([`Git] repos only). 610 + If [force] is [true] (default to [false]) doesn't fail if the tag 611 + already exists. *) 612 + 613 + val delete_tag : t -> string -> unit result 614 + (** [delete_tag r t] deletes tag [t] in repo [r]. *) 615 + end 616 + 617 + (** {1:pkgdescr Package description} *) 618 + 619 + (** Build configuration. *) 620 + module Conf : sig 621 + 622 + (** {1:kconv Key value converters} *) 623 + 624 + type 'a conv 625 + (** The type for key value converters. *) 626 + 627 + val conv : 628 + ?docv:string -> (string -> 'a result) -> (Format.formatter -> 'a -> unit) -> 629 + 'a conv 630 + (** [conv ~docv parse print] is a configuration value converter 631 + parsing values with [parse] and printing them with 632 + [print]. [docv] is a documentation meta-variable used in the 633 + documentation to stand for the configuration value, defaults to 634 + ["VALUE"]. *) 635 + 636 + val bool : bool conv 637 + (** [bool] is a converter for booleans. *) 638 + 639 + val int : int conv 640 + (** [int] is a converter for integers. *) 641 + 642 + val string : string conv 643 + (** [string] is a converter for strings. *) 644 + 645 + val fpath : fpath conv 646 + (** [fpath] is a converter for file paths. *) 647 + 648 + val some : ?none:string -> 'a conv -> 'a option conv 649 + (** [some conv] is like [conv] but wraps the parsed value in [Some]. 650 + [none] is the string printed for [None] by the converter printer, 651 + defaults to [""]. *) 652 + 653 + (** {1:key Keys} 654 + 655 + {b Warning.} Configuration keys must be created before the call 656 + to {!Pkg.describe}. Yes you are right, that's a little bit dirty. *) 657 + 658 + type 'a key 659 + (** The type for configuration keys whose lookup value is of type ['a]. 660 + 661 + A configuration key has a name and represents a value of type 662 + ['a] in a build configuration. If ["name"] is the name of the key 663 + then its value can be specified on the command line using 664 + [--name v] where [v] is the value of the key and is parsed 665 + according to the key's {{!conv}value converter}. 666 + 667 + There are a few predefined key, see the {{!conf}configuration section}. *) 668 + 669 + val key : 670 + ?docv:string -> ?doc:string -> ?env:string -> string -> 'a conv -> 671 + absent:'a -> 'a key 672 + (** [key name conv ~absent ~env ~doc ~docv] is a configuration key 673 + with name [name] parsed from the command line with [conv]. 674 + [absent] is used if the value is not specified on the command 675 + line. If [env] is specified and exists in the process environment, 676 + the value of the variable is parsed with [conv] and used instead 677 + of [absent] when needed. 678 + 679 + [doc] is a documentation string for the key. [docv] is a documentation 680 + meta-variable to stand for the key value, defaulting to the 681 + [docv] of [conv]. In [doc], occurences of the substring ["$(docv)"] 682 + are replaced by the value of [docv]. *) 683 + 684 + val discovered_key : 685 + ?docv:string -> ?doc:string -> ?env:string -> string -> 'a conv -> 686 + absent:(unit -> 'a result) -> 'a key 687 + (** [discovered_key] is like {!key} but the absent value is discovered, 688 + {e if needed}, with [absent]. *) 689 + 690 + val with_pkg : ?default:bool -> string -> bool key 691 + (** [with_pkg ~default pkg] is a boolean configuration key named 692 + [(strf "with-%s" pkg)] to assert existence of opam packages. 693 + If absent defaults to [default]. 694 + 695 + Usually specified in opam build instructions with: 696 + {["--with-thatpkg" thatpkg:installed]} along with an entry in the 697 + depopt field of the opam file. 698 + 699 + {b Warning.} Only use this combinator for denoting opam 700 + package existence, the resulting key may switch to a discovery 701 + process in the future. *) 702 + 703 + (** {1:conf Configurations} *) 704 + 705 + type t 706 + (** The type for configurations. *) 707 + 708 + val value : t -> 'a key -> 'a 709 + (** [value c k] is the value of configuration key [k] in [c]. 710 + 711 + @raise Invalid_argument If [k] was (illegaly) created after the call 712 + to {!Pkg.describe} or if dirty tricks are being played. *) 713 + 714 + val pkg_name : t -> string 715 + (** [pkg_name c] is either the value of the package name as given to 716 + {!Pkg.describe} or the value of a predefined key [--pkg-name] which 717 + overrides the package name. This defines the name of the generated 718 + opam install file. Used to handle {{!multiopam}multiple 719 + opam packages}. *) 720 + 721 + val build_dir : t -> fpath 722 + (** [build_dir c] is either the value of build directory as given 723 + to {!Pkg.describe} via {!Pkg.build} or the value of a predefined 724 + key [--build-dir] which overrides the package definition. *) 725 + 726 + val vcs : t -> bool 727 + (** [vcs c] is the value of a predefined key [--vcs]. 728 + It is [true] if the package directory is VCS managed. Usually 729 + should not be specified manually: if absent it is determined 730 + automatically by using {!Topkg.Vcs.find} and used to determine 731 + the {!build_context}. *) 732 + 733 + val dev_pkg : t -> bool 734 + (** [dev_pkg c] is the value of a predefined key [--dev-pkg]. 735 + It is [true] if the build is initiated by an installer like opam 736 + and the package sources are a checkout from a VCS rather 737 + than a distribution archive. Usually specified in opam build instruction 738 + with either: 739 + {[ 740 + "--dev-pkg" "%{dev}%" # for opam >= 2.0 741 + "--dev-pkg" "%{pinned}%" # < 2.0 742 + ]} 743 + *) 744 + 745 + val pinned : t -> bool 746 + (** @deprecated use {!dev_pkg} 747 + 748 + [pinned c] is the value of a predefined key [--pinned]. It has 749 + exactly the same semantics as {!dev_pkg} but is misnamed. *) 750 + 751 + val jobs : t -> int 752 + (** [jobs c] is the value of a predefined key [--jobs]. 753 + If absent it is determined from the build context as follows. 754 + {ul 755 + {- [`Dev] builds default to number of CPU cores, or 4 if it cannot be determined.} 756 + {- all other contexts default to 4}} *) 757 + 758 + type build_context = [`Dev | `Distrib | `Pin ] 759 + (** The type for build contexts. See {!val:build_context} for semantics. *) 760 + 761 + val build_context : t -> [`Dev | `Distrib | `Pin ] 762 + (** [build_context c] is the build context of [c]. This is derived from 763 + {!vcs} and {!dev_pkg} (or the deprecated {!pinned}) as follows. 764 + {ul 765 + {- [`Distrib] iff [not (vcs c)]. No VCS is present, this is a build from 766 + a distribution. If there are configuration bits they should 767 + be setup according to the build configuration.} 768 + {- [`Dev] iff [vcs c && not (dev_pkg c || pinned c)]. This is a 769 + development build invoked manually in a source repository. The 770 + repository checkout should likely not be touched and configuration 771 + bits not be setup. This is happening for example if the developer 772 + is testing the package description in her working source repository 773 + by invoking [pkg/pkg.ml] or [topkg build].} 774 + {- [`Pin] iff [vcs c && (dev_pkg c || pinned c)]. This is a package 775 + manager development build. In this case the repository checkout may 776 + need to be massaged into a pseudo-distribution for the package to be 777 + installed. This means that distribution watermarking and massaging 778 + should be performed, see {!Pkg.distrib} and the [prepare_on_pin] 779 + argument of {!Pkg.build}. Besides exisiting configuration bits 780 + should be setup according to the 781 + build environment. {b Note.} This is called [`Pin] due to a blind 782 + spot, a more approriate name would be something like [`Dev_pkg] 783 + build.}} *) 784 + 785 + val build_tests : t -> bool 786 + (** [build_tests c] is the value of a predefined key [--tests] that 787 + indicates if the tests should be built. If absent the value 788 + depends on the {!build_context}, it is [true] in the [`Dev] 789 + context and [false] in the other contexts. *) 790 + 791 + val debug : t -> bool 792 + (** [debug c] is the value of a predefined key [--debug] that 793 + indicates if the build should include debugging information 794 + in the build artefacts. If absent the value is [true] or the 795 + value of the environment variable TOPKG_CONF_DEBUG if 796 + specified. *) 797 + 798 + val debugger_support : t -> bool 799 + (** [debugger_support c] is the value of a predefined key 800 + [--debugger-support] that indicates if build artefacts needed 801 + for source level debuggers should be built and installed. If 802 + absent the value is [true] or the value of the environment 803 + variable TOPKG_CONF_DEBUGGER_SUPPORT if specified. *) 804 + 805 + val profile : t -> bool 806 + (** [profile c] is the value of a predefined key [--profile] that 807 + indicates if the build artefacts include run-time profiling support. If 808 + absent the value is [false] or the value of the environment variable 809 + TOPKG_CONF_PROFILE if specified. *) 810 + 811 + val toolchain : t -> string option 812 + (** [toolchain c] is the value of a predefined key [--toolchain] that 813 + specifies the ocamlbuild toolchain. If absent the value is [None] or 814 + the value of the environment variable TOPKG_CONF_TOOLCHAIN if 815 + specified. *) 816 + 817 + val dump : Format.formatter -> t -> unit 818 + (** [dump ppf c] formats an unspecified representation of [c] on [ppf]. *) 819 + 820 + (** {1:tool Tool lookup} 821 + 822 + If your package description needs to run tools (e.g. in the 823 + pre and post build hooks, see {!Pkg.build}) it should get the 824 + tool to invoke with the following function. This allows to 825 + control the executable being run which is useful for 826 + cross-compilation scenarios. *) 827 + 828 + type os = [ `Build_os | `Host_os ] 829 + (** The type for operating systems. 830 + {ul 831 + {- [`Build_os] is the build OS, the OS on which the package is built.} 832 + {- [`Host_os] is the host OS, the OS on which the package is hosted 833 + and runs.}} *) 834 + 835 + val tool : ?conf:t -> string -> os -> Cmd.t 836 + (** [tool ~conf cmd os] is a command [cmd] which can be run on the build OS 837 + which produces output suitable for the OS [os], taking into account 838 + configuration [conf]. 839 + 840 + The actual command returned depends on the following lookup procedure, 841 + here exemplified on the invocation [tool "mytool" `Host_os] (resp. 842 + [`Build_os]). 843 + {ol 844 + {- [Cmd.v "cmd"], if the environment variable HOST_OS_MYTOOL (resp. 845 + BUILD_OS_MYTOOL) is set to ["cmd"]} 846 + {- [Cmd.v (Fpath.append path "mytool")], if the environment variable 847 + HOST_OS_XBIN (resp. BUILD_OS_BIN) is set to [path].} 848 + {- [Cmd.v ("mytool" ^ "suff")], if the environment variable 849 + HOST_OS_SUFF (resp. BUILD_OS_SUFF).} 850 + {- [Cmd.(v "ocamlfind" % "-toolchain" % "toolchain" % "mytool")] if 851 + ["mytool"] is part of the OCaml tools that can be invoked through 852 + ocamlfind, [toolchain conf] is not [None], and [os] is [`Host_os].} 853 + {- [Cmd.(v "ocamlfind" % "mytool")] if ["mytool"] is part of 854 + the OCaml tools that can be invoked through ocamlfind.} 855 + {- [Cmd.v "mytool"] otherwise.}} *) 856 + 857 + (** {1:ocaml OCaml configuration} *) 858 + 859 + (** OCaml configuration. *) 860 + module OCaml : sig 861 + 862 + (** {1:conf OCaml system configuration} *) 863 + 864 + type conf = t 865 + 866 + type t 867 + (** The type for OCaml configurations. *) 868 + 869 + val v : conf -> os -> t 870 + (** [v c os] is the configuration of the OCaml system for the OS 871 + [os] obtained by reading the output of [tool "ocamlc" os] with 872 + the [-config] option. *) 873 + 874 + val find : string -> t -> string option 875 + (** [find k c] looks up key [k] in configuration [c]. *) 876 + 877 + val version : t -> int * int * int * string option 878 + (** [version] is the compiler version string 879 + ["major.minor[.patchlevel][+additional-info]"] parsed into 880 + [(major, minor, patch, additional-info)]. *) 881 + 882 + val ext_asm : t -> string 883 + (** [ext_asm] is the file extension for assembly files, e.g. [".s"]. *) 884 + 885 + val ext_obj : t -> string 886 + (** [ext_asm] is the file extension for C object files, e.g. [".o"]. *) 887 + 888 + val ext_lib : t -> string 889 + (** [ext_lib] is the file extension for C static libraries, e.g. [".a"]. *) 890 + 891 + val ext_dll : t -> string 892 + (** [ext_dll] is the file extension for C dynamically linked libraries, 893 + e.g. [".so"]. *) 894 + 895 + val ext_exe : t -> string 896 + (** [ext_exe] is the file extension for binary executables, e.g. [".exe"] 897 + or [""]. Until at least OCaml 4.03 this information is not readily 898 + available (see 899 + {{:http://caml.inria.fr/mantis/view.php?id=7172}PR #7173}) and 900 + discovered as described in 901 + {{:http://lists.ocaml.org/pipermail/wg-windows/2015-July/000037.html} 902 + this message}. *) 903 + 904 + val native : t -> bool 905 + (** [native] is [true] if native compilation (i.e. [ocamlopt]) is 906 + available. Until at least OCaml 4.03 this information is not 907 + readily available (see 908 + {{:http://caml.inria.fr/mantis/view.php?id=7172}PR #7173}) and 909 + [true] is returned iff the standard library directory has the 910 + [libasmrun] C static library. *) 911 + 912 + val native_dynlink : t -> bool 913 + (** [native_dynlink] is [true] if native dynamic linking is 914 + available. Until at least OCaml 4.03 this information is not 915 + readily available (see 916 + {{:http://caml.inria.fr/mantis/view.php?id=7172}PR #7173}) and 917 + [true] is returned iff the standard library directory has the 918 + [dynlink.cmxa] library. *) 919 + 920 + val supports_shared_libraries : t -> bool 921 + (** [supports_shared_libraries] is [true] if compilation of C 922 + shared libraries is supported. Until OCaml 4.08 this 923 + information is not readily available (see 924 + {{:https://github.com/ocaml/ocaml/pull/1691}PR 1691}). Before 925 + that it checks for the existence of either ["libasmrun_shared" 926 + ^ ext_dll c] or ["libcamlrun_shared" ^ ext_dll c] in the standard 927 + library directory. *) 928 + 929 + val word_size : t -> int 930 + (** [word_size] is the bit size of one word on the OS that will 931 + execute the programs produced by the compiler (i.e. the value 932 + of {!Sys.word_size} in these programs). Until at least OCaml 933 + 4.03 this information is not readily available (see 934 + {{:http://caml.inria.fr/mantis/view.php?id=7172}PR #7173}) and 935 + the information is read from the C SIZEOF_PTR macro defined in 936 + the file [caml/config.h] of the standard library directory. *) 937 + 938 + val dump : Format.formatter -> t -> unit 939 + (** [dump ppf c] prints an unspecified representation of [c] on [ppf]. *) 940 + end 941 + end 942 + 943 + (** Exts defines sets of file extensions. *) 944 + module Exts : sig 945 + 946 + (** {1:fexts File extensions} *) 947 + 948 + type ext 949 + (** The type for file extensions. *) 950 + 951 + type t = ext list 952 + (** The type for lists of file extensions. *) 953 + 954 + val interface : t 955 + (** [interface] is [exts [".mli"; ".cmi"; ".cmti"]]. *) 956 + 957 + val cmx : ext list 958 + (** [cmx] is [ext ".cmx"]. *) 959 + 960 + val api : t 961 + (** [api] is [interface @ cmx]. *) 962 + 963 + val real_c_library : ext list 964 + (** [real_c_library] is the extension for C libraries (archives). 965 + This should be used by C libraries (e.g. stubs) compiled by 966 + OCaml. For example {!Topkg.Pkg.clib} uses this. The actual value 967 + is determined from {{!Conf.OCaml}OCaml's configuration}. *) 968 + 969 + val c_library : ext list 970 + (** [c_library] is the extension for C libraries (archives). This is like 971 + {!real_c_library} but for those C archive that are generated by OCaml 972 + build artefacts. The actual value is determined from 973 + {{!Conf.OCaml}OCaml's configuration}. *) 974 + 975 + val c_dll_library : ext list 976 + (** [c_dll_library] is the extension for C dynamic libraries (archives). The 977 + actual value is determined from {{!Conf.OCaml}OCaml's configuration}. *) 978 + 979 + val library : ext list 980 + (** [library] is [exts [".cma"; ".cmxa"; ".cmxs"] @ c_library] *) 981 + 982 + val module_library : ext list 983 + (** [module_library] is [(api @ library)]. *) 984 + 985 + val exe : ext list 986 + (** [exe] is the extension for executables. The actual value is determined 987 + from {{!Conf.OCaml}OCaml's configuration}. *) 988 + 989 + val exts : string list -> ext list 990 + (** [exts ss] is [ss] as a list of extensions. *) 991 + 992 + val ext : string -> ext list 993 + (** [ext s] is [s] as a list of extensions. *) 994 + 995 + (**/**) 996 + val ext_to_string : Conf.OCaml.t -> ext -> string 997 + (**/**) 998 + end 999 + 1000 + (** Package description. 1001 + 1002 + See the {{!basics}basics}. *) 1003 + module Pkg : sig 1004 + 1005 + (** {1:install Installation description} 1006 + 1007 + The installation description generates an opam install file 1008 + which is simply a description of file moves (in the [mv] sense) 1009 + from the build or source directory to standard install 1010 + directories. Describing these moves in a given build 1011 + configuration effectively determines what needs to built by the 1012 + {{!build}package build command}. 1013 + 1014 + {b Note.} Always use ["/"] as a directory seperator for paths, even 1015 + on Windows. *) 1016 + 1017 + type install 1018 + (** The type for representing a set of install moves. *) 1019 + 1020 + val nothing : install 1021 + (** [nothing] is an empty set of install moves. *) 1022 + 1023 + val flatten : install list -> install 1024 + (** [flatten installs] is the union of all the install moves in [installs]. *) 1025 + 1026 + type field = 1027 + ?force:bool -> ?built:bool -> ?cond:bool -> ?exts:Exts.t -> ?dst:fpath -> 1028 + fpath -> install 1029 + (** The type for an install field, a function that describe file 1030 + moves to a particular installation directory. In the simplest form 1031 + a call [field src] simply moves the file [src] at the root of the 1032 + install directory of the field. 1033 + 1034 + In general [field ~force ~built ~cond ~exts ~dst src] generates install 1035 + move as follows: 1036 + {ul 1037 + {- [dst] is the path were the source is written to. Expressed 1038 + relative to the install directory of the field. Defaults 1039 + to [Fpath.basename src], i.e. at the root of the install directory. 1040 + If [dst] is a {{!Fpath.is_dir_path}directory path}, the destination 1041 + is [(dst ^ Fpath.basename src)].} 1042 + {- If [exts] is present and non empty, generates the list of 1043 + paths [List.map (fun e -> src ^ e)] and a move for 1044 + each of these. For example [field ~exts:]{!Exts.api}[ "src/m"] would 1045 + generate a move for the files ["src/m.mli"], ["src/m.cmi"], 1046 + ["src/m.cmti"], ["src/m.cmx"].} 1047 + {- If [cond] is [false] (defaults to [true]) no file move is generated. 1048 + This provides a convenient way to conditionalize installation based 1049 + on the build configuration for example: 1050 + {[let jsoo = Conf.value jsoo in 1051 + Pkg.mllib ~cond:jsoo "src/mylib_jsoo.mllib" 1052 + ]}} 1053 + {- If [built] is [true] (default), [src] is expressed relative 1054 + to the {{!build}build directory} of the distribution and the path 1055 + [src] is be given to {{!build}build system invocation} 1056 + for construction. 1057 + If [false], [src] is relative to the root of the distribution 1058 + and is excluded from the build system invocation; this can 1059 + be used for installing files that don't need to be built.} 1060 + {- If [force] is [true] (defaults to [false]) it disables the automatic 1061 + [src] filtering performed by the library. When [false], 1062 + the library automatically disable certain build artefact 1063 + depending on {{!Conf.OCaml}OCaml's configuration}, one such 1064 + example is filtering native code build artefact if the OCaml install 1065 + doesn't support 1066 + {{!Conf.OCaml.native}native code compilation}}} *) 1067 + 1068 + type exec_field = ?auto:bool -> field 1069 + (** The type for fields that install executable files. This is like {!field} 1070 + except for the additional [auto] parameter: 1071 + {ul 1072 + {- If [auto] is [true] (default) then [field src dst] 1073 + automatically adds the [".native"] suffix to [src] if 1074 + {!Conf.OCaml.native} is [true] and the [".byte"] suffix 1075 + otherwise. Besides it automatically adds {!Exts.exe} to 1076 + [dst], which handles things correctly on the various Windows 1077 + ports.} 1078 + {- If [auto] is [false] you have full control according to 1079 + {!field}.}} *) 1080 + 1081 + val bin : exec_field 1082 + (** [bin] is a field that installs to a common [bin/] directory. *) 1083 + 1084 + val doc : field 1085 + (** [doc] is a field that installs to a package specific [doc/] 1086 + directory *) 1087 + 1088 + val etc : field 1089 + (** [etc] is a field that installs to a package specific [etc/] 1090 + directory. *) 1091 + 1092 + val lib : field 1093 + (** [lib] is a field that installs to a package specific [lib/] 1094 + directory. *) 1095 + 1096 + val lib_root : field 1097 + (** [lib_root] is a field that install to a common [lib/] directory. *) 1098 + 1099 + val libexec : exec_field 1100 + (** [libexec] is a field that installs to a package specific [lib/] 1101 + directory but with the executable bit set. *) 1102 + 1103 + val libexec_root : exec_field 1104 + (** [libexec_root] is a field that install to a common [lib/] directory 1105 + but with the executable bit set. *) 1106 + 1107 + val man : field 1108 + (** [man] is a field that installs to a common [man/] directory. See 1109 + the {{:https://opam.ocaml.org/doc/manual/dev-manual.html#sec25} 1110 + opam manual} for details. *) 1111 + 1112 + val misc : field 1113 + (** [misc] is a field that installs to an arbitrary absolute path, 1114 + the user is prompted for authorization, 1115 + see the {{:https://opam.ocaml.org/doc/manual/dev-manual.html#sec25} 1116 + opam manual} for details. *) 1117 + 1118 + val sbin : exec_field 1119 + (** [sbin] is a field that installs to a common [sbin/] directory. *) 1120 + 1121 + val share : field 1122 + (** [share] is a field that installs to a package specific [share/] 1123 + directory. *) 1124 + 1125 + val share_root : field 1126 + (** [share_root] is a field that installs to a common [share/] 1127 + directory. *) 1128 + 1129 + val stublibs : field 1130 + (** [stublibs] is a field that install to a common [lib/stublibs/] 1131 + directory. Used for OCaml C stub directory. *) 1132 + 1133 + val toplevel : field 1134 + (** [toplevel] is a field that installs to a common [lib/toplevel/] 1135 + directory. *) 1136 + 1137 + (**/**) 1138 + val unknown : string -> field 1139 + (**/**) 1140 + 1141 + (** {2:tests Test executable description} *) 1142 + 1143 + val test : ?run:bool -> ?dir:fpath -> ?args:Cmd.t -> exec_field 1144 + (** [test] is a special {{!exec_field}executable field}: it doesn't 1145 + install the described executable (as such the [dst] argument is 1146 + ignored at the moment). If [run] is [true] (default) executes 1147 + the test with [args] when [ocaml pkg/pkg.ml test] is run; this 1148 + will notably run to test the distribution tarball. If 1149 + [run] is [false] the test needs to be invoked explicitely. 1150 + [dir] specifies the current working directory for the test, expressed 1151 + relative to the root directory of the package (defaults to [.]). *) 1152 + 1153 + (** {2 OCamlbuild higher-level installs} 1154 + 1155 + The following functions are OCamlbuild specific higher level 1156 + installs that generate moves by reading OCamlbuild specification 1157 + files. They also automatically handle the {!Conf.debugger_support} 1158 + configuration key by building and installing the build artefacts 1159 + needed by debuggers whenever its value is [true]. *) 1160 + 1161 + val mllib : 1162 + ?field:field -> ?cond:bool -> ?cma:bool -> ?cmxa:bool -> ?cmxs:bool -> 1163 + ?api:string list -> ?dst_dir:fpath -> fpath -> install 1164 + (** [mllib ~field ~cond ~cma ~cmxa ~cmxs ~api ~dst_dir mllib] installs an 1165 + OCaml library described by the 1166 + {{:https://github.com/ocaml/ocamlbuild/blob/master/manual/manual.adoc#Sec_Archives_documentation}OCamlbuild .mllib file} [mllib] with: 1167 + {ul 1168 + {- [field] is the field where it gets installed (defaults to {!lib}).} 1169 + {- If [cond] is [false] (defaults to [true]), no move is generated.} 1170 + {- [cma], [cmxa], [cmxs] determine if corresponding archives are 1171 + built and installed, they all default to [true].} 1172 + {- [api] is the list of modules that defines the public interface 1173 + of the library, if [None] all the modules mentioned in [mllib] 1174 + are part of the public interface.} 1175 + {- [dst_dir] is the destination directory of the library 1176 + in the field. If unspecified this is the root of the field's 1177 + directory.}} *) 1178 + 1179 + val clib : 1180 + ?dllfield:field -> 1181 + ?libfield:field -> 1182 + ?cond:bool -> ?lib_dst_dir:fpath -> fpath -> install 1183 + (** [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: 1184 + {ul 1185 + {- [dllfield] is the field where the C DLL archive gets installed, 1186 + (defaults to {!stublibs})} 1187 + {- [libfield] is the field where the C static archive gets installed 1188 + (defaults to {!lib})} 1189 + {- If [cond] is [false] (defaults to [true]), no move is generated.} 1190 + {- [lib_dst_dir] is the destination directory of the library in the 1191 + [libfield] field. If unspecified this is the root of the field's 1192 + directory. This does not affect the [dllfield], DLLs are always 1193 + installed at the root directory of the [dllfield].}} *) 1194 + 1195 + (** {1:build Build description} *) 1196 + 1197 + type build 1198 + (** The type for package build description. *) 1199 + 1200 + val build : 1201 + ?prepare_on_pin:bool -> 1202 + ?dir:fpath -> 1203 + ?pre:(Conf.t -> unit result) -> 1204 + ?cmd:(Conf.t -> Conf.os -> fpath list -> unit result) -> 1205 + ?post:(Conf.t -> unit result) -> 1206 + ?clean:(Conf.os -> build_dir:fpath -> unit result) -> unit -> build 1207 + (** [build ~prepare_on_pin ~dir ~cmd ~pre ~post] describes the package 1208 + build procedure. 1209 + {ul 1210 + {- [prepare_on_pin] if [true] (default) distribution 1211 + preparation is performed if a [`Pin] 1212 + {{!Conf.build_context}build context} is detected. This means that 1213 + the checkout is watermarked and the massage hook is invoked, 1214 + see step 2. of {{!distdetails}distribution creation}.} 1215 + {- [dir] is the directory where build artefacts are generated, 1216 + (defaults to ["_build"]). Note that his value can be overriden 1217 + from the command line.} 1218 + {- [pre] is a hook that is invoked with the build context, after 1219 + distribution preparation if applicable, but before the build 1220 + command. It can be used to adapt the build setup according to 1221 + the build configuration. Default is a nop.} 1222 + {- [cmd] invokes the build system to build the files 1223 + determined by {{!install}install} moves. 1224 + It is given the build configuration, an {{!Conf.os}OS 1225 + specification}, the list of files to build relative to the 1226 + {{!Conf.build_dir}build directory}, and build the given 1227 + files in the build directory. The default is: 1228 + {[ 1229 + fun c os files -> OS.Cmd.run @@ Cmd.(Pkg.build_cmd c os %% of_list files) 1230 + ]}} 1231 + {- [post] is a hook that is invoked with the build context after 1232 + the build command returned sucessfully. Default is a nop.} 1233 + {- [clean] is invoked to clean a build. It is given 1234 + an {{!Conf.os}OS specification} and a build directory to 1235 + clean. The default is: 1236 + {[ 1237 + let clean os ~build_dir = OS.Cmd.run @@ Pkg.clean_cmd os ~build_dir 1238 + ]}}} 1239 + {b Warning.} If you are invoking tools in your hooks consider 1240 + using {!Conf.tool} to look them up it helps for cross-compilation. *) 1241 + 1242 + (** {2:ocamlbuild OCamlbuild support} 1243 + 1244 + If you are using [ocamlbuild], the following functions 1245 + help to customize the build system invocation according to the 1246 + configuration. *) 1247 + 1248 + val build_cmd : Conf.t -> Conf.os -> Cmd.t 1249 + (** [build_cmd c os] is the default build command to which 1250 + files to build are given. Its value is defined by: 1251 + {[fun c os -> 1252 + let ocamlbuild = Conf.tool "ocamlbuild" os in 1253 + let build_dir = Conf.build_dir c in 1254 + let toolchain = 1255 + match Topkg_conf.toolchain c with 1256 + | Some toolchain -> Topkg_cmd.(v "-toolchain" % toolchain) 1257 + | _ -> Topkg_cmd.empty 1258 + in 1259 + let debug = Cmd.(on (Conf.debug c) (v "-tag" % "debug")) in 1260 + let profile = Cmd.(on (Conf.profile c) (v "-tag" % "profile")) in 1261 + Cmd.(ocamlbuild % "-use-ocamlfind" %% toolchain % "-classic-display" %% 1262 + debug %% profile % "-build-dir" % build_dir)]} *) 1263 + 1264 + val clean_cmd : Conf.os -> build_dir:fpath -> Cmd.t 1265 + (** [clean_cmd os ~build_dir] is the default clean command. Its value 1266 + is defined by: 1267 + {[fun os ~build_dir -> 1268 + let ocamlbuild = Conf.tool "ocamlbuild" os in 1269 + Cmd.(ocamlbuild % "-use-ocamlfind" % "-classic-display" % 1270 + "-build-dir" % build_dir % "-clean") ]} *) 1271 + 1272 + val ocb_tag : Conf.t -> 'a Conf.key -> string -> Cmd.t 1273 + (** [ocb_tag c key name] is a command fragment adding the 1274 + [ocamlbuild] parameterized tag [name] with [key]'s value to 1275 + the default set of tags. The key value is converted to a string 1276 + using the printer of the key value {{!Conf.conv}converter}. 1277 + 1278 + For example with a key [k : bool Conf.key] whose value is 1279 + [true], [ocb_tag c k "foo"] adds the tag [foo(true)] to the 1280 + default tags. A sample build command for {!build} using 1281 + this key would be: 1282 + {[ 1283 + let cmd c os files = 1284 + OS.Cmd.run Cmd.(build_cmd c os %% ocb_tag c k "foo" %% of_list files)]} *) 1285 + 1286 + val ocb_bool_tag : Conf.t -> bool Conf.key -> string -> Cmd.t 1287 + (** [ocb_bool_tag c key name] is a command fragment adding 1288 + the [ocamlbuild] tag [name] to the default set of tags iff [key]'s 1289 + value is [true]. *) 1290 + 1291 + val ocb_bool_tags : Conf.t -> (bool Conf.key * string) list -> Cmd.t 1292 + (** [ocb_bool_tags c assoc] is the concatenation of {!ocb_bool_tag} 1293 + for all pairs in [assoc]. *) 1294 + 1295 + (** {1:distrib Distribution description} *) 1296 + 1297 + type watermark = string * [ `String of string | `Version | `Version_num 1298 + | `Name | `Vcs of [`Commit_id] 1299 + | `Opam of fpath option * string * string] 1300 + (** The type for watermarks. A watermark identifier, e.g. ["ID"] and its 1301 + definition: 1302 + {ul 1303 + {- [`String s], [s] is the definition.} 1304 + {- [`Name], is the name of package.} 1305 + {- [`Version], is the version of the distribution.} 1306 + {- [`Version_num], is the version of the distribution with a potential 1307 + leading ['v'] or ['V'] dropped.} 1308 + {- [`Vcs `Commit_id], is the commit identifier (hash) of the 1309 + distribution. May be post-fixed by ["dirty"] in 1310 + {{!Conf.build_context}dev package ([`Pin]) builds}.} 1311 + {- [`Opam (file, field, sep)], is the values of the field 1312 + [field] concatenated with separator [sep] of the opam file 1313 + [file], expressed relative to the distribution root directory, if 1314 + [file] is [None] this is the package's default opam file, see 1315 + {!describe}. Not all fields are supported see the value of 1316 + {!Topkg_care.Opam.File.field_names}. {b Warning.} In 1317 + {{!Conf.build_context}dev package ([`Pin]) builds}, [`Opam] 1318 + watermarks are only substituted if the package [topkg-care] is 1319 + installed.}} 1320 + 1321 + When a file is watermarked with an identifier ["ID"], any occurence of 1322 + the sequence [%%ID%%] in its content is substituted by its definition. *) 1323 + 1324 + type distrib 1325 + (** The type for describing distribution creation. *) 1326 + 1327 + val distrib : 1328 + ?watermarks:watermark list -> 1329 + ?files_to_watermark:(unit -> fpath list result) -> 1330 + ?massage:(unit -> unit result) -> 1331 + ?exclude_paths:(unit -> fpath list result) -> 1332 + ?uri:string -> unit -> distrib 1333 + (** [distrib ~watermarks ~files_to_watermark ~massage 1334 + ~exclude_paths ~uri ()] influences the distribution creation 1335 + process performed by the [topkg] tool. 1336 + See the {{!distdetails}full details about distribution creation}. 1337 + 1338 + In the following the {e distribution build directory} is a 1339 + private clone of the package's source repository's [HEAD] when 1340 + [topkg distrib] is invoked. 1341 + {ul 1342 + {- [watermarks] defines the source watermarks for the distribution, 1343 + defaults to {!watermarks}.} 1344 + {- [files_to_watermark] is invoked in the distribution build 1345 + directory to determine the files to watermark, defaults 1346 + to {!files_to_watermark}.} 1347 + {- [massage] is invoked in the distribution build directory, 1348 + after watermarking, but before archiving. It can be used to 1349 + generate distribution time build artefacts. Defaults to {!massage}.} 1350 + {- [exclude_paths ()] is invoked in the distribution build 1351 + directory, after massaging, to determine the paths that are 1352 + excluded from being added to the distribution archive. Defaults to 1353 + {!exclude_paths}.} 1354 + {- [uri] is an URI pattern that specifies the location of the 1355 + distribution on the WWW. In this string any sub-string 1356 + ["$(NAME)"] is replaced by the package name, ["$(VERSION)"] is replaced 1357 + by the distribution version string and ["$(VERSION_NUM)"] by the 1358 + distribution version string, chopping an initial 1359 + ['v'] or ['V'] character if present. This argument is used to 1360 + generate the [url] file of an opam package for the distribution; 1361 + it will be deprecated in the future in favour of a [x-distrib-uri] 1362 + field in the opam file. If the value is unspecified it defaults to: 1363 + {[PKG_HOMEPAGE/releases/$(NAME)-$(VERSION_NUM).tbz]} 1364 + where PKG_HOMEPAGE is the package's opam file [homepage] field. 1365 + As a special case if the 1366 + hostname of PKG_HOMEPAGE is [github] the following is used: 1367 + {[PKG_DEV_REPO/releases/download/$(VERSION)/$(NAME)-$(VERSION_NUM).tbz]} 1368 + where PKG_DEV_REPO is the package's opam file [dev-repo] field 1369 + without the [.git] suffix and a possible [git+] prefix.}} *) 1370 + 1371 + val watermarks : watermark list 1372 + (** [watermarks] is the default list of watermarks. It has the following 1373 + elements: 1374 + {ul 1375 + {- [("NAME", `Name)]} 1376 + {- [("VERSION", `Version)]} 1377 + {- [("VERSION_NUM", `Version_num)]} 1378 + {- [("VCS_COMMIT_ID", `Vcs [`Commit_id])]} 1379 + {- [("PKG_MAINTAINER", `Opam (None, "maintainer", ", "))]} 1380 + {- [("PKG_AUTHORS", `Opam (None, "authors", ", ")]} 1381 + {- [("PKG_HOMEPAGE", `Opam (None, "homepage", " ")]} 1382 + {- [("PKG_ISSUES", `Opam (None, "bug-reports", " ")]} 1383 + {- [("PKG_DOC", `Opam (None, "doc", " "))]} 1384 + {- [("PKG_LICENSE", `Opam (None, "license", ", ")]} 1385 + {- [("PKG_REPO", `Opam (None, "dev-repo", " "))]}} 1386 + Prepending to the list overrides default definitions. *) 1387 + 1388 + val files_to_watermark : unit -> fpath list result 1389 + (** [files_to_watermark ()] is the default list of files to 1390 + watermark. It is invoked in the distribution build directory 1391 + and gets the set of {{!Vcs.tracked_files}tracked files} of this 1392 + directory from which it removes the files that end with [.eps], [.flv], 1393 + [.gif], [.ico], [.jpeg], [.jpg], [.mov], [.mp3], [.mp4], [.otf], 1394 + [.pdf], [.png], [.ps], [.ttf], [.woff]. *) 1395 + 1396 + val massage : unit -> unit result 1397 + (** [massage] is the default distribution massaging function. It is 1398 + invoked in the distribution build directory and does nothing. *) 1399 + 1400 + val exclude_paths : unit -> fpath list result 1401 + (** [exclude_paths ()] is the default list of paths to exclude 1402 + from the distribution archive. It is invoked in the distribution build 1403 + directory and returns the following static set of files. 1404 + {[ 1405 + fun () -> Ok [".git"; ".gitignore"; ".gitattributes"; ".hg"; ".hgignore"; 1406 + "build"; "Makefile"; "_build"]]} *) 1407 + 1408 + (** {1 Distribution publication description} *) 1409 + 1410 + type publish 1411 + (** The type for describing distribution publication. *) 1412 + 1413 + val publish : 1414 + ?artefacts:[`Doc | `Distrib | `Alt of string ] list -> unit -> publish 1415 + (** [publish ~artefacts ()] influences the distribution publication process 1416 + performed by the [topkg] tool: 1417 + {ul 1418 + {- [artefacts] defines which artefacts are published by an invocation 1419 + of [topkg publish] without arguments (defaults to 1420 + [[`Doc;`Distrib]]).}} *) 1421 + 1422 + (** {1 Package description} *) 1423 + 1424 + type std_file 1425 + (** The type for specifying a standard file. *) 1426 + 1427 + val std_file : ?install:bool -> fpath -> std_file 1428 + (** [std_file ~install p] is a standard file [p] expressed relative 1429 + to the distribution root directory. The file is 1430 + automatically installed if [install] is [true] (default). *) 1431 + 1432 + type meta_file 1433 + (** The type for specifying an OCamlfind META file. *) 1434 + 1435 + val meta_file : ?lint:bool -> ?install:bool -> fpath -> meta_file 1436 + (** [meta_file ~lint ~install p] is a META file [p] expressed relative 1437 + to the distribution root directory. The file is automatically 1438 + installed in the {!lib} field if [install] is [true] (default). 1439 + If [lint] is [true] (default), it is OCamlfind linted. *) 1440 + 1441 + type opam_file 1442 + (** The type for specifying an opam file. *) 1443 + 1444 + val opam_file : 1445 + ?lint:bool -> ?lint_deps_excluding:string list option -> ?install:bool -> 1446 + fpath -> opam_file 1447 + (** [opam_file ~lint ~lint_deps_excluding ~install p] is an opam file 1448 + [p] expressd relative to the distribution root directory such that: 1449 + {ul 1450 + {- If [install] is [true] (default), it is automatically installed 1451 + in the {!lib} field.} 1452 + {- If [lint] is [true] (default), it is opam linted.} 1453 + {- If [lint_deps_excluding] is [Some excludes], [topkg] 1454 + checks that each of the opam package dependencies is mentioned 1455 + as a root package in the OCamlbuild [_tags] file and vice-versa. The 1456 + following package names are excluded from this test: 1457 + {ul 1458 + {- The packages names mentioned in [excludes].} 1459 + {- Package names that start with ["conf-"]} 1460 + {- {!Topkg_care.OCamlfind.base_packages}} 1461 + {- {!Topkg_care.Opam.ocaml_base_packages}}} 1462 + If [None] the dependency check is disabled.}} *) 1463 + 1464 + val describe : 1465 + ?delegate:Cmd.t -> 1466 + ?readmes:std_file list -> 1467 + ?licenses:std_file list -> 1468 + ?change_logs:std_file list-> 1469 + ?metas:meta_file list -> 1470 + ?opams:opam_file list -> 1471 + ?lint_files:fpath list option -> 1472 + ?lint_custom:(unit -> R.msg result list) -> 1473 + ?distrib:distrib -> 1474 + ?publish:publish -> 1475 + ?build:build -> 1476 + string -> (Conf.t -> install list result) -> unit 1477 + (** [describe name install] describes a package named [name] with: 1478 + {ul 1479 + {- [delegate], the package delegate command to use. If unspecfied 1480 + determined by the delegate lookup procedure, see 1481 + [topkg help delegate] for more information.} 1482 + {- [readmes] are readme files, defaults to 1483 + [[std_file "README.md"]]. Automatic install is in the 1484 + {!doc} field.} 1485 + {- [licenses] are license files, defaults to 1486 + [[std_file "LICENSE.md"]]. Automatic install is in the {!doc} field.} 1487 + {- [change_logs] are change logs, defaults to 1488 + [[std_file "CHANGES.md"]]. The first file of the list is the 1489 + one that is acted upon by the [topkg log] command. 1490 + Automatic install is in the {!doc} field.} 1491 + {- [metas] the package's ocamlfind META files, defaults to 1492 + [[ meta_file "pkg/META" ]].} 1493 + {- [opams] the package's opam package files, defaults to 1494 + [[opam_file "opam"]]. The default opam file used by a package 1495 + description depends on the package [name] (which can 1496 + be overriden from the command line). The opam file lookup 1497 + procedure selects the first path in [opams] whose filename is 1498 + [(name ^ ".opam")] and, failing 1499 + to do so, it fallbacks to an ["opam"] file at the root of the 1500 + distribution.} 1501 + {- [lint_files] if [Some files], ensures that all files mentioned in 1502 + [readme], [license], [change_log], [metas], [opams] and [files] 1503 + are present in the distribution. Defaults to [Some []]. 1504 + If [None] disables the file existence tests (including readme, 1505 + change_log, license, metas, opams, metas.)} 1506 + {- [lint_custom] defines a custom linting process run with the current 1507 + directory set at the root of the distribution. Successes and errors 1508 + in the returned list are reported as such and any error in the list 1509 + makes the lint fail. Defaults to [None].} 1510 + {- [distrib], specifies the distribution process, defaults to 1511 + {!distrib}[ ()].} 1512 + {- [publish], specifies the publication process, defaults to 1513 + {!publish}[ ()].} 1514 + {- [build], specifies the build process, defaults to {!build}[ ()].} 1515 + {- [install] given a {{!Conf.t}build configuration} specifies 1516 + the install moves. Note that some of standard files are 1517 + automatically installed and don't need to be specified, see 1518 + {!std_file}, {!meta_file} and {!opam_file}.}} *) 1519 + 1520 + (** {1:distdetails Package distribution creation details} 1521 + 1522 + The following describes the exact steps performed by [topkg 1523 + distrib] to create the distribution archive. Note that [topkg] 1524 + allows to override or disable part of the process via command 1525 + line arguments, e.g. to specify the version string manually or 1526 + skip linting. See [topkg distrib --help] for more information. 1527 + 1528 + The distribution process assumes that the source repository 1529 + working directory is clean so that its definitions are consistent 1530 + with those of the distribution build directory. A warning is 1531 + generated if this is not the case as it may end up in inconsistent 1532 + distribution archives (but which may be fine to only publish 1533 + a documentation update). 1534 + 1535 + Let [$NAME] be the name of the package, [$BUILD] be its 1536 + {{!build}build directory}, [$VERSION] be the VCS tag description 1537 + (e.g. [git-describe(1)] if you are using [git]) of the source 1538 + repository HEAD commit and [distrib] the {{!distrib}distribution 1539 + description} found in the source's repository [pkg/pkg.ml] file. 1540 + {ol 1541 + {- Clone the source repository at [HEAD] as the distribution build 1542 + directory [$BUILD/$NAME-$VERSION.build].} 1543 + {- Prepare the distribution: 1544 + {ol 1545 + {- Invoke the [files_to_watermark] function of [distrib] in the 1546 + distribution build directory to determine the files to watermark 1547 + with [watermarks] and perform the watermarking process.} 1548 + {- Adds a version field with value [$VERSION] to the opam files 1549 + mentioned by {!Pkg.describe}.} 1550 + {- Run the [massage] function of [distrib] in the distribution 1551 + build directory. This can be used to create distribution time 1552 + build artefacts.}}} 1553 + {- Invoke the [exclude_paths] function of [distrib] in the 1554 + distribution build directory to determine the paths to exclude 1555 + from the archive.} 1556 + {- Create a distribution tarball [$BUILD/$NAME-$VERSION.tbz] with the 1557 + file hierarchy in [$BUILD/$NAME-$VERSION.build], 1558 + excluding the paths determined at the preceeding point and delete the 1559 + clone [$BUILD/$NAME-$VERSION.build]. File modifications times in 1560 + the archive are set to [HEAD]'s commit time and file 1561 + permissions are preserved. Any other form of file metadata is 1562 + discarded in the archive.} 1563 + {- Test the distribution. Unpack it in directory [$BUILD/$NAME-$VERSION], 1564 + lint the distribution, build the package in the current 1565 + build environment with its tests, run the tests, on success 1566 + delete [$BUILD/$NAME-$VERSION]. Note that this uses the archive's 1567 + [pkg/pkg.ml] file, which should not be different from the source's 1568 + repository file if the latter was clean when [topkg distrib] was 1569 + invoked.}} 1570 + 1571 + {2:watnote Note on watermarking} 1572 + 1573 + It is right to doubt the beauty and be concerned about the 1574 + watermarking process. However experience shows that alternatives 1575 + like having an OCaml module generated with the appropriate 1576 + information doesn't work well in practice. Version numbers do 1577 + not only show up in OCaml source code. They also appear in 1578 + documentation comments, metadata files, textual data files and 1579 + non-OCaml source files. 1580 + 1581 + Watermarking by default all the non binary files of the 1582 + distribution allows one to write %‌%VERSION%% in any context and 1583 + be sure it is be substituted with the right version number in 1584 + dev package ([`Pin]) and distribution ([`Distrib]) 1585 + {{!Conf.build_context}build contexts} (this occurence was not 1586 + subsituted because a ZERO WIDTH NON-JOINER U+200C was introduced between 1587 + the first two percent characters). 1588 + 1589 + If this scheme poses a problem for certain files or you remain 1590 + unconvinced, simply filter the result of {!files_to_watermark} or 1591 + replace it by the exact files you would like to watermark. *) 1592 + end 1593 + 1594 + (** {1:private Private} *) 1595 + 1596 + (** Private definitions. 1597 + 1598 + {b Warning.} The following definitions are subject to change even 1599 + between minor versions of the library. [Topkg] users {b must not} 1600 + use these definitions to describe their package. *) 1601 + module Private : sig 1602 + 1603 + (** {1:private Private} *) 1604 + 1605 + val disable_main : unit -> unit 1606 + (** [disable_main ()] disables [Topkg]'s main invoked on 1607 + {!Pkg.describe}. Invoke this function in your main function if 1608 + you are not using [Topkg] in a description file but as as a 1609 + library. *) 1610 + 1611 + (** Topkg interprocess communication codec. 1612 + 1613 + Codecs for communication between the [topkg] tool and topkg 1614 + description files. *) 1615 + module Codec : sig 1616 + 1617 + (** {1 Decode errors} *) 1618 + 1619 + (** The type for decode errors. 1620 + {ul 1621 + {- [Corrupted (kind, data)], an error occured while decoding 1622 + [data] for [kind].} 1623 + {- [Version (exp, fnd)], a {{!version}versioned} decoder 1624 + expected version [exp] but found [fnd]}} *) 1625 + type error = Corrupted of (string * string) | Version of int * int 1626 + 1627 + val pp_error : Format.formatter -> error -> unit 1628 + (** [pp_error ppf e] prints an unspecified representation of [e] 1629 + on [ppf]. *) 1630 + 1631 + exception Error of error 1632 + (** Raised on decode errors. *) 1633 + 1634 + (** {1:codecs Codecs} *) 1635 + 1636 + type 'a t 1637 + (** The type for codec for OCaml values of type ['a]. *) 1638 + 1639 + val v : kind:string -> enc:('a -> string) -> dec:(string -> 'a) -> 'a t 1640 + (** [v kind enc dec] is a codec for value identified as [kind] using 1641 + [enc] to encode and [dec] to decode. *) 1642 + 1643 + val kind : 'a t -> string 1644 + (** [kind c] is [c]'s kind. *) 1645 + 1646 + val enc : 'a t -> 'a -> string 1647 + (** [enc c] is [c]'s encoder. *) 1648 + 1649 + val dec : 'a t -> string -> 'a 1650 + (** [dec c] is [c]'s decoder. The decoder @raise Error in case of 1651 + decode error *) 1652 + 1653 + val dec_result : 'a t -> string -> 'a result 1654 + (** [dec c] is like {!dec} but doesn't raise. The exception is 1655 + turned into an error message using {!pp_error}. *) 1656 + 1657 + val with_kind : string -> 'a t -> 'a t 1658 + (** [with_kind k c] is [c] with kind [k]. *) 1659 + 1660 + val write : fpath -> 'a t -> 'a -> unit result 1661 + (** [write f c v] encodes value [v] with [c] to [f]. *) 1662 + 1663 + val read : fpath -> 'a t -> 'a result 1664 + (** [read f c] reads a value with [c] from [f]. *) 1665 + 1666 + (** {1:base Base type codecs} *) 1667 + 1668 + val unit : unit t 1669 + (** [unit] codecs a [()]. *) 1670 + 1671 + val const : 'a -> 'a t 1672 + (** [const v] codecs the constant [v]. *) 1673 + 1674 + val bool : bool t 1675 + (** [bool] codecs booleans. *) 1676 + 1677 + val int : int t 1678 + (** [int] codecs integers. *) 1679 + 1680 + val string : string t 1681 + (** [string] codecs strings. *) 1682 + 1683 + val option : 'a t -> 'a option t 1684 + (** [option el] codecs [el] options. *) 1685 + 1686 + val result : ok:'a t -> error:'b t -> ('a, 'b) r t 1687 + (** [result ~ok ~error] codecs [ok], [error] results. *) 1688 + 1689 + val list : 'a t -> 'a list t 1690 + (** [list el] codecs [el] lists. *) 1691 + 1692 + val pair : 'a t -> 'b t -> ('a * 'b) t 1693 + (** [pair c0 c1] codecs [c0], [c1] pairs. *) 1694 + 1695 + val t3 : 'a t -> 'b t -> 'c t -> ('a * 'b * 'c) t 1696 + (** [t3] is like {!pair} but for triples. *) 1697 + 1698 + val t4 : 'a t -> 'b t -> 'c t -> 'd t -> ('a * 'b * 'c * 'd) t 1699 + (** [t4] is like {!pair} but for quadruples. *) 1700 + 1701 + val t5 : 'a t -> 'b t -> 'c t -> 'd t -> 'e t -> 1702 + ('a * 'b * 'c * 'd * 'e) t 1703 + (** [t5] is like {!pair} but for qintuples. *) 1704 + 1705 + val alt : kind:string -> ('a -> int) -> 'a t array -> 'a t 1706 + (** [alt tag cs] codecs values by tagging them with [tag] and 1707 + using the corresponding codec in [cs]. 1708 + 1709 + @raise Invalid_argument if [Array.length cs > 256]. *) 1710 + 1711 + val version : int -> 'a t -> 'a t 1712 + (** [version num c] versions codec [c] with number [num]. 1713 + On decode a version number mismatch raises an error 1714 + see {!error}. *) 1715 + 1716 + val view : ?kind:string -> ('a -> 'b) * ('b -> 'a) -> 'b t -> 'a t 1717 + (** [view kind t c] views [t] as [c] for codecing. *) 1718 + 1719 + (** {1:topkg Topkg types} *) 1720 + 1721 + val msg : [`Msg of string ] t 1722 + (** [msg] codecs error messages. *) 1723 + 1724 + val result_error_msg : 'a t -> 'a result t 1725 + (** [result_error_msg ok] codecs [ok] or error message results. *) 1726 + 1727 + val fpath : Fpath.t t 1728 + (** [fpath] codecs files paths. *) 1729 + 1730 + val cmd : Cmd.t t 1731 + (** [cmd] codecs command line fragments. *) 1732 + end 1733 + 1734 + (** Package description. *) 1735 + module Pkg : sig 1736 + 1737 + type t 1738 + (** The type for package descriptions. *) 1739 + 1740 + val empty : t 1741 + (** [empty] is an empty package description. *) 1742 + 1743 + val name : t -> string 1744 + (** [name p] is [p]'s name. *) 1745 + 1746 + val delegate : t -> Cmd.t option 1747 + (** [delegate p]is [p]'s delegate. *) 1748 + 1749 + val build_dir : t -> fpath 1750 + (** [build_dir p] is [p]'s build directory. *) 1751 + 1752 + val readmes : t -> fpath list 1753 + (** [readme p] is [p]'s readme files. *) 1754 + 1755 + val change_logs : t -> fpath list 1756 + (** [change_logs p] is [p]'s change logs. *) 1757 + 1758 + val licenses : t -> fpath list 1759 + (** [licenses p] is [p]'s license files. *) 1760 + 1761 + val opam : name:string -> t -> fpath 1762 + (** [opam name p] is [p]'s opam file for opam package [name]. *) 1763 + 1764 + (** {1:distrib Distrib} *) 1765 + 1766 + val distrib_uri : t -> string option 1767 + (** [distrib_uri p] is [p]'s distribution location URI pattern. 1768 + See {!Pkg.distrib}. *) 1769 + 1770 + (** {1:publish Publish} *) 1771 + 1772 + val publish_artefacts : t -> [`Distrib | `Doc | `Alt of string ] list 1773 + (** [publish_artefacts p] is [p]'s distribution publication artefacts. 1774 + See {!Pkg.publish}. *) 1775 + 1776 + (** {1:lints Lints} 1777 + 1778 + {b Note.} In the following [None] values mean that 1779 + the lint is disabled by the package description. *) 1780 + 1781 + val lint_custom : t -> (unit -> R.msg result list) option 1782 + (** [lint_custom p] is [p]'s custom linting function (if any). 1783 + 1784 + {b Note.} Use {!Ipc.lint_custom} to run the function 1785 + from another program. *) 1786 + 1787 + val lint_files : t -> fpath list option 1788 + (** [lint_files p] are [p]'s files to check for existence. *) 1789 + 1790 + val lint_metas : t -> (fpath * bool) list 1791 + (** [lint_metas p] are [p]'s META file to OCamlfind lint. *) 1792 + 1793 + val lint_opams : t -> (fpath * bool * string list option) list 1794 + (** [lint_opams p] are [p]'s opam file opam lint and dependency 1795 + lint. *) 1796 + 1797 + (** {1:codec Codec} *) 1798 + 1799 + val codec : t Codec.t 1800 + (** [codec] is a codec for package descriptions. *) 1801 + end 1802 + 1803 + (** Topkg interprocess communication. *) 1804 + module Ipc : sig 1805 + 1806 + (** {1:ipc Interprocess communication} *) 1807 + 1808 + type 'a t 1809 + (** The type for interpocess communication transfering values of 1810 + type ['a]. *) 1811 + 1812 + val cmd : 'a t -> Cmd.t 1813 + (** [cmd ipc] are the command line arguments provided to the child 1814 + process. *) 1815 + 1816 + val codec : 'a t -> 'a Codec.t 1817 + (** [codec ipc] is the codec used to transfer the value. *) 1818 + 1819 + val answer : 'a t -> fpath 1820 + (** [answer ipc] is the file path from which the value can 1821 + be decoded from. *) 1822 + 1823 + (** {1:req Requests} *) 1824 + 1825 + val pkg : unit -> Pkg.t t 1826 + (** [pkg ()] is an IPC to get the package description. *) 1827 + 1828 + val lint_custom : unit -> R.msg result list option t 1829 + (** [lint_custom ()] is an IPC to run the custom linting. *) 1830 + 1831 + val distrib_prepare : 1832 + dist_build_dir:fpath -> name:string -> version:string -> opam:fpath -> 1833 + opam_adds:string -> fpath list result t 1834 + (** [distrib_prepare dist_build_dir name version opam] is an IPC to 1835 + prepare a distribution in directory [dist_build_dir]. This 1836 + sets the cwd to [dist_build_dir], performs the distribution 1837 + watermarking process with [name] used for [`Name], [version] used 1838 + for [`Version] and [opam] as the default file for opam watermarks. 1839 + It then performs distribution massaging and returns the file paths 1840 + to exclude from the distribution archive. *) 1841 + end 1842 + 1843 + (** opam helpers. *) 1844 + module Opam : sig 1845 + 1846 + (** {1:opam opam} *) 1847 + 1848 + (** opam package file access. 1849 + 1850 + Normally opam metadata access is only needed at distribution 1851 + time and this is handled by {!Topkg_care.Opam.File} using the 1852 + [opam-format] library. 1853 + 1854 + However there is one case where we want to be able to access 1855 + the metadata from [Topkg]: on pin builds where the 1856 + {{!Pkg.watermark}watermarking} process needs to be run to turn 1857 + the repo into a pseudo-distribution. 1858 + 1859 + Since we don't want [Topkg] to have any dependency and that 1860 + [opam] currently doesn't allow to consult the fields of arbitrary 1861 + opam files (see 1862 + {{:https://github.com/ocaml/opam/issues/2446} issue #2446}) we 1863 + assume a pin build has the [topkg] tool installed and call 1864 + to it to get the opam fields for watermarking (if [topkg] is 1865 + unavailable the watermarks are simply undefined). *) 1866 + module File : sig 1867 + 1868 + (** {1:file opam file} *) 1869 + 1870 + type t = (string * string list) list 1871 + (** The type for a simplified model the fields of an opam 1872 + file. See {!Topkg_care.Opam.File}. *) 1873 + 1874 + val codec : t Codec.t 1875 + (** [codec] is a codec for opam file fields. *) 1876 + 1877 + val fields : fpath -> ((string * string list) list) result 1878 + (** [fields file] are the fields of the opam file [file] which 1879 + are obtained by calling the [topkg] topkg executable. *) 1880 + end 1881 + end 1882 + end 1883 + 1884 + (** {1:basics Basics} 1885 + 1886 + {!Topkg} is a packager for distributing OCaml software. Fundamentally 1887 + it only provides a simple and flexible mechanism to describe an opam 1888 + {{:http://opam.ocaml.org/doc/manual/dev-manual.html#sec25}[install] 1889 + file} according to the build configuration which is used to infer one 1890 + corresponding invocation of your build system. 1891 + 1892 + This simple idea brings the following advantages: 1893 + {ol 1894 + {- It frees you from implementing an install procedure in your build 1895 + system: this task is delegated to {{:http://opam.ocaml.org}opam}, 1896 + to the [opam-installer] tool or anything that understands an opam 1897 + install file.} 1898 + {- It doesn't reclaim control over your build system. It only invokes 1899 + it {e once} with a list of targets determined by the package 1900 + {{!section:Pkg.install}install description}.} 1901 + {- It is very flexible, supports a wide range of installation scenarios 1902 + and is expressed in the reasonable language.}} 1903 + 1904 + Beyond this a [Topkg] package description provides information about 1905 + the package's distribution creation and publication procedures. This 1906 + enables swift and correct package distribution management via the [topkg] 1907 + tool, see {!care}. 1908 + 1909 + {ul 1910 + {- {!setup}} 1911 + {- {!build}} 1912 + {- {!descr}} 1913 + {- {!installdescr}} 1914 + {- {!care}} 1915 + {- {!advanced} 1916 + {ul {- {!config_store}} 1917 + {- {!multiopam}}}} 1918 + {- {!menagerie}}} 1919 + 1920 + {1:setup Source repository setup} 1921 + 1922 + The root of you source repository should have the following layout: 1923 + {ul 1924 + {- [pkg/pkg.ml], the package description written with {!Topkg}'s API. 1925 + See {{!descr}package description.}} 1926 + {- [pkg/META], an 1927 + {{:http://projects.camlcity.org/projects/findlib.html}ocamlfind} 1928 + META file describing your package. See {!Pkg.describe} to configure this.} 1929 + {- [_tags], ocamlbuild file with at least a [true : bin_annot] line. 1930 + See {{!cmt}handling cmt and cmti} files for details.} 1931 + {- [opam], the package's opam metadata. See {!build}.} 1932 + {- [README.md], your readme file. See {!Pkg.describe} to configure this.} 1933 + {- [LICENSE.md], your license file. See {!Pkg.describe} to configure this.} 1934 + {- [CHANGES.md], your change log. See {!Pkg.describe} to configure this.}} 1935 + 1936 + {2:carcass_ad Quick setup (advertisement)} 1937 + 1938 + If you start a new library 1939 + {{:http://erratique.ch/software/carcass}[carcass]} can generate 1940 + the structural boilerplate with your personal information and preferences. 1941 + Invoke: 1942 + {v 1943 + carcass body topkg/pkg mypkg 1944 + (cd mypkg && git init && git add . && git commit -m "First commit.") 1945 + opam pin add -kgit mypkg mypkg#master 1946 + v} 1947 + 1948 + and you have a library that is [{opam,ocamlfind}]-able with correct 1949 + version watermarks on releases and opam pins. You are now only a few 1950 + invocations away to release it in the OCaml opam repository, see 1951 + [topkg help release] for doing so; but don't forget to document it and 1952 + make it do something useful. 1953 + 1954 + {1:build opam and package build instructions} 1955 + 1956 + The package needs to build-depend on [topkg] as well as [ocamlfind] 1957 + which is used by the package description file [pkg/pkg.ml] to find the 1958 + [topkg] library; it is likely that you are using [ocamlbuild] too. So 1959 + the depends field of your opam file should at least have: 1960 + 1961 + {v 1962 + depends: [ 1963 + "ocamlfind" {build} 1964 + "ocamlbuild" {build} 1965 + "topkg" {build & >= 0.9.0} ] 1966 + v} 1967 + 1968 + The build instructions of the package are simply an invocation of 1969 + [pkg/pkg.ml] with the [build] command and a specification of the 1970 + build configuration on the command line. In the simplest case, if 1971 + your package has no configuration options, this simply boils 1972 + down to: 1973 + {[ 1974 + # For opam >= 2.0 1975 + build: [[ "ocaml" "pkg/pkg.ml" "build" "--dev-pkg" "%{dev}%" ]] 1976 + 1977 + # For opam < 2.0 1978 + build: [[ "ocaml" "pkg/pkg.ml" "build" "--dev-pkg" "%{pinned}%" ]] 1979 + ]} 1980 + 1981 + The ["--dev-pkg"] configuration key is used to inform the package 1982 + description about the {{!Conf.build_context}build context}. This 1983 + invocation of [pkg/pkg.ml] executes your build system with a set of 1984 + targets determined from the build configuration and generates in the 1985 + root directory of your distribution an opam [install] file that opam 1986 + uses to install and uninstall your package. 1987 + 1988 + This is all you need to specify. Do not put anything in the remove 1989 + field of the opam file. Likewise there is no need to invoke 1990 + [ocamlfind] with your [META] file. Your [META] file should simply be 1991 + installed in the directory of the [lib] field which happens 1992 + automatically by default. 1993 + 1994 + If you described {{!Pkg.tests}tests} then you should specify 1995 + the instructions as follows (unfortunately for opam < 2.0 this involves 1996 + the repetition of the build line): 1997 + {[ 1998 + # For opam >= 2.0 1999 + 2000 + build: 2001 + [[ "ocaml" "pkg/pkg.ml" "build" "--dev-pkg" "%{dev}%" 2002 + "--tests" "%{build-test}%" ]] 2003 + run-test: 2004 + [[ "ocaml" "pkg/pkg.ml" "test" ]] 2005 + 2006 + # For opam < 2.0 2007 + 2008 + build: 2009 + [[ "ocaml" "pkg/pkg.ml" "build" "--dev-pkg" "%{pinned}%" ]] 2010 + 2011 + build-test: 2012 + [[ "ocaml" "pkg/pkg.ml" "build" "--dev-pkg" "%{pinned}%" "--tests" "true" ] 2013 + [ "ocaml" "pkg/pkg.ml" "test" ]] 2014 + ]} 2015 + 2016 + {b Beyond opam.} If you need to support another package system you 2017 + can invoke [pkg/pkg.ml] as above and then manage the installation and 2018 + uninstallation at a given [$DESTDIR] with the generated opam [install] 2019 + file using [opam-installer] tool or any other program that understand 2020 + these files. 2021 + 2022 + {1:descr Package description} 2023 + 2024 + The {!Pkg.describe} function has a daunting number of arguments and 2025 + configuration options. However if you keep things simple and stick to 2026 + the defaults, much of this does not need to be specified. 2027 + 2028 + For example, if we consider the basic {{!setup}setup} mentioned 2029 + above for a library described with an OCamlbuild [src/mylib.mllib] 2030 + file, your package description file [pkg/pkg.ml] simply looks like 2031 + this: 2032 + {[ 2033 + #!/usr/bin/env ocaml 2034 + #use "topfind" 2035 + #require "topkg" 2036 + open Topkg 2037 + 2038 + let () = 2039 + Pkg.describe "mylib" @@ fun c -> 2040 + Ok [ Pkg.mllib "src/mylib.mllib" ] 2041 + ]} 2042 + 2043 + {b Tip.} To allow 2044 + {{:https://github.com/the-lambda-church/merlin}merlin} to function 2045 + correctly in your package description, issue [M-x merlin-use topkg] in 2046 + [emacs] or [:MerlinUse topkg] in [vim]. 2047 + 2048 + This simple description builds and installs the library described by 2049 + the [src/mylib.mllib] file. It works correctly if native code 2050 + compilation or native dynamic linking is not available. It 2051 + automatically installs the package's META file in the directory of the 2052 + [lib] field and your readme, change log and license file in the 2053 + directory of the [doc] field. 2054 + 2055 + Try to test the package build description with [topkg build], this 2056 + builds the package according to the description and build 2057 + configuration and writes a [mylib.install] at the root of the 2058 + distribution that describes the package installation. If everything 2059 + went well, you are now ready to release, see [topkg help release] for 2060 + the procedure. 2061 + 2062 + To debug package descriptions it useful to dry run the build. This 2063 + prevents the package from building and only writes the [mylib.install] file 2064 + determined according to the build configuration. 2065 + {[ 2066 + topkg build -d # Only write the opam install file 2067 + topkg build -d -v # Also print the build configuration 2068 + topkg help troubleshoot # More troubleshooting tips 2069 + ]} 2070 + Note that [topkg build] does nothing more than invoke 2071 + [ocaml "pkg/pkg.ml" build]. If you would like to see the build 2072 + {{!section:Conf.key}configuration 2073 + options} of a package description you should do: 2074 + {v 2075 + ocaml pkg/pkg.ml help 2076 + ./pkg/pkg.ml help # If has exec right 2077 + v} 2078 + 2079 + {1:installdescr Install description} 2080 + 2081 + An opam [install] file is a description of a standard UNIX install. It 2082 + has fields for each of the standard directories [lib], [bin], [man], 2083 + [etc], etc. Each of these fields lists the files to install in the 2084 + corresponding directory (or subdirectories). See the 2085 + {{:http://opam.ocaml.org/doc/manual/dev-manual.html#sec25}install file 2086 + specification} in the opam developer manual for more information. 2087 + 2088 + A topkg install description is just a convenient and compact way to 2089 + describe an opam install file according to the build configuration. In 2090 + turn this also describes what needs to be built which allows topkg to 2091 + call the build system appropriately. 2092 + 2093 + For each opam install field there is a corresponding field function 2094 + that you can use to generate install moves. The documentation of 2095 + {!Pkg.field} and {!Pkg.exec_field} describes how you can use or omit 2096 + their various arguments to simplify the description. Topkg also provides 2097 + a few higher-level convenience functions like {!Pkg.mllib} and 2098 + {!Pkg.clib} which allow to reuse the description work already done 2099 + for OCamlbuild. 2100 + 2101 + In the following we review a few basic install use cases. The 2102 + {{!menagerie}menagerie} provides links to full and more complex examples. 2103 + 2104 + {2:installlib Installing libraries and C stubs} 2105 + 2106 + It is possible to use the {!Pkg.lib} field function and appropriate 2107 + {{!Exts}file extensions} to manually install a library, but this 2108 + quickly becomes tedious. The higher-level {!Pkg.mllib} install function 2109 + brings this to a single line by reading from a OCamlbuild [mllib] file. 2110 + Given a library described in [src/mylib.mllib] file use: 2111 + {[ 2112 + Pkg.mllib "src/mylib.mllib" 2113 + ]} 2114 + This will make all the modules mentioned in the [mllib] file part of 2115 + the API (i.e. install its [cmi] files). You can restrict 2116 + the API by using the [~api] optional argument. In the following, only 2117 + [Mod1] and [Mod2] define the API, the other modules of [mllib] remain hidden 2118 + to the end user (but unfortunately not to the linkers...): 2119 + {[ 2120 + Pkg.mllib ~api:["Mod1"; "Mod2"] "src/myllib.mllib" 2121 + ]} 2122 + 2123 + A shortcut also exists for installing C stubs: {!Pkg.clib}. Simply use 2124 + it on an existing [src/libmystub.clib] file (N.B. OCamlbuild 2125 + mandates that your [clib] file name starts with [lib]): 2126 + {[ 2127 + Pkg.clib "src/libmystub.clib" 2128 + ]} 2129 + This will generate the appropriate install moves in the [lib] and [stublib] 2130 + fields. 2131 + 2132 + {2:installbin Installing binaries} 2133 + 2134 + In opam, binaries can only be installed by using the [bin], [sbin] and 2135 + [libexec] fields. Trying to install a binary in other fields will not 2136 + set its executable bit 2137 + ({{:https://github.com/ocaml/opam/issues/2430}discussion}). 2138 + 2139 + Most of the time one wants to install native binaries if native code 2140 + compilation is available and bytecode ones if it is not. The [auto] 2141 + argument of {!Pkg.exec_field} does exactly this if omited. 2142 + 2143 + So if you have an executable to install in the [bin] field whose [main] 2144 + is in [src/myexec.ml], describe it with: 2145 + {[ 2146 + Pkg.bin "src/myexec" 2147 + ]} 2148 + As with any other field it easy to rename the executable along the 2149 + way with the [dst] argument: 2150 + {[ 2151 + Pkg.bin "src/myexec" ~dst:"my-exec" 2152 + ]} 2153 + Note that using the default value of [auto] also automatically handles 2154 + the extension business for Windows platforms. 2155 + 2156 + {2:installcond Conditional install} 2157 + 2158 + An easy and readable way to handle conditional installs is to use the 2159 + [cond] argument of {{!Pkg.field}field functions}. The following shows 2160 + how to conditionaly build and install a binary depending on the opam 2161 + [cmdliner] package being installed: 2162 + {[ 2163 + let cmdliner = Conf.with_pkg "cmdliner" 2164 + let () = 2165 + Pkg.describe "mypkg" @@ fun c -> 2166 + let cmdliner = Conf.value c cmdliner in 2167 + Ok [ Pkg.bin ~cond:cmdliner "src/myexec" ] 2168 + ]} 2169 + Note that {{!Conf.key}configuration keys} must be created before the 2170 + call to {!Pkg.describe}. Their value can then be accessed with the 2171 + {!Conf.value} from the configuration given to the install move 2172 + function you specify. 2173 + 2174 + For this conditional install the opam build instructions look like this: 2175 + {[ 2176 + depopts: [ "cmdliner" ] 2177 + build: [[ 2178 + "ocaml" "pkg/pkg.ml" "build" 2179 + "--dev-pkg" "%{dev}%" # use "%{pinned}%" for opam < 2.0 2180 + "--with-cmdliner" cmdliner:installed ]] 2181 + ]} 2182 + 2183 + {2:installautoconds Automatic install conditions} 2184 + 2185 + Some conditions related to native code and native code dynamic linking 2186 + happen automatically. For example moves with paths ending with [.cmxs] 2187 + are automatically dropped if {!Conf.OCaml.native_dynlink} is [false] 2188 + in the current build configuration. This behaviour can be disabled by 2189 + using the [force] argument of {{!Pkg.field}field functions}. 2190 + 2191 + {2:installexts Extension sets and platform dependent extensions} 2192 + 2193 + The {{!Pkg.field}field functions} have an optional [exts] argument. If 2194 + present these extensions are appended to the [src] path given to the 2195 + function. The module {!Exts} defines a few predefined extension 2196 + sets. For example a single module library archive implemented in 2197 + [src/mylib.ml] can be declared by: 2198 + {[ 2199 + Pkg.lib ~exts:Exts.module_library "src/mylib" 2200 + ]} 2201 + which is, effectively, a shortcut for: 2202 + {[ 2203 + [ Pkg.lib "src/mylib.mli"; 2204 + Pkg.lib "src/mylib.cmti"; 2205 + Pkg.lib "src/mylib.cmi"; 2206 + Pkg.lib "src/mylib.cmx"; 2207 + Pkg.lib "src/mylib.cma"; 2208 + Pkg.lib "src/mylib.a"; 2209 + Pkg.lib "src/mylib.cmxa"; 2210 + Pkg.lib "src/mylib.cmxs"; ] 2211 + ]} 2212 + 2213 + Extensions sets are also used to support platform independent installs. 2214 + For example to install a static C library archive you should use 2215 + the second invocation, not the first one: 2216 + {[ 2217 + Pkg.lib "src/libmylib.a" (* DON'T do this *) 2218 + Pkg.lib ~exts:Exts.c_library "src/libmylib" (* Do this *) 2219 + ]} 2220 + this ensures that the correct, platform dependent, suffix is used. 2221 + 2222 + {2:installrename Renaming and installing in subdirectories} 2223 + 2224 + By default install moves simply install the file at the root directory 2225 + of the field. Using the [dst] optional argument you can rename the file and/or 2226 + install it to subdirectories. 2227 + {[ 2228 + Pkg.lib "src/b.cmo" ~dst:"hey" (* Install as [hey] file. *) 2229 + Pkg.lib "src/b.cmo" ~dst:"hey/" (* Install in [hey] subdirectory *) 2230 + Pkg.lib "src/b.cmo" ~dst:"hey/ho.cmo" (* Install as [ho.cmo] in [hey]. *) 2231 + ]} 2232 + 2233 + {2:cmt Handling cmt and cmti files} 2234 + 2235 + Since the OCaml tools generate [.cmt] and [.cmti] files only as a side 2236 + effect they are treated specially: they are not built. For 2237 + OCamlbuild you should add this line to your [_tags] file: 2238 + {[ 2239 + true : bin_annot 2240 + ]} 2241 + this will build them as a side effect of other build invocations. In 2242 + the generated opam install file the [cmt] and [cmti] files are 2243 + prefixed by a ? so that if they are not built the install does not 2244 + fail. 2245 + 2246 + {1:care Package care} 2247 + 2248 + Developing and distributing packages implies a lot of mundane but 2249 + nevertheless important tasks. The [topkg] tool, guided by information 2250 + provided by {!Pkg.describe} helps you with these tasks. 2251 + 2252 + For example [topkg lint] makes sure that the package source repository 2253 + or distribution follows a few established (or your) conventions and 2254 + that the META and opam files of the package pass their respective 2255 + linter. [topkg distrib] will create watermarked and reproducible 2256 + distribution archives (see {!Pkg.distrib}) while [topkg publish] and 2257 + [topkg opam] will help publishing them. All this and more is described 2258 + with details in [topkg]'s help system, invoke: 2259 + {v 2260 + topkg help release 2261 + topkg help 2262 + v} 2263 + 2264 + to get an extended introduction and pointers to these features. 2265 + 2266 + {2:doccare Documentation care} 2267 + 2268 + Topkg provides support to make it easier to write and publish your 2269 + package documentation. The [topkg doc -r] command generates and refreshes 2270 + renderings of the package documentation while you work on it. 2271 + 2272 + Documentation publication is always derived from a generated 2273 + distribution archive (the latter doesn't need to be published of 2274 + course). So to push documentation fixes and clarifications simply invoke: 2275 + 2276 + {v 2277 + topkg distrib 2278 + topkg publish doc 2279 + v} 2280 + 2281 + Make sure you include %‌%VERSION%% watermarks in the documentation so 2282 + that readers know exactly which version they are reading. By default 2283 + this will be substituted by a VCS tag description so it will be 2284 + precise even though you may not have properly tagged a new release 2285 + for the package. 2286 + 2287 + {1:advanced Advanced topics} 2288 + 2289 + {2:config_store Storing build configuration information in software} 2290 + 2291 + The following sample setup shows how to store build configuration 2292 + information in build artefacts. 2293 + 2294 + In this example we store the location of the install's [etc] directory 2295 + in the build artefacts. The setup also works seamlessly during 2296 + development if build artefacts are invoked from the root directory of 2297 + the source repository. 2298 + 2299 + We have the following file layout in our source repository: 2300 + {v 2301 + etc/mypkg.conf # Configuration file 2302 + src/mypkg_etc.ml # Module with path to the etc dir 2303 + v} 2304 + 2305 + the contents of [src/mypkg_etc.ml] is simply: 2306 + {[ 2307 + (* This file is overwritten by distribution builds. During development 2308 + it refers to the [etc] directory at the root directory of the 2309 + source repository. *) 2310 + 2311 + let dir = "etc" 2312 + ]} 2313 + the value [Mypkg_etc.dir] is used in the sources to refer to the [etc] 2314 + directory of the install. In the package description file 2315 + [pkg/pkg.ml] we have the following lines: 2316 + {[ 2317 + let (* 1 *) etc_dir = 2318 + let doc = "Use $(docv) as the etc install directory" in 2319 + Conf.(key "etc-dir" fpath ~absent:"etc" ~doc) 2320 + 2321 + let (* 2 *) etc_config c = match Conf.build_context c with 2322 + | `Dev -> Ok () (* Do nothing, the repo src/mypkg_etc.ml will do *) 2323 + | `Pin | `Distrib -> 2324 + let config = strf "let dir = %S" (Conf.value c etc_dir) in 2325 + OS.File.write "src/mypkg_etc.ml" config 2326 + 2327 + let () = 2328 + let build = Pkg.build ~pre:etc_config () in 2329 + Pkg.describe "mypkg" ~build @@ fun c -> 2330 + Ok [ (* 3 *) Pkg.etc "etc/mpypkg.conf"; ... ] 2331 + ]} 2332 + 2333 + In words: 2334 + {ol 2335 + {- We declare a configuration key ["etc-dir"] that holds the location 2336 + of the install [etc] directory.} 2337 + {- We have a pre-build hook that writes the file 2338 + [src/mypkg_etc.ml] with its actual value on [`Pin] and [`Distrib] builds.} 2339 + {- We install the [etc/mypkg.conf] configuration in the install [etc] 2340 + directory.}} 2341 + The opam build instructions for the package are: 2342 + {[ 2343 + build: [[ 2344 + "ocaml" "pkg/pkg.ml" "build" 2345 + "--dev-pkg" "%{dev}%" # use "%{pinned}%" for opam < 2.0 2346 + "--etc-dir" mypkg:etc ]] 2347 + ]} 2348 + 2349 + {2:multiopam Multiple opam packages for a single distribution} 2350 + 2351 + It is not too hard to define multiple opam packages for the same 2352 + distribution. Topkg itself 2353 + {{:https://github.com/dbuenzli/topkg/blob/master/DEVEL.md}uses this 2354 + trick} to manage its dependencies between [topkg] and [topkg-care]. 2355 + 2356 + To achieve this your package description file can simply condition 2357 + the package install description on the package name 2358 + {{!Conf.pkg_name}communicated by the configuration}. In this setup 2359 + you'll likely have one [$PKG.opam] per [$PKG] at the root of your source 2360 + repository, you should declare them in the description too, so that 2361 + they get properly linted and used by the [topkg] tool when appropriate 2362 + (see how the opam file is looked up according to the package name 2363 + in {!Pkg.describe}). Here is a blueprint: 2364 + {[ 2365 + let () = 2366 + let opams = 2367 + let install = false in 2368 + [ Pkg.opam_file ~install "mypkg-main.opam"; 2369 + Pkg.opam_file ~install "mypkg-snd.opam"; ] 2370 + in 2371 + Pkg.describe ~opams "mypkg-main" @@ fun c -> 2372 + match Conf.pkg_name c with 2373 + | "mypkg-main" -> 2374 + Ok [ Pkg.lib "mypkg-main.opam" ~dst:"opam"; 2375 + (* mypkg-main install *) ] 2376 + | "mypkg-snd" -> 2377 + Ok [ Pkg.lib "mypkg-snd.opam" ~dst:"opam"; 2378 + (* mypkg-snd install *) ] 2379 + | other -> 2380 + R.error_msgf "unknown package name: %s" other 2381 + ]} 2382 + The build instructions of these opam files need to give the name of 2383 + the package to the build invocation so that the right install description 2384 + can be selected: 2385 + {[ 2386 + build: [[ 2387 + "ocaml" "pkg/pkg.ml" "build" 2388 + "--pkg-name" name 2389 + "--dev-pkg" "%{dev}%" # use "%{pinned}%" for opam < 2.0 2390 + ]] 2391 + ]} 2392 + 2393 + In general you will use the default, main, package name and its opam file to 2394 + create and publish the distribution archive file and all packages 2395 + will use the same distribution; the opam packages will only differ in their 2396 + opam file. Releasing the set of packages then becomes: 2397 + {[ 2398 + # Release the distribution and base package (use topkg bistro 2399 + # for doing this via a single invocation) 2400 + topkg distrib 2401 + topkg publish 2402 + topkg opam pkg 2403 + topkg opam submit 2404 + 2405 + # Create and release the other opam package based on the ditrib 2406 + topkg opam pkg --pkg-name mypkg-snd 2407 + topkg opam submit --pkg-name mypkg-snd 2408 + ]} 2409 + 2410 + See [topkg help release] for more information about releasing 2411 + packages with [topkg]. 2412 + 2413 + {1:menagerie Menagerie of [pkg.ml] files} 2414 + 2415 + This is a menagerie of [pkg.ml] with a description of what they 2416 + showcase. The examples are approximatively sorted by increasing 2417 + complexity. 2418 + 2419 + In all these packages the readme, change log and license file are 2420 + automatically installed in the directory of the [doc] field and the 2421 + ocamlfind META file and opam file of the package are automatically installed 2422 + in the directory of the [lib] field. 2423 + 2424 + {{:https://github.com/dbuenzli/hmap/blob/master/pkg/pkg.ml}Hmap} 2425 + ({{:https://github.com/dbuenzli/hmap/blob/master/pkg/META}META}, 2426 + {{:https://github.com/dbuenzli/hmap/blob/master/opam}[opam]}) 2427 + {ul {- Single module library archive [hmap]. The simplest you can get.}} 2428 + 2429 + {{:https://github.com/dbuenzli/fpath/blob/master/pkg/pkg.ml}Fpath} 2430 + ({{:https://github.com/dbuenzli/fpath/blob/master/pkg/META}META}, 2431 + {{:https://github.com/dbuenzli/fpath/blob/master/opam}[opam]}) 2432 + {ul 2433 + {- Single module library archive [fpath].} 2434 + {- Private library archive [fpath_top] for toplevel support.}} 2435 + 2436 + {{:https://github.com/dbuenzli/astring/blob/master/pkg/pkg.ml}Astring} 2437 + ({{:https://github.com/dbuenzli/astring/blob/master/pkg/META}META}, 2438 + {{:https://github.com/dbuenzli/astring/blob/master/opam}[opam]}) 2439 + {ul 2440 + {- Library archive [astring] namespaced by one module.} 2441 + {- Private library archive for toplevel support ([astring_top]).} 2442 + {- Installation of sample code in the [doc/] directory.}} 2443 + 2444 + {{:https://github.com/dbuenzli/fmt/blob/master/pkg/pkg.ml}Fmt} 2445 + ({{:https://github.com/dbuenzli/fmt/blob/master/pkg/META}META}, 2446 + {{:https://github.com/dbuenzli/fmt/blob/master/opam}[opam]}) 2447 + {ul 2448 + {- Single module library archive ([fmt]).} 2449 + {- Private library archive [fmt_top] for toplevel support.} 2450 + {- Single module library archive [fmt_tty] 2451 + conditional on the presence of the opam [base-unix] package.} 2452 + {- Single module library archive [fmt_cli] conditional 2453 + on the presence of the opam [cmdliner] package.}} 2454 + 2455 + {{:https://github.com/dbuenzli/ptime/blob/master/pkg/pkg.ml}Ptime} 2456 + ({{:https://github.com/dbuenzli/ptime/blob/master/pkg/META}META}, 2457 + {{:https://github.com/dbuenzli/ptime/blob/master/opam}opam}) 2458 + {ul 2459 + {- Single module library archive [ptime].} 2460 + {- Private library archive [ptime_top] for toplevel support.} 2461 + {- Library archive [ptime_clock] targeting regular OSes using 2462 + C stubs and installed in the [os/] subdirectory of [lib] field along 2463 + with a private library archive [ptime_clock_top] for toplevel support.} 2464 + {- Library archive [ptime_clock] targeting 2465 + JavaScript installed in the [jsoo/] subdirectory of [lib] field conditional 2466 + on the presence of the opam [js_of_ocaml] package.} 2467 + {- Installation of sample code in the [doc/] directory.}} 2468 + 2469 + {{:https://github.com/dbuenzli/uucp/blob/master/pkg/pkg.ml}Uucp} 2470 + ({{:https://github.com/dbuenzli/uucp/blob/master/pkg/META}META}, 2471 + {{:https://github.com/dbuenzli/uucp/blob/master/opam}opam}) 2472 + {ul 2473 + {- Library archive [uucp] namespaced by a single module.} 2474 + {- Custom watermark for substituting the supported Unicode version.} 2475 + {- Generation of distribution time build artefacts via a 2476 + {{!Pkg.distrib}massage} hook which invokes an 2477 + {{:https://github.com/dbuenzli/uucp/blob/master/pkg/build_support.ml} 2478 + OCaml script} that downloads the UCD XML file 2479 + and extracts compact and efficient representation of it as OCaml 2480 + source data structures it writes in the [src/] directory.} 2481 + {- Adds the [support/] path to the {{!Pkg.distrib}paths to exclude} 2482 + from the distribution.} 2483 + {- Installation of development information and sample code in the 2484 + [doc/] directory.}} 2485 + 2486 + {{:https://github.com/dbuenzli/carcass/blob/master/pkg/pkg.ml}Carcass} 2487 + ({{:https://github.com/dbuenzli/carcass/blob/master/pkg/META}META}, 2488 + {{:https://github.com/dbuenzli/carcass/blob/master/opam}opam}) 2489 + {ul 2490 + {- Library archive [carcass] namespaced by a single module.} 2491 + {- Single module library archive [carcass_cli].} 2492 + {- Executable [carcass].} 2493 + {- {{!config_store}Stores} install [etc] location in the software artefacts 2494 + with a {{!Pkg.build}pre-build hook}.} 2495 + {- Adjusts the {{!Pkg.distrib}files to watermark} to ignore the files in the 2496 + [etc] file hierarchy of the distribution.} 2497 + {- Installs the [etc] hierarchy of the distribution in the [etc] field 2498 + directly from the source tree (i.e. the files are not built).}} 2499 + 2500 + {{:https://github.com/dbuenzli/topkg/blob/master/pkg/pkg.ml}Topkg} 2501 + ({{:https://github.com/dbuenzli/topkg/blob/master/pkg/META}META}, 2502 + {{:https://github.com/dbuenzli/topkg/blob/master/topkg.opam}topkg.opam}, 2503 + {{:https://github.com/dbuenzli/topkg/blob/master/topkg-care.opam} 2504 + topkg-care.opam}) 2505 + {ul 2506 + {- Ignore the funky source bootstraping ([#mod_use] directives), that's 2507 + only for using [topkg] on itself.} 2508 + {- Makes {{!multiopam}multiple opam packages} for the same 2509 + distribution.} 2510 + {- Multiple opam file declaration and dependency linting exclusions: 2511 + the build system mentions packages that are not relevant to 2512 + all opam files. Manual, per package, opam file install in the [lib] 2513 + field.} 2514 + {- Manual [META] install, a single one is installed for all opam packages 2515 + by the base package [topkg]. This leverages the [if_exists] ocamlfind 2516 + mecanism.} 2517 + {- The [topkg] package installs the library archive [topkg] namespaced 2518 + by a single module.} 2519 + {- The [topkg-care] package installs the library archive [topkg-care] 2520 + namespaced by a single module and the binaries [topkg] and 2521 + [toy-github-topkg-delegate]}} 2522 + *)
+20
vendor/opam/topkg/src/topkg.mllib
··· 1 + Topkg 2 + Topkg_build 3 + Topkg_cmd 4 + Topkg_codec 5 + Topkg_conf 6 + Topkg_distrib 7 + Topkg_fexts 8 + Topkg_fpath 9 + Topkg_install 10 + Topkg_ipc 11 + Topkg_log 12 + Topkg_main 13 + Topkg_opam 14 + Topkg_os 15 + Topkg_pkg 16 + Topkg_publish 17 + Topkg_result 18 + Topkg_string 19 + Topkg_test 20 + Topkg_vcs
+86
vendor/opam/topkg/src/topkg_build.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + open Topkg_result 7 + 8 + let ocamlbuild_flags = 9 + Topkg_cmd.(empty % "-use-ocamlfind" % "-classic-display") 10 + 11 + let build_cmd c os = 12 + let ocamlbuild = Topkg_conf.tool "ocamlbuild" os in 13 + let build_dir = Topkg_conf.build_dir c in 14 + let toolchain = 15 + match Topkg_conf.toolchain c with 16 + | Some toolchain -> Topkg_cmd.(v "-toolchain" % toolchain) 17 + | _ -> Topkg_cmd.empty 18 + in 19 + let debug = Topkg_cmd.(on (Topkg_conf.debug c) (v "-tag" % "debug")) in 20 + let profile = Topkg_cmd.(on (Topkg_conf.profile c) (v "-tag" % "profile")) in 21 + let jobs = 22 + let n = Topkg_conf.jobs c in 23 + Topkg_log.info (fun m -> m "using %d jobs" n); 24 + Topkg_cmd.(on (n != 1) (v "-j" % string_of_int n)) in 25 + Topkg_cmd.(ocamlbuild %% ocamlbuild_flags %% toolchain %% jobs %% 26 + debug %% profile % "-build-dir" % build_dir) 27 + 28 + let clean_cmd os ~build_dir = 29 + let ocamlbuild = Topkg_conf.tool "ocamlbuild" os in 30 + Topkg_cmd.(ocamlbuild %% ocamlbuild_flags % 31 + "-build-dir" % build_dir % "-clean") 32 + 33 + type t = 34 + { prepare_on_pin : bool; 35 + dir : Topkg_fpath.t; 36 + pre : Topkg_conf.t -> unit result; 37 + cmd : Topkg_conf.t -> Topkg_conf.os -> Topkg_fpath.t list -> unit result; 38 + post : Topkg_conf.t -> unit result; 39 + clean : Topkg_conf.os -> build_dir:Topkg_fpath.t -> unit result; } 40 + 41 + let with_dir b dir = { b with dir } 42 + 43 + let nop = fun _ -> Ok () 44 + 45 + let cmd c os files = 46 + let targets = String.concat "\n" files in 47 + Topkg_os.File.write "pkg.itarget" targets >>= fun () -> 48 + Topkg_os.Cmd.run @@ Topkg_cmd.(build_cmd c os % "pkg.otarget") 49 + 50 + let clean os ~build_dir = 51 + Topkg_os.Cmd.run @@ clean_cmd os ~build_dir 52 + 53 + let v 54 + ?(prepare_on_pin = true) ?(dir = "_build") ?(pre = nop) ?(cmd = cmd) 55 + ?(post = nop) ?(clean = clean) () = 56 + { prepare_on_pin; dir; pre; cmd; post; clean; } 57 + 58 + let prepare_on_pin b = b.prepare_on_pin 59 + let dir b = b.dir 60 + let pre b = b.pre 61 + let cmd b = b.cmd 62 + let post b = b.post 63 + let clean b = b.clean 64 + let codec = 65 + let prepare_on_pin = Topkg_codec.(with_kind "prepare_on_pin" @@ bool) in 66 + let dir = Topkg_codec.(with_kind "dir" @@ string) in 67 + let fields = 68 + let stub _ = invalid_arg "not executable outside package definition" in 69 + (fun b -> b.prepare_on_pin, b.dir), 70 + (fun (prepare_on_pin, dir) -> 71 + { prepare_on_pin; dir; pre = stub; cmd = stub; post = stub; 72 + clean = stub }) 73 + in 74 + Topkg_codec.version 0 @@ 75 + Topkg_codec.(view ~kind:"build" fields (pair prepare_on_pin dir)) 76 + 77 + let ocb_tag c key tag = 78 + let tag = Topkg_string.strf "%s(%a)" tag (Topkg_conf.pp_value c) key in 79 + Topkg_cmd.(v "-tag" % tag) 80 + 81 + let ocb_bool_tag c key tag = 82 + Topkg_cmd.(on (Topkg_conf.value c key) @@ v "-tag" % tag) 83 + 84 + let ocb_bool_tags c tags = 85 + let f (key, tag) = Topkg_cmd.(%%) (ocb_bool_tag c key tag) in 86 + List.fold_right f tags Topkg_cmd.empty
+42
vendor/opam/topkg/src/topkg_build.mli
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** Package build description. *) 7 + 8 + open Topkg_result 9 + 10 + (** {1 Build} *) 11 + 12 + type t 13 + 14 + val build_cmd : Topkg_conf.t -> Topkg_conf.os -> Topkg_cmd.t 15 + val clean_cmd : Topkg_conf.os -> build_dir:Topkg_fpath.t -> Topkg_cmd.t 16 + 17 + val v : 18 + ?prepare_on_pin:bool -> 19 + ?dir:Topkg_fpath.t -> 20 + ?pre:(Topkg_conf.t -> unit result) -> 21 + ?cmd:(Topkg_conf.t -> Topkg_conf.os -> Topkg_fpath.t list -> unit result) -> 22 + ?post:(Topkg_conf.t -> unit result) -> 23 + ?clean:(Topkg_conf.os -> build_dir:Topkg_fpath.t -> unit result) -> 24 + unit -> t 25 + 26 + val with_dir : t -> Topkg_fpath.t -> t 27 + 28 + val prepare_on_pin : t -> bool 29 + val dir : t -> Topkg_fpath.t 30 + val pre : t -> (Topkg_conf.t -> unit result) 31 + val cmd : 32 + t -> (Topkg_conf.t -> Topkg_conf.os -> Topkg_fpath.t list -> unit result) 33 + 34 + val post : t -> (Topkg_conf.t -> unit result) 35 + val clean : t -> (Topkg_conf.os -> build_dir:Topkg_fpath.t -> unit result) 36 + 37 + val codec : t Topkg_codec.t 38 + 39 + val ocb_tag : Topkg_conf.t -> 'a Topkg_conf.key -> string -> Topkg_cmd.t 40 + val ocb_bool_tag : Topkg_conf.t -> bool Topkg_conf.key -> string -> Topkg_cmd.t 41 + val ocb_bool_tags : 42 + Topkg_conf.t -> (bool Topkg_conf.key * string) list -> Topkg_cmd.t
+41
vendor/opam/topkg/src/topkg_cmd.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (* Command line fragments *) 7 + 8 + type t = string list 9 + 10 + let empty = [] 11 + let is_empty = function [] -> true | _ -> false 12 + let v a = [a] 13 + let ( % ) l a = a :: l 14 + let ( %% ) l0 l1 = List.rev_append (List.rev l1) l0 15 + let add_arg l a = l % a 16 + let add_args l a = l %% a 17 + let on bool l = if bool then l else [] 18 + let p f = f 19 + 20 + (* Predicates and comparison *) 21 + 22 + let equal l l' = l = l' 23 + let compare l l' = compare l l' 24 + 25 + (* Conversions and pretty printing *) 26 + 27 + let to_rev_list line = line 28 + let to_list line = List.rev line 29 + let of_list ?slip line = match slip with 30 + | None -> List.rev line 31 + | Some slip -> List.fold_left (fun acc v -> v :: slip :: acc) [] line 32 + 33 + let dump ppf cmd = 34 + let pp_elt ppf s = Format.fprintf ppf "%s" (Filename.quote s) in 35 + let rec loop = function 36 + | [] -> () 37 + | v :: vs -> 38 + if vs = [] then pp_elt ppf v else 39 + (Format.fprintf ppf "%a@ " pp_elt v; loop vs) 40 + in 41 + Format.fprintf ppf "@[<1>["; loop (List.rev cmd); Format.fprintf ppf "]@]"
+30
vendor/opam/topkg/src/topkg_cmd.mli
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** Command lines 7 + 8 + See {!Topkg.Cmd} for documentation. *) 9 + 10 + (** {1 Command lines} *) 11 + 12 + type t 13 + 14 + val v : string -> t 15 + val empty : t 16 + val is_empty : t -> bool 17 + val ( % ) : t -> string -> t 18 + val ( %% ) : t -> t -> t 19 + val add_arg : t -> string -> t 20 + val add_args : t -> t -> t 21 + val on : bool -> t -> t 22 + val p : Topkg_fpath.t -> string 23 + 24 + val equal : t -> t -> bool 25 + val compare : t -> t -> int 26 + 27 + val to_rev_list : t -> string list 28 + val to_list : t -> string list 29 + val of_list : ?slip:string -> string list -> t 30 + val dump : Format.formatter -> t -> unit
+251
vendor/opam/topkg/src/topkg_codec.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + open Topkg_result 7 + 8 + (* Decode errors *) 9 + 10 + type error = Corrupted of (string * string) | Version of int * int 11 + 12 + let pp_error ppf = function 13 + | Corrupted (kind, v) -> 14 + Format.fprintf ppf "corrupted %s in %S" kind v 15 + | Version (exp, fnd) -> 16 + Format.fprintf ppf "version mismatch, expected %d found %d" exp fnd 17 + 18 + exception Error of error 19 + 20 + let err ~kind v = raise (Error (Corrupted (kind, v))) 21 + let err_version ~exp ~fnd = raise (Error (Version (exp, fnd))) 22 + 23 + (* Codecs *) 24 + 25 + type 'a t = 26 + { kind : string; 27 + enc : 'a -> string; 28 + dec : string -> 'a; } 29 + 30 + let v ~kind ~enc ~dec = { kind; enc; dec } 31 + let kind c = c.kind 32 + let enc c = c.enc 33 + let dec c = c.dec 34 + let with_kind kind c = { c with kind } 35 + 36 + let dec_result c s = try Ok (dec c s) with 37 + | Error err -> 38 + R.error_msgf "Decode %s: %a Input data: %S" (kind c) pp_error err s 39 + 40 + let write file c v = 41 + Topkg_os.File.write file (enc c v) 42 + |> R.reword_error_msg ~replace:true 43 + (fun err -> R.msgf "Encode %s to %s: %s" (kind c) file err) 44 + 45 + let read file c = 46 + Topkg_os.File.read file >>= fun s -> 47 + try Ok (dec c s) with 48 + | Error e -> R.error_msgf "Decode %s from %s: %a" (kind c) file pp_error e 49 + 50 + (* Base type codecs *) 51 + 52 + let tail s = Topkg_string.with_index_range ~first:1 s 53 + 54 + let unit = 55 + let kind = "unit" in 56 + let enc = function () -> "\x00" in 57 + let dec = function "\x00" -> () | s -> err ~kind s in 58 + v ~kind ~enc ~dec 59 + 60 + let const c = 61 + let kind = "const" in 62 + let enc = function _ -> "" in 63 + let dec = function "" -> c | s -> err ~kind s in 64 + v ~kind ~enc ~dec 65 + 66 + let bool = 67 + let kind = "bool" in 68 + let enc = function false -> "\x00" | true -> "\x01" in 69 + let dec = function "\x00" -> false | "\x01" -> true | s -> err ~kind s in 70 + v ~kind ~enc ~dec 71 + 72 + let int = 73 + let kind = "int" in 74 + let enc = string_of_int (* will do for now *) in 75 + let dec s = try int_of_string s with Failure _ -> err ~kind s in 76 + v ~kind ~enc ~dec 77 + 78 + let string = 79 + let kind = "string" in 80 + let enc s = s in 81 + let dec s = s in 82 + v ~kind ~enc ~dec 83 + 84 + let option some = 85 + let kind = Printf.sprintf "(%s) option" (kind some) in 86 + let enc = function None -> "\x00" | Some v -> "\x01" ^ (enc some v) in 87 + let dec s = match Topkg_string.head s with 88 + | Some '\x00' -> None 89 + | Some '\x01' -> Some (dec some (tail s)) 90 + | _ -> err ~kind s 91 + in 92 + v ~kind ~enc ~dec 93 + 94 + let result ~ok ~error = 95 + let kind = Printf.sprintf "(%s, %s) result" (kind ok) (kind error) in 96 + let enc = function 97 + | Ok v -> "\x00" ^ (enc ok v) 98 + | Error e -> "\x01" ^ (enc error e) 99 + in 100 + let dec s = match Topkg_string.head s with 101 + | Some '\x00' -> Ok (dec ok (tail s)) 102 + | Some '\x01' -> Error (dec error (tail s)) 103 + | _ -> err ~kind s 104 + in 105 + v ~kind ~enc ~dec 106 + 107 + let list el = 108 + let kind = Printf.sprintf "(%s) list" (kind el) in 109 + let enc vs = 110 + let b = Buffer.create 255 in 111 + let rec loop = function 112 + | [] -> Buffer.add_char b '\x00' 113 + | v :: vs -> 114 + let venc = (enc el) v in 115 + let venc_len = String.length venc in 116 + Buffer.add_char b '\x01'; 117 + Buffer.add_string b (string_of_int venc_len) (* will do for now *); 118 + Buffer.add_char b '\x01'; 119 + Buffer.add_string b venc; 120 + loop vs 121 + in 122 + loop vs; Buffer.contents b 123 + in 124 + let dec s = 125 + let rec loop acc s = match Topkg_string.head s with 126 + | Some '\x00' -> acc 127 + | Some '\x01' -> 128 + begin match Topkg_string.find_byte ~start:1 '\x01' s with 129 + | None -> err ~kind s 130 + | Some one -> 131 + try 132 + let last = one - 1 in 133 + let len = Topkg_string.with_index_range ~first:1 ~last s in 134 + let len = int_of_string len in 135 + let first = one + 1 in 136 + let last = first + len - 1 in 137 + let venc = Topkg_string.with_index_range ~first ~last s in 138 + let rest = Topkg_string.with_index_range ~first:(last + 1) s in 139 + loop ((dec el venc) :: acc) rest 140 + with Failure _ (* of int_of_string *) -> err ~kind s 141 + end 142 + | _ -> err ~kind s 143 + in 144 + List.rev (loop [] s) 145 + in 146 + v ~kind ~enc ~dec 147 + 148 + let seq = list string 149 + 150 + let pair c0 c1 = 151 + let kind = Printf.sprintf "%s * %s" (kind c0) (kind c1) in 152 + let enc (v0, v1) = enc seq [enc c0 v0; enc c1 v1] in 153 + let dec s = match dec seq s with 154 + | [lenc; renc] -> (dec c0 lenc), (dec c1 renc) 155 + | _ -> err ~kind s 156 + in 157 + v ~kind ~enc ~dec 158 + 159 + let t2 = pair 160 + 161 + let t3 c0 c1 c2 = 162 + let kind = Printf.sprintf "%s * %s * %s" (kind c0) (kind c1) (kind c2) in 163 + let seq = list string in 164 + let enc (v0, v1, v2) = enc seq [enc c0 v0; enc c1 v1; enc c2 v2] in 165 + let dec s = match (dec seq) s with 166 + | [v0; v1; v2] -> (dec c0 v0), (dec c1 v1), (dec c2 v2) 167 + | _ -> err ~kind s 168 + in 169 + v ~kind ~enc ~dec 170 + 171 + let t4 c0 c1 c2 c3 = 172 + let kind = 173 + Printf.sprintf "%s * %s * %s * %s" (kind c0) (kind c1) (kind c2) (kind c3) 174 + in 175 + let seq = list string in 176 + let enc (v0, v1, v2, v3) = 177 + enc seq [enc c0 v0; enc c1 v1; enc c2 v2; enc c3 v3] 178 + in 179 + let dec s = match (dec seq) s with 180 + | [v0; v1; v2; v3] -> (dec c0 v0), (dec c1 v1), (dec c2 v2), (dec c3 v3) 181 + | _ -> err ~kind s 182 + in 183 + v ~kind ~enc ~dec 184 + 185 + let t5 c0 c1 c2 c3 c4 = 186 + let kind = 187 + Printf.sprintf "%s * %s * %s * %s * %s" 188 + (kind c0) (kind c1) (kind c2) (kind c3) (kind c4) 189 + in 190 + let seq = list string in 191 + let enc (v0, v1, v2, v3, v4) = 192 + enc seq [enc c0 v0; enc c1 v1; enc c2 v2; enc c3 v3; enc c4 v4] 193 + in 194 + let dec s = match (dec seq) s with 195 + | [v0; v1; v2; v3; v4] -> 196 + (dec c0 v0), (dec c1 v1), (dec c2 v2), (dec c3 v3), (dec c4 v4) 197 + | _ -> err ~kind s 198 + in 199 + v ~kind ~enc ~dec 200 + 201 + let alt ~kind tag cs = 202 + let l = Array.length cs in 203 + if l > 256 then invalid_arg @@ Topkg_string.strf "too many codecs (%d)" l; 204 + let enc v = 205 + let tag = tag v in 206 + Printf.sprintf "%c%s" (Char.chr tag) (enc cs.(tag) v) 207 + in 208 + let dec s = match Topkg_string.head s with 209 + | None -> err ~kind s 210 + | Some tag -> 211 + let tag = Char.code tag in 212 + if tag < Array.length cs then dec cs.(tag) (tail s) else 213 + err ~kind s 214 + in 215 + v ~kind ~enc ~dec 216 + 217 + let version version = 218 + let enc_version = string_of_int version in 219 + fun c -> 220 + let kind = Printf.sprintf "(%s) v%s" (kind c) enc_version in 221 + let enc v = String.concat "\x00" [enc_version; enc c v] in 222 + let dec s = match Topkg_string.cut ~sep:'\x00' s with 223 + | None -> err ~kind s 224 + | Some (fnd_version, s) -> 225 + try 226 + let fnd = int_of_string fnd_version in 227 + if fnd <> version then err_version ~exp:version ~fnd else 228 + dec c s 229 + with 230 + | Failure _ (* of int_of_string *) -> err ~kind s 231 + in 232 + v ~kind ~enc ~dec 233 + 234 + let view ?kind:k (inj, proj) c = 235 + let kind = match k with None -> kind c | Some k -> k in 236 + let enc v = enc c (inj v) in 237 + let dec s = proj (dec c s) in 238 + v ~kind ~enc ~dec 239 + 240 + let msg = 241 + let msg = (fun (`Msg m) -> m), (fun m -> `Msg m) in 242 + view msg string 243 + 244 + let result_error_msg ok = result ~ok ~error:msg 245 + let fpath = string 246 + let cmd = 247 + let cmd = 248 + (fun cmd -> Topkg_cmd.to_list cmd), 249 + (fun l -> Topkg_cmd.of_list l) 250 + in 251 + view cmd (list string)
+51
vendor/opam/topkg/src/topkg_codec.mli
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** Topkg interprocess communication codec. 7 + 8 + See {!Topkg.Private.Codec} for documentation. *) 9 + 10 + (** {1 Codec} *) 11 + 12 + open Topkg_result 13 + 14 + type error = Corrupted of (string * string) | Version of int * int 15 + val pp_error : Format.formatter -> error -> unit 16 + exception Error of error 17 + val err : kind:string -> string -> 'a 18 + 19 + type 'a t 20 + 21 + val v : kind:string -> enc:('a -> string) -> dec:(string -> 'a) -> 'a t 22 + val kind : 'a t -> string 23 + val enc : 'a t -> 'a -> string 24 + val dec : 'a t -> string -> 'a 25 + val dec_result : 'a t -> string -> 'a result 26 + val with_kind : string -> 'a t -> 'a t 27 + val write : Topkg_fpath.t -> 'a t -> 'a -> unit result 28 + val read : Topkg_fpath.t -> 'a t -> 'a result 29 + 30 + val unit : unit t 31 + val const : 'a -> 'a t 32 + val bool : bool t 33 + val int : int t 34 + val string : string t 35 + val option : 'a t -> 'a option t 36 + val result : ok:'a t -> error:'b t -> ('a, 'b) r t 37 + val list : 'a t -> 'a list t 38 + val pair : 'a t -> 'b t -> ('a * 'b) t 39 + val t2 : 'a t -> 'b t -> ('a * 'b) t 40 + val t3 : 'a t -> 'b t -> 'c t -> ('a * 'b * 'c) t 41 + val t4 : 'a t -> 'b t -> 'c t -> 'd t -> ('a * 'b * 'c * 'd) t 42 + val t5 : 'a t -> 'b t -> 'c t -> 'd t -> 'e t -> ('a * 'b * 'c * 'd * 'e) t 43 + val alt : kind:string -> ('a -> int) -> 'a t array -> 'a t 44 + val version : int -> 'a t -> 'a t 45 + val view : ?kind:string -> ('a -> 'b) * ('b -> 'a) -> 'b t -> 'a t 46 + 47 + val msg : [`Msg of string ] t 48 + val result_error_msg : 'a t -> 'a result t 49 + 50 + val fpath : Topkg_fpath.t t 51 + val cmd : Topkg_cmd.t t
+632
vendor/opam/topkg/src/topkg_conf.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + open Topkg_result 7 + 8 + (* Configuration value converters *) 9 + 10 + type 'a conv = 11 + { parse : (string -> 'a result); 12 + print : (Format.formatter -> 'a -> unit); 13 + docv : string; } 14 + 15 + let conv ?(docv = "VALUE") parse print = { parse; print; docv } 16 + let conv_parser conv = conv.parse 17 + let conv_printer conv = conv.print 18 + let conv_docv conv = conv.docv 19 + let conv_with_docv conv ~docv = { conv with docv } 20 + 21 + let bool = 22 + let parse s = try Ok (bool_of_string s) with 23 + | Invalid_argument _ -> R.error_msgf "%S: Can't parse boolean value" s 24 + in 25 + conv ~docv:"BOOL" parse Format.pp_print_bool 26 + 27 + let int = 28 + let parse s = try Ok (int_of_string s) with 29 + | Failure _ -> R.error_msgf "%S: Can't parse integer value" s 30 + in 31 + conv ~docv:"INT" parse Format.pp_print_int 32 + 33 + let string = conv ~docv:"STRING" (fun s -> Ok s) Format.pp_print_string 34 + let fpath = conv_with_docv string ~docv:"PATH" 35 + 36 + let some ?(none = "") conv = 37 + let parse s = match conv.parse s with 38 + | Ok v -> Ok (Some v) 39 + | Error _ as e -> e 40 + in 41 + let print ppf = function 42 + | None -> Format.pp_print_string ppf none 43 + | Some v -> conv.print ppf v 44 + in 45 + { conv with parse; print } 46 + 47 + (* Universal type, see http://mlton.org/UniversalType *) 48 + 49 + type univ = exn 50 + let univ (type s) () = 51 + let module M = struct exception E of s option end in 52 + (fun x -> M.E (Some x)), (function M.E x -> x | _ -> None) 53 + 54 + (* Configuration keys *) 55 + 56 + let key_id = 57 + let count = ref (-1) in 58 + fun () -> incr count; !count 59 + 60 + type 'a absent = Value of 'a | Discover of (unit -> 'a result) 61 + type 'a key = 62 + { id : int; (* unique id for the key. *) 63 + name : string; (* key name. *) 64 + conv : 'a conv; (* key value converter. *) 65 + absent : 'a absent; (* value if unspecified. *) 66 + env : string option; (* environment variable to override [absent]. *) 67 + to_univ : 'a -> univ; (* convert to universal value. *) 68 + of_univ : univ -> 'a option; (* convert from universal value. *) 69 + doc : string; } (* documentation for the key. *) 70 + 71 + module Key = struct 72 + type t = V : 'a key -> t 73 + let compare (V k0) (V k1) = (compare : int -> int -> int) k0.id k1.id 74 + end 75 + 76 + module Kset = Set.Make (Key) 77 + module Kmap = Map.Make (Key) 78 + 79 + let key_index = ref Kset.empty 80 + 81 + let cli_opts_of_key_index () = 82 + let add_key (Key.V k as key) acc = ("--" ^ k.name, key) :: acc in 83 + Kset.fold add_key !key_index [] 84 + 85 + let _key ?docv ?(doc = "Undocumented") ?env name conv absent = 86 + let id = key_id () in 87 + let to_univ, of_univ = univ () in 88 + let conv = match docv with 89 + | None -> conv 90 + | Some docv -> conv_with_docv conv ~docv 91 + in 92 + let key = { id; name; conv; absent; env; to_univ; of_univ; doc } in 93 + key_index := Kset.add (Key.V key) !key_index; 94 + key 95 + 96 + let key ?docv ?doc ?env name conv ~absent = 97 + _key ?docv ?doc ?env name conv (Value absent) 98 + 99 + let discovered_key ?docv ?doc ?env name conv ~absent = 100 + _key ?docv ?doc ?env name conv (Discover absent) 101 + 102 + let key_absent_value k = (* WARNING raises Failure *) 103 + let absent_field k = match k.absent with 104 + | Value v -> v 105 + | Discover discover -> 106 + match discover () (* exciting... *) with 107 + | Ok v -> v 108 + | Error (`Msg m) -> failwith (Topkg_string.strf "key %s: %s" k.name m) 109 + in 110 + match k.env with 111 + | None -> absent_field k 112 + | Some var -> 113 + match Topkg_os.Env.var var with 114 + | None -> absent_field k 115 + | Some ev -> 116 + match conv_parser k.conv ev with 117 + | Ok v -> v 118 + | Error (`Msg m) -> 119 + failwith (Topkg_string.strf "key %s: env %s: %s" k.name var m) 120 + 121 + let with_pkg ?(default = true) pkg = 122 + let doc = Topkg_string.strf "true if package %s is installed." pkg in 123 + key ("with-" ^ pkg) bool ~absent:default ~doc 124 + 125 + (* Predefined keys *) 126 + 127 + let pkg_name = 128 + let doc = "The name $(docv) of the package (and hence the opam install \ 129 + file). If absent provided by the package description." 130 + in 131 + let absent () = assert false (* handled specially by [of_cli_args] *) in 132 + discovered_key "pkg-name" string ~absent ~doc ~docv:"NAME" 133 + 134 + let build_dir = 135 + let doc = "Specifies the build directory $(docv)." in 136 + let absent () = assert false (* handled specially by [of_cli_args] *) in 137 + discovered_key "build-dir" string ~absent ~doc ~docv:"BUILD_DIR" 138 + 139 + let vcs = 140 + let doc = "Specifies if the package directory is VCS managed." in 141 + let absent () = Topkg_vcs.find ~dir:"." () >>= function 142 + | None -> Ok false 143 + | Some _ -> Ok true 144 + in 145 + discovered_key "vcs" bool ~absent ~doc 146 + 147 + let pinned = 148 + let doc = "Deprecated, use --dev-pkg. The semantics is the same." 149 + in 150 + key "pinned" bool ~absent:false ~doc 151 + 152 + let dev_pkg = 153 + let doc = "Specifies that the build is a dev package build (e.g. in opam)." in 154 + key "dev-pkg" bool ~absent:false ~doc 155 + 156 + 157 + let tests = 158 + let doc = "Specifies whether tests should be built. If absent depends \ 159 + on the build context, true for development and false otherwise." 160 + in 161 + let absent () = Ok None in 162 + discovered_key "tests" (some bool) ~absent ~doc 163 + 164 + let debug = 165 + let doc = "Debug build. Save debugging information in build artefacts. This \ 166 + key should not be specified explicitly in your package build \ 167 + instructions." 168 + in 169 + key "debug" bool ~env:"TOPKG_CONF_DEBUG" ~absent:true ~doc 170 + 171 + let debugger_support = 172 + let doc = "Debugger support. Build and install build artefacts needed \ 173 + by debuggers. This key should not be specified explicitly in \ 174 + your package build instructions." 175 + in 176 + key "debugger-support" bool ~env:"TOPKG_CONF_DEBUGGER_SUPPORT" ~absent:true 177 + ~doc 178 + 179 + let profile = 180 + let doc = "Profiling build. Include run-time profiling support in build \ 181 + artefacts. This key should not be specified explicitly \ 182 + in your package build instructions." 183 + in 184 + key "profile" bool ~env:"TOPKG_CONF_PROFILE" ~absent:false ~doc 185 + 186 + let toolchain = 187 + let doc = "Specifies the ocamlfind toolchain." in 188 + key "toolchain" (some string) ~env:"TOPKG_CONF_TOOLCHAIN" ~absent:None ~doc 189 + 190 + (* Key documentation *) 191 + 192 + let pp_cli_opt ppf opt_name absent env doc docv = 193 + let prf = Format.fprintf in 194 + let pp_doc ppf doc = 195 + let subst = function "docv" -> docv | s -> Topkg_string.strf "$(%s)" s in 196 + let b = Buffer.create 244 in 197 + let doc = 198 + try Buffer.add_substitute b subst doc; Buffer.contents b 199 + with Not_found -> doc 200 + in 201 + Topkg_string.pp_text ppf doc 202 + in 203 + let pp_absent absent env ppf () = match absent, env with 204 + | "", None -> () 205 + | "", Some var -> prf ppf "@ (or %s env)" var 206 + | absent, None -> prf ppf "@ (absent=%s)" absent 207 + | absent, Some var -> prf ppf "@ (absent=%s or %s env)" absent var 208 + in 209 + prf ppf "@[<v4>@[%s %s %a@]@,@[%a@]@]" 210 + opt_name docv (pp_absent absent env) () pp_doc doc 211 + 212 + let pp_key ppf k = 213 + let absent = match k.absent with 214 + | Discover _ -> "discovered" 215 + | Value v -> Topkg_string.strf "@[<h>%a@]" (conv_printer k.conv) v 216 + in 217 + let opt_name = match k.name with 218 + | "pkg-name" -> "-n NAME, --pkg-name" (* a bit ugly to special case *) 219 + | n -> Topkg_string.strf "--%s" n 220 + in 221 + let docv = conv_docv k.conv in 222 + pp_cli_opt ppf opt_name absent k.env k.doc docv 223 + 224 + let pp_keys_cli_opts ppf () = 225 + let pp_key is_first (Key.V k) = 226 + if is_first then () else Format.pp_print_cut ppf (); 227 + pp_key ppf k; false 228 + in 229 + let by_name (Key.V k) (Key.V k') = compare k.name k'.name in 230 + let keys = List.sort by_name (Kset.elements !key_index) in 231 + Format.fprintf ppf "@[<v>"; 232 + ignore (List.fold_left pp_key true keys); 233 + Format.fprintf ppf "@]"; 234 + () 235 + 236 + (* Configurations *) 237 + 238 + type t = univ Kmap.t 239 + 240 + let empty = Kmap.empty 241 + let is_empty = Kmap.is_empty 242 + let mem k c = Kmap.mem (Key.V k) c 243 + let add k v c = Kmap.add (Key.V k) (k.to_univ v) c 244 + let rem k c = Kmap.remove (Key.V k) c 245 + let find k c = try k.of_univ (Kmap.find (Key.V k) c) with Not_found -> None 246 + let value c k = match find k c with 247 + | Some v -> v 248 + | None -> 249 + invalid_arg 250 + (Topkg_string.strf "configuration key %s undefined, did you create 251 + a key after the call to Pkg.describe ?" (* dirty bastard *) k.name) 252 + 253 + let pp_value c ppf k = k.conv.print ppf (value c k) 254 + 255 + let dump ppf c = 256 + let dump_binding (Key.V k) v is_first = 257 + if is_first then () else Format.pp_print_cut ppf (); 258 + match k.of_univ v with 259 + | None -> assert false 260 + | Some v -> 261 + Format.fprintf ppf "%s: @[%a@]" k.name (conv_printer k.conv) v; 262 + false 263 + in 264 + Format.fprintf ppf "@[<v>"; 265 + ignore (Kmap.fold dump_binding c true); 266 + Format.fprintf ppf "@]"; 267 + () 268 + 269 + let of_cli_args ~pkg_name:name ~build_dir:bdir args = 270 + let cli_opts = cli_opts_of_key_index () in 271 + let cli_opts = ("-n", Key.V pkg_name) :: cli_opts in 272 + let strf = Topkg_string.strf in 273 + let rec parse_keys conf = function (* WARNING raises *) 274 + | key :: def :: defs -> 275 + begin match try Some (List.assoc key cli_opts) with Not_found -> None with 276 + | None -> failwith (Topkg_string.strf "key %s: Unknown key." key) 277 + | Some (Key.V k) -> 278 + if mem k conf 279 + then failwith (strf "key %s: Repeated definition." key) else 280 + match (conv_parser k.conv) def with 281 + | Ok v -> parse_keys (add k v conf) defs 282 + | Error (`Msg e) -> failwith (strf "key %s: %s." key e) 283 + end 284 + | [] -> conf 285 + | key :: [] -> failwith (strf "key %s: No value specified." key) 286 + in 287 + let add_if_absent (Key.V k) conf = (* WARNING raises *) 288 + if mem k conf then conf else 289 + add k (key_absent_value k) conf 290 + in 291 + let ensure_pkg_name_and_bdir conf = 292 + let conf = if mem pkg_name conf then conf else add pkg_name name conf in 293 + if mem build_dir conf then conf else add build_dir bdir conf 294 + in 295 + try 296 + let cli_conf = ensure_pkg_name_and_bdir (parse_keys empty args) in 297 + Ok (Kset.fold add_if_absent !key_index cli_conf) 298 + with 299 + | Failure e -> R.error_msg e 300 + 301 + let pkg_name c = value c pkg_name 302 + let build_dir c = value c build_dir 303 + let toolchain c = value c toolchain 304 + let vcs c = value c vcs 305 + let pinned c = value c pinned 306 + let dev_pkg c = value c dev_pkg 307 + 308 + type build_context = [`Dev | `Distrib | `Pin ] 309 + let build_context c = 310 + if not (vcs c) then `Distrib else 311 + if (pinned c || dev_pkg c) then `Pin else 312 + `Dev 313 + 314 + let build_tests c = match value c tests with 315 + | Some b -> b 316 + | None -> 317 + match build_context c with 318 + | `Dev -> true 319 + | _ -> false 320 + 321 + let debug c = value c debug 322 + let debugger_support c = value c debugger_support 323 + let profile c = value c profile 324 + 325 + (* Tool lookup *) 326 + 327 + type os = [ `Build_os | `Host_os ] 328 + 329 + let os_to_string = function 330 + | `Build_os -> "build-os" 331 + | `Host_os -> "host-os" 332 + 333 + let os_tool_env name os = 334 + let pre = match os with `Build_os -> "BUILD_OS_" | `Host_os -> "HOST_OS_" in 335 + pre ^ Topkg_string.uppercase_ascii name 336 + 337 + let os_bin_dir_env = function 338 + | `Build_os -> "BUILD_OS_BIN" 339 + | `Host_os -> "HOST_OS_XBIN" 340 + 341 + let os_suff_env = function 342 + | `Build_os -> "BUILD_OS_SUFF" 343 + | `Host_os -> "HOST_OS_SUFF" 344 + 345 + let ocamlfindable ?conf name os = match name with 346 + | "ocamlc" | "ocamlcp" | "ocamlmktop" | "ocamlopt" | "ocamldoc" | "ocamldep" 347 + | "ocamlmklib" | "ocamlbrowser" as tool -> 348 + let toolchain = 349 + match conf with 350 + | None -> Topkg_cmd.empty 351 + | Some c -> 352 + match os, toolchain c with 353 + | `Host_os, Some toolchain -> Topkg_cmd.(v "-toolchain" % toolchain) 354 + | _ -> Topkg_cmd.empty 355 + in 356 + Some Topkg_cmd.(v "ocamlfind" %% toolchain % tool) 357 + | _ -> None 358 + 359 + let tool ?conf name os = match Topkg_os.Env.var (os_tool_env name os) with 360 + | Some cmd -> Topkg_cmd.v cmd 361 + | None -> 362 + match Topkg_os.Env.var (os_bin_dir_env os) with 363 + | Some path -> Topkg_cmd.v Topkg_fpath.(path // name) 364 + | None -> 365 + match Topkg_os.Env.var (os_suff_env os) with 366 + | Some suff -> Topkg_cmd.v (name ^ suff) 367 + | None -> 368 + match ocamlfindable ?conf name os with 369 + | Some cmd -> cmd 370 + | None -> Topkg_cmd.v name 371 + 372 + let get_ncpus () = 373 + let on_fail () = 1 in 374 + let decode_int = Topkg_codec.(dec_result int) in 375 + if Sys.win32 then 376 + match Topkg_os.Env.var "NUMBER_OF_PROCESSORS" with 377 + | Some s -> decode_int s |> 378 + Topkg_log.on_error_msg ~level:Topkg_log.Debug ~use:on_fail 379 + | None -> on_fail () 380 + else 381 + let run_tool name args ~on_fail = 382 + Topkg_log.on_error_msg ~level:Topkg_log.Debug ~use:on_fail @@ 383 + Topkg_os.Cmd.(run_out Topkg_cmd.(tool name `Build_os %% of_list args) |> 384 + out_string |> success >>= decode_int) 385 + in 386 + run_tool "getconf" ["_NPROCESSORS_ONLN"] ~on_fail:(fun () -> 387 + run_tool "sysctl" ["-n"; "hw.ncpu"] ~on_fail) 388 + 389 + let jobs = 390 + let doc = "Allow to run $(docv) commands at once when building." in 391 + let absent () = Ok None in 392 + discovered_key "jobs" (some int) ~absent ~doc ~docv:"JOBS" 393 + 394 + let default_jobs = 4 395 + let jobs c = match value c jobs with 396 + | Some n -> n 397 + | None -> 398 + match build_context c with 399 + | `Dev -> get_ncpus () 400 + | _ -> default_jobs 401 + 402 + (* OCaml configuration, as communicated by ocamlc -config *) 403 + 404 + module OCaml = struct 405 + 406 + type conf = t 407 + 408 + (* Log strings *) 409 + 410 + let conf fmt = "OCaml %s conf: " ^^ fmt 411 + let conf_key fmt = conf ("key %s: " ^^ fmt) 412 + 413 + (* Configuration *) 414 + 415 + type t = 416 + { os : os; mutable conf : (string * string) list; 417 + (* Mutability is only used to add the value found by a discover 418 + procedure for keys that are not yet exposed in ocamlc -config. 419 + See http://caml.inria.fr/mantis/view.php?id=7172 *) } 420 + 421 + let empty os = { os; conf = [] } 422 + 423 + let read_config c os = 424 + let parse_line acc l = match Topkg_string.cut ~sep:':' l with 425 + | Some (k, v) -> (k, String.trim v) :: acc 426 + | None -> 427 + Topkg_log.warn (fun m -> 428 + m (conf "cannot parse line %S") (os_to_string os) l); 429 + acc 430 + in 431 + begin 432 + let ocamlc = tool ?conf:c "ocamlc" os in 433 + Topkg_os.Cmd.(run_out Topkg_cmd.(ocamlc % "-config") |> to_lines) 434 + >>= fun lines -> Ok (List.(rev (fold_left parse_line [] lines))) 435 + >>= fun conf -> Ok { os; conf } 436 + end 437 + |> R.reword_error_msg ~replace:true 438 + (fun msg -> R.msgf (conf " %s") (os_to_string os) msg) 439 + |> Topkg_log.on_error_msg ~level:Topkg_log.Warning ~use:(fun () -> empty os) 440 + 441 + let cache = Hashtbl.create 2 442 + let v c os = 443 + try Hashtbl.find cache (c, os) 444 + with Not_found -> 445 + let config = read_config (Some c) os in 446 + Hashtbl.add cache (c, os) config; 447 + config 448 + 449 + let add_discovery k v c = c.conf <- (k, v) :: c.conf 450 + let find k c = try Some (List.assoc k c.conf) with Not_found -> None 451 + let get ~absent k c = match find k c with 452 + | Some v -> v 453 + | None -> 454 + Topkg_log.warn (fun m -> 455 + m (conf_key "undefined, using %S") (os_to_string c.os) k absent); 456 + absent 457 + 458 + let get_string_with_discovery k c ~discover = match find k c with 459 + | Some v -> v 460 + | None -> let v = discover k c in add_discovery k v c; v 461 + 462 + let get_bool_with_discovery k c ~discover = 463 + let maybe_v = match find k c with 464 + | None -> None 465 + | Some v -> 466 + try Some (bool_of_string v) with 467 + | (* That good old joke... *) Invalid_argument _ -> 468 + Topkg_log.warn (fun m -> 469 + m (conf_key "could not parse boolean,@ trying to discover") 470 + (os_to_string c.os) k); 471 + None 472 + in 473 + match maybe_v with 474 + | Some v -> v 475 + | None -> let v = discover k c in add_discovery k (string_of_bool v) c; v 476 + 477 + let get_int_with_discovery k c ~discover = 478 + let maybe_v = match find k c with 479 + | None -> None 480 + | Some v -> 481 + try Some (int_of_string v) with 482 + | Failure _ -> 483 + Topkg_log.warn (fun m -> 484 + m (conf_key "could not parse integer,@ trying to discover") 485 + (os_to_string c.os) k); 486 + None 487 + in 488 + match maybe_v with 489 + | Some v -> v 490 + | None -> let v = discover k c in add_discovery k (string_of_int v) c; v 491 + 492 + let find_stdlib c = find "standard_library" c 493 + 494 + let get_bool_stdlib_file_exists_discovery k c ~file ~on_error = 495 + get_bool_with_discovery k c ~discover:begin fun k c -> 496 + match find_stdlib c with 497 + | None -> 498 + Topkg_log.warn (fun m -> 499 + m (conf_key 500 + "undefined, stdlib dir not found for discovery@ using %B") 501 + (os_to_string c.os) k on_error); 502 + on_error 503 + | Some stdlib_dir -> 504 + match Topkg_os.File.exists (Topkg_fpath.(stdlib_dir // file c)) with 505 + | Ok exist -> exist 506 + | Error (`Msg e) -> 507 + Topkg_log.warn (fun m -> 508 + m (conf_key "undefined,@ discovery error: %s,@ using %B") 509 + (os_to_string c.os) k e on_error); 510 + on_error 511 + end 512 + 513 + let version c = (* parses the specification described in Sys.ocaml_version *) 514 + let dumb_version = 0, 0, 0, None in 515 + let k = "version" in 516 + match find k c with 517 + | None -> 518 + Topkg_log.warn (fun m -> 519 + m (conf_key "missing, using 0.0.0") (os_to_string c.os) k); 520 + dumb_version 521 + | Some version -> 522 + match Topkg_string.parse_version version with 523 + | Some version -> version 524 + | None -> 525 + Topkg_log.warn (fun m -> 526 + m (conf_key "cannot parse from %S, using 0.0.0") 527 + (os_to_string c.os) k version); 528 + dumb_version 529 + 530 + let ext_obj c = get ~absent:".o" "ext_obj" c 531 + let ext_asm c = get ~absent:".s" "ext_asm" c 532 + let ext_lib c = get ~absent:".a" "ext_lib" c 533 + let ext_dll c = get ~absent:".so" "ext_dll" c 534 + let ext_exe c = 535 + get_string_with_discovery "ext_exe" c ~discover:begin fun k c -> 536 + (* Not exposed until at least 4.03. The discover logic is based on 537 + the knowledge articulated in this message: 538 + http://lists.ocaml.org/pipermail/wg-windows/2015-July/000037.html *) 539 + let find_c_toolchain c = match find "ccomp_type" c, find "os_type" c with 540 + | None, _ | _, None -> None 541 + | Some ccomp_type, Some os_type -> 542 + match ccomp_type, os_type with 543 + | "msvc", _ -> Some `Win_msvc 544 + | "cc", "Win32" -> Some `Win_cc 545 + | _, _ -> Some `Other 546 + in 547 + match find_c_toolchain c with 548 + | Some (`Win_msvc | `Win_cc) -> ".exe" 549 + | Some `Other -> "" 550 + | None -> 551 + Topkg_log.warn (fun m -> 552 + m (conf_key "undefined and@ no C toolchain@ detected,@ using \"\"") 553 + (os_to_string c.os) k;); 554 + "" 555 + end 556 + 557 + let native c = 558 + let file c = "libasmrun" ^ (ext_lib c) in 559 + get_bool_stdlib_file_exists_discovery "native" c ~file ~on_error:false 560 + 561 + let native_dynlink c = 562 + let file c = 563 + let (major_ocaml_version, _minor, _patch, _extra) = version c in 564 + if major_ocaml_version >= 5 then 565 + "dynlink/dynlink.cmxa" 566 + else 567 + "dynlink.cmxa" 568 + in 569 + get_bool_stdlib_file_exists_discovery "natdynlink" c ~file ~on_error:false 570 + 571 + let supports_shared_libraries c = 572 + let key = "supports_shared_libraries" in 573 + let asm_dynlib c = "libasmrun_shared" ^ (ext_dll c) in 574 + let caml_dynlib c = "libcamlrun_shared" ^ (ext_dll c) in 575 + let on_error = false in 576 + get_bool_stdlib_file_exists_discovery key c ~file:asm_dynlib ~on_error || 577 + get_bool_stdlib_file_exists_discovery key c ~file:caml_dynlib ~on_error 578 + 579 + let word_size c = 580 + let sizeof_ptr_of_config_h file = 581 + let err l = 582 + R.error_msgf "could not parse SIZEOF_PTR from %S in %s" l file 583 + in 584 + let rec parse = function 585 + | [] -> R.error_msgf "could not find SIZEOF_PTR in %s" file 586 + | l :: ls -> 587 + let l = Topkg_string.trim l in 588 + let affix = "#define SIZEOF_PTR" in 589 + let is_size_of_ptr = Topkg_string.is_prefix ~affix l in 590 + if not is_size_of_ptr then parse ls else 591 + match Topkg_string.cut ~rev:true ~sep:' ' l with 592 + | None -> err l 593 + | Some (_, size) -> 594 + try Ok (int_of_string size * 8) with Failure _ -> err l 595 + in 596 + Topkg_os.File.read file 597 + >>= fun conf -> Ok (Topkg_string.cuts ~sep:'\n' conf) 598 + >>= fun lines -> parse lines 599 + in 600 + get_int_with_discovery "word_size" c ~discover:begin fun k c -> 601 + let on_error = 64 in 602 + match find_stdlib c with 603 + | None -> 604 + Topkg_log.warn (fun m -> 605 + m (conf_key 606 + "undefined, stdlib dir not found for discovery@ using %d") 607 + (os_to_string c.os) k on_error); 608 + on_error 609 + | Some stdlib_dir -> 610 + let config_h = "caml/config.h" in 611 + match 612 + Topkg_os.File.must_exist Topkg_fpath.(stdlib_dir // config_h) 613 + >>= fun conf -> sizeof_ptr_of_config_h conf 614 + with 615 + | Ok v -> v 616 + | Error (`Msg e) -> 617 + Topkg_log.warn (fun m -> 618 + m (conf_key "undefined,@ discovery error: %s,@ using %d") 619 + (os_to_string c.os) k e on_error); 620 + on_error 621 + end 622 + 623 + let dump ppf c = 624 + let pp_elt ppf (k, v) = Format.fprintf ppf "(%S, %S)" k v in 625 + let rec loop = function 626 + | [] -> () 627 + | v :: vs -> 628 + if vs = [] then (Format.fprintf ppf "@[%a@]" pp_elt v) else 629 + (Format.fprintf ppf "@[%a@];@ " pp_elt v; loop vs) 630 + in 631 + Format.fprintf ppf "@[<1>["; loop c.conf; Format.fprintf ppf "]@]" 632 + end
+96
vendor/opam/topkg/src/topkg_conf.mli
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** Build configuration 7 + 8 + See {!Topkg.Conf}. *) 9 + 10 + open Topkg_result 11 + 12 + (** {1 Configuration key value converters} *) 13 + 14 + type 'a conv 15 + 16 + val conv : 17 + ?docv:string -> (string -> 'a result) -> (Format.formatter -> 'a -> unit) -> 18 + 'a conv 19 + 20 + val conv_with_docv : 'a conv -> docv:string -> 'a conv 21 + val conv_parser : 'a conv -> (string -> 'a result) 22 + val conv_printer : 'a conv -> (Format.formatter -> 'a -> unit) 23 + val conv_docv : 'a conv -> string 24 + 25 + val bool : bool conv 26 + val int : int conv 27 + val string : string conv 28 + val fpath : Topkg_fpath.t conv 29 + val some : ?none:string -> 'a conv -> 'a option conv 30 + 31 + (** {1 Configuration keys} *) 32 + 33 + type 'a key 34 + 35 + val key : 36 + ?docv:string -> ?doc:string -> ?env:string -> string -> 'a conv -> 37 + absent:'a -> 'a key 38 + 39 + val discovered_key : 40 + ?docv:string -> ?doc:string -> ?env:string -> string -> 'a conv -> 41 + absent:(unit -> 'a result) -> 'a key 42 + 43 + val with_pkg : ?default:bool -> string -> bool key 44 + 45 + val pp_keys_cli_opts : Format.formatter -> unit -> unit 46 + 47 + (** {1 Build configuration} *) 48 + 49 + type t 50 + val empty : t 51 + val value : t -> 'a key -> 'a 52 + val pp_value : t -> Format.formatter -> 'a key -> unit 53 + val dump : Format.formatter -> t -> unit 54 + val of_cli_args : 55 + pkg_name:string -> build_dir:Topkg_fpath.t -> string list -> t result 56 + 57 + val pkg_name : t -> string 58 + val build_dir : t -> Topkg_fpath.t 59 + val vcs : t -> bool 60 + val pinned : t -> bool 61 + val dev_pkg : t -> bool 62 + val jobs : t -> int 63 + 64 + type build_context = [`Dev | `Distrib | `Pin ] 65 + val build_context : t -> [`Dev | `Distrib | `Pin ] 66 + val build_tests : t -> bool 67 + 68 + val debug : t -> bool 69 + val debugger_support : t -> bool 70 + val profile : t -> bool 71 + val toolchain : t -> string option 72 + 73 + (** {1 Tool lookup} *) 74 + 75 + type os = [ `Build_os | `Host_os ] 76 + val tool : ?conf:t -> string -> os -> Topkg_cmd.t 77 + 78 + (** {1 OCaml configuration} *) 79 + 80 + module OCaml : sig 81 + type conf = t 82 + type t 83 + val v : conf -> os -> t 84 + val find : string -> t -> string option 85 + val version : t -> int * int * int * string option 86 + val ext_obj : t -> string 87 + val ext_asm : t -> string 88 + val ext_lib : t -> string 89 + val ext_dll : t -> string 90 + val ext_exe : t -> string 91 + val native : t -> bool 92 + val native_dynlink : t -> bool 93 + val supports_shared_libraries : t -> bool 94 + val word_size : t -> int 95 + val dump : Format.formatter -> t -> unit 96 + end
+151
vendor/opam/topkg/src/topkg_distrib.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + open Topkg_result 7 + 8 + (* Watermarks *) 9 + 10 + type watermark = 11 + string * 12 + [ `String of string 13 + | `Name 14 + | `Version 15 + | `Version_num 16 + | `Vcs of [ `Commit_id ] 17 + | `Opam of Topkg_fpath.t option * string * string ] 18 + 19 + let opam_fields file = 20 + (Topkg_opam.File.fields file) 21 + |> R.reword_error_msg ~replace:true (fun msg -> R.msgf "Watermarks: %s" msg) 22 + |> Topkg_log.on_error_msg ~level:Topkg_log.Warning ~use:(fun () -> []) 23 + 24 + let opam_field = 25 + let find k m = try Some (List.assoc k m) with Not_found -> None in 26 + let opam_memo = ref [] in (* memoizes the opam files *) 27 + let rec opam_field file field = match find file !opam_memo with 28 + | None -> 29 + opam_memo := (file, (opam_fields file)) :: !opam_memo; 30 + opam_field file field 31 + | Some fields -> 32 + match find field fields with 33 + | Some vs -> vs 34 + | None -> 35 + Topkg_log.warn 36 + (fun m -> m "file %s: opam field %S undefined or unsupported" 37 + file field); 38 + ["UNDEFINED"] 39 + in 40 + opam_field 41 + 42 + let vcs_commit_id () = 43 + (Topkg_vcs.get () >>= fun repo -> Topkg_vcs.head ~dirty:true repo) 44 + |> R.reword_error_msg ~replace:true 45 + (fun msg -> R.msgf "Watermarks: VCS commit id determination: %s" msg) 46 + |> Topkg_log.on_error_msg ~level:Topkg_log.Warning 47 + ~use:(fun () -> "UNDEFINED") 48 + 49 + let define_watermarks ~name ~version ~opam watermarks = 50 + let define (id, v) = 51 + let (id, v as def) = match v with 52 + | `String s -> (id, s) 53 + | `Version -> (id, version) 54 + | `Version_num -> (id, Topkg_string.drop_initial_v version) 55 + | `Name -> (id, name) 56 + | `Vcs `Commit_id -> (id, vcs_commit_id ()) 57 + | `Opam (file, field, sep) -> 58 + let file = match file with None -> opam | Some file -> file in 59 + (id, String.concat sep (opam_field file field)) 60 + in 61 + Topkg_log.info (fun m -> m "Watermark %s = %S" id v); 62 + def 63 + in 64 + List.map define watermarks 65 + 66 + let watermark_file ws file = 67 + Topkg_os.File.read file >>= fun content -> 68 + Topkg_os.File.write_subst file ws content >>= fun () -> 69 + Topkg_log.info (fun m -> m "Watermarked %s" file); Ok () 70 + 71 + let rec watermark_files ws = function 72 + | [] -> Ok () 73 + | f :: fs -> watermark_file ws f >>= fun () -> watermark_files ws fs 74 + 75 + (* Defaults *) 76 + 77 + let default_watermarks = 78 + let space = " " in 79 + let comma = ", " in 80 + [ "NAME", `Name; 81 + "VERSION", `Version; 82 + "VERSION_NUM", `Version_num; 83 + "VCS_COMMIT_ID", `Vcs `Commit_id; 84 + "PKG_MAINTAINER", `Opam (None, "maintainer", comma); 85 + "PKG_AUTHORS", `Opam (None, "authors", comma); 86 + "PKG_HOMEPAGE", `Opam (None, "homepage", comma); 87 + "PKG_ISSUES", `Opam (None, "bug-reports", space); 88 + "PKG_DOC", `Opam (None, "doc", space); 89 + "PKG_LICENSE", `Opam (None, "license", comma); 90 + "PKG_REPO", `Opam (None, "dev-repo", space); ] 91 + 92 + let default_files_to_watermark = 93 + let is_file f = 94 + Topkg_os.File.exists f |> Topkg_log.on_error_msg ~use:(fun _ -> false) 95 + in 96 + let is_binary_ext ext = 97 + let module Set = Set.Make (String) in 98 + let exts = 99 + Set.(empty |> 100 + add ".eps" |> add ".flv" |> add ".gif" |> add ".ico" |> 101 + add ".jpeg" |> add ".jpg" |> add ".mov" |> add ".mp3" |> 102 + add ".mp4" |> add ".otf" |> add ".pdf" |> add ".png" |> 103 + add ".ps" |> add ".ttf" |> add ".woff") 104 + in 105 + Set.mem ext exts 106 + in 107 + let keep f = not (is_binary_ext @@ Topkg_fpath.get_ext f) && is_file f in 108 + fun () -> 109 + Topkg_vcs.get () 110 + >>= fun repo -> Topkg_vcs.tracked_files repo 111 + >>= fun files -> Ok (List.filter keep files) 112 + 113 + let default_massage () = Ok () 114 + 115 + let default_exclude_paths () = 116 + Ok [".git"; ".gitignore"; ".gitattributes"; ".hg"; ".hgignore"; "build"; 117 + "Makefile"; "_build"] 118 + 119 + (* Distribution *) 120 + 121 + type t = 122 + { watermarks : watermark list; 123 + files_to_watermark : unit -> Topkg_fpath.t list result; 124 + massage : unit -> unit result; 125 + exclude_paths : unit -> Topkg_fpath.t list result; 126 + uri : string option; } 127 + 128 + let v 129 + ?(watermarks = default_watermarks) 130 + ?(files_to_watermark = default_files_to_watermark) 131 + ?(massage = fun () -> Ok ()) 132 + ?(exclude_paths = default_exclude_paths) 133 + ?uri () = 134 + { watermarks; files_to_watermark; massage; exclude_paths; uri } 135 + 136 + let watermarks d = d.watermarks 137 + let files_to_watermark d = d.files_to_watermark 138 + let massage d = d.massage 139 + let exclude_paths d = d.exclude_paths 140 + let uri d = d.uri 141 + let codec = 142 + let uri = Topkg_codec.(with_kind "uri" @@ option string) in 143 + let fields = 144 + let stub () = invalid_arg "not executable outside package definition" in 145 + (fun d -> d.uri), 146 + (fun uri -> 147 + { watermarks = [] (* bad *); files_to_watermark = stub; 148 + massage = stub; exclude_paths = stub; uri }) 149 + in 150 + Topkg_codec.version 0 @@ 151 + Topkg_codec.(view ~kind:"distrib" fields uri)
+53
vendor/opam/topkg/src/topkg_distrib.mli
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** {1 Distribution description} 7 + 8 + See {!section:Topkg.Pkg.distrib}. *) 9 + 10 + (** {1 Distribution} *) 11 + 12 + open Topkg_result 13 + 14 + (* Watermarks *) 15 + 16 + type watermark = 17 + string * 18 + [ `String of string | `Name | `Version | `Version_num | `Vcs of [ `Commit_id ] 19 + | `Opam of Topkg_fpath.t option * string * string ] 20 + 21 + val define_watermarks : 22 + name:string -> version:string -> opam:Topkg_fpath.t -> 23 + watermark list -> (string * string) list 24 + 25 + val watermark_file : (string * string) list -> Topkg_fpath.t -> unit result 26 + val watermark_files : 27 + (string * string) list -> Topkg_fpath.t list -> unit result 28 + 29 + (* Distribution *) 30 + 31 + type t 32 + 33 + val v : 34 + ?watermarks:watermark list -> 35 + ?files_to_watermark:(unit -> Topkg_fpath.t list result) -> 36 + ?massage:(unit -> unit result) -> 37 + ?exclude_paths:(unit -> Topkg_fpath.t list result) -> 38 + ?uri:string -> 39 + unit -> t 40 + 41 + val watermarks : t -> watermark list 42 + val files_to_watermark : t -> (unit -> Topkg_fpath.t list result) 43 + val massage : t -> (unit -> unit result) 44 + val exclude_paths : t -> (unit -> Topkg_fpath.t list result) 45 + val uri : t -> string option 46 + val codec : t Topkg_codec.t 47 + 48 + (* Defaults *) 49 + 50 + val default_watermarks : watermark list 51 + val default_files_to_watermark : unit -> Topkg_fpath.t list result 52 + val default_massage : unit -> unit result 53 + val default_exclude_paths : unit -> Topkg_fpath.t list result
+31
vendor/opam/topkg/src/topkg_fexts.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + type ext = [ `Ext of string | `Obj | `Real_clib | `Lib | `Dll | `Exe ] 7 + type t = ext list 8 + 9 + let interface = [ `Ext ".mli"; `Ext ".cmi"; `Ext ".cmti"; ] 10 + let cmx = [ `Ext ".cmx" ] 11 + let api = interface @ cmx 12 + let real_c_library = [ `Real_clib ] 13 + let c_library = [ `Lib ] 14 + let c_dll_library = [ `Dll ] 15 + let library = [` Ext ".cma"; `Ext ".cmxa"; `Ext ".cmxs" ] @ c_library 16 + let module_library = (api @ library) 17 + let exe = [ `Exe ] 18 + let ext e = [ `Ext e ] 19 + let exts es = List.map (fun e -> `Ext e) es 20 + 21 + let ext_to_string c = 22 + let ext_obj = Topkg_conf.OCaml.ext_obj c in 23 + let ext_lib = Topkg_conf.OCaml.ext_lib c in 24 + let ext_dll = Topkg_conf.OCaml.ext_dll c in 25 + let ext_exe = Topkg_conf.OCaml.ext_exe c in 26 + function 27 + | `Ext s -> s 28 + | `Obj -> ext_obj 29 + | `Lib | `Real_clib -> ext_lib 30 + | `Dll -> ext_dll 31 + | `Exe -> ext_exe
+27
vendor/opam/topkg/src/topkg_fexts.mli
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** File extensions. 7 + 8 + See {!Topkg.Exts} for documentation. *) 9 + 10 + (** {1 File extensions} *) 11 + 12 + type ext = [`Ext of string | `Obj | `Real_clib | `Lib | `Dll | `Exe] 13 + 14 + type t = ext list 15 + 16 + val interface : ext list 17 + val api : ext list 18 + val cmx : ext list 19 + val real_c_library : ext list 20 + val c_library : ext list 21 + val c_dll_library : ext list 22 + val library : ext list 23 + val module_library : ext list 24 + val exe : ext list 25 + val exts : string list -> ext list 26 + val ext : string -> ext list 27 + val ext_to_string : Topkg_conf.OCaml.t -> ext -> string
+49
vendor/opam/topkg/src/topkg_fpath.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + type t = string 7 + 8 + let dir_sep_prefix s = 9 + Topkg_string.is_prefix ~affix:Filename.dir_sep s || 10 + (String.length s > 0 && s.[0] = '/') 11 + 12 + let dir_sep_suffix s = 13 + Topkg_string.is_suffix ~affix:Filename.dir_sep s || 14 + (String.length s > 0 && s.[String.length s - 1] = '/') 15 + 16 + let append = 17 + fun p q -> match p with 18 + | "" -> q 19 + | p -> 20 + match q with 21 + | "" -> p 22 + | q -> 23 + if dir_sep_prefix q then q else 24 + if dir_sep_suffix p then (p ^ q) else 25 + (p ^ "/" ^ q) 26 + 27 + let ( // ) = append 28 + 29 + let is_dir_path p = match p with 30 + | "." | ".." -> true 31 + | _ -> 32 + let is_suffix affix = Topkg_string.is_suffix ~affix p in 33 + List.exists is_suffix ["/"; "/.."; "/."] 34 + 35 + let is_file_path p = not (is_dir_path p) 36 + 37 + let basename s = Filename.basename s 38 + let dirname s = Filename.dirname s 39 + 40 + let last_dot_index s = try Some (String.rindex s '.') with Not_found -> None 41 + let get_ext s = match last_dot_index s with 42 + | None -> "" 43 + | Some i -> Topkg_string.with_index_range ~first:i s 44 + 45 + let has_ext e p = Topkg_string.is_suffix ~affix:e p 46 + 47 + let rem_ext s = match last_dot_index s with 48 + | None -> s 49 + | Some i -> Topkg_string.with_index_range ~last:(i - 1) s
+24
vendor/opam/topkg/src/topkg_fpath.mli
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** File system paths. 7 + 8 + See {!Topkg.Fpath}. *) 9 + 10 + (** {1 File system paths} *) 11 + 12 + type t = string 13 + val append : t -> t -> t 14 + val ( // ) : t -> t -> t 15 + 16 + val is_dir_path : t -> bool 17 + val is_file_path : t -> bool 18 + 19 + val basename : t -> string 20 + val dirname : t -> string 21 + 22 + val get_ext : t -> string 23 + val has_ext : string -> t -> bool 24 + val rem_ext : t -> t
+297
vendor/opam/topkg/src/topkg_install.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + open Topkg_result 7 + 8 + type move_scheme = 9 + { field : 10 + [ `Test of bool * Topkg_fpath.t option * Topkg_cmd.t 11 + | Topkg_opam.Install.field ]; 12 + auto_bin : bool; 13 + force : bool; 14 + built : bool; 15 + exts : Topkg_fexts.t; 16 + src : string; 17 + dst : string; 18 + debugger_support : bool; (* This a bit hacky, only used by 19 + OCamlbuild higher-level installs *) } 20 + 21 + type t = move_scheme list 22 + 23 + let nothing = [] 24 + 25 + let flatten ls = (* We don't care about order *) 26 + let rec push acc = function v :: vs -> push (v :: acc) vs | [] -> acc in 27 + let rec loop acc = function 28 + | l :: ls -> loop (push acc l) ls 29 + | [] -> acc 30 + in 31 + loop [] ls 32 + 33 + let split_ext s = match Topkg_string.cut ~rev:true s ~sep:'.' with 34 + | None -> s, `Ext "" 35 + | Some (name, ext) -> name, `Ext (Topkg_string.strf ".%s" ext) 36 + 37 + let bin_drop_exts native = if native then [] else Topkg_fexts.ext ".native" 38 + let lib_drop_exts native native_dynlink supports_shared_libraries = 39 + match native with 40 + | false -> Topkg_fexts.(c_library @ exts [".cmx"; ".cmxa"; ".cmxs"]) 41 + | true -> 42 + match supports_shared_libraries with 43 + | false -> `Dll :: Topkg_fexts.ext ".cmxs" 44 + | true -> 45 + match native_dynlink with 46 + | false -> Topkg_fexts.ext ".cmxs" 47 + | true -> [] 48 + 49 + let to_build ?header c os i = 50 + let bdir = Topkg_conf.build_dir c in 51 + let debugger_support = Topkg_conf.debugger_support c in 52 + let build_tests = Topkg_conf.build_tests c in 53 + let ocaml_conf = Topkg_conf.OCaml.v c os in 54 + let native = Topkg_conf.OCaml.native ocaml_conf in 55 + let native_dylink = Topkg_conf.OCaml.native_dynlink ocaml_conf in 56 + let supports_shared_libraries = 57 + Topkg_conf.OCaml.supports_shared_libraries ocaml_conf 58 + in 59 + let ext_to_string = Topkg_fexts.ext_to_string ocaml_conf in 60 + let file_to_str (n, ext) = Topkg_string.strf "%s%s" n (ext_to_string ext) in 61 + let maybe_build = [ ".cmti"; ".cmt" ] in 62 + let bin_drops = bin_drop_exts native in 63 + let lib_drops = 64 + lib_drop_exts native native_dylink supports_shared_libraries 65 + in 66 + let add acc m = 67 + if m.debugger_support && not debugger_support then acc else 68 + let mv (targets, moves, tests as acc) ((_, src_ext) as src) dst = 69 + let drop = not m.force && match m.field with 70 + | `Bin -> List.exists (( = ) src_ext) bin_drops 71 + | `Lib | `Lib_root | `Stublibs -> 72 + List.exists (( = ) src_ext) lib_drops 73 + | _ -> false 74 + in 75 + if drop then (targets, moves, tests) else 76 + let src = file_to_str src in 77 + let dst = file_to_str dst in 78 + let maybe = List.exists (Filename.check_suffix src) maybe_build in 79 + let targets = if m.built && not maybe then src :: targets else targets in 80 + let src = if m.built then Topkg_string.strf "%s/%s" bdir src else src in 81 + match m.field with 82 + | `Test (run, dir, args) -> 83 + if not build_tests then acc else 84 + let test = Topkg_test.v src ~args ~run ~dir in 85 + (targets, moves, test :: tests) 86 + | #Topkg_opam.Install.field as field -> 87 + let move = (field, Topkg_opam.Install.move ~maybe src ~dst) in 88 + (targets, move :: moves, tests) 89 + in 90 + let src, dst = 91 + if not m.auto_bin then m.src, m.dst else 92 + ((if native then m.src ^ ".native" else m.src ^ ".byte"), 93 + m.dst ^ (ext_to_string `Exe)) 94 + in 95 + if m.exts = [] then mv acc (split_ext src) (split_ext dst) else 96 + let expand acc ext = mv acc (src, ext) (dst, ext) in 97 + List.fold_left expand acc m.exts 98 + in 99 + let targets, moves, tests = List.fold_left add ([], [], []) (flatten i) in 100 + let tests = if build_tests then Some tests else None in 101 + targets, ((`Header header), moves), tests 102 + 103 + (* Install fields *) 104 + 105 + type field = 106 + ?force:bool -> ?built:bool -> ?cond:bool -> ?exts:Topkg_fexts.t -> 107 + ?dst:string -> string -> t 108 + 109 + let _field field 110 + ?(debugger_support = false) 111 + ?(auto = true) ?(force = false) ?(built = true) ?(cond = true) ?(exts = []) 112 + ?dst src = 113 + if not cond then [] else 114 + let dst = match dst with 115 + | None -> Topkg_fpath.basename src 116 + | Some dst -> 117 + if Topkg_fpath.is_file_path dst then dst else 118 + dst ^ (Topkg_fpath.basename src) 119 + in 120 + [{ field; auto_bin = auto; force; built; exts; src; dst; 121 + debugger_support; }] 122 + 123 + let field field = 124 + _field ~debugger_support:false ~auto:false field 125 + 126 + let field_exec field ?auto ?force ?built ?cond ?exts ?dst src = 127 + _field field ~debugger_support:false 128 + ?auto ?force ?built ?cond ?exts ?dst src 129 + 130 + let bin = field_exec `Bin 131 + let doc = field `Doc 132 + let etc = field `Etc 133 + let lib = field `Lib 134 + let lib_root = field `Lib_root 135 + let libexec = field_exec `Libexec 136 + let libexec_root = field_exec `Libexec_root 137 + let man = field `Man 138 + let misc = field `Misc 139 + let sbin = field_exec `Sbin 140 + let share = field `Share 141 + let share_root = field `Share_root 142 + let stublibs = field `Stublibs 143 + let toplevel = field `Toplevel 144 + let unknown name = field (`Unknown name) 145 + let test ?(run = true) ?dir ?(args = Topkg_cmd.empty) = 146 + field_exec (`Test (run, dir, args)) 147 + 148 + (* OCamlbuild higher-level installs *) 149 + 150 + let parse_mllib contents = (* list of module name and path (for dir/Mod) *) 151 + let lines = Topkg_string.cuts ~sep:'\n' contents in 152 + let add_mod acc l = 153 + let path = String.trim @@ match Topkg_string.cut ~sep:'#' l with 154 + | None -> l 155 + | Some (p, _ (* comment *)) -> p 156 + in 157 + if path = "" then acc else 158 + let mod_name = Topkg_string.capitalize_ascii @@ Topkg_fpath.basename path in 159 + (mod_name, path) :: acc 160 + in 161 + List.fold_left add_mod [] lines 162 + 163 + let field_of_field field = (* hack, recover a field from a field function... *) 164 + match (List.hd (field "")).field with 165 + | `Test (run, dir, args) -> assert false 166 + | #Topkg_opam.Install.field as field -> field 167 + 168 + let mllib 169 + ?(field = lib) ?(cond = true) ?(cma = true) ?(cmxa = true) ?(cmxs = true) 170 + ?api ?dst_dir mllib 171 + = 172 + if not cond then [] else 173 + let debugger_support_field = 174 + _field ~debugger_support:true ~auto:false (field_of_field field) 175 + in 176 + let lib_dir = Topkg_fpath.dirname mllib in 177 + let lib_base = Topkg_fpath.rem_ext mllib in 178 + let dst f = match dst_dir with 179 + | None -> None 180 + | Some dir -> Some (Topkg_fpath.append dir (Topkg_fpath.basename f)) 181 + in 182 + let api mllib_content = 183 + let mod_names = List.map fst mllib_content in 184 + match api with 185 + | None -> mod_names (* all the .mllib modules if unspecified *) 186 + | Some api -> 187 + let in_mllib i = List.mem (Topkg_string.capitalize_ascii i) mod_names in 188 + let api, orphans = List.partition in_mllib api in 189 + let warn o = 190 + Topkg_log.warn (fun m -> m "mllib %s: unknown interface %s" mllib o) 191 + in 192 + List.iter warn orphans; 193 + api 194 + in 195 + let library = 196 + let add_if cond v vs = if cond then v :: vs else vs in 197 + let exts = 198 + add_if cma (`Ext ".cma") @@ add_if cmxa (`Ext ".cmxa") @@ 199 + add_if cmxs (`Ext ".cmxs") @@ add_if (cmxa || cmxs) `Lib [] 200 + in 201 + field ?dst:(dst lib_base) ~exts lib_base 202 + in 203 + let add_mods acc mllib_content = 204 + let api = api mllib_content in 205 + let add_mod acc (m, path) = 206 + let fname = Topkg_string.uncapitalize_ascii (Topkg_fpath.basename path) in 207 + let fpath = match Topkg_fpath.dirname path with 208 + | "." -> Topkg_fpath.append lib_dir fname 209 + | parent -> Topkg_fpath.(append lib_dir (append parent fname)) 210 + in 211 + let dst = dst fname in 212 + let exts, debugger_support_exts = match List.mem m api with 213 + | true -> Topkg_fexts.api, Topkg_fexts.exts [".ml"; ".cmt"] 214 + | false -> 215 + let debugger_support_exts = 216 + (* Hidden modules are not forced to have an mli this 217 + information is not explicitely specified at the topkg 218 + level so we look up the file system. *) 219 + if Sys.file_exists (fpath ^ ".mli") 220 + then [".mli"; ".ml"; ".cmti"; ".cmt"] 221 + else [".ml"; ".cmt"] 222 + in 223 + Topkg_fexts.cmx, Topkg_fexts.exts debugger_support_exts 224 + in 225 + field ?dst ~exts fpath :: 226 + debugger_support_field ?dst ~exts:debugger_support_exts fpath :: 227 + acc 228 + in 229 + List.fold_left add_mod acc mllib_content 230 + in 231 + begin 232 + Topkg_os.File.read mllib 233 + >>= fun contents -> Ok (parse_mllib contents) 234 + >>= fun mllib_content -> Ok (flatten @@ add_mods [library] mllib_content) 235 + end 236 + |> Topkg_log.on_error_msg ~use:(fun () -> []) 237 + 238 + let parse_clib contents = 239 + let lines = Topkg_string.cuts ~sep:'\n' contents in 240 + let add_obj_path acc l = 241 + let path = String.trim @@ match Topkg_string.cut ~sep:'#' l with 242 + | None -> l 243 + | Some (p, _ (* comment *)) -> p 244 + in 245 + if path = "" then acc else path :: acc 246 + in 247 + List.fold_left add_obj_path [] lines 248 + 249 + let clib 250 + ?(dllfield = stublibs) ?(libfield = lib) ?(cond = true) ?lib_dst_dir clib 251 + = 252 + if not cond then [] else 253 + let debugger_support_field = 254 + _field ~debugger_support:true ~auto:false (field_of_field lib) 255 + in 256 + let lib_dir = Topkg_fpath.dirname clib in 257 + let lib_base = 258 + let base = Topkg_fpath.(basename @@ rem_ext clib) in 259 + if Topkg_string.is_prefix ~affix:"lib" base 260 + then Ok (Topkg_string.with_index_range ~first:3 base) 261 + else R.error_msgf "%s: OCamlbuild .clib file must start with 'lib'" clib 262 + in 263 + let lib_dst f = match lib_dst_dir with 264 + | None -> None 265 + | Some dir -> Some (Topkg_fpath.append dir (Topkg_fpath.basename f)) 266 + in 267 + let add_debugger_support cobjs = 268 + let add_cobj acc path = 269 + let fname = Topkg_fpath.(rem_ext @@ basename path) in 270 + let fpath = match Topkg_fpath.dirname path with 271 + | "." -> Topkg_fpath.append lib_dir fname 272 + | parent -> Topkg_fpath.(append lib_dir (append parent fname)) 273 + in 274 + let dst = lib_dst fname in 275 + debugger_support_field ?dst ~exts:(Topkg_fexts.ext ".c") fpath :: acc 276 + in 277 + List.fold_left add_cobj [] cobjs 278 + in 279 + begin 280 + lib_base 281 + >>= fun lib_base -> Topkg_os.File.read clib 282 + >>= fun contents -> 283 + let cobjs = parse_clib contents in 284 + let lib = Topkg_fpath.append lib_dir ("lib" ^ lib_base) in 285 + let lib = libfield ~exts:Topkg_fexts.real_c_library lib ?dst:(lib_dst lib)in 286 + let dll = Topkg_fpath.append lib_dir ("dll" ^ lib_base) in 287 + let dll = dllfield ~exts:Topkg_fexts.c_dll_library dll in 288 + Ok (flatten @@ lib :: dll :: add_debugger_support cobjs) 289 + end 290 + |> Topkg_log.on_error_msg ~use:(fun () -> []) 291 + 292 + (* Dummy codec *) 293 + 294 + let codec : t Topkg_codec.t = (* we don't care *) 295 + let fields = (fun _ -> ()), (fun () -> []) in 296 + Topkg_codec.version 0 @@ 297 + Topkg_codec.(view ~kind:"install" fields unit)
+52
vendor/opam/topkg/src/topkg_install.mli
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** Package install. *) 7 + 8 + (** {1 Install} *) 9 + 10 + type t 11 + 12 + val nothing : t 13 + val flatten : t list -> t 14 + val to_build : 15 + ?header:string -> 16 + Topkg_conf.t -> 17 + Topkg_conf.os -> t list -> 18 + (Topkg_fpath.t list * Topkg_opam.Install.t * Topkg_test.t list option) 19 + 20 + type field = 21 + ?force:bool -> ?built:bool -> ?cond:bool -> ?exts:Topkg_fexts.t -> 22 + ?dst:string -> string -> t 23 + 24 + val bin : ?auto:bool -> field 25 + val doc : field 26 + val etc : field 27 + val lib : field 28 + val lib_root : field 29 + val libexec : ?auto:bool -> field 30 + val libexec_root : ?auto:bool -> field 31 + val man : field 32 + val misc : field 33 + val sbin : ?auto:bool -> field 34 + val share : field 35 + val share_root : field 36 + val stublibs : field 37 + val toplevel : field 38 + val unknown : string -> field 39 + 40 + val test : 41 + ?run:bool -> ?dir:Topkg_fpath.t -> ?args:Topkg_cmd.t -> ?auto:bool -> field 42 + 43 + val mllib : 44 + ?field:field -> ?cond:bool -> ?cma:bool -> ?cmxa:bool -> ?cmxs:bool -> 45 + ?api:string list -> ?dst_dir:Topkg_fpath.t -> Topkg_fpath.t -> t 46 + 47 + val clib : 48 + ?dllfield:field -> 49 + ?libfield:field -> 50 + ?cond:bool -> ?lib_dst_dir:Topkg_fpath.t -> Topkg_fpath.t -> t 51 + 52 + val codec : t Topkg_codec.t
+87
vendor/opam/topkg/src/topkg_ipc.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + open Topkg_result 7 + 8 + type 'a t = 9 + { cmd : Topkg_cmd.t; 10 + codec : 'a Topkg_codec.t; 11 + answer : Topkg_fpath.t; } 12 + 13 + let v ?answer cmd codec = 14 + let answer = match answer with 15 + | Some a -> a 16 + | None -> 17 + (Topkg_os.File.tmp ()) 18 + |> R.reword_error_msg ~replace:true 19 + (fun m -> R.msgf "Could not create IPC answer file: %s, using stdout" m) 20 + |> Topkg_log.on_error_msg ~use:(fun () -> Topkg_os.File.dash) 21 + in 22 + let cmd = Topkg_cmd.(v answer %% cmd) in 23 + { cmd; codec; answer } 24 + 25 + let cmd ipc = ipc.cmd 26 + let codec ipc = ipc.codec 27 + let answer ipc = ipc.answer 28 + 29 + let error_args args = 30 + R.error_msgf "IPC: %a, unknown arguments" 31 + Topkg_cmd.dump (Topkg_cmd.of_list args) 32 + 33 + (* Package description IPC. Description functions raise Invalid_argument 34 + at the other end. *) 35 + 36 + let pkg () = v Topkg_cmd.(v "pkg") Topkg_pkg.codec 37 + let answer_pkg answer p = Topkg_codec.write answer Topkg_pkg.codec p 38 + 39 + (* Run custom lint IPC *) 40 + 41 + let lint_custom_codec = Topkg_codec.(option @@ list @@ result_error_msg @@ msg) 42 + let lint_custom () = 43 + let cmd = Topkg_cmd.(v "lint" % "custom") in 44 + v cmd lint_custom_codec 45 + 46 + let answer_lint_custom answer p = 47 + let custom_run = match (Topkg_pkg.lint_custom p) with 48 + | None -> None 49 + | Some custom -> Some (custom ()) 50 + in 51 + Topkg_codec.write answer lint_custom_codec custom_run 52 + 53 + (* Distrib prepare IPC *) 54 + 55 + let distrib_prepared_codec = 56 + Topkg_codec.version 0 @@ 57 + Topkg_codec.(with_kind "prepared" @@ result_error_msg (list fpath)) 58 + 59 + let distrib_prepare ~dist_build_dir ~name ~version ~opam ~opam_adds = 60 + let cmd = 61 + Topkg_cmd.(v "distrib" % "prepare" % 62 + "dist-build-dir" % dist_build_dir % "name" % name % 63 + "version" % version % "opam" % opam % "opam-adds" % opam_adds) 64 + in 65 + v cmd distrib_prepared_codec 66 + 67 + let answer_distrib_prepare 68 + answer p ~dist_build_dir ~name ~version ~opam ~opam_adds 69 + = 70 + Topkg_codec.write answer distrib_prepared_codec @@ 71 + Topkg_pkg.distrib_prepare p ~dist_build_dir ~name ~version ~opam ~opam_adds 72 + 73 + (* IPC answer *) 74 + 75 + let write_answer cmd p = match Topkg_cmd.to_list cmd with 76 + | answer :: "pkg" :: [] -> 77 + answer_pkg answer p 78 + | answer :: "lint" :: "custom" :: [] -> 79 + answer_lint_custom answer p 80 + | answer :: "distrib" :: "prepare" :: 81 + "dist-build-dir" :: dist_build_dir :: "name" :: name :: 82 + "version" :: version :: "opam" :: opam :: "opam-adds" :: opam_adds :: [] -> 83 + answer_distrib_prepare 84 + answer p ~dist_build_dir ~name ~version ~opam ~opam_adds 85 + 86 + | args -> 87 + error_args args
+27
vendor/opam/topkg/src/topkg_ipc.mli
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** Topkg interprocess communication. 7 + 8 + See {!Topkg.Private.Ipc} for documentation. *) 9 + 10 + open Topkg_result 11 + 12 + (** {1 Interprocess communication} *) 13 + 14 + type 'a t 15 + 16 + val v : ?answer:Topkg_fpath.t -> Topkg_cmd.t -> 'a Topkg_codec.t -> 'a t 17 + val cmd : 'a t -> Topkg_cmd.t 18 + val codec : 'a t -> 'a Topkg_codec.t 19 + val answer : 'a t -> Topkg_fpath.t 20 + 21 + val pkg : unit -> Topkg_pkg.t t 22 + val lint_custom : unit -> Topkg_result.R.msg Topkg_result.result list option t 23 + val distrib_prepare : 24 + dist_build_dir:string -> name:string -> version:string -> opam:string -> 25 + opam_adds:string -> Topkg_fpath.t list result t 26 + 27 + val write_answer : Topkg_cmd.t -> Topkg_pkg.t -> unit Topkg_result.result
+88
vendor/opam/topkg/src/topkg_log.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + open Topkg_result 7 + 8 + type level = App | Error | Warning | Info | Debug 9 + 10 + let exec = match Array.length Sys.argv with 11 + | 0 -> Filename.basename Sys.executable_name 12 + | n -> Filename.basename Sys.argv.(0) 13 + 14 + let _level = 15 + let default = Some Warning in 16 + let init = 17 + try match Sys.getenv "TOPKG_VERBOSITY" with 18 + | l when Topkg_string.is_prefix ~affix:"quiet" l -> None 19 + | l when Topkg_string.is_prefix ~affix:"error" l -> Some Error 20 + | l when Topkg_string.is_prefix ~affix:"warning" l -> Some Warning 21 + | l when Topkg_string.is_prefix ~affix:"info" l -> Some Info 22 + | l when Topkg_string.is_prefix ~affix:"debug" l -> Some Debug 23 + | l -> 24 + Format.eprintf 25 + "%s: @[TOPKG_VERBOSITY env var unknown value: %S@]@." exec l; 26 + default 27 + with Not_found | Sys_error _ -> default 28 + in 29 + ref init 30 + 31 + let level () = !_level 32 + let set_level l = _level := l 33 + 34 + let level_to_string = function 35 + | None -> "quiet" | Some App -> "app" | Some Error -> "error" 36 + | Some Warning -> "warning" | Some Info -> "info" | Some Debug -> "debug" 37 + 38 + let level_of_string = function 39 + | "quiet" -> Ok None 40 + | "app" -> Ok (Some App) 41 + | "error" -> Ok (Some Error) 42 + | "warning" -> Ok (Some Warning) 43 + | "info" -> Ok (Some Info) 44 + | "debug" -> Ok (Some Debug) 45 + | l -> R.error_msgf "%S: unknown log level" l 46 + 47 + type 'a msgf = 48 + (?header:string -> ('a, Format.formatter, unit) format -> 'a) -> unit 49 + 50 + let _err_count = ref 0 51 + let err_count () = !_err_count 52 + 53 + let _warn_count = ref 0 54 + let warn_count () = !_warn_count 55 + 56 + let pp_level_header ppf (h,l) = match h with 57 + | Some h -> Format.fprintf ppf "[%s] " h 58 + | None -> 59 + Format.pp_print_string ppf begin match l with 60 + | App -> "" 61 + | Error -> "[ERROR] " 62 + | Warning -> "[WARNING] " 63 + | Info -> "[INFO] " 64 + | Debug -> "[DEBUG] " 65 + end 66 + 67 + let msg level msgf = match !_level with 68 + | None -> () 69 + | Some level' when level > level' -> 70 + if level = Error then incr _err_count else 71 + if level = Warning then incr _warn_count else () 72 + | Some _ -> 73 + (if level = Error then incr _err_count else 74 + if level = Warning then incr _warn_count else ()); 75 + let pr = if level = App then Format.printf else Format.eprintf in 76 + msgf @@ 77 + (fun ?header fmt -> 78 + pr ("%s: %a@[" ^^ fmt ^^ "@]@.") exec pp_level_header (header, level)) 79 + 80 + let app msgf = msg App msgf 81 + let err msgf = msg Error msgf 82 + let warn msgf = msg Warning msgf 83 + let info msgf = msg Info msgf 84 + let debug msgf = msg Debug msgf 85 + 86 + let on_error_msg ?(level = Error) ~use = function 87 + | Ok v -> v 88 + | Error (`Msg e) -> msg level (fun m -> m "%s" e); use ()
+33
vendor/opam/topkg/src/topkg_log.mli
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** Log 7 + 8 + Abridged [logs]. See {!Topkg.Log} for documentation. *) 9 + 10 + (** {1 Log} *) 11 + 12 + open Topkg_result 13 + 14 + type level = App | Error | Warning | Info | Debug 15 + 16 + val level : unit -> level option 17 + val set_level : level option -> unit 18 + val level_to_string : level option -> string 19 + val level_of_string : string -> (level option, [`Msg of string]) r 20 + 21 + type 'a msgf = 22 + (?header:string -> ('a, Format.formatter, unit) format -> 'a) -> unit 23 + 24 + val msg : level -> 'a msgf -> unit 25 + val app : 'a msgf -> unit 26 + val err : 'a msgf -> unit 27 + val warn : 'a msgf -> unit 28 + val info : 'a msgf -> unit 29 + val debug : 'a msgf -> unit 30 + 31 + val on_error_msg : ?level:level -> use:(unit -> 'a) -> 'a result -> 'a 32 + val err_count : unit -> int 33 + val warn_count : unit -> int
+292
vendor/opam/topkg/src/topkg_main.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + open Topkg_result 7 + 8 + (* Help *) 9 + 10 + let exec = match Array.length Sys.argv with 11 + | 0 -> Filename.basename Sys.executable_name 12 + | n -> Filename.basename Sys.argv.(0) 13 + 14 + let usage () = Topkg_string.strf "Usage: %s COMMAND [OPTION]..." exec 15 + let try_help () = 16 + Topkg_string.strf "%s\nTry `%s --help' for more information." (usage ()) exec 17 + 18 + let pp_help ppf () = 19 + let prf = Format.fprintf in 20 + prf ppf "@[<v>@,"; 21 + prf ppf "Commands:@,"; 22 + prf ppf " build [OPTION]...@,"; 23 + prf ppf " @[Build the package, see `Build options' below.@]@,"; 24 + prf ppf " test [OPTION]... [TEST]... [-- [ARG]...]@,"; 25 + prf ppf " @[Run all or the given built package tests, \ 26 + see `Test options' below.@]@,"; 27 + prf ppf " clean [OPTION]...@,"; 28 + prf ppf " @[Clean the package build, see `Clean options' below.@]@,"; 29 + prf ppf " help@,"; 30 + prf ppf " @[Show this help.@]@,"; 31 + prf ppf " ipc VERBOSITY [ARG]...@,"; 32 + prf ppf " @[Interprocess communication with topkg care tools.@]@,@,"; 33 + prf ppf "Options:@,"; 34 + prf ppf " -h, -help, --help@,"; 35 + prf ppf " Show this help.@,"; 36 + prf ppf " -q, --quiet@,"; 37 + prf ppf " Be quiet. Takes over -v.@,"; 38 + prf ppf " -v, --verbose (absent=warning or TOPKG_VERBOSITY env)@,"; 39 + prf ppf " Increase verbosity. Repeatable, more than twice is useless.@,"; 40 + prf ppf " --version@,"; 41 + prf ppf " Show topkg's version information.@,@,"; 42 + prf ppf "Build options:@,"; 43 + prf ppf " -d, --dry-run@,"; 44 + prf ppf " @[Do not run build instructions,@ only@ determine@ and@ \ 45 + write@ the@ opam@ install@ file.@]@,"; 46 + prf ppf " -r ARG, --raw ARG (repeatable)@,"; 47 + prf ppf " @[Do not run build instructions or write@ the@ opam@ \ 48 + install@ file,@ only@ invoke@ the build@ system@ with@ \ 49 + the given ARG argument.@]@,@,"; 50 + prf ppf " @[%a@]@,@," Topkg_conf.pp_keys_cli_opts (); 51 + prf ppf "Test options:@,"; 52 + prf ppf " --build-dir BUILD_DIR (absent=discovered)@,"; 53 + prf ppf " @[Specifies the build directory BUILD_DIR.@]@,"; 54 + prf ppf " -l, --list@,"; 55 + prf ppf " @[Do not run the tests, list them.@]@,@,"; 56 + prf ppf "Clean options:@,"; 57 + prf ppf " --build-dir BUILD_DIR (absent=discovered)@,"; 58 + prf ppf " @[Specifies the build directory BUILD_DIR.@]@,"; 59 + prf ppf " -n NAME, --pkg-name NAME (absent=discovered)@,"; 60 + prf ppf " @[The name NAME of the package (and hence the opam \ 61 + install file).@ If absent provided by the package@ \ 62 + description.@]@,"; 63 + () 64 + 65 + (* Commands *) 66 + 67 + let help_cmd pkg = 68 + let pr = Format.printf in 69 + let name = Topkg_pkg.name pkg in 70 + pr "%s's %s - Describes the %s package.@." name exec name; 71 + pr "%s@." (usage ()); 72 + pr "%a@." pp_help (); 73 + Ok 0 74 + 75 + let version_cmd pkg = print_endline "topkg %%VERSION%%"; Ok 0 76 + 77 + let build_cmd pkg kind args = 78 + let log_conf c = 79 + Topkg_log.info (fun m -> m "Build configuration:@\n%a" Topkg_conf.dump c) 80 + in 81 + let adjust_pkg_to_conf pkg c = 82 + let name = Topkg_conf.pkg_name c in 83 + let build_dir = Topkg_conf.build_dir c in 84 + Topkg_pkg.with_name_and_build_dir pkg ~name ~build_dir 85 + in 86 + let pkg_name = Topkg_pkg.name pkg in 87 + let build_dir = Topkg_pkg.build_dir pkg in 88 + Topkg_conf.of_cli_args ~pkg_name ~build_dir args 89 + >>= fun c -> Ok (log_conf c; adjust_pkg_to_conf pkg c) 90 + >>= fun pkg -> Topkg_pkg.build pkg ~kind c `Host_os 91 + 92 + let test_cmd pkg name build_dir list tests args = 93 + let pkg = Topkg_pkg.with_name_and_build_dir ?name ?build_dir pkg in 94 + Topkg_pkg.test pkg ~list ~tests ~args 95 + 96 + let clean_cmd pkg name build_dir = 97 + let pkg = Topkg_pkg.with_name_and_build_dir ?name ?build_dir pkg in 98 + Topkg_pkg.clean pkg `Host_os 99 + 100 + let ipc_cmd pkg args = 101 + Topkg_ipc.write_answer (Topkg_cmd.of_list args) pkg >>= fun () -> Ok 0 102 + 103 + let run_cmd pkg cmd args = match cmd with 104 + | `Help -> help_cmd pkg 105 + | `Version -> version_cmd pkg 106 + | `Build kind -> build_cmd pkg kind args 107 + | `Test (pkg_name, bdir, list, tests, args) -> 108 + test_cmd pkg pkg_name bdir list tests args 109 + | `Clean (pkg_name, bdir) -> clean_cmd pkg pkg_name bdir 110 + | `Ipc -> ipc_cmd pkg args 111 + 112 + (* Cli interface *) 113 + 114 + let default_verb = Topkg_log.level () 115 + let incr_verb = function 116 + | Some Topkg_log.Warning -> Some Topkg_log.Info 117 + | Some Topkg_log.Info -> Some Topkg_log.Debug 118 + | v -> v 119 + 120 + let is_opt s = Topkg_string.(is_prefix ~affix:"-" s || is_prefix ~affix:"--" s) 121 + 122 + let parse_cli_help_version_verbosity args = 123 + let is_help = function "-h" | "--help" | "-help" -> true | _ -> false in 124 + let is_verb = function "-v" | "--verbose" -> true | _ -> false in 125 + let is_quiet = function "-q" | "--quiet" -> true | _ -> false in 126 + let is_version = function "--version" -> true | _ -> false in 127 + let rec loop cmd verb acc = function 128 + | a :: args when is_help a -> loop `Help verb acc args 129 + | a :: args when is_verb a -> loop cmd (incr_verb verb) acc args 130 + | a :: args when is_quiet a -> loop cmd None acc args 131 + | a :: args when is_version a -> 132 + let cmd = if cmd = `Help then `Help else `Version in 133 + loop cmd verb acc args 134 + | ("--" :: _ | [] as rest) -> cmd, verb, List.rev (List.rev_append rest acc) 135 + | a :: args -> loop cmd verb (a :: acc) args 136 + in 137 + match args with 138 + | "help" :: args -> loop `Help default_verb [] args 139 + | args -> loop `Cmd default_verb [] args 140 + 141 + let parse_build_args args = 142 + let rec loop dry_run raws acc = function 143 + | ("-r" | "--raw" as opt) :: args -> 144 + if args = [] then R.error_msgf "option `%s': missing argument" opt else 145 + loop dry_run (List.hd args :: raws) acc (List.tl args) 146 + | ("-d" | "--dry-run") :: args -> 147 + loop true raws acc args 148 + | a :: args -> 149 + loop dry_run raws (a :: acc) args 150 + | [] -> 151 + let args = List.rev acc in 152 + match dry_run, raws with 153 + | false, [] -> Ok (`Build, args) 154 + | false, raws -> Ok (`Raw (List.rev raws), args) 155 + | true, [] -> Ok (`Dry_run, args) 156 + | true, _ -> 157 + R.error_msg "option `--dry-run' and `--raw' are mutually exclusive" 158 + in 159 + loop false [] [] args 160 + 161 + let parse_test_args args = 162 + let rec loop pkg_name build_dir list tests = function 163 + | ("--" :: args) -> 164 + Ok (pkg_name, build_dir, list, List.rev tests, 165 + Some (Topkg_cmd.of_list args)) 166 + | [] -> Ok (pkg_name, build_dir, list, List.rev tests, None) 167 + | "--build-dir" :: bdir :: args -> loop pkg_name (Some bdir) list tests args 168 + | ("-l" | "--list") :: args -> loop pkg_name build_dir true tests args 169 + | ("-n" | "--pkg-name") :: n :: args -> 170 + loop (Some n) build_dir list tests args 171 + | a :: args -> 172 + if is_opt a then R.error_msgf "unknown option `%s'" a else 173 + loop pkg_name build_dir list (a :: tests) args 174 + in 175 + loop None None false [] args 176 + 177 + let parse_clean_args args = 178 + let rec loop pkg_name build_dir = function 179 + | "--build-dir" :: bdir :: args -> loop pkg_name (Some bdir) args 180 + | ("-n" | "--pkg-name") :: n :: args -> loop (Some n) build_dir args 181 + | [] -> Ok (pkg_name, build_dir) 182 + | a :: args -> 183 + R.error_msgf "don't know what to do with `%s'" a 184 + in 185 + loop None None args 186 + 187 + let parse_ipc_args args = 188 + begin match args with 189 + | [] -> R.error_msg "missing verbosity and IPC arguments" 190 + | verbosity :: args -> 191 + Topkg_log.level_of_string verbosity 192 + >>= fun verbosity -> match args with 193 + | [] -> R.error_msg "no IPC arguments specified" 194 + | args -> Ok (verbosity, args) 195 + end 196 + |> R.reword_error_msg ~replace:true (fun e -> R.msgf "ipc: %s" e) 197 + 198 + let parse_cli () = 199 + let args = List.tl (Array.to_list Sys.argv) in 200 + begin match args with 201 + | "ipc" :: args -> (* args may be data so don't interpret anything *) 202 + parse_ipc_args args >>= fun (verb, args) -> Ok (`Ipc, verb, args) 203 + | args -> 204 + match parse_cli_help_version_verbosity args with 205 + | `Help, _, _ as cmd -> Ok cmd 206 + | `Version, _, _ as cmd -> Ok cmd 207 + | `Cmd, verbosity, args -> 208 + match args with 209 + | "build" :: args -> 210 + parse_build_args args >>= fun (kind, args) -> 211 + Ok (`Build kind, verbosity, args) 212 + | "test" :: args -> 213 + parse_test_args args 214 + >>= fun (pkg_name, bdir, list, tests, args) -> 215 + Ok (`Test (pkg_name, bdir, list, tests, args), verbosity, []) 216 + | "clean" :: args -> 217 + parse_clean_args args >>= fun (pkg_name, bdir) -> 218 + Ok (`Clean (pkg_name, bdir), verbosity, []) 219 + | cmd :: _ -> R.error_msgf "Unknown command '%s'" cmd 220 + | [] -> R.error_msg "No command specified" 221 + end 222 + |> R.reword_error_msg ~replace:true (fun e -> R.msgf "%s\n%s" e (try_help ())) 223 + 224 + (* Main *) 225 + 226 + let check_log ret = 227 + let msg = format_of_string "Package description has %d %s, see log above." in 228 + let log kind count = 229 + if count > 0 then match kind with 230 + | `Errs -> Topkg_log.err (fun m -> m msg count "error(s)") 231 + | `Warns -> Topkg_log.warn (fun m -> m msg count "warning(s)") 232 + in 233 + let errs = Topkg_log.err_count () in 234 + let warns = Topkg_log.warn_count () in 235 + log `Errs errs; 236 + log `Warns warns; 237 + Ok (if ret + errs > 0 then 1 else 0) 238 + 239 + let setup_log_level level = 240 + Topkg_log.set_level level; 241 + Topkg_log.info (fun m -> m "topkg %%VERSION%%, running main"); 242 + Ok () 243 + 244 + let main pkg = 245 + begin 246 + parse_cli () 247 + >>= fun (cmd, log_level, args) -> setup_log_level log_level 248 + >>= fun () -> run_cmd pkg cmd args 249 + >>= fun ret -> match cmd with 250 + | `Build _ -> (check_log ret) 251 + | _ -> Ok ret 252 + end 253 + |> Topkg_log.on_error_msg ~use:(fun () -> 1) 254 + 255 + (* Main execution handling. 256 + 257 + The call to [describe] runs the [main] function unless prevented by 258 + a previous call to [disable]. 259 + 260 + We install an [at_exit] function that checks whether either [main] 261 + ran or [disable] was called and report an error message if that is 262 + not the case; this is what happens, for example, in case of syntax 263 + error in the package description. Unfortunately because of 264 + http://caml.inria.fr/mantis/view.php?id=7178 we cannot return with 265 + a non-zero exit code at that point. We could by raising an exception 266 + but it gets confusing, see http://caml.inria.fr/mantis/view.php?id=7253. *) 267 + 268 + let must_run_main = ref true 269 + let disable () = must_run_main := false 270 + 271 + let pkg = ref None 272 + let describe 273 + ?delegate ?readmes ?licenses ?change_logs ?metas ?opams ?lint_files 274 + ?lint_custom ?distrib ?publish ?build name installs 275 + = 276 + match !pkg with 277 + | Some _ -> invalid_arg "Topkg.Pkg.describe already called once" 278 + | None -> 279 + let p = 280 + Topkg_pkg.v ?delegate ?readmes ?licenses ?change_logs ?metas ?opams 281 + ?lint_files ?lint_custom ?distrib ?publish ?build name installs 282 + in 283 + pkg := Some p; 284 + if !must_run_main then (must_run_main := false; exit (main p)) else () 285 + 286 + let check_something_useful_happened () = 287 + if !must_run_main then 288 + Topkg_log.err (fun m -> m "%a" Topkg_string.pp_text 289 + "No package description found. A syntax error may have \ 290 + occured or did you forget to call Topkg.Pkg.describe ?") 291 + 292 + let () = at_exit check_something_useful_happened
+26
vendor/opam/topkg/src/topkg_main.mli
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** Entry point for [pkg.ml] files. *) 7 + 8 + (** {1 Main} *) 9 + 10 + open Topkg_result 11 + 12 + val describe : 13 + ?delegate:Topkg_cmd.t -> 14 + ?readmes:Topkg_pkg.std_file list -> 15 + ?licenses:Topkg_pkg.std_file list -> 16 + ?change_logs:Topkg_pkg.std_file list -> 17 + ?metas:Topkg_pkg.meta_file list -> 18 + ?opams:Topkg_pkg.opam_file list -> 19 + ?lint_files:Topkg_fpath.t list option -> 20 + ?lint_custom:(unit -> R.msg result list) -> 21 + ?distrib:Topkg_distrib.t -> 22 + ?publish:Topkg_publish.t -> 23 + ?build:Topkg_build.t -> 24 + string -> (Topkg_conf.t -> Topkg_install.t list result) -> unit 25 + 26 + val disable : unit -> unit
+114
vendor/opam/topkg/src/topkg_opam.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + open Topkg_result 7 + 8 + (* opam File *) 9 + 10 + module File = struct 11 + type t = (string * string list) list 12 + 13 + let codec = 14 + Topkg_codec.version 0 @@ 15 + Topkg_codec.with_kind "opam fields" @@ 16 + Topkg_codec.(list (pair string (list string))) 17 + 18 + let topkg_cmd = Topkg_cmd.v "topkg" 19 + let topkg_cmd_available () = 20 + Topkg_os.Cmd.must_exist topkg_cmd 21 + |> R.reword_error_msg ~replace:true 22 + (fun m -> R.msgf "%s. Did you install topkg-care ?" m) 23 + 24 + let ipc_cmd file = 25 + (* Propagate the log level to the IPC call *) 26 + let level = Topkg_log.(level_to_string (level ())) in 27 + let verbosity = Topkg_string.strf "--verbosity=%s" level in 28 + Topkg_cmd.(v "ipc" % verbosity % "opam-fields" % file) 29 + 30 + let fields file = 31 + begin 32 + let cmd = Topkg_cmd.(topkg_cmd %% ipc_cmd file) in 33 + topkg_cmd_available () 34 + >>= fun _ -> Topkg_os.File.must_exist file 35 + >>= fun _ -> Topkg_os.Cmd.(run_out cmd |> to_string) 36 + >>= fun s -> (Topkg_codec.dec_result codec s) 37 + end 38 + |> R.reword_error_msg ~replace:true 39 + (fun msg -> R.msgf "opam fields of %s: %s" file msg) 40 + end 41 + 42 + (* opam install file *) 43 + 44 + module Install = struct 45 + 46 + type field = 47 + [ `Bin 48 + | `Doc 49 + | `Etc 50 + | `Lib 51 + | `Lib_root 52 + | `Libexec 53 + | `Libexec_root 54 + | `Man 55 + | `Misc 56 + | `Sbin 57 + | `Share 58 + | `Share_root 59 + | `Stublibs 60 + | `Toplevel 61 + | `Unknown of string ] 62 + 63 + let field_to_string = function 64 + | `Bin -> "bin" 65 + | `Doc -> "doc" 66 + | `Etc -> "etc" 67 + | `Lib -> "lib" 68 + | `Lib_root -> "lib_root" 69 + | `Libexec -> "libexec" 70 + | `Libexec_root -> "libexec_root" 71 + | `Man -> "man" 72 + | `Misc -> "misc" 73 + | `Sbin -> "sbin" 74 + | `Share -> "share" 75 + | `Share_root -> "share_root" 76 + | `Stublibs -> "stublibs" 77 + | `Toplevel -> "toplevel" 78 + | `Unknown name -> name 79 + 80 + type move = { src : string; dst : string option; maybe : bool; } 81 + 82 + let move ?(maybe = false) ?dst src = { src; dst; maybe } 83 + 84 + type t = [ `Header of string option ] * (field * move) list 85 + 86 + let to_string (`Header h, mvs) = 87 + let b = Buffer.create 1024 in 88 + let pr b fmt = Printf.bprintf b fmt in 89 + let pr_header b = function None -> () | Some h -> pr b "# %s\n\n" h in 90 + let pr_src b src maybe = 91 + pr b " \"%s%s\"" (if maybe then "?" else "") src 92 + in 93 + let pr_dst b dst = match dst with 94 + | None -> () 95 + | Some dst -> pr b " {\"%s\"}" dst 96 + in 97 + let pr_field_end b last = if last <> "" (* not start *) then pr b " ]\n" in 98 + let pr_field b last field = 99 + if last = field then pr b "\n" else 100 + (pr_field_end b last; pr b "%s: [\n" field) 101 + in 102 + let pr_move b last (field, { src; dst; maybe }) = 103 + pr_field b last field; 104 + pr_src b src maybe; 105 + pr_dst b dst; 106 + field 107 + in 108 + let sortable (field, mv) = (field_to_string field, mv) in 109 + let moves = List.sort compare (List.rev_map sortable mvs) in 110 + pr_header b h; 111 + let last = List.fold_left (pr_move b) "" moves in 112 + pr_field_end b last; 113 + Buffer.contents b 114 + end
+63
vendor/opam/topkg/src/topkg_opam.mli
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** opam helpers. 7 + 8 + See also {!Topkg.Private.Opam}. *) 9 + 10 + open Topkg_result 11 + 12 + module File : sig 13 + type t = (string * string list) list 14 + val codec : t Topkg_codec.t 15 + val fields : Topkg_fpath.t -> t result 16 + end 17 + 18 + (** opam install file. 19 + 20 + A module to generate opam install files. 21 + 22 + {b Reference}. 23 + {{:http://opam.ocaml.org/doc/manual/dev-manual.html#sec25} 24 + Syntax and semantics} of opam install files. *) 25 + module Install : sig 26 + 27 + (** {1 opam install files} *) 28 + 29 + type field = 30 + [ `Bin 31 + | `Doc 32 + | `Etc 33 + | `Lib 34 + | `Lib_root 35 + | `Libexec 36 + | `Libexec_root 37 + | `Man 38 + | `Misc 39 + | `Sbin 40 + | `Share 41 + | `Share_root 42 + | `Stublibs 43 + | `Toplevel 44 + | `Unknown of string ] 45 + (** The type for opam install file fields. *) 46 + 47 + type move 48 + (** The type for file moves. *) 49 + 50 + val move : ?maybe:bool -> ?dst:Topkg_fpath.t -> Topkg_fpath.t -> move 51 + (** [move ~maybe ~dst src] moves [src] to [dst], where [dst] is a 52 + path relative to the directory corresponding to the 53 + {{!field}field}. If [maybe] is [true] (defaults to [false]), 54 + then [src] may not exist, otherwise an install error will occur 55 + if the file doesn't exist. *) 56 + 57 + type t = [ `Header of string option ] * (field * move) list 58 + (** The type for opam install files. An optional starting header 59 + comment and a list of field moves. *) 60 + 61 + val to_string : t -> string 62 + (** [to_string t] is [t] as syntactically valid opam install file. *) 63 + end
+282
vendor/opam/topkg/src/topkg_os.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + open Topkg_result 7 + 8 + (* Environment variables. *) 9 + 10 + module Env = struct 11 + let var name = try Some (Sys.getenv name) with Not_found -> None 12 + let opt_var name ~absent = match var name with None -> absent | Some v -> v 13 + end 14 + 15 + (* Directory operations *) 16 + 17 + module Dir = struct 18 + 19 + (* Existence *) 20 + 21 + let exists dir = 22 + try Ok (Sys.(file_exists dir && is_directory dir)) with 23 + | Sys_error e -> R.error_msgf "%s: %s" dir e 24 + 25 + let must_exist dir = exists dir >>= function 26 + | true -> Ok dir 27 + | false -> R.error_msgf "%s: no such directory" dir 28 + 29 + (* Current working directory *) 30 + 31 + let current () = try Ok (Sys.getcwd ()) with Sys_error e -> R.error_msg e 32 + let set_current d = 33 + try Ok (Sys.chdir d) with Sys_error e -> R.error_msgf "%s: %s" d e 34 + 35 + let with_current dir f v = 36 + current () 37 + >>= fun original -> set_current dir 38 + >>= fun () -> let r = f v in set_current original 39 + >>= fun () -> Ok r 40 + 41 + (* Directory contents *) 42 + 43 + let contents ?(dotfiles = false) ?(rel = false) p = 44 + try 45 + let files = Array.to_list @@ Sys.readdir p in 46 + if rel && dotfiles then Ok files else 47 + let rec loop acc = function 48 + | [] -> List.rev acc 49 + | f :: fs -> 50 + let acc = 51 + if not dotfiles && Topkg_string.is_prefix ~affix:"." f then acc else 52 + if rel then f :: acc else Topkg_fpath.append p f :: acc 53 + in 54 + loop acc fs 55 + in 56 + Ok (loop [] files) 57 + with 58 + | Sys_error e -> R.error_msgf "%s: %s" p e 59 + end 60 + 61 + (* File system operations *) 62 + 63 + module File = struct 64 + 65 + (* Famous file paths *) 66 + 67 + let null = match Sys.os_type with 68 + | "Win32" -> "NUL" 69 + | _ -> "/dev/null" 70 + 71 + let dash = "-" 72 + 73 + (* Existence and deletion *) 74 + 75 + let exists f = 76 + try Ok (Sys.(file_exists f && not (is_directory f))) with 77 + | Sys_error e -> R.error_msgf "%s: %s" f e 78 + 79 + let must_exist f = exists f >>= function 80 + | true -> Ok f 81 + | false -> R.error_msgf "%s: no such file" f 82 + 83 + let delete ?(must_exist = false) f = 84 + try 85 + if not must_exist && not (Sys.file_exists f) then Ok () else 86 + Ok (Sys.remove f) 87 + with 88 + | Sys_error e -> R.error_msgf "%s: %s" f e 89 + 90 + (* Folding over files *) 91 + 92 + let fold ?(skip = fun _ -> false) f acc paths = 93 + let is_dir d = try Sys.is_directory d with Sys_error _ -> false in 94 + let readdir d = 95 + try Array.to_list (Sys.readdir d) with Sys_error _ -> [] 96 + in 97 + let keep p = not (skip p) in 98 + let process acc file = f file acc in 99 + let rec aux f acc = function 100 + | (d :: ds) :: up -> 101 + let paths = List.rev_map (Filename.concat d) (readdir d) in 102 + let paths = List.find_all keep paths in 103 + let dirs, files = List.partition is_dir paths in 104 + let acc = List.fold_left process acc files in 105 + aux f acc (dirs :: ds :: up) 106 + | [] :: [] -> acc 107 + | [] :: up -> aux f acc up 108 + | _ -> assert false 109 + in 110 + let paths = List.find_all keep paths in 111 + let dirs, files = List.partition is_dir paths in 112 + let acc = List.fold_left process acc files in 113 + Ok (aux f acc (dirs :: [])) 114 + 115 + (* Reading and writing *) 116 + 117 + let with_parent_check op op_name file = 118 + let err_no_parent op_name file = 119 + Topkg_string.strf 120 + "%s: Cannot %s file, parent directory does not exist" file op_name 121 + in 122 + (Dir.must_exist (Topkg_fpath.dirname file) 123 + >>= fun _ -> Ok (op file)) 124 + |> R.reword_error @@ fun _ -> `Msg (err_no_parent op_name file) 125 + 126 + let safe_open_in_bin = with_parent_check open_in_bin "read" 127 + let safe_open_out_bin = with_parent_check open_out_bin "write" 128 + 129 + let read file = 130 + try 131 + let close ic = if file = dash then () else close_in_noerr ic in 132 + (if file = dash then Ok stdin else safe_open_in_bin file) >>= fun ic -> 133 + try 134 + let len = in_channel_length ic in 135 + let buf = Bytes.create len in 136 + really_input ic buf 0 len; close ic; 137 + Ok (Bytes.unsafe_to_string buf) 138 + with exn -> close ic; raise exn 139 + with Sys_error e -> R.error_msgf "%s: %s" file e 140 + 141 + let write file s = 142 + try 143 + let close oc = if file = dash then () else close_out_noerr oc in 144 + (if file = dash then Ok stdout else safe_open_out_bin file) >>= fun oc -> 145 + try output_string oc s; flush oc; close oc; Ok () 146 + with exn -> close oc; raise exn 147 + with Sys_error e -> R.error_msgf "%s: %s" file e 148 + 149 + let write_subst file vars s = (* very ugly mister, too lazy to rewrite *) 150 + try 151 + let close oc = if file = dash then () else close_out_noerr oc in 152 + (if file = dash then Ok stdout else safe_open_out_bin file) >>= fun oc -> 153 + try 154 + let start = ref 0 in 155 + let last = ref 0 in 156 + let len = String.length s in 157 + while (!last < len - 4) do 158 + if not (s.[!last] = '%' && s.[!last + 1] = '%') then incr last else 159 + begin 160 + let start_subst = !last in 161 + let last_id = ref (!last + 2) in 162 + let stop = ref false in 163 + while (!last_id < len - 1 && not !stop) do 164 + if not (s.[!last_id] = '%' && s.[!last_id + 1] = '%') then begin 165 + if s.[!last_id] <> ' ' then (incr last_id) else 166 + (stop := true; last := !last_id) 167 + end else begin 168 + let id_start = start_subst + 2 in 169 + let id = String.sub s (id_start) (!last_id - id_start) in 170 + try 171 + let subst = List.assoc id vars in 172 + output oc (Bytes.unsafe_of_string s) 173 + !start (start_subst - !start); 174 + output_string oc subst; 175 + stop := true; 176 + start := !last_id + 2; 177 + last := !last_id + 2; 178 + with Not_found -> 179 + stop := true; 180 + last := !last_id 181 + end 182 + done; 183 + (* we exited the loop because we reached eof *) 184 + if not !stop then last := !last_id 185 + end 186 + done; 187 + output oc (Bytes.unsafe_of_string s) !start (len - !start); 188 + flush oc; 189 + close oc; 190 + Ok () 191 + with exn -> close oc; raise exn 192 + with Sys_error e -> R.error_msgf "%s: %s" file e 193 + 194 + let tmp () = 195 + try 196 + let f = Filename.temp_file (Filename.basename Sys.argv.(0)) "topkg" in 197 + at_exit (fun () -> ignore (delete f)); Ok f 198 + with Sys_error e -> R.error_msg e 199 + end 200 + 201 + (* Running commands *) 202 + 203 + module Cmd = struct 204 + 205 + let err_empty_line = "no command, empty command line" 206 + 207 + let line ?stdout ?stderr cmd = 208 + let strf = Printf.sprintf in 209 + if Topkg_cmd.is_empty cmd then failwith err_empty_line else 210 + let cmd = List.rev_map Filename.quote (Topkg_cmd.to_rev_list cmd) in 211 + let cmd = String.concat " " cmd in 212 + let redirect fd f = strf " %d>%s" fd (Filename.quote f) in 213 + let stdout = match stdout with None -> "" | Some f -> redirect 1 f in 214 + let stderr = match stderr with None -> "" | Some f -> redirect 2 f in 215 + let win_quote = if Sys.win32 then "\"" else "" in 216 + strf "%s%s%s%s%s" win_quote cmd stdout stderr win_quote 217 + 218 + let exec ?stdout ?stderr cmd = 219 + try 220 + let line = line ?stdout ?stderr cmd in 221 + Topkg_log.debug (fun m -> m ~header:"EXEC" "@[<1>[%s]@]" line); 222 + Ok ((), (cmd, `Exited (Sys.command line))) 223 + with Sys_error e | Failure e -> R.error_msg e 224 + 225 + (* Command existence *) 226 + 227 + let test_cmd = match Sys.os_type with 228 + | "Win32" -> Topkg_cmd.v "where" 229 + | _ -> Topkg_cmd.(v "command" % "-v") 230 + 231 + let cmd_bin cmd = 232 + try List.hd (Topkg_cmd.to_list cmd) with 233 + | Failure _ -> failwith err_empty_line 234 + 235 + let exists cmd = 236 + try 237 + let bin = cmd_bin cmd in 238 + let cmd = Topkg_cmd.(test_cmd % bin) in 239 + match exec ~stdout:File.null ~stderr:File.null cmd with 240 + | Ok (_, (_, `Exited 0)) -> Ok true 241 + | Ok _ -> Ok false 242 + | Error _ as e -> e 243 + with 244 + Failure e -> R.error_msg e 245 + 246 + let must_exist cmd = exists cmd >>= function 247 + | false -> R.error_msgf "%s: no such command" (cmd_bin cmd) 248 + | true -> Ok cmd 249 + 250 + (* Running commands *) 251 + 252 + type run_status = Topkg_cmd.t * [ `Exited of int ] 253 + 254 + let success r = r >>= function 255 + | (v, (_, `Exited 0)) -> Ok v 256 + | (v, (cmd, `Exited c)) -> 257 + R.error_msgf "cmd %a: exited with %d" Topkg_cmd.dump cmd c 258 + 259 + let run ?err:stderr cmd = exec ?stderr cmd |> success 260 + let run_status ?err:stderr cmd = 261 + exec ?stderr cmd >>= function ((), (_, st)) -> Ok st 262 + 263 + type run_out = { cmd : Topkg_cmd.t; err : Topkg_fpath.t option } 264 + 265 + let out_string ?(trim = true) o = 266 + File.tmp () 267 + >>= fun file -> exec ?stderr:o.err ~stdout:file o.cmd 268 + >>= fun ((), st) -> File.read file 269 + >>= fun out -> Ok ((if trim then String.trim out else out), st) 270 + 271 + let out_lines ?trim o = 272 + out_string ?trim o >>= function (s, st) -> 273 + Ok ((if s = "" then [] else Topkg_string.cuts ~sep:'\n' s), st) 274 + 275 + let out_file stdout o = exec ?stderr:o.err ~stdout o.cmd 276 + let out_stdout o = exec ?stderr:o.err ?stdout:None o.cmd 277 + 278 + let to_string ?trim o = out_string ?trim o |> success 279 + let to_lines ?trim o = out_lines ?trim o |> success 280 + let to_file stdout o = out_file stdout o |> success 281 + let run_out ?err cmd = { cmd; err } 282 + end
+73
vendor/opam/topkg/src/topkg_os.mli
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** OS interaction. 7 + 8 + Abridged [bos]. See {!Topkg.OS} for documentation. *) 9 + 10 + (** {1 OS} *) 11 + 12 + open Topkg_result 13 + 14 + module Env : sig 15 + val var : string -> string option 16 + val opt_var : string -> absent:string -> string 17 + end 18 + 19 + module File : sig 20 + val null : Topkg_fpath.t 21 + val dash : Topkg_fpath.t 22 + 23 + val exists : Topkg_fpath.t -> bool result 24 + val must_exist : Topkg_fpath.t -> Topkg_fpath.t result 25 + val delete : ?must_exist:bool -> Topkg_fpath.t -> unit result 26 + 27 + val fold : 28 + ?skip:(Topkg_fpath.t -> bool) -> (Topkg_fpath.t -> 'a -> 'a) -> 29 + 'a -> Topkg_fpath.t list -> 'a result 30 + 31 + val read : Topkg_fpath.t -> string result 32 + val write : Topkg_fpath.t -> string -> unit result 33 + val write_subst : 34 + Topkg_fpath.t -> (string * string) list -> string -> unit result 35 + 36 + val tmp : unit -> Topkg_fpath.t result 37 + end 38 + 39 + module Dir : sig 40 + val exists : Topkg_fpath.t -> bool result 41 + val must_exist : Topkg_fpath.t -> Topkg_fpath.t result 42 + 43 + val current : unit -> Topkg_fpath.t result 44 + val set_current : Topkg_fpath.t -> unit result 45 + val with_current : Topkg_fpath.t -> ('a -> 'b) -> 'a -> 'b result 46 + 47 + val contents : 48 + ?dotfiles:bool -> ?rel:bool -> Topkg_fpath.t -> Topkg_fpath.t list result 49 + end 50 + 51 + module Cmd : sig 52 + val exists : Topkg_cmd.t -> bool result 53 + val must_exist : Topkg_cmd.t -> Topkg_cmd.t result 54 + 55 + val run : ?err:Topkg_fpath.t -> Topkg_cmd.t -> unit result 56 + val run_status : ?err:Topkg_fpath.t -> Topkg_cmd.t -> [`Exited of int] result 57 + 58 + 59 + type run_status = Topkg_cmd.t * [`Exited of int ] 60 + val success : ('a * run_status) result -> 'a result 61 + 62 + type run_out 63 + 64 + val out_string : ?trim:bool -> run_out -> (string * run_status) result 65 + val out_lines : ?trim:bool -> run_out -> (string list * run_status) result 66 + val out_file : Topkg_fpath.t -> run_out -> (unit * run_status) result 67 + val out_stdout : run_out -> (unit * run_status) result 68 + 69 + val to_string : ?trim:bool -> run_out -> string result 70 + val to_lines : ?trim:bool -> run_out -> string list result 71 + val to_file : Topkg_fpath.t -> run_out -> unit result 72 + val run_out : ?err:Topkg_fpath.t -> Topkg_cmd.t -> run_out 73 + end
+325
vendor/opam/topkg/src/topkg_pkg.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + open Topkg_result 7 + 8 + type std_file = Topkg_fpath.t * bool 9 + let std_file ?(install = true) file = file, install 10 + 11 + type meta_file = std_file * bool 12 + let meta_file ?(lint = true) ?install file = 13 + std_file ?install file, lint 14 + 15 + type opam_file = std_file * bool * string list option 16 + let opam_file ?(lint = true) ?(lint_deps_excluding = Some []) ?install file = 17 + std_file ?install file, lint, lint_deps_excluding 18 + 19 + let std_files_installs readmes licenses change_logs metas opams = 20 + let add field (p, install) acc = if install then field p :: acc else acc in 21 + List.fold_right (add Topkg_install.doc) readmes @@ 22 + List.fold_right (add Topkg_install.doc) licenses @@ 23 + List.fold_right (add Topkg_install.doc) change_logs @@ 24 + List.fold_right (fun (m, _) -> add Topkg_install.lib m) metas @@ 25 + List.fold_right (fun (o, _, _) -> add Topkg_install.lib o) opams @@ 26 + [] 27 + 28 + type t = 29 + { name : string; 30 + delegate : Topkg_cmd.t option; 31 + readmes : std_file list; 32 + licenses : std_file list; 33 + change_logs : std_file list; 34 + metas : meta_file list; 35 + opams : opam_file list; 36 + lint_files : Topkg_fpath.t list option; 37 + lint_custom :(unit -> R.msg result list) option; 38 + distrib : Topkg_distrib.t; 39 + publish : Topkg_publish.t; 40 + build : Topkg_build.t; 41 + installs : Topkg_conf.t -> Topkg_install.t list result; } 42 + 43 + let v 44 + ?delegate 45 + ?(readmes = [("README.md", true)]) 46 + ?(licenses = [("LICENSE.md", true)]) 47 + ?(change_logs = [("CHANGES.md", true)]) 48 + ?(metas = [meta_file "pkg/META"]) 49 + ?(opams = [opam_file "opam"]) 50 + ?(lint_files = Some []) 51 + ?lint_custom 52 + ?(distrib = Topkg_distrib.v ()) 53 + ?(publish = Topkg_publish.v ()) 54 + ?(build = Topkg_build.v ()) 55 + name installs 56 + = 57 + { name; delegate; readmes; licenses; change_logs; metas; opams; lint_files; 58 + lint_custom; distrib; publish; build; installs } 59 + 60 + let empty = v "" (fun _ -> Ok []) 61 + let with_name_and_build_dir ?name ?build_dir p = 62 + let name = match name with None -> p.name | Some n -> n in 63 + let build = match build_dir with 64 + | None -> p.build 65 + | Some build_dir -> Topkg_build.with_dir p.build build_dir 66 + in 67 + { p with name; build } 68 + 69 + let name p = p.name 70 + let delegate p = p.delegate 71 + let readmes p = List.map fst p.readmes 72 + let change_logs p = List.map fst p.change_logs 73 + let licenses p = List.map fst p.licenses 74 + let distrib p = p.distrib 75 + let build p = p.build 76 + let install p c = 77 + p.installs c >>= fun installs -> 78 + let std_files = 79 + std_files_installs p.readmes p.licenses p.change_logs p.metas p.opams 80 + in 81 + Ok (List.rev_append std_files installs) 82 + 83 + let std_files p = 84 + List.map fst p.readmes @ List.map fst p.licenses @ 85 + List.map fst p.change_logs @ 86 + List.(rev_append (rev_map (fun (m, _) -> fst m) p.metas) 87 + (rev (rev_map (fun (o, _, _) -> fst o) p.opams))) 88 + 89 + let build_dir p = Topkg_build.dir p.build 90 + let opam ~name p = 91 + let has_name (o, _, _) = Topkg_fpath.(basename @@ rem_ext @@ fst o) = name in 92 + let opams = p.opams in 93 + match try Some (List.find has_name opams) with Not_found -> None with 94 + | Some (opam, _, _) -> fst opam 95 + | None -> 96 + if name <> p.name then 97 + Topkg_log.warn 98 + (fun m -> m "No opam file for %s, using 'opam'" p.name); 99 + "opam" 100 + 101 + let codec = 102 + let stub _ = invalid_arg "not executable outside package definition" in 103 + let string_list_option = Topkg_codec.(option @@ list string) in 104 + let std_file = Topkg_codec.(pair string bool) in 105 + let meta_file = Topkg_codec.(pair std_file bool) in 106 + let opam_file = Topkg_codec.(t3 std_file bool (string_list_option)) in 107 + (* fields *) 108 + let name = Topkg_codec.(with_kind "name" @@ string) in 109 + let delegate = Topkg_codec.(with_kind "delegate" @@ option cmd) in 110 + let readmes = Topkg_codec.(with_kind "readmes" @@ list std_file) in 111 + let licenses = Topkg_codec.(with_kind "licenses" @@ list std_file) in 112 + let change_logs = Topkg_codec.(with_kind "change_logs" @@ list std_file) in 113 + let metas = Topkg_codec.(with_kind "metas" @@ list meta_file) in 114 + let opams = Topkg_codec.(with_kind "opams" @@ list opam_file) in 115 + let lint_files = Topkg_codec.(with_kind "lint_files" @@ string_list_option) in 116 + let lint_custom = 117 + let kind = "lint_custom" in 118 + let enc = function None -> "\x00" | Some _ -> "\x01" in 119 + let dec = function 120 + | "\x00" -> None | "\x01" -> Some stub | s -> Topkg_codec.err ~kind s in 121 + Topkg_codec.v ~kind ~enc ~dec 122 + in 123 + let distrib = Topkg_distrib.codec in 124 + let publish = Topkg_publish.codec in 125 + let build = Topkg_build.codec in 126 + let fields = 127 + (fun p -> (p.name, p.delegate, p.readmes, p.licenses, p.change_logs), 128 + (p.metas, p.opams, p.lint_files, p.lint_custom, p.distrib), 129 + (p.publish, p.build)), 130 + (fun ((name, delegate, readmes, licenses, change_logs), 131 + (metas, opams, lint_files, lint_custom, distrib), 132 + (publish, build)) -> 133 + { name; delegate; readmes; licenses; change_logs; 134 + metas; opams; lint_files; lint_custom; distrib; 135 + publish; build; installs = stub }) 136 + in 137 + Topkg_codec.version 0 @@ 138 + Topkg_codec.(view ~kind: "package" fields 139 + (t3 140 + (t5 name delegate readmes licenses change_logs) 141 + (t5 metas opams lint_files lint_custom distrib) 142 + (t2 publish build))) 143 + (* Distrib *) 144 + 145 + let distrib_version_opam_files p ~version ~opam_adds = 146 + let version = Topkg_string.drop_initial_v version in 147 + let version_opam_file acc ((file, _), _, _) = 148 + acc 149 + >>= fun () -> Topkg_os.File.read file 150 + >>= fun o -> Ok (Topkg_string.strf "version: \"%s\"\n%s%s" 151 + version opam_adds o) 152 + >>= fun o -> Topkg_os.File.write file o 153 + in 154 + List.fold_left version_opam_file (Ok ()) p.opams 155 + 156 + let distrib_uri p = Topkg_distrib.uri p.distrib 157 + let distrib_prepare p ~dist_build_dir ~name ~version ~opam ~opam_adds = 158 + let d = distrib p in 159 + let ws = Topkg_distrib.watermarks d in 160 + let ws_defs = Topkg_distrib.define_watermarks ws ~name ~version ~opam in 161 + Topkg_os.Dir.set_current dist_build_dir 162 + >>= fun () -> Topkg_distrib.files_to_watermark d () 163 + >>= fun files -> Topkg_distrib.watermark_files ws_defs files 164 + >>= fun () -> distrib_version_opam_files p ~version ~opam_adds 165 + >>= fun () -> Topkg_distrib.massage d () 166 + >>= fun () -> Topkg_distrib.exclude_paths d () 167 + 168 + let distrib_prepare_pin p = 169 + let name = name p in 170 + let opam = opam ~name p in 171 + let d = distrib p in 172 + let ws = Topkg_distrib.watermarks d in 173 + Topkg_vcs.get () 174 + >>= fun repo -> Topkg_vcs.describe ~dirty:true repo 175 + >>= fun version -> Ok(Topkg_distrib.define_watermarks ws ~name ~version ~opam) 176 + >>= fun ws_defs -> Topkg_distrib.files_to_watermark d () 177 + >>= fun files -> Topkg_distrib.watermark_files ws_defs files 178 + >>= fun () -> distrib_version_opam_files p ~version ~opam_adds:"" 179 + >>= fun () -> Topkg_distrib.massage d () 180 + 181 + (* Publish *) 182 + 183 + let publish_artefacts p = Topkg_publish.artefacts p.publish 184 + 185 + (* Test *) 186 + 187 + let tests_file p = 188 + Topkg_fpath.(build_dir p // Topkg_string.strf "_topkg-%s.tests" (name p)) 189 + 190 + let tests_file_codec = Topkg_codec.(option @@ list Topkg_test.codec) 191 + let write_tests_file p tests = 192 + Topkg_codec.write (tests_file p) tests_file_codec tests 193 + 194 + let tests_run tests ~args = 195 + let run_test root_dir acc t = 196 + let exec = Topkg_test.exec t in 197 + let args = match args with Some args -> args | None -> Topkg_test.args t in 198 + let cmd = Topkg_cmd.(v Topkg_fpath.(root_dir // exec) %% args) in 199 + let run () = 200 + Topkg_log.info (fun m -> m "Running test %s" exec); 201 + (Topkg_os.Cmd.run_status cmd >>| fun (`Exited c) -> c) 202 + |> Topkg_log.on_error_msg ~use:(fun _ -> 1) 203 + in 204 + let res = match Topkg_test.dir t with 205 + | None -> Ok (run ()) 206 + | Some dir -> Topkg_os.Dir.with_current dir run () 207 + in 208 + acc + (res |> Topkg_log.on_error_msg ~use:(fun _ -> 1)) 209 + in 210 + Topkg_os.Dir.current () 211 + >>= fun root_dir -> match List.fold_left (run_test root_dir) 0 tests with 212 + | 0 -> Ok 0 213 + | n -> Topkg_log.err (fun m -> m "Some tests failed."); Ok 1 214 + 215 + let tests_list tests = 216 + let list_test t = 217 + let args = Topkg_cmd.to_list (Topkg_test.args t) in 218 + let args = String.concat " " (List.map Filename.quote args) in 219 + let exec = Topkg_test.exec t in 220 + let run = if Topkg_test.run t then "[default]" else "" in 221 + print_endline (Topkg_string.strf "%s %s %s" exec args run) 222 + in 223 + List.iter list_test tests 224 + 225 + let tests_select queries tests = match queries with 226 + | [] -> List.filter (fun t -> Topkg_test.run t) tests 227 + | qs -> 228 + let query q = 229 + let add_test acc t = 230 + let exec = Topkg_test.exec t in 231 + if q = exec then t :: acc else 232 + let exec = Topkg_fpath.basename exec in 233 + if q = exec then t :: acc else 234 + let exec = Topkg_fpath.rem_ext exec in 235 + if q = exec then t :: acc else acc 236 + in 237 + match List.fold_left add_test [] tests with 238 + | [] -> Topkg_log.warn (fun m -> m "No test matching `%s'" q); [] 239 + | acc -> List.rev acc 240 + in 241 + List.flatten (List.map query queries) 242 + 243 + let test p ~list ~tests:queries ~args = 244 + let tests_file = tests_file p in 245 + Topkg_os.File.exists tests_file >>= function 246 + | false -> 247 + R.error_msgf "%s: no such file. Did you forget to build the package ?" 248 + tests_file 249 + | true -> 250 + Topkg_codec.read tests_file tests_file_codec 251 + >>= function 252 + | None -> 253 + R.error_msgf "The tests were not built." 254 + | Some tests -> 255 + match list with 256 + | true -> tests_list tests; Ok 0 257 + | false -> 258 + let no_test = format_of_string "No tests to run." in 259 + match tests_select queries tests with 260 + | [] when queries = [] -> Topkg_log.app (fun m -> m no_test); Ok 0 261 + | [] -> Topkg_log.err (fun m -> m no_test); Ok 1 262 + | tests -> tests_run tests ~args 263 + 264 + (* Build *) 265 + 266 + let install_file p = p.name ^ ".install" 267 + 268 + let write_opam_install_file p install = 269 + let file = install_file p in 270 + let install = Topkg_opam.Install.to_string install in 271 + Topkg_os.File.write file install 272 + >>| fun () -> Topkg_log.info (fun m -> m "Wrote opam install file %s" file) 273 + 274 + let run_build_hook kind hook c = 275 + (hook c) 276 + |> R.reword_error_msg ~replace:true 277 + (fun e -> R.msgf "%s-build hook failed: %s" kind e) 278 + 279 + let build p ~kind c os = match kind with 280 + | `Raw args -> (Topkg_build.cmd p.build) c os args >>= fun () -> Ok 0 281 + | `Dry_run | `Build -> 282 + install p c 283 + >>= fun is -> Ok (Topkg_install.to_build ~header:p.name c os is) 284 + >>= fun (targets, install, tests) -> match kind = `Dry_run with 285 + | true -> write_opam_install_file p install >>= fun () -> Ok 0 286 + | false -> 287 + let is_pin = Topkg_conf.build_context c = `Pin in 288 + let prepare = match Topkg_build.prepare_on_pin p.build && is_pin with 289 + | false -> Ok () 290 + | true -> 291 + (distrib_prepare_pin p) 292 + |> R.reword_error_msg ~replace:true 293 + (fun e -> R.msgf "Pin distribution preparation failed: %s" e) 294 + in 295 + prepare 296 + >>= fun () -> run_build_hook "Pre" (Topkg_build.pre p.build) c 297 + >>= fun () -> (Topkg_build.cmd p.build) c os targets 298 + >>= fun () -> write_opam_install_file p install 299 + >>= fun () -> write_tests_file p tests 300 + >>= fun () -> run_build_hook "Post" (Topkg_build.post p.build) c 301 + >>= fun () -> Ok 0 302 + 303 + (* Clean *) 304 + 305 + let clean p os = 306 + let file = install_file p in 307 + let build_dir = build_dir p in 308 + Topkg_os.File.delete file 309 + >>= fun () -> Topkg_os.File.delete "pkg.itarget" 310 + >>= fun () -> (Topkg_build.clean p.build) os ~build_dir 311 + >>= fun () -> Ok 0 312 + 313 + (* Lint *) 314 + 315 + let lint_custom p = p.lint_custom 316 + 317 + let lint_files p = match p.lint_files with 318 + | None (* disabled *) -> None 319 + | Some fs -> Some (List.rev_append (std_files p) fs) 320 + 321 + let lint_metas p = 322 + List.map (fun ((p, _), lint) -> (p, lint)) p.metas 323 + 324 + let lint_opams p = 325 + List.map (fun ((p, _), lint, lint_deps) -> (p, lint, lint_deps)) p.opams
+89
vendor/opam/topkg/src/topkg_pkg.mli
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** Package descriptions. *) 7 + 8 + (** {1 Package} *) 9 + 10 + open Topkg_result 11 + 12 + type std_file 13 + val std_file : ?install:bool -> Topkg_fpath.t -> std_file 14 + 15 + type meta_file 16 + val meta_file : ?lint:bool -> ?install:bool -> Topkg_fpath.t -> meta_file 17 + 18 + type opam_file 19 + val opam_file : 20 + ?lint:bool -> ?lint_deps_excluding:string list option -> ?install:bool -> 21 + Topkg_fpath.t -> opam_file 22 + 23 + type t 24 + 25 + val empty : t 26 + val with_name_and_build_dir : 27 + ?name:string -> ?build_dir:Topkg_fpath.t -> t -> t 28 + 29 + val v : 30 + ?delegate:Topkg_cmd.t -> 31 + ?readmes:std_file list -> 32 + ?licenses:std_file list -> 33 + ?change_logs:std_file list -> 34 + ?metas:meta_file list -> 35 + ?opams:opam_file list -> 36 + ?lint_files:Topkg_fpath.t list option -> 37 + ?lint_custom:(unit -> R.msg result list) -> 38 + ?distrib:Topkg_distrib.t -> 39 + ?publish:Topkg_publish.t -> 40 + ?build:Topkg_build.t -> string -> 41 + (Topkg_conf.t -> Topkg_install.t list result) -> t 42 + 43 + val name : t -> string 44 + val delegate : t -> Topkg_cmd.t option 45 + val readmes : t -> Topkg_fpath.t list 46 + val change_logs : t -> Topkg_fpath.t list 47 + val licenses : t -> Topkg_fpath.t list 48 + val distrib : t -> Topkg_distrib.t 49 + val build : t -> Topkg_build.t 50 + val install : t -> Topkg_conf.t -> Topkg_install.t list result 51 + val codec : t Topkg_codec.t 52 + 53 + (* Derived accessors *) 54 + 55 + val build_dir : t -> Topkg_fpath.t 56 + val opam : name:string -> t -> Topkg_fpath.t 57 + 58 + (* Distrib *) 59 + 60 + val distrib_uri : t -> string option 61 + val distrib_prepare : 62 + t -> dist_build_dir:Topkg_fpath.t -> name:string -> version:string -> 63 + opam:Topkg_fpath.t -> opam_adds:string -> Topkg_fpath.t list result 64 + 65 + (* Publish *) 66 + 67 + val publish_artefacts : t -> [ `Distrib | `Doc | `Alt of string ] list 68 + 69 + (* Test *) 70 + 71 + val test : 72 + t -> list:bool -> tests:string list -> args:Topkg_cmd.t option -> int result 73 + 74 + (* Build *) 75 + 76 + val build : 77 + t -> kind:[`Build | `Dry_run | `Raw of string list ] -> 78 + Topkg_conf.t -> Topkg_conf.os -> int result 79 + 80 + (* Clean *) 81 + 82 + val clean : t -> Topkg_conf.os -> int result 83 + 84 + (* Lint *) 85 + 86 + val lint_custom : t -> (unit -> R.msg result list) option 87 + val lint_files : t -> Topkg_fpath.t list option 88 + val lint_metas : t -> (Topkg_fpath.t * bool) list 89 + val lint_opams : t -> (Topkg_fpath.t * bool * string list option) list
+31
vendor/opam/topkg/src/topkg_publish.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + open Topkg_result 7 + 8 + type artefact = [`Distrib | `Doc | `Alt of string] 9 + type t = { artefacts : artefact list } 10 + 11 + let v ?(artefacts = [`Doc; `Distrib]) () = { artefacts } 12 + let artefacts p = p.artefacts 13 + let codec_artefact = 14 + let tag = function `Distrib -> 0 | `Doc -> 1 | `Alt _ -> 2 in 15 + let codecs = 16 + let alt_case = 17 + ((function `Alt s -> s | _ -> assert false), 18 + (function s -> `Alt s)) 19 + in 20 + Topkg_codec.([| const `Distrib; const `Doc; view alt_case string |]) 21 + in 22 + Topkg_codec.alt ~kind:"artefact" tag codecs 23 + 24 + let codec = 25 + let artefacts = Topkg_codec.(list codec_artefact) in 26 + let fields = 27 + (fun p -> p.artefacts), 28 + (fun artefacts -> { artefacts }) 29 + in 30 + Topkg_codec.version 0 @@ 31 + Topkg_codec.(view ~kind:"publish" fields artefacts)
+21
vendor/opam/topkg/src/topkg_publish.mli
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** {1 Distribution publication description} 7 + 8 + See {!section:Topkg.Pkg.publish}. *) 9 + 10 + (** {1 Distribution} *) 11 + 12 + open Topkg_result 13 + 14 + (* Publication *) 15 + 16 + type artefact = [`Distrib | `Doc | `Alt of string] 17 + type t 18 + 19 + val v : ?artefacts:artefact list -> unit -> t 20 + val artefacts : t -> artefact list 21 + val codec : t Topkg_codec.t
+33
vendor/opam/topkg/src/topkg_result.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + let ( >>= ) v f = match v with Ok v -> f v | Error _ as e -> e 7 + let ( >>| ) v f = match v with Ok v -> Ok (f v) | Error _ as e -> e 8 + 9 + type ('a, 'b) r = ('a, 'b) result = Ok of 'a | Error of 'b 10 + type 'a result = ('a, [`Msg of string]) r 11 + 12 + module R = struct 13 + type msg = [`Msg of string ] 14 + 15 + let msgf fmt = 16 + let kmsg _ = `Msg (Format.flush_str_formatter ()) in 17 + Format.kfprintf kmsg Format.str_formatter fmt 18 + 19 + let reword_error reword = function 20 + | Ok _ as r -> r 21 + | Error e -> Error (reword e) 22 + 23 + let error_msg m = Error (`Msg m) 24 + let error_msgf fmt = 25 + let kerr _ = Error (`Msg (Format.flush_str_formatter ())) in 26 + Format.kfprintf kerr Format.str_formatter fmt 27 + 28 + let reword_error_msg ?(replace = false) reword = function 29 + | Ok _ as r -> r 30 + | Error (`Msg e) -> 31 + let (`Msg e' as v) = reword e in 32 + if replace then Error v else error_msgf "%s\n%s" e e' 33 + end
+29
vendor/opam/topkg/src/topkg_result.mli
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** Results 7 + 8 + Abbridged [rresult]. See {!section:Topkg.prels} for documention. *) 9 + 10 + val ( >>= ) : ('a, 'b) result -> ('a -> ('c, 'b) result) -> ('c, 'b) result 11 + val ( >>| ) : ('a, 'b) result -> ('a -> 'c) -> ('c, 'b) result 12 + 13 + type ('a, 'b) r = ('a, 'b) result = Ok of 'a | Error of 'b 14 + type 'a result = ('a, [ `Msg of string]) r 15 + 16 + module R : sig 17 + val reword_error : ('b -> 'c) -> ('a, 'b) r -> ('a, 'c) r 18 + 19 + type msg = [ `Msg of string ] 20 + 21 + val msgf : ('a, Format.formatter, unit, [> msg]) format4 -> 'a 22 + 23 + val error_msg : string -> ('b, [> msg]) r 24 + val error_msgf : 25 + ('a, Format.formatter, unit, ('b, [> msg]) r) format4 -> 'a 26 + 27 + val reword_error_msg : 28 + ?replace:bool -> (string -> msg) -> ('a, msg) r -> ('a, [> msg]) r 29 + end
+154
vendor/opam/topkg/src/topkg_string.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + let strf = Format.asprintf 7 + 8 + include String 9 + 10 + let head s = if s = "" then None else Some s.[0] 11 + 12 + (* Predicates *) 13 + 14 + let is_prefix ~affix s = 15 + let len_a = length affix in 16 + let len_s = length s in 17 + if len_a > len_s then false else 18 + let max_idx_a = len_a - 1 in 19 + let rec loop i = 20 + if i > max_idx_a then true else 21 + if unsafe_get affix i <> unsafe_get s i then false else loop (i + 1) 22 + in 23 + loop 0 24 + 25 + let is_suffix ~affix s = 26 + let max_idx_a = length affix - 1 in 27 + let max_idx_s = length s - 1 in 28 + if max_idx_a > max_idx_s then false else 29 + let rec loop i = 30 + if i > max_idx_a then true else 31 + if unsafe_get affix (max_idx_a - i) <> unsafe_get s (max_idx_s - i) 32 + then false 33 + else loop (i + 1) 34 + in 35 + loop 0 36 + 37 + let for_all sat s = 38 + let max_idx = length s - 1 in 39 + let rec loop i = 40 + if i > max_idx then true else 41 + if sat (unsafe_get s i) then loop (i + 1) else false 42 + in 43 + loop 0 44 + 45 + let exists sat s = 46 + let max_idx = length s - 1 in 47 + let rec loop i = 48 + if i > max_idx then false else 49 + if sat (unsafe_get s i) then true else loop (i + 1) 50 + in 51 + loop 0 52 + 53 + (* Traversing *) 54 + 55 + let find_byte ?(start = 0) c s = 56 + let max = String.length s - 1 in 57 + if start > max then None else 58 + try Some (String.index_from s start c) with Not_found -> None 59 + 60 + (* Extracting substrings *) 61 + 62 + let with_index_range ?(first = 0) ?last s = 63 + let max = String.length s - 1 in 64 + let last = match last with 65 + | None -> max 66 + | Some l when l > max -> max 67 + | Some l -> l 68 + in 69 + let first = if first < 0 then 0 else first in 70 + if first > last then "" else 71 + String.sub s first (last - first + 1) 72 + 73 + let cut ?(rev = false) ~sep s = 74 + let find_index = if rev then String.rindex else String.index in 75 + match try Some (find_index s sep) with Not_found -> None with 76 + | None -> None 77 + | Some i -> 78 + Some (String.sub s 0 i, String.sub s (i+1) (String.length s - i - 1)) 79 + 80 + let cuts ?(empty = true) ~sep s = 81 + let no_empty = not empty in 82 + let rec loop acc s = match cut ~sep s with 83 + | Some (v, vs) -> loop (if no_empty && v = "" then acc else (v :: acc)) vs 84 + | None -> List.rev (if no_empty && s = "" then acc else (s :: acc)) 85 + in 86 + loop [] s 87 + 88 + (* Version strings *) 89 + 90 + let parse_version v = 91 + let version = 92 + if is_prefix ~affix:"v" v then with_index_range ~first:1 v else v 93 + in 94 + let cut_left_plus_or_tilde s = 95 + let cut = match String.index_opt s '+', String.index_opt s '~' with 96 + | None, None -> None 97 + | (Some _ as i), None | None, (Some _ as i) -> i 98 + | Some i, Some i' -> Some (if i < i' then i else i') 99 + in 100 + match cut with 101 + | None -> None 102 + | Some i -> 103 + Some (with_index_range ~last:(i - 1) s, with_index_range ~first:i s) 104 + in 105 + try match cut ~sep:'.' version with 106 + | None -> None 107 + | Some (maj, rest) -> 108 + let maj = int_of_string maj in 109 + match cut ~sep:'.' rest with 110 + | None -> 111 + begin match cut_left_plus_or_tilde rest with 112 + | None -> Some (maj, int_of_string rest, 0, None) 113 + | Some (min, i) -> Some (maj, int_of_string min, 0, Some i) 114 + end 115 + | Some (min, rest) -> 116 + let min = int_of_string min in 117 + begin match cut_left_plus_or_tilde rest with 118 + | None -> Some (maj, min, int_of_string rest, None) 119 + | Some (p, i) -> Some (maj, min, int_of_string p, Some i) 120 + end 121 + with 122 + | Failure _ -> None 123 + 124 + let drop_initial_v version = match head version with 125 + | Some ('v' | 'V') -> with_index_range ~first:1 version 126 + | None | Some _ -> version 127 + 128 + (* Formatters *) 129 + 130 + let pp_text ppf s = (* was c&p from Fmt, pp_print_text is 4.02 *) 131 + let is_nl_or_sp c = c = '\n' || c = ' ' in 132 + let rec stop_at sat ~start ~max s = 133 + if start > max then start else 134 + if sat s.[start] then start else 135 + stop_at sat ~start:(start + 1) ~max s 136 + in 137 + let sub s start stop ~max = 138 + if start = stop then "" else 139 + if start = 0 && stop > max then s else 140 + String.sub s start (stop - start) 141 + in 142 + let max = String.length s - 1 in 143 + let rec loop start s = match stop_at is_nl_or_sp ~start ~max s with 144 + | stop when stop > max -> Format.pp_print_string ppf (sub s start stop ~max) 145 + | stop -> 146 + Format.pp_print_string ppf (sub s start stop ~max); 147 + begin match s.[stop] with 148 + | ' ' -> Format.pp_print_space ppf () 149 + | '\n' -> Format.pp_force_newline ppf () 150 + | _ -> assert false 151 + end; 152 + loop (stop + 1) s 153 + in 154 + loop 0 s
+34
vendor/opam/topkg/src/topkg_string.mli
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** Strings. 7 + 8 + See {!Topkg.String} for documentation. *) 9 + 10 + val strf : ('a, Format.formatter, unit, string) format4 -> 'a 11 + 12 + include module type of String 13 + 14 + val head : string -> char option 15 + 16 + val is_prefix : affix:string -> string -> bool 17 + val is_suffix : affix:string -> string -> bool 18 + val for_all : (char -> bool) -> string -> bool 19 + val exists : (char -> bool) -> string -> bool 20 + 21 + val find_byte : ?start:int -> char -> string -> int option 22 + 23 + val trim : string -> string 24 + val cut : ?rev:bool -> sep:char -> string -> (string * string) option 25 + val cuts : ?empty:bool -> sep:char -> string -> string list 26 + 27 + val with_index_range : ?first:int -> ?last:int -> string -> string 28 + 29 + val uppercase_ascii : string -> string 30 + 31 + val parse_version : string -> (int * int * int * string option) option 32 + val drop_initial_v : string -> string 33 + 34 + val pp_text : Format.formatter -> string -> unit
+22
vendor/opam/topkg/src/topkg_test.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + type t = 7 + { exec : Topkg_fpath.t; 8 + args : Topkg_cmd.t; 9 + run : bool; 10 + dir : Topkg_fpath.t option; } 11 + 12 + let v exec ~args ~run ~dir = { exec; args; run; dir } 13 + let exec t = t.exec 14 + let args t = t.args 15 + let run t = t.run 16 + let dir t = t.dir 17 + let codec = 18 + let fields = 19 + (fun t -> (t.exec, t.args, t.run, t.dir)), 20 + (fun (exec, args, run, dir) -> { exec; args; run; dir}) 21 + in 22 + Topkg_codec.(view ~kind:"test" fields (t4 fpath cmd bool (option fpath)))
+18
vendor/opam/topkg/src/topkg_test.mli
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** Topkg test description. *) 7 + 8 + type t 9 + 10 + val v : 11 + Topkg_fpath.t -> args:Topkg_cmd.t -> run:bool -> 12 + dir:Topkg_fpath.t option -> t 13 + 14 + val exec : t -> Topkg_fpath.t 15 + val args : t -> Topkg_cmd.t 16 + val run : t -> bool 17 + val dir : t -> Topkg_fpath.t option 18 + val codec : t Topkg_codec.t
+389
vendor/opam/topkg/src/topkg_vcs.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + open Topkg_result 7 + 8 + let parse_changes lines = 9 + try 10 + let parse_line l = match Topkg_string.cut ~sep:' ' l with 11 + | None -> failwith (Topkg_string.strf "%S: can't parse log line" l) 12 + | Some cut -> cut 13 + in 14 + Ok (List.(rev @@ rev_map parse_line lines)) 15 + with Failure msg -> Error (`Msg msg) 16 + 17 + (* Version control system repositories *) 18 + 19 + type commit_ish = string 20 + 21 + type kind = [ `Git | `Hg ] 22 + 23 + let pp_kind ppf = function 24 + | `Git -> Format.pp_print_string ppf "git" 25 + | `Hg -> Format.pp_print_string ppf "hg" 26 + 27 + let dirtify id = id ^ "-dirty" 28 + 29 + type t = kind * Topkg_cmd.t * Topkg_fpath.t 30 + 31 + let git = 32 + let git = Topkg_cmd.v (Topkg_os.Env.opt_var "TOPKG_GIT" ~absent:"git") in 33 + lazy (Topkg_os.Cmd.exists git >>= fun exists -> Ok (exists, git)) 34 + 35 + let hg = 36 + let hg = Topkg_cmd.v (Topkg_os.Env.opt_var "TOPKG_HG" ~absent:"hg") in 37 + lazy (Topkg_os.Cmd.exists hg >>= fun exists -> Ok (exists, hg)) 38 + 39 + let vcs_cmd kind cmd dir = match kind with 40 + | `Git -> Topkg_cmd.(cmd % "--git-dir" % dir) 41 + | `Hg -> Topkg_cmd.(cmd % "--repository" % dir) 42 + 43 + let v k cmd ~dir = (k, cmd, dir) 44 + let kind (k, _, _) = k 45 + let dir (_, _, dir) = dir 46 + let cmd (kind, cmd, dir) = vcs_cmd kind cmd dir 47 + 48 + (* Git support *) 49 + 50 + let git_work_tree (_, _, dir) = 51 + Topkg_cmd.(v "--work-tree" % Topkg_fpath.dirname dir) 52 + 53 + let find_git () = Lazy.force git >>= function 54 + | (false, _) -> Ok None 55 + | (true, git) -> 56 + let git_dir = Topkg_cmd.(git % "rev-parse" % "--git-dir") in 57 + Topkg_os.Cmd.(run_out ~err:Topkg_os.File.null git_dir |> out_string) 58 + >>= function 59 + | (dir, (_, `Exited 0)) -> Ok (Some (v `Git git ~dir)) 60 + | _ -> Ok None 61 + 62 + let err_git cmd c = R.error_msgf "%a exited with %d" Topkg_cmd.dump cmd c 63 + let run_git r args out = 64 + let git = Topkg_cmd.(cmd r %% args) in 65 + Topkg_os.Cmd.(run_out git |> out) >>= function 66 + | (v, (_, `Exited 0)) -> Ok v 67 + | (_, (_, `Exited c)) -> err_git git c 68 + 69 + let git_is_dirty r = 70 + let status = 71 + Topkg_cmd.(cmd r %% git_work_tree r % "status" % "--porcelain") 72 + in 73 + Topkg_os.Cmd.(run_out ~err:Topkg_os.File.null status |> out_string) 74 + >>= function 75 + | ("", (_, `Exited 0)) -> Ok false 76 + | (_, (_, `Exited 0)) -> Ok true 77 + | (_, (_, `Exited c)) -> err_git status c 78 + 79 + let git_file_is_dirty r file = 80 + let diff = 81 + Topkg_cmd.(cmd r %% git_work_tree r % "diff-index" % "--quiet" % "HEAD" % 82 + p file) 83 + in 84 + Topkg_os.Cmd.(run_status ~err:Topkg_os.File.null diff) >>= function 85 + | `Exited 0 -> Ok false 86 + | `Exited 1 -> Ok true 87 + | `Exited c -> err_git diff c 88 + 89 + let dirtify_if ~dirty r id = match dirty with 90 + | false -> Ok id 91 + | true -> 92 + git_is_dirty r >>= fun is_dirty -> 93 + Ok (if is_dirty then dirtify id else id) 94 + 95 + let git_head ~dirty r = 96 + run_git r Topkg_cmd.(v "rev-parse" % "HEAD") Topkg_os.Cmd.out_string 97 + >>= fun id -> dirtify_if ~dirty r id 98 + 99 + let git_commit_id ~dirty r commit_ish = 100 + let dirty = dirty && commit_ish = "HEAD" in 101 + let id = Topkg_cmd.(v "rev-parse" % "--verify" % 102 + (commit_ish ^ "^{commit}")) 103 + in 104 + run_git r id Topkg_os.Cmd.out_string >>= fun id -> dirtify_if ~dirty r id 105 + 106 + let git_commit_ptime_s r commit_ish = 107 + let time = Topkg_cmd.(v "show" % "-s" % "--format=%ct" % commit_ish) in 108 + run_git r time Topkg_os.Cmd.out_string 109 + >>= fun ptime -> try Ok (int_of_string ptime) with 110 + | Failure _ -> R.error_msgf "Could not parse timestamp from %S" ptime 111 + 112 + let git_describe ~dirty r commit_ish = 113 + let dirty = dirty && commit_ish = "HEAD" in 114 + run_git r 115 + Topkg_cmd.(git_work_tree r % "describe" % "--always" %% 116 + on dirty (v "--dirty") %% on (not dirty) (v commit_ish)) 117 + Topkg_os.Cmd.out_string 118 + 119 + let git_tags r = 120 + run_git r Topkg_cmd.(v "tag" % "--list") Topkg_os.Cmd.out_lines 121 + 122 + let git_changes r ~after ~until = 123 + let range = 124 + if after = "" then until else 125 + Topkg_string.strf "%s..%s" after until 126 + in 127 + let changes = Topkg_cmd.(v "log" % "--oneline" % "--no-decorate" % range) in 128 + run_git r changes Topkg_os.Cmd.out_lines 129 + >>= fun commits -> parse_changes commits 130 + 131 + let git_tracked_files r ~tree_ish = 132 + let tracked = 133 + Topkg_cmd.(git_work_tree r % "ls-tree" % "--name-only" % "-r" % tree_ish) 134 + in 135 + run_git r tracked Topkg_os.Cmd.out_lines 136 + 137 + let git_clone r ~dir:d = 138 + let clone = Topkg_cmd.(v "clone" % "--local" % (dir r) % d) in 139 + run_git r clone Topkg_os.Cmd.out_stdout >>= fun _ -> Ok () 140 + 141 + let git_checkout r ~branch ~commit_ish = 142 + let branch = match branch with 143 + | None -> Topkg_cmd.empty 144 + | Some branch -> Topkg_cmd.(v "-b" % branch) 145 + in 146 + run_git r Topkg_cmd.(v "checkout" % "--quiet" %% branch % commit_ish) 147 + Topkg_os.Cmd.out_string 148 + >>= fun _ -> Ok () 149 + 150 + let git_commit_files r ~msg files = 151 + let msg = match msg with 152 + | None -> Topkg_cmd.empty 153 + | Some m -> Topkg_cmd.(v "-m" % m) 154 + in 155 + let files = Topkg_cmd.(of_list @@ List.map p files) in 156 + run_git r Topkg_cmd.(v "commit" %% msg %% files) Topkg_os.Cmd.out_stdout 157 + 158 + let git_tag r ~force ~sign ~msg ~commit_ish tag = 159 + let msg = match msg with 160 + | None -> Topkg_cmd.empty 161 + | Some m -> Topkg_cmd.(v "-m" % m) 162 + in 163 + let flags = Topkg_cmd.(on force (v "-f") %% on sign (v "-s")) in 164 + run_git r Topkg_cmd.(v "tag" % "-a" %% flags %% msg % tag % commit_ish) 165 + Topkg_os.Cmd.out_stdout 166 + 167 + let git_delete_tag r tag = 168 + run_git r Topkg_cmd.(v "tag" % "-d" % tag) Topkg_os.Cmd.out_stdout 169 + 170 + (* Hg support *) 171 + 172 + let hg_rev commit_ish = match commit_ish with "HEAD" -> "tip" | c -> c 173 + 174 + let find_hg () = Lazy.force hg >>= function 175 + | (false, _) -> Ok None 176 + | (true, hg) -> 177 + let hg_root = Topkg_cmd.(hg % "root") in 178 + Topkg_os.Cmd.(run_out ~err:Topkg_os.File.null hg_root |> out_string) 179 + >>= function 180 + | (dir, (_, `Exited 0)) -> Ok (Some (v `Hg hg ~dir)) 181 + | _ -> Ok None 182 + 183 + let err_hg cmd c = R.error_msgf "%a exited with %d" Topkg_cmd.dump cmd c 184 + let run_hg r args out = 185 + let hg = Topkg_cmd.(cmd r %% args) in 186 + Topkg_os.Cmd.(run_out hg |> out) >>= function 187 + | (v, (_, `Exited 0)) -> Ok v 188 + | (_, (_, `Exited c)) -> err_hg hg c 189 + 190 + let hg_id r ~rev = 191 + run_hg r Topkg_cmd.(v "id" % "-i" % "--rev" % rev) Topkg_os.Cmd.out_string 192 + >>= fun id -> 193 + let len = String.length id in 194 + let is_dirty = String.length id > 0 && id.[len - 1] = '+' in 195 + let id = if is_dirty then String.sub id 0 (len - 1) else id in 196 + Ok (id, is_dirty) 197 + 198 + let hg_is_dirty r = 199 + hg_id r ~rev:"tip" >>= function (id, is_dirty) -> Ok is_dirty 200 + 201 + let hg_file_is_dirty r file = 202 + run_hg r Topkg_cmd.(v "status" % p file) Topkg_os.Cmd.out_string >>= function 203 + | "" -> Ok false 204 + | _ -> Ok true 205 + 206 + let hg_head ~dirty r = 207 + hg_id r ~rev:"tip" >>= function (id, is_dirty) -> 208 + Ok (if is_dirty && dirty then dirtify id else id) 209 + 210 + let hg_commit_id ~dirty r ~rev = 211 + hg_id r ~rev >>= fun (id, is_dirty) -> 212 + Ok (if is_dirty && dirty then dirtify id else id) 213 + 214 + let hg_commit_ptime_s r ~rev = 215 + let time = Topkg_cmd.(v "log" % "--template" % "{date(date, \"%s\")}" % 216 + "--rev" % rev) 217 + in 218 + run_hg r time Topkg_os.Cmd.out_string 219 + >>= fun ptime -> try Ok (int_of_string ptime) with 220 + | Failure _ -> R.error_msgf "Could not parse timestamp from %S" ptime 221 + 222 + let hg_describe ~dirty r ~rev = 223 + let get_distance s = try Ok (int_of_string s) with 224 + | Failure _ -> R.error_msgf "%s: Could not parse hg tag distance." (dir r) 225 + in 226 + let parent t = 227 + run_hg r Topkg_cmd.(v "parent" % "--rev" % rev % "--template" % t) 228 + Topkg_os.Cmd.out_string 229 + in 230 + parent "{latesttagdistance}" >>= get_distance 231 + >>= begin function 232 + | 1 -> parent "{latesttag}" 233 + | n -> parent "{latesttag}-{latesttagdistance}-{node|short}" 234 + end 235 + >>= fun descr -> match dirty with 236 + | false -> Ok descr 237 + | true -> 238 + hg_id ~rev:"tip" r >>= fun (_, is_dirty) -> 239 + Ok (if is_dirty then dirtify descr else descr) 240 + 241 + let hg_tags r = 242 + run_hg r Topkg_cmd.(v "tags" % "--quiet" (* sic *)) Topkg_os.Cmd.out_lines 243 + 244 + let hg_changes r ~after ~until = 245 + let rev = Topkg_string.strf "%s::%s" after until in 246 + let changes = 247 + Topkg_cmd.(v "log" % "--template" % "{node|short} {desc|firstline}\\n" 248 + % "--rev" % rev) 249 + in 250 + run_hg r changes Topkg_os.Cmd.out_lines 251 + >>= fun commits -> parse_changes commits 252 + >>= function 253 + | [] -> Ok [] 254 + | after :: rest -> Ok (List.rev rest) (* hg order is reverse from git *) 255 + 256 + let hg_tracked_files r ~rev = 257 + run_hg r Topkg_cmd.(v "manifest" % "--rev" % rev) Topkg_os.Cmd.out_lines 258 + 259 + let hg_clone r ~dir:d = 260 + let clone = Topkg_cmd.(v "clone" % (dir r) % d) in 261 + run_hg r clone Topkg_os.Cmd.out_stdout 262 + 263 + let hg_checkout r ~branch ~rev = 264 + run_hg r Topkg_cmd.(v "update" % "--rev" % rev) Topkg_os.Cmd.out_string 265 + >>= fun _ -> match branch with 266 + | None -> Ok () 267 + | Some branch -> 268 + run_hg r Topkg_cmd.(v "branch" % branch) Topkg_os.Cmd.out_string 269 + >>= fun _ -> Ok () 270 + 271 + let hg_commit_files r ~msg files = 272 + let msg = match msg with 273 + | None -> Topkg_cmd.empty 274 + | Some m -> Topkg_cmd.(v "-m" % m) 275 + in 276 + let files = Topkg_cmd.(of_list @@ List.map p files) in 277 + run_hg r Topkg_cmd.(v "commit" %% msg %% files) Topkg_os.Cmd.out_stdout 278 + 279 + let hg_tag r ~force ~sign ~msg ~rev tag = 280 + if sign then R.error_msgf "Tag signing is not supported by hg" else 281 + let msg = match msg with 282 + | None -> Topkg_cmd.empty 283 + | Some m -> Topkg_cmd.(v "-m" % m) 284 + in 285 + run_hg r Topkg_cmd.(v "tag" %% on force (v "-f") %% msg % "--rev" % rev % tag) 286 + Topkg_os.Cmd.out_stdout 287 + 288 + let hg_delete_tag r tag = 289 + run_hg r Topkg_cmd.(v "tag" % "--remove" % tag) Topkg_os.Cmd.out_stdout 290 + 291 + (* Generic VCS support *) 292 + 293 + let find ?dir () = match dir with 294 + | None -> 295 + begin find_git () >>= function 296 + | Some _ as v -> Ok v 297 + | None -> find_hg () 298 + end 299 + | Some dir -> 300 + let git_dir = Topkg_fpath.append dir ".git" in 301 + Topkg_os.Dir.exists git_dir >>= function 302 + | true -> 303 + begin Lazy.force git >>= function 304 + | (_, cmd) -> Ok (Some (v `Git cmd ~dir:git_dir)) 305 + end 306 + | false -> 307 + let hg_dir = Topkg_fpath.append dir ".hg" in 308 + Topkg_os.Dir.exists hg_dir >>= function 309 + | false -> Ok None 310 + | true -> 311 + Lazy.force hg >>= function 312 + (_, cmd) -> Ok (Some (v `Hg cmd ~dir)) 313 + 314 + let get ?dir () = find ?dir () >>= function 315 + | Some r -> Ok r 316 + | None -> 317 + let dir = match dir with 318 + | None -> Topkg_os.Dir.current () 319 + | Some dir -> Ok dir 320 + in 321 + dir >>= function dir -> 322 + R.error_msgf "%s: No VCS repository found" dir 323 + 324 + let pp ppf r = Format.fprintf ppf "(%a, %s)" pp_kind (kind r) (dir r) 325 + 326 + (* Repository state *) 327 + 328 + let is_dirty = function 329 + | (`Git, _, _ as r) -> git_is_dirty r 330 + | (`Hg, _ , _ as r ) -> hg_is_dirty r 331 + 332 + let not_dirty r = is_dirty r >>= function 333 + | false -> Ok () 334 + | true -> R.error_msgf "The VCS repo is dirty, commit or stash your changes." 335 + 336 + let file_is_dirty r file = match r with 337 + | (`Git, _, _ as r) -> git_file_is_dirty r file 338 + | (`Hg, _, _ as r ) -> hg_file_is_dirty r file 339 + 340 + let head ?(dirty = true) = function 341 + | (`Git, _, _ as r) -> git_head ~dirty r 342 + | (`Hg, _, _ as r) -> hg_head ~dirty r 343 + 344 + let commit_id ?(dirty = true) ?(commit_ish = "HEAD") = function 345 + | (`Git, _, _ as r) -> git_commit_id ~dirty r commit_ish 346 + | (`Hg, _, _ as r) -> hg_commit_id ~dirty r ~rev:(hg_rev commit_ish) 347 + 348 + let commit_ptime_s ?(commit_ish = "HEAD") = function 349 + | (`Git, _, _ as r) -> git_commit_ptime_s r commit_ish 350 + | (`Hg, _, _ as r) -> hg_commit_ptime_s r ~rev:(hg_rev commit_ish) 351 + 352 + let describe ?(dirty = true) ?(commit_ish = "HEAD") = function 353 + | (`Git, _, _ as r) -> git_describe ~dirty r commit_ish 354 + | (`Hg, _, _ as r) -> hg_describe ~dirty r ~rev:(hg_rev commit_ish) 355 + 356 + let tags = function 357 + | (`Git, _, _ as r) -> git_tags r 358 + | (`Hg, _, _ as r) -> hg_tags r 359 + 360 + let changes ?(until = "HEAD") r ~after = match r with 361 + | (`Git, _, _ as r) -> git_changes r ~after ~until 362 + | (`Hg, _, _ as r) -> hg_changes r ~after:(hg_rev after) ~until:(hg_rev until) 363 + 364 + let tracked_files ?(tree_ish = "HEAD") = function 365 + | (`Git, _, _ as r) -> git_tracked_files r ~tree_ish 366 + | (`Hg, _, _ as r) -> hg_tracked_files r ~rev:(hg_rev tree_ish) 367 + 368 + (* Operations *) 369 + 370 + let clone r ~dir = match r with 371 + | (`Git, _, _ as r) -> git_clone r ~dir 372 + | (`Hg, _, _ as r) -> hg_clone r ~dir 373 + 374 + let checkout ?branch r ~commit_ish = match r with 375 + | (`Git, _, _ as r) -> git_checkout r ~branch ~commit_ish 376 + | (`Hg, _, _ as r) -> hg_checkout r ~branch ~rev:(hg_rev commit_ish) 377 + 378 + let commit_files ?msg r files = match r with 379 + | (`Git, _, _ as r) -> git_commit_files r ~msg files 380 + | (`Hg, _, _ as r) -> hg_commit_files r ~msg files 381 + 382 + let tag ?(force = false) ?(sign = false) ?msg ?(commit_ish = "HEAD") r tag = 383 + match r with 384 + | (`Git, _, _ as r) -> git_tag r ~force ~sign ~msg ~commit_ish tag 385 + | (`Hg, _, _ as r) -> hg_tag r ~force ~sign ~msg ~rev:(hg_rev commit_ish) tag 386 + 387 + let delete_tag r tag = match r with 388 + | (`Git, _, _ as r) -> git_delete_tag r tag 389 + | (`Hg, _, _ as r) -> hg_delete_tag r tag
+48
vendor/opam/topkg/src/topkg_vcs.mli
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** VCS repositories. 7 + 8 + See {!Topkg.Vcs} for documentation. *) 9 + 10 + (** {1 VCS} *) 11 + 12 + open Topkg_result 13 + 14 + type kind = [ `Git | `Hg ] 15 + val pp_kind : Format.formatter -> kind -> unit 16 + 17 + type commit_ish = string 18 + 19 + type t 20 + 21 + val kind : t -> kind 22 + val dir : t -> Topkg_fpath.t 23 + val find : ?dir:Topkg_fpath.t -> unit -> t option result 24 + val get : ?dir:Topkg_fpath.t -> unit -> t result 25 + val cmd : t -> Topkg_cmd.t 26 + val pp : Format.formatter -> t -> unit 27 + 28 + val is_dirty : t -> bool result 29 + val not_dirty : t -> unit result 30 + val file_is_dirty : t -> Topkg_fpath.t -> bool result 31 + val head : ?dirty:bool -> t -> string result 32 + val commit_id : ?dirty:bool -> ?commit_ish:string -> t -> string result 33 + val commit_ptime_s : ?commit_ish:commit_ish -> t -> int result 34 + val describe : ?dirty:bool -> ?commit_ish:string -> t -> string result 35 + val tags : t -> string list result 36 + val changes : 37 + ?until:string -> t -> after:string -> (string * string) list result 38 + 39 + val tracked_files : ?tree_ish:string -> t -> Topkg_fpath.t list result 40 + 41 + val clone : t -> dir:Topkg_fpath.t -> unit result 42 + val checkout : ?branch:string -> t -> commit_ish:string -> unit result 43 + val commit_files : ?msg:string -> t -> Topkg_fpath.t list -> unit result 44 + 45 + val delete_tag : t -> string -> unit result 46 + val tag : 47 + ?force:bool -> ?sign:bool -> ?msg:string -> ?commit_ish:string -> t -> 48 + string -> unit result
+45
vendor/opam/topkg/test/echo-delegate
··· 1 + #!/usr/bin/env ocaml 2 + #use "topfind" 3 + #require "bos.setup" 4 + #require "fmt" 5 + open Bos_setup 6 + 7 + let ok = Ok 0 8 + let unsupported = Ok 1 9 + 10 + let publish = function 11 + | "distrib" :: uri :: name :: version :: msg :: archive :: _ -> 12 + Fmt.pr "publish distrib %S %S %S %S %S@." uri name version msg archive; ok 13 + | "doc" :: uri :: name :: version :: msg :: docdir :: _ -> 14 + Fmt.pr "publish doc %S %S %S %S %S@." uri name version msg docdir; ok 15 + | "alt" :: k :: uri :: name :: version :: msg :: archive :: _ -> 16 + Fmt.pr "publish alt %S %S %S %S %S %S@." k uri name version msg archive; ok 17 + | args -> 18 + unsupported 19 + 20 + let issue = function 21 + | "list" :: uri :: _ -> Fmt.pr "issue list %S@." uri; ok 22 + | "show" :: uri :: id :: _ -> Fmt.pr "issue show %S %S@." uri id; ok 23 + | "open" :: uri :: t :: d :: _ -> Fmt.pr "issue open %S %S %S@." uri t d; ok 24 + | "close" :: uri :: id :: m :: _ -> Fmt.pr "issue close %S %S %S@." uri id m; ok 25 + | args -> unsupported 26 + 27 + let request = function 28 + | "publish" :: args -> publish args 29 + | "issue" :: args -> issue args 30 + | args -> unsupported 31 + 32 + let main () = 33 + let doc = "the unsupportive delegate" in 34 + begin match OS.Arg.(parse ~doc ~pos:string ()) with 35 + | "ipc" :: verbosity :: req -> 36 + Logs.level_of_string verbosity 37 + >>= fun level -> Logs.set_level level; request req 38 + | "ipc" :: [] -> 39 + R.error_msg "malformed delegate request, verbosity is missing" 40 + | args -> 41 + R.error_msgf "unknown arguments: %s" (String.concat ~sep:" " args) 42 + end 43 + |> Logs.on_error_msg ~use:(fun () -> 2) 44 + 45 + let () = exit (main ())
+8
vendor/opam/topkg/test/test.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2016 Daniel C. Bünzli. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + let () = 7 + let args = String.concat " " (List.tl (Array.to_list Sys.argv)) in 8 + Printf.printf "The test is ok, the arguments are: %s\n" args
+43
vendor/opam/topkg/test/unsupportive-delegate
··· 1 + #!/usr/bin/env ocaml 2 + #use "topfind" 3 + #require "bos.setup" 4 + open Bos_setup 5 + 6 + let unsupported = Ok 1 7 + 8 + let publish = function 9 + | "distrib" :: uri :: name :: version :: msg :: archive :: _ -> 10 + unsupported 11 + | "doc" :: uri :: name :: version :: msg :: docdir :: _ -> 12 + unsupported 13 + | "alt" :: kind :: uri :: name :: version :: msg :: archive :: _ -> 14 + unsupported 15 + | args -> 16 + unsupported 17 + 18 + let issue = function 19 + | "list" :: uri :: _ -> unsupported 20 + | "show" :: uri :: id :: _ -> unsupported 21 + | "open" :: uri :: title :: descr :: _ -> unsupported 22 + | "close" :: uri :: id :: msg :: _ -> unsupported 23 + | args -> unsupported 24 + 25 + let request = function 26 + | "publish" :: args -> publish args 27 + | "issue" :: args -> issue args 28 + | args -> unsupported 29 + 30 + let main () = 31 + let doc = "the unsupportive delegate" in 32 + begin match OS.Arg.(parse ~doc ~pos:string ()) with 33 + | "ipc" :: verbosity :: req -> 34 + Logs.level_of_string verbosity 35 + >>= fun level -> Logs.set_level level; request req 36 + | "ipc" :: [] -> 37 + R.error_msg "malformed delegate request, verbosity is missing" 38 + | args -> 39 + R.error_msgf "unknown arguments: %s" (String.concat ~sep:" " args) 40 + end 41 + |> Logs.on_error_msg ~use:(fun () -> 2) 42 + 43 + let () = exit (main ())
+53
vendor/opam/topkg/topkg-care.opam
··· 1 + opam-version: "2.0" 2 + name: "topkg-care" 3 + synopsis: "The transitory OCaml software packager" 4 + description: """\ 5 + **Warning** Topkg is in maintenance mode and should not longer be used. 6 + 7 + Topkg is a packager for distributing OCaml software. It provides an 8 + API to describe the files a package installs in a given build 9 + configuration and to specify information about the package's 10 + distribution, creation and publication procedures. 11 + 12 + The optional topkg-care package provides the `topkg` command line tool 13 + which helps with various aspects of a package's life cycle: creating 14 + and linting a distribution, releasing it on the WWW, publish its 15 + documentation, add it to the OCaml opam repository, etc. 16 + 17 + Topkg is distributed under the ISC license and has **no** 18 + dependencies. This is what your packages will need as a *build* 19 + dependency. 20 + 21 + Topkg-care is distributed under the ISC license it depends on 22 + [fmt][fmt], [logs][logs], [bos][bos], [cmdliner][cmdliner], 23 + [webbrowser][webbrowser] and `opam-format`. 24 + 25 + [fmt]: http://erratique.ch/software/fmt 26 + [logs]: http://erratique.ch/software/logs 27 + [bos]: http://erratique.ch/software/bos 28 + [cmdliner]: http://erratique.ch/software/cmdliner 29 + [webbrowser]: http://erratique.ch/software/webbrowser 30 + 31 + Home page: <http://erratique.ch/software/topkg>""" 32 + maintainer: "Daniel Bünzli <daniel.buenzl i@erratique.ch>" 33 + authors: "The topkg programmers" 34 + license: "ISC" 35 + tags: ["packaging" "ocamlbuild" "org:erratique"] 36 + homepage: "https://erratique.ch/software/topkg" 37 + doc: "https://erratique.ch/software/topkg/doc" 38 + bug-reports: "https://github.com/dbuenzli/topkg/issues" 39 + depends: [ 40 + "ocaml" {>= "4.08.0"} 41 + "ocamlfind" {build & >= "1.6.1"} 42 + "ocamlbuild" 43 + "topkg" {= version} 44 + "fmt" {>= "0.9.0"} 45 + "logs" 46 + "bos" {>= "0.2.1"} 47 + "cmdliner" {>= "1.3.0"} 48 + "webbrowser" 49 + "opam-format" {>= "2.0.0"} 50 + ] 51 + build: ["ocaml" "pkg/pkg.ml" "build" "--pkg-name" name "--dev-pkg" "%{dev}%"] 52 + dev-repo: "git+https://erratique.ch/repos/topkg.git" 53 + x-maintenance-intent: ["(latest)"]
+46
vendor/opam/topkg/topkg.opam
··· 1 + opam-version: "2.0" 2 + name: "topkg" 3 + synopsis: "The transitory OCaml software packager" 4 + description: """\ 5 + **Warning** Topkg is in maintenance mode and should not longer be used. 6 + 7 + Topkg is a packager for distributing OCaml software. It provides an 8 + API to describe the files a package installs in a given build 9 + configuration and to specify information about the package's 10 + distribution, creation and publication procedures. 11 + 12 + The optional topkg-care package provides the `topkg` command line tool 13 + which helps with various aspects of a package's life cycle: creating 14 + and linting a distribution, releasing it on the WWW, publish its 15 + documentation, add it to the OCaml opam repository, etc. 16 + 17 + Topkg is distributed under the ISC license and has **no** 18 + dependencies. This is what your packages will need as a *build* 19 + dependency. 20 + 21 + Topkg-care is distributed under the ISC license it depends on 22 + [fmt][fmt], [logs][logs], [bos][bos], [cmdliner][cmdliner], 23 + [webbrowser][webbrowser] and `opam-format`. 24 + 25 + [fmt]: http://erratique.ch/software/fmt 26 + [logs]: http://erratique.ch/software/logs 27 + [bos]: http://erratique.ch/software/bos 28 + [cmdliner]: http://erratique.ch/software/cmdliner 29 + [webbrowser]: http://erratique.ch/software/webbrowser 30 + 31 + Home page: <http://erratique.ch/software/topkg>""" 32 + maintainer: "Daniel Bünzli <daniel.buenzl i@erratique.ch>" 33 + authors: "The topkg programmers" 34 + license: "ISC" 35 + tags: ["packaging" "ocamlbuild" "org:erratique"] 36 + homepage: "https://erratique.ch/software/topkg" 37 + doc: "https://erratique.ch/software/topkg/doc" 38 + bug-reports: "https://github.com/dbuenzli/topkg/issues" 39 + depends: [ 40 + "ocaml" {>= "4.08.0"} 41 + "ocamlfind" {build & >= "1.6.1"} 42 + "ocamlbuild" 43 + ] 44 + build: ["ocaml" "pkg/pkg.ml" "build" "--pkg-name" name "--dev-pkg" "%{dev}%"] 45 + dev-repo: "git+https://erratique.ch/repos/topkg.git" 46 + x-maintenance-intent: ["(latest)"]