-
Notifications
You must be signed in to change notification settings - Fork 20
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RFC: obuild: refactor handling of commands and options #180
Conversation
My two cents: don't open modules, this is not Haskell code, use well-chosen module aliases. But even for printf, a module alias may not hurt:
|
Yes, this is not something I would have done otherwise. As I wrote earlier (see the "drawback" paragraph), this is due to the way obuild builds stuff: considering nothing requires these new modules explicitly, then the dependency detection does not consider them part of the sources, and thus simply does not build them at all. Unless you have any idea about out to overcome this situation and still let obuild explicitly build modules even not explicitly used, another option could be change the modules to add a function like |
Forgive me, but doing this compiles fine:
Hence, I don't see your point. |
Shameless plug, if you need/want to parse command lines with super terse code, there is this: |
I'll try to be more verbose:
What happens during the build of obuild using obuilt (either with an existing version, or after bootstrap) is:
|
OK. But what I said is that module aliases do the same job as your opens, while the code becomes easier to maintain (because you know where things come from). |
The current system mixes manual command line parsing for global options, and the Arg module for command options, which is not optimal: - no way to generate the list of global command line options, nor to document them - the per-command options are used only when parsing, so showing them for help is not possible - two different ways to parse options Also, the handling of commands themselves is messy: - all of them are implemented in the same module, overloading it with different tasks - their help texts are structured in lists of lines, and with a separate list to maintain - the names of the commands are hardcoded in a couple of places To overcome this situation, create a small Cmd module to represent a single command, with all the information stored there: name, options, help texts, and the actual function for it. Split all the commands in own modules named Cmd_<command>, which contains only the command declaration, its function, and all the helper stuff for it. Consequently refactor the command line handling: use the dynamic parsing feature of Arg, so the global command line options (which are now converted to Arg) are parsed first, and then the command options are parsed. The results are various: - each command is in its own modules, with its options clearly represented, and help texts side with them - global command line options are parsed properly - main.ml is very small, way more readable, and will almost require no changes for adding new commands (see drawback below) - the 'help' command had to be basically rewritten due to the refactoring; OTOH, `obuild help <command>` works fine now - `obuild --help` works fine now (fixes ocaml-obuild#136) The only drawback is that, due to the way obuild is built using obuild, the modules of the commands are not built if nothing references them. As result, create no-op aliases for all of them in main.ml.
d097149
to
9f258ad
Compare
I see now -- your patch above slightly fooled me, since it was not related to the issue I talked about. I updated the last commit now with that, thanks for the hint! |
While I agree with you that the cli code should be distributed in different files, I am not convinced by your approach. Particularly the dynamic registration, I would prefer that the compiler can check everything. |
Thanks for taking the time to review it.
I am not sure, what is not checked right now?
I gave a quick look at it, and it seems pretty big, and possibly way too much for what obuild seems to need.
As package maintainer in a couple of distros, I recommend to not do this, even if it seems convenient. It is very likely that this copy will not be maintained and rot, and thus be only a maintenance burden (e.g. for compatibility with new OCaml versions). |
Indeed, before I will decide which framework would be useful, I will need to try to compile with different versions of ocaml, and check the general feeling of the cli. |
I have a very small CLI parsing library: |
You already mentioned it here :) However:
I still do think that the code I wrote is still a good compromise between refactoring vs external code embedding. |
@ptoscano Yes, and minicli will never support all that (because of the "mini" in the name, and the fact that I use it for fast software prototyping). |
@ptoscano Just quoting your code, for fun:
In minicli, you would write this:
and you would put some documentation string in the usage function of your program. |
Then it cannot be used for obuild, as it needs to have global options and per-command options. And that's why ...
... your example to prove that minicli is better than what I wrote does not work. Your example will make -j as global option, not only specific for the "build" subcommand. Even more so, it does not help with documentation, which is a current problem in obuild (single & incomplete Since you are in mood of "just for fun", what about a) trying my code b) acknowledge that your code simply is not enough for obuild? Thanks. |
The current system mixes manual command line parsing for global options, and the
Arg
module for command options, which is not optimal:Also, the handling of commands themselves is messy:
To overcome this situation, create a small
Cmd
module to represent a single command, with all the information stored there: name, options, help texts, and the actual function for it. Split all the commands in own modules namedCmd_<command>
, which contains only the commanddeclaration, its function, and all the helper stuff for it.
Consequently refactor the command line handling: use the dynamic parsing feature of
Arg
, so the global command line options (which are now converted toArg
) are parsed first, and then the command options are parsed.The results are various:
main.ml
is very small, way more readable, and will almost require no changes for adding new commands (see drawback below)help
command had to be basically rewritten due to the refactoring; OTOH,obuild help <command>
works fine nowobuild --help
works fine now (fixes --help doesn't work #136)The only drawback is that, due to the way obuild is built using obuild, the modules of the commands are not built if nothing references them. As result, create no-op aliases for all of them in
main.ml
.This is mostly good, but still marked as RFC for few reasons: