<html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns:m="http://schemas.microsoft.com/office/2004/12/omml" xmlns="http://www.w3.org/TR/REC-html40">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="Generator" content="Microsoft Word 14 (filtered medium)">
<style><!--
/* Font Definitions */
@font-face
        {font-family:"Cambria Math";
        panose-1:2 4 5 3 5 4 6 3 2 4;}
@font-face
        {font-family:Calibri;
        panose-1:2 15 5 2 2 2 4 3 2 4;}
@font-face
        {font-family:Tahoma;
        panose-1:2 11 6 4 3 5 4 4 2 4;}
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
        {margin:0cm;
        margin-bottom:.0001pt;
        font-size:12.0pt;
        font-family:"Times New Roman","serif";}
a:link, span.MsoHyperlink
        {mso-style-priority:99;
        color:blue;
        text-decoration:underline;}
a:visited, span.MsoHyperlinkFollowed
        {mso-style-priority:99;
        color:purple;
        text-decoration:underline;}
span.hoenzb
        {mso-style-name:hoenzb;}
span.EmailStyle18
        {mso-style-type:personal-reply;
        font-family:"Calibri","sans-serif";
        color:windowtext;}
.MsoChpDefault
        {mso-style-type:export-only;
        font-family:"Calibri","sans-serif";
        mso-fareast-language:EN-US;}
@page WordSection1
        {size:612.0pt 792.0pt;
        margin:72.0pt 72.0pt 72.0pt 72.0pt;}
div.WordSection1
        {page:WordSection1;}
--></style><!--[if gte mso 9]><xml>
<o:shapedefaults v:ext="edit" spidmax="1026" />
</xml><![endif]--><!--[if gte mso 9]><xml>
<o:shapelayout v:ext="edit">
<o:idmap v:ext="edit" data="1" />
</o:shapelayout></xml><![endif]-->
</head>
<body lang="EN-GB" link="blue" vlink="purple">
<div class="WordSection1">
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Calibri","sans-serif"">Tempting as it would be to spend the next couple of hours hacking it, my 2 euro-cents… why not just make the lexer in opam itself lossless, and expose that in the API? So
 every single token emitted by the lexer becomes a pair including the “whitespace” (= all whitespace, newlines, comments, etc.) which follow that token and any tokens which may be ambiguously lexed (e.g. integers where arbitrary zero prefixes or where bases
 are permitted) have to be deferred. All that’s needed then is a function which transforms these lossless tokens to the original lexer’s tokens (so dropping the whitespace/comment parts, possibly performing a few int_of_string and other related functions –
 but all aided by the type system) for plugging into the parsing stage. It’s even relatively transparent to have the “debugging” version of opam verify that the stream is actually reversible to the original file.<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Calibri","sans-serif""><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Calibri","sans-serif"">A task like this then uses the full-parsed power of opam-lib to identify opam files which need updating (as you are) and then a fairly simple state machine over the lossless
 lexer stream to update it. <o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Calibri","sans-serif""><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Calibri","sans-serif"">I think that having a rigid format used for opam-repository is a good idea, but given that presumably that wouldn’t want to become mandatory for all repositories, being able
 to do formatting (and even version) preserving updates seems useful. Having effectively a richer lexer also means no fork required, so better maintainability, and also puts the onus of supporting the updating of an older version opam file on the
<i>script author</i> rather than the opam dev team (as presumably OPAM will always remain capable of reading older format files??). Making the update be an FSM over raw lexer tokens is also slightly nicer and less error-prone than a regex approach.<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Calibri","sans-serif""><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Calibri","sans-serif""><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Calibri","sans-serif"">David<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Calibri","sans-serif""><o:p> </o:p></span></p>
<div style="border:none;border-left:solid blue 1.5pt;padding:0cm 0cm 0cm 4.0pt">
<div>
<div style="border:none;border-top:solid #B5C4DF 1.0pt;padding:3.0pt 0cm 0cm 0cm">
<p class="MsoNormal"><b><span lang="EN-US" style="font-size:10.0pt;font-family:"Tahoma","sans-serif"">From:</span></b><span lang="EN-US" style="font-size:10.0pt;font-family:"Tahoma","sans-serif""> opam-devel [mailto:opam-devel-bounces@lists.ocaml.org]
<b>On Behalf Of </b>Gabriel Scherer<br>
<b>Sent:</b> 23 November 2015 12:19<br>
<b>To:</b> Thomas Gazagnaire<br>
<b>Cc:</b> opam-devel<br>
<b>Subject:</b> Re: [opam-devel] [ANN] opam-fmt 1.0<o:p></o:p></span></p>
</div>
</div>
<p class="MsoNormal"><o:p> </o:p></p>
<div>
<div>
<p class="MsoNormal" style="margin-bottom:12.0pt">One idea I just had when re-reading the scavenged comments is to just consider comment lines as independent items. If you add ordering information to the internal opam representation to keep the items in the
 order, then I suspect that all the examples above would be reformatted in a way that is acceptable to the original author. One final bit of sophistication would be to store (in the comment items) a boolean for whether they started on their own line, or are
 to be inserted at the end of the previous line.<o:p></o:p></p>
</div>
<p class="MsoNormal">Of course this fake structure is less true to the real document structure than properly placed attribute nodes would be. It seems easier to implement (and thus a decent compromise to make), but then maybe not: keeping an ordering may be
 non-trivial for data whose current internal representation is not a set of things (for example dependency formulas are actually full logic formulas; but then if we keep to Conjunctive Normal Forms they can have a list-like representation).<o:p></o:p></p>
</div>
<div>
<p class="MsoNormal"><o:p> </o:p></p>
<div>
<p class="MsoNormal">On Mon, Nov 23, 2015 at 1:11 PM, Thomas Gazagnaire <<a href="mailto:thomas@gazagnaire.org" target="_blank">thomas@gazagnaire.org</a>> wrote:<o:p></o:p></p>
<p class="MsoNormal">Thanks Gabriel, that's very useful!<br>
<br>
For the various parsers, it might make sense to simply fork (or copy/paste) bits of opam parsers from various versions of opam and try to fix these parsers to:<br>
<br>
- keep comments<br>
- support some kind of alignments?<br>
<span style="color:#888888"><br>
<span class="hoenzb">Thomas</span></span><o:p></o:p></p>
<div>
<div>
<p class="MsoNormal" style="margin-bottom:12.0pt"><br>
<br>
> On 22 Nov 2015, at 20:55, Gabriel Scherer <<a href="mailto:gabriel.scherer@gmail.com">gabriel.scherer@gmail.com</a>> wrote:<br>
><br>
>> - Any chance you could use cmdliner instead of Arg?  Fairly minor, but<br>
>>  all the other plugins use it and it's nice to have the same behaviours<br>
>>  for OPAM plugins where possible.<br>
><br>
> I'm fond of Daniel's design work, so I would gladly move to<br>
> Cmdliner -- Arg was just what I could easily use "in anger" for<br>
> a first try.<br>
><br>
>> - How does this behave on pre-1.2 files?  I think it's about time that<br>
>>  we deprecate pre-1.2 so that we can have clean metadata standards<br>
>>  about the new fields such as dev-repo.<br>
><br>
> opam-fmt updates older opam files to its own support version -- and<br>
> refuses to work on newer files. Two things:<br>
><br>
> I think it would be a better design to have a family of scripts<br>
> opam-fmt-1.0, opam-fmt-1.1, opam-fmt-1.2 etc. and a "mother script"<br>
> opam-fmt that calls the right reformatter according to the file<br>
> version¹. However, that requires changes in the packaging of opam-lib,<br>
> so that the package for distinct versions can be installed<br>
> simultaneously (they would be separate packages<br>
> opam-lib-VER, and ocamlfind packages as well).<br>
><br>
> ¹: another option would be for opam-lib to also support pairs of parsing<br>
> and printing functions for older format version specifically, but that<br>
> is an invasive choice to make in a codebase. Right now there is a tiny<br>
> bit of logic to know which fields are 1.0 or 1.1-specific, but this<br>
> would be much more ambitious.<br>
><br>
> There are various warnings implemented in opam-lib that could be emitted<br>
> during the processing of files by opam-fmt -- they may be already<br>
> available depending on the OPAMDEBUG variable or something, but an<br>
> explicit support in the interface could be nice. When I tested<br>
> reformatting opam-repository, I observed that a large part of its opam<br>
> files raise such warnings (so indeed there seems to be a metadata<br>
> problem in the repository today).<br>
><br>
>> - Having a bot regularly go through and reformat the entire repository<br>
>>  also seems quite legit.  The alternative would be to reformat at the<br>
>>  merge point, but this would require a staging branch.<br>
><br>
> I'm not sure what you call "merge point"; my idea was to put the burden<br>
> of reformatting onto users submitting PRs against the<br>
> repository. (Regular reformatting are a sensible idea, but they run in<br>
> the problem of loss of information, whether distributed manual<br>
> reformatting keeps humans closer in the loop)<br>
><br>
>> - I would really like opam-fmt to be lossless, so preserving comments<br>
>>  seems like an extension that we should implement.  Ideally everyone<br>
>>  can just call it on their packages without thinking about it.<br>
><br>
> I have mixed feelings about trying to be lossless. At the very least,<br>
> one should recognize that setting this as a design goal would impose<br>
> a significant burden on the developers of the parsing/printing functions<br>
> in opam-lib.<br>
><br>
> Some human choices (alignment of string fields for example) are rather<br>
> difficult and fragile to recognize -- and they could complexify the<br>
> codebase. Even for comments, right now you cannot tell to which<br>
> configuration item an element is attached. There are several ways around<br>
> this, which are interesting to consider but also involve a fair amount<br>
> of work:<br>
><br>
> - You could use ocamldoc-like placement rules: "always after the<br>
>  relevant field" (a first comment would be a file-global comment), or<br>
>  "either before or after, but an empty line between the comment and<br>
>  a non-relevant field"; this seems painful and not-that-easy to<br>
>  implement.<br>
><br>
> - You could move to a docstring-like (or attribute-like) syntax where<br>
>  comments are explicitly attached to an AST node; from a language<br>
>  design point of view this would be my preference, but it may require<br>
>  a change in concrete syntax.<br>
><br>
> - Finally, the choices you can make in this design space depend a lot on<br>
>  whether reformatting will be performed by humans or by bots. If your<br>
>  comments-attachment rules are obscure, humans have the opportunity to<br>
>  reformat, see that they got them wrong, and reiterate. Bots will just<br>
>  put stuff at the wrong place.<br>
><br>
> I think that the people that maintain this corner of opam today are<br>
> those that will pay the greater cost if "lossless" becomes a design<br>
> goal, so it should be their choice to make.<br>
><br>
><br>
> In the meantime, it would be interesting to have a look at how opam<br>
> files in the repository actually use comments. With<br>
><br>
>  find packages -name 'opam' | xargs grep --color=always " #"<br>
><br>
> I see 75 occurences of comments, 38 of which are just "TODO fixme". The<br>
> 37 others seem rather interesting, below are a few representative examples:<br>
><br>
> packages/arakoon/arakoon.1.8.6/opam:<br>
>>  "lwt" { = "2.4.8" }  # 2.4.9 had an incompatible API change<br>
><br>
> Having textual exaplanations for choice of bound is a reasonable<br>
> use-case for attributes.<br>
><br>
> packages/camlp4/camlp4.4.01/opam:<br>
>>  build: [] # dummy package<br>
><br>
> This could be replaced by a dedicated note/comment field.<br>
><br>
> packages/frama-c/frama-c.20150201/opam:<br>
>>  "lablgtk" { >= "2.18.2" } #for ocaml >= 4.02.1<br>
><br>
> I don't understand the semantics of this one.<br>
><br>
> packages/git/git.1.6.0/opam:<br>
>>  depopts: [<br>
>>    # --enable-mirage<br>
>>    "mirage-types-lwt"<br>
>>    "mirage-http"<br>
>>    "mirage-flow"<br>
>>    "channel"<br>
>>    # --enable-unix<br>
>>    "cohttp"<br>
>>    "conduit"<br>
>>    "base-unix"<br>
>>  ]<br>
><br>
> This usage is very interesting, it seems to call for a hierarchy inside<br>
> the "depopts" list (and "dependencies" as well, I suppose), with<br>
> annotations on sub-groups of dependencies.<br>
><br>
> packages/gsl/gsl.1.18.2/opam:<br>
>>  depends: [<br>
>>    "base-bigarray"<br>
>>    "camlp4"<br>
>>    "ocamlfind" {>= "1.3.1"}<br>
>>    # Included from _opam file<br>
>>    "conf-gsl"<br>
>>  ]<br>
><br>
> I don't know what this comment means.<br>
><br>
> packages/lz4/lz4.1.0.0/opam:<br>
>> depexts: [<br>
>>  [["debian"] ["liblz4-dev"]]<br>
>>  # [["ubuntu"] ["liblz4-dev"]] reenable when CI updates its Ubuntu<br>
>>  [["source"] ["<a href="https://.../install.sh">https://.../install.sh</a>"]]<br>
>> ]<br>
><br>
> Again, this would require annotations.<br>
><br>
> packages/ppx_deriving/ppx_deriving.0.3/opam:<br>
>> build: [<br>
>>  # If there is no native dynlink, we can't use native builds<br>
>>  "ocaml" "pkg/<a href="http://build.ml" target="_blank">build.ml</a>" "native=true"<br>
>>                         "native-dynlink=true"<br>
>> ]<br>
><br>
> packages/frama-c-e-acsl/frama-c-e-acsl.0.5/opam:<br>
>> build: [<br>
>>  ["ocaml" "<a href="http://run_autoconf_if_needed.ml" target="_blank">run_autoconf_if_needed.ml</a>"] #when used in pinned mode the configure *can* not yet be generated<br>
>>  ["./configure" "--prefix" prefix]<br>
>>  [make]<br>
>> ]<br>
><br>
> packages/clangml/clangml.0.5.2/opam:<br>
>> depexts: [<br>
>>  [["debian"] ["libboost-dev" "llvm-3.4-dev" "clang-3.4" "libclang-3.4-dev" "binutils-dev"]]<br>
>>  [["ubuntu"] ["libboost-dev" "llvm-3.4-dev" "clang-3.4" "libclang-3.4-dev" "binutils-dev"]]<br>
>>  [["gentoo"] ["dev-libs/boost" "sys-devel/llvm-3.4.1-r2" "sys-devel/clang-3.4.0-r100" "sys-devel/binutils"]]<br>
>>  # archlinux has no package providing llvm and clang 3.4.1<br>
>>  [["archlinux"] ["boost" "binutils"]]<br>
>> ]<br>
><br>
> packages/mtime/mtime.0.8.1/opam:<br>
>> depends: [ "ocamlfind"<br>
>>           "js_of_ocaml" # FIXME should become a deptopt<br>
>> ]<br>
><br>
> On Sun, Nov 22, 2015 at 8:09 PM, Anil Madhavapeddy <<a href="mailto:anil@recoil.org">anil@recoil.org</a>> wrote:<br>
>> Thanks for this Gabriel! Quick notes:<br>
>><br>
>> - I would really like opam-fmt to be lossless, so preserving comments<br>
>>  seems like an extension that we should implement.  Ideally everyone<br>
>>  can just call it on their packages without thinking about it.<br>
>><br>
>> - Having a bot regularly go through and reformat the entire repository<br>
>>  also seems quite legit.  The alternative would be to reformat at the<br>
>>  merge point, but this would require a staging branch.<br>
>><br>
>> - Any chance you could use cmdliner instead of Arg?  Fairly minor, but<br>
>>  all the other plugins use it and it's nice to have the same behaviours<br>
>>  for OPAM plugins where possible.<br>
>><br>
>> - How does this behave on pre-1.2 files?  I think it's about time that<br>
>>  we deprecate pre-1.2 so that we can have clean metadata standards<br>
>>  about the new fields such as dev-repo.<br>
>><br>
>> regards,<br>
>> Anil<br>
>><br>
>>> On 21 Nov 2015, at 21:53, Gabriel Scherer <<a href="mailto:gabriel.scherer@gmail.com">gabriel.scherer@gmail.com</a>> wrote:<br>
>>><br>
>>> Hi opam-devel,<br>
>>><br>
>>> As part of the discussion in<br>
>>><br>
>>> bulk addition of 'ocamlbuild {build}' dependencies<br>
>>> <a href="https://github.com/ocaml/opam-repository/pull/5140" target="_blank">
https://github.com/ocaml/opam-repository/pull/5140</a><br>
>>><br>
>>> it became apparent that performing bulk updates on opam-repository is<br>
>>> made harder by the fact that the parse-print roundtrip does not<br>
>>> preserve human-formatted opam files. For my proposed patch I tried to<br>
>>> separate the reformatting of opam file (to follow the opam-lib printer<br>
>>> convention) from the actual changes in two separate commits, but that<br>
>>> means more work for script authors, and also creates patches that are<br>
>>> harder to review. (If (re)formatting was controlled by the maintainer<br>
>>> of the OPAM packages instead of authors of bulk updates, we would be<br>
>>> more confident in its correctness.)<br>
>>><br>
>>> In order to move that discussion forward (how to maintain opam<br>
>>> metadata in a way that is easy for both human and scripts to work<br>
>>> with?), I propose the opam-fmt script that can be found at<br>
>>> <a href="https://github.com/gasche/opam-fmt/" target="_blank">https://github.com/gasche/opam-fmt/</a><br>
>>><br>
>>> I wrote it in the last few days and there are probably some rough<br>
>>> edges. Once I feel that it should work, I may try to package it on the<br>
>>> opam-repo (in the meantime, clone then pin).<br>
>>><br>
>>> This suggests one possible way forward: publicize opam-fmt, encourage<br>
>>> users to reformat their opam files using it, adapt the opam-repository<br>
>>> QA to call `opam fmt --check` on opam files proposed in PR to enforce<br>
>>> it, and after some time (once we trust it works as expected thanks to<br>
>>> the human guinea pigs) reformat all older opam files to get a perfect<br>
>>> round-trip on all files of the repository.<br>
>>> It is not clear to me that this is the best way forward. (For example<br>
>>> it means that, in the current state of the opam file parsing/printing<br>
>>> code, comments in opam files would always be erased by reformatting.<br>
>>> Should we remove comments from the valid syntax of opam files, or<br>
>>> attach them to configuration lines to re-print them correctly later,<br>
>>> or maybe refuse to work on files with comments?) Opam developers and<br>
>>> repository maintainers may decide that the cost of caring about<br>
>>> reformatting outweigh the benefits in terms of scriptability.<br>
>>><br>
>>> What do you think?<br>
>>> _______________________________________________<br>
>>> opam-devel mailing list<br>
>>> <a href="mailto:opam-devel@lists.ocaml.org">opam-devel@lists.ocaml.org</a><br>
>>> <a href="http://lists.ocaml.org/listinfo/opam-devel" target="_blank">http://lists.ocaml.org/listinfo/opam-devel</a><br>
>>><br>
>><br>
> _______________________________________________<br>
> opam-devel mailing list<br>
> <a href="mailto:opam-devel@lists.ocaml.org">opam-devel@lists.ocaml.org</a><br>
> <a href="http://lists.ocaml.org/listinfo/opam-devel" target="_blank">http://lists.ocaml.org/listinfo/opam-devel</a><o:p></o:p></p>
</div>
</div>
</div>
<p class="MsoNormal"><o:p> </o:p></p>
</div>
</div>
</div>
</body>
</html>