From 0e56fe29a9afeee00e02e722496678df89d37d50 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 17 Nov 2009 13:59:39 +0200 Subject: Complete the implementation of the option documentation Add the man page generator. Port CLI usage, HTML documentation and the man page to the auto-generated version. Update examples and documentation. --- NEWS | 27 +++++ cli/context.cxx | 79 ++++++++++++- cli/context.hxx | 9 +- cli/generator.cxx | 129 +++++++++++++++++++-- cli/html.cxx | 12 ++ cli/makefile | 1 + cli/man.cxx | 163 ++++++++++++++++++++++++++ cli/man.hxx | 14 +++ cli/options.cli | 114 ++++++++++++++----- cli/options.cxx | 107 +++++++++++++---- cli/options.hxx | 36 +++++- cli/options.ixx | 54 ++++++++- cli/source.cxx | 8 +- doc/cli-epilogue.1 | 21 ++++ doc/cli-epilogue.xhtml | 24 ++++ doc/cli-prologue.1 | 59 ++++++++++ doc/cli-prologue.xhtml | 79 +++++++++++++ doc/cli.1 | 223 +++++++++++++++++++++--------------- doc/cli.xhtml | 199 +++++++++++++++++++++----------- doc/guide/index.xhtml | 284 +++++++++++++++++++++++++++++++++++++++++----- doc/makefile | 26 ++++- examples/hello/driver.cxx | 7 +- examples/hello/hello.cli | 16 ++- 23 files changed, 1420 insertions(+), 271 deletions(-) create mode 100644 cli/man.cxx create mode 100644 cli/man.hxx create mode 100644 doc/cli-epilogue.1 create mode 100644 doc/cli-epilogue.xhtml create mode 100644 doc/cli-prologue.1 create mode 100644 doc/cli-prologue.xhtml diff --git a/NEWS b/NEWS index a4e1bbf..f43c04c 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,30 @@ +Version 1.1.0 + + * Support for option documentation. Option documentation is used to print + the usage information as well as to generate the program documentation in + the HTML and man page formats. For details, see Sections 2.5, "Adding + Documentation" and 3.3, "Option Documentation" in the CLI Language Getting + Started Guide. New CLI compiler command line options related to this + feature: + + --suppress-usage + --long-usage + --option-length + --generate-cxx + --generate-man + --generate-html + --man-prologue + --man-epilogue + --html-prologue + --html-epilogue + --man-suffix + --html-suffix + --class + --stdout + + The CLI compiler usage, HTML documentation, and man page are auto-generated + using this feature. + Version 1.0.0 * First public release. diff --git a/cli/context.cxx b/cli/context.cxx index ccd3c8b..05e15cc 100644 --- a/cli/context.cxx +++ b/cli/context.cxx @@ -309,7 +309,19 @@ format (string const& s, output_type ot) { case '\\': { - r += '\\'; + switch (ot) + { + case ot_man: + { + r += "\\e"; + break; + } + default: + { + r += '\\'; + break; + } + } break; } case '"': @@ -461,6 +473,12 @@ format (string const& s, output_type ot) switch (ot) { + case ot_plain: + { + if (b & 1) + r += "'"; + break; + } case ot_html: { if (b & 1) @@ -474,6 +492,17 @@ format (string const& s, output_type ot) break; } + case ot_man: + { + if ((b & 6) == 6) + r += "\\f(BI"; + else if (b & 2) + r += "\\fI"; + else if (b & 4) + r += "\\fB"; + + break; + } default: break; } @@ -490,6 +519,7 @@ format (string const& s, output_type ot) switch (ot) { case ot_plain: + case ot_man: { r += '\n'; break; @@ -506,25 +536,43 @@ format (string const& s, output_type ot) } } } + else if (s[i] == '.') + { + if (ot == ot_man) + r += "\\."; + else + r += '.'; + } else if (!blocks.empty () && s[i] == '}') { unsigned char b (blocks.top ()); switch (ot) { - case ot_html: + case ot_plain: { if (b & 1) - r += ""; + r += "'"; + break; + } + case ot_html: + { + if (b & 4) + r += ""; if (b & 2) r += ""; - if (b & 4) - r += ""; + if (b & 1) + r += ""; break; } + case ot_man: + { + if (b & 6) + r += "\\fP"; + } default: break; } @@ -541,6 +589,27 @@ format (string const& s, output_type ot) return r; } +string context:: +fq_name (semantics::nameable& n, bool cxx_name) +{ + using namespace semantics; + + string r; + + if (dynamic_cast (&n)) + { + return ""; // Map to global namespace. + } + else + { + r = fq_name (n.scope ()); + r += "::"; + r += cxx_name ? escape (n.name ()) : n.name (); + } + + return r; +} + // namespace // diff --git a/cli/context.hxx b/cli/context.hxx index f6d53c3..30539af 100644 --- a/cli/context.hxx +++ b/cli/context.hxx @@ -70,7 +70,8 @@ public: enum output_type { ot_plain, - ot_html + ot_html, + ot_man }; static string @@ -96,6 +97,12 @@ public: } public: + // Return fully-qualified C++ or CLI name. + // + string + fq_name (semantics::nameable& n, bool cxx_name = true); + +public: context (std::ostream&, semantics::cli_unit&, options_type const&); context (context&); diff --git a/cli/generator.cxx b/cli/generator.cxx index 6043825..7531ffe 100644 --- a/cli/generator.cxx +++ b/cli/generator.cxx @@ -21,6 +21,7 @@ #include "runtime-inline.hxx" #include "runtime-source.hxx" +#include "man.hxx" #include "html.hxx" #include "context.hxx" @@ -35,10 +36,23 @@ using semantics::path; namespace { - static char const header[] = - "// This code was generated by CLI, a command line interface\n" - "// compiler for C++.\n" - "//\n\n"; + static char const cxx_header[] = + "// This code was generated by CLI, a command line interface\n" + "// compiler for C++.\n" + "//\n\n"; + + static char const man_header[] = + ".\\\"\n" + ".\\\" The following documentation was generated by CLI, a command\n" + ".\\\" line interface compiler for C++.\n" + ".\\\"\n"; + + static char const html_header[] = + "\n" + "\n\n"; string make_guard (string const& file, context& ctx) @@ -63,6 +77,18 @@ namespace return ctx.escape (r); } + + void + open (ifstream& ifs, string const& path) + { + ifs.open (path.c_str (), ios_base::in | ios_base::binary); + + if (!ifs.is_open ()) + { + cerr << path << ": error: unable to open in read mode" << endl; + throw generator::failed (); + } + } } generator:: @@ -103,6 +129,8 @@ generate (options const& ops, semantics::cli_unit& unit, path const& p) fs::auto_removes auto_rm; + // C++ output. + // if (gen_cxx) { bool inl (!ops.suppress_inline ()); @@ -177,10 +205,10 @@ generate (options const& ops, semantics::cli_unit& unit, path const& p) // Print headers. // - hxx << header; + hxx << cxx_header; if (inl) - ixx << header; - cxx << header; + ixx << cxx_header; + cxx << cxx_header; typedef compiler::ostream_filter @@ -255,8 +283,83 @@ generate (options const& ops, semantics::cli_unit& unit, path const& p) } } + // man output + // + if (gen_man) + { + // Prologue & epilogue. + // + ifstream prologue, epilogue; + { + string file (ops.man_prologue ()); + + if (!file.empty ()) + open (prologue, file); + } + + { + string file (ops.man_epilogue ()); + + if (!file.empty ()) + open (epilogue, file); + } + + ofstream man; + + if (!ops.stdout ()) + { + path man_path (base + ops.man_suffix ()); + + if (!ops.output_dir ().empty ()) + man_path = path (ops.output_dir ()) / man_path; + + man.open (man_path.string ().c_str ()); + + if (!man.is_open ()) + { + cerr << "error: unable to open '" << man_path << "' in write mode" + << endl; + throw failed (); + } + + auto_rm.add (man_path); + } + + ostream& os (ops.stdout () ? cout : man); + + if (prologue.is_open ()) + os << prologue.rdbuf (); + + os << man_header; + + context ctx (os, unit, ops); + generate_man (ctx); + + if (epilogue.is_open ()) + os << epilogue.rdbuf (); + } + + // HTML output + // if (gen_html) { + // Prologue & epilogue. + // + ifstream prologue, epilogue; + { + string file (ops.html_prologue ()); + + if (!file.empty ()) + open (prologue, file); + } + + { + string file (ops.html_epilogue ()); + + if (!file.empty ()) + open (epilogue, file); + } + ofstream html; if (!ops.stdout ()) @@ -278,8 +381,18 @@ generate (options const& ops, semantics::cli_unit& unit, path const& p) auto_rm.add (html_path); } - context ctx (ops.stdout () ? cout : html, unit, ops); + ostream& os (ops.stdout () ? cout : html); + + if (prologue.is_open ()) + os << prologue.rdbuf (); + + os << html_header; + + context ctx (os, unit, ops); generate_html (ctx); + + if (epilogue.is_open ()) + os << epilogue.rdbuf (); } auto_rm.cancel (); diff --git a/cli/html.cxx b/cli/html.cxx index b147703..be675c7 100644 --- a/cli/html.cxx +++ b/cli/html.cxx @@ -162,6 +162,17 @@ namespace virtual void traverse (type& c) { + string const& n (options.class_ ()); + + if (!n.empty ()) + { + string fqn (fq_name (c, false)); + fqn = string (fqn, 2, fqn.size () - 2); // Get rid of leading ::. + + if (n != fqn) + return; + } + os << "
" << endl; names (c, names_option_); @@ -170,6 +181,7 @@ namespace } private: + bool generated_; option option_; traversal::names names_option_; }; diff --git a/cli/makefile b/cli/makefile index 7edd27e..0ebba36 100644 --- a/cli/makefile +++ b/cli/makefile @@ -16,6 +16,7 @@ source.cxx \ runtime-header.cxx \ runtime-inline.cxx \ runtime-source.cxx \ +man.cxx \ html.cxx \ generator.cxx \ name-processor.cxx diff --git a/cli/man.cxx b/cli/man.cxx new file mode 100644 index 0000000..cb7822c --- /dev/null +++ b/cli/man.cxx @@ -0,0 +1,163 @@ +// file : cli/man.cxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009 Code Synthesis Tools CC +// license : MIT; see accompanying LICENSE file + +#include "man.hxx" + +namespace +{ + struct option: traversal::option, context + { + option (context& c) : context (c) {} + + virtual void + traverse (type& o) + { + using semantics::names; + + names& n (o.named ()); + + os << ".IP \"\\fB"; + + for (names::name_iterator i (n.name_begin ()); i != n.name_end (); ++i) + { + if (i != n.name_begin ()) + os << "\\fP|\\fB"; + + os << *i; + } + + os << "\\fP"; + + type::doc_list const& doc (o.doc ()); + string type (o.type ().name ()); + + std::set arg_set; + + if (type != "bool") + { + string s ( + translate_arg ( + doc.size () > 0 ? doc[0] : string (""), arg_set)); + + os << ' ' << format (s, ot_man); + } + + os << "\"" << endl; + + string d; + + // If we have both the long and the short descriptions, use + // the long one. + // + if (type == "bool") + { + if (doc.size () > 1) + d = doc[1]; + else if (doc.size () > 0) + d = doc[0]; + } + else + { + if (doc.size () > 2) + d = doc[2]; + else if (doc.size () > 1) + d = doc[1]; + } + + // Format the documentation string. + // + d = format (translate (d, arg_set), ot_man); + + if (!d.empty ()) + { + size_t b (0), e (0), i (0); + + for (size_t n (d.size ()); i < n; ++i) + { + if (d[i] == ' ' || d[i] == '\n') + e = i; + + if (d[i] == '\n' || (i - b >= 76 && e != b)) + { + if (b != 0) + os << endl; + + os << string (d, b, e - b); + + if (d[i] == '\n') + os << endl; + + b = e = e + 1; + } + } + + // Flush the last line. + // + if (b != i) + { + if (b != 0) + os << endl; + + os << string (d, b, i - b); + } + } + + os << endl + << endl; + } + }; + + // + // + struct class_: traversal::class_, context + { + class_ (context& c) + : context (c), option_ (c) + { + names_option_ >> option_; + } + + virtual void + traverse (type& c) + { + string const& n (options.class_ ()); + + if (!n.empty ()) + { + string fqn (fq_name (c, false)); + fqn = string (fqn, 2, fqn.size () - 2); // Get rid of leading ::. + + if (n != fqn) + return; + } + + names (c, names_option_); + } + + private: + bool generated_; + option option_; + traversal::names names_option_; + }; +} + +void +generate_man (context& ctx) +{ + traversal::cli_unit unit; + traversal::names unit_names; + traversal::namespace_ ns; + class_ cl (ctx); + + unit >> unit_names >> ns; + unit_names >> cl; + + traversal::names ns_names; + + ns >> ns_names >> ns; + ns_names >> cl; + + unit.dispatch (ctx.unit); +} diff --git a/cli/man.hxx b/cli/man.hxx new file mode 100644 index 0000000..7a4100a --- /dev/null +++ b/cli/man.hxx @@ -0,0 +1,14 @@ +// file : cli/man.hxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009 Code Synthesis Tools CC +// license : MIT; see accompanying LICENSE file + +#ifndef CLI_MAN_HXX +#define CLI_MAN_HXX + +#include "context.hxx" + +void +generate_man (context&); + +#endif // CLI_MAN_HXX diff --git a/cli/options.cli b/cli/options.cli index 1127553..085a26a 100644 --- a/cli/options.cli +++ b/cli/options.cli @@ -20,110 +20,170 @@ class options std::string --output-dir | -o { "", - "Write generated files to ." + "Write the generated files to instead of the current directory." }; bool --suppress-inline { - "Generate all functions non-inline." + "Generate all functions non-inline. By default simple functions are + made inline. This option suppresses creation of the inline file." }; bool --suppress-usage { - "Suppress generation of usage printing code." + "Suppress the generation of the usage printing code." + }; + + bool --long-usage + { + "If no short documentation string is provided, use the complete + long documentation string in usage. By default, in this situation + only the first sentence from the long string is used." + }; + + std::size_t --option-length = 0 + { + "", + "Indent option descriptions characters when printing usage. This is + useful when you have multiple options classes, potentially in separate + files, and would like their usage to have the same indentation level." }; bool --generate-cxx { - "" + "Generate C++ code. If neither \cb{--generate-man} nor \cb{--generate-html} + is specified, this mode is assumed by default." }; bool --generate-man { - "" + "Generate documentation in the man page format." }; bool --generate-html { - "" + "Generate documentation in the HTML format." }; - bool --stdout + std::string --man-prologue { - "" + "", + "Insert the content of at the beginning of the man page file." }; - std::size_t --option-length = 0 + std::string --man-epilogue { - "", - "Indent option description characters when printing usage." + "", + "Insert the content of at the end of the man page file." + }; + + std::string --html-prologue + { + "", + "Insert the content of at the beginning of the HTML file." + }; + + std::string --html-epilogue + { + "", + "Insert the content of at the end of the HTML file." + }; + + std::string --class + { + "", + "Generate the man page or HTML documentation only for the options + class. The name should be a fully-qualified options class name, + for example, \cb{app::options}. This functionality is useful if you need + to insert custom documentation between options belonging to different + classes." + }; + + bool --stdout + { + "Write output to STDOUT instead of a file. This option is not valid + when generating C++ code and is normally used to combine generated + documentation for several option classes in a single file." }; std::string --hxx-suffix = ".hxx" { "", - "Use instead of the default '.hxx' to construct the name of + "Use instead of the default \cb{.hxx} to construct the name of the generated header file." }; std::string --ixx-suffix = ".ixx" { "", - "Use instead of the default '.ixx' to construct the name of + "Use instead of the default \cb{.ixx} to construct the name of the generated inline file." }; std::string --cxx-suffix = ".cxx" { "", - "Use instead of the default '.cxx' to construct the name of + "Use instead of the default \cb{.cxx} to construct the name of the generated source file." }; + std::string --man-suffix = ".1" + { + "", + "Use instead of the default \cb{.1} to construct the name of + the generated man page file." + }; + std::string --html-suffix = ".html" { "", - "Use instead of the default '.html' to construct the name of - the generated HTML file." + "Use instead of the default \cb{.html} to construct the name + of the generated HTML file." }; std::string --option-prefix = "-" { "", - "Use instead of the default '-' as an option prefix." + "Use instead of the default \cb{-} as an option prefix. Unknown + command line arguments that start with this prefix are treated as unknown + options. If you set the option prefix to the empty value, then all the + unknown command line arguments will be treated as program arguments." }; std::string --option-separator = "--" { "", - "Use instead of the default '--' as an optional separator between - options and arguments." + "Use instead of the default \cb{--} as an optional separator between + options and arguments. All the command line arguments that are parsed + after this separator are treated as program arguments. Set the option + separator to the empty value if you don't want this functionality." }; bool --include-with-brackets { - "Use angle brackets (<>) instead of quotes (\"\") in generated #include - directives." + "Use angle brackets (<>) instead of quotes (\"\") in the generated + \cb{#include} directives." }; std::string --include-prefix { "", - "Add to generated #include directive paths." + "Add to the generated \cb{#include} directive paths." }; std::string --guard-prefix { "", - "Add to generated header inclusion guards." + "Add to the generated header inclusion guards. The prefix is + transformed to upper case and characters that are illegal in a + preprocessor macro name are replaced with underscores." }; std::map --reserved-name { "=", - "Add to the list of names that should not be used as identifiers. - The name can optionally be followed by '=' and the replacement - name that should be used instead.", - "" + "Add with an optional replacement to the list of names + that should not be used as identifiers. If provided, the replacement + name is used instead. All C++ keywords are already in this list." }; }; diff --git a/cli/options.cxx b/cli/options.cxx index 1f495b9..38ed14d 100644 --- a/cli/options.cxx +++ b/cli/options.cxx @@ -243,14 +243,21 @@ options (int argc, output_dir_ (), suppress_inline_ (), suppress_usage_ (), + long_usage_ (), + option_length_ (0), generate_cxx_ (), generate_man_ (), generate_html_ (), + man_prologue_ (), + man_epilogue_ (), + html_prologue_ (), + html_epilogue_ (), + class__ (), stdout_ (), - option_length_ (0), hxx_suffix_ (".hxx"), ixx_suffix_ (".ixx"), cxx_suffix_ (".cxx"), + man_suffix_ (".1"), html_suffix_ (".html"), option_prefix_ ("-"), option_separator_ ("--"), @@ -273,14 +280,21 @@ options (int start, output_dir_ (), suppress_inline_ (), suppress_usage_ (), + long_usage_ (), + option_length_ (0), generate_cxx_ (), generate_man_ (), generate_html_ (), + man_prologue_ (), + man_epilogue_ (), + html_prologue_ (), + html_epilogue_ (), + class__ (), stdout_ (), - option_length_ (0), hxx_suffix_ (".hxx"), ixx_suffix_ (".ixx"), cxx_suffix_ (".cxx"), + man_suffix_ (".1"), html_suffix_ (".html"), option_prefix_ ("-"), option_separator_ ("--"), @@ -303,14 +317,21 @@ options (int argc, output_dir_ (), suppress_inline_ (), suppress_usage_ (), + long_usage_ (), + option_length_ (0), generate_cxx_ (), generate_man_ (), generate_html_ (), + man_prologue_ (), + man_epilogue_ (), + html_prologue_ (), + html_epilogue_ (), + class__ (), stdout_ (), - option_length_ (0), hxx_suffix_ (".hxx"), ixx_suffix_ (".ixx"), cxx_suffix_ (".cxx"), + man_suffix_ (".1"), html_suffix_ (".html"), option_prefix_ ("-"), option_separator_ ("--"), @@ -334,14 +355,21 @@ options (int start, output_dir_ (), suppress_inline_ (), suppress_usage_ (), + long_usage_ (), + option_length_ (0), generate_cxx_ (), generate_man_ (), generate_html_ (), + man_prologue_ (), + man_epilogue_ (), + html_prologue_ (), + html_epilogue_ (), + class__ (), stdout_ (), - option_length_ (0), hxx_suffix_ (".hxx"), ixx_suffix_ (".ixx"), cxx_suffix_ (".cxx"), + man_suffix_ (".1"), html_suffix_ (".html"), option_prefix_ ("-"), option_separator_ ("--"), @@ -360,22 +388,41 @@ print_usage (::std::ostream& os) os << "--version Print version and exit." << ::std::endl; - os << "--output-dir|-o Write generated files to ." << ::std::endl; + os << "--output-dir|-o Write the generated files to instead of the" << ::std::endl + << " current directory." << ::std::endl; os << "--suppress-inline Generate all functions non-inline." << ::std::endl; - os << "--suppress-usage Suppress generation of usage printing code." << ::std::endl; + os << "--suppress-usage Suppress the generation of the usage printing code." << ::std::endl; - os << "--generate-cxx" << std::endl; + os << "--long-usage If no short documentation string is provided, use" << ::std::endl + << " the complete long documentation string in usage." << ::std::endl; - os << "--generate-man" << std::endl; + os << "--option-length Indent option descriptions characters when" << ::std::endl + << " printing usage." << ::std::endl; - os << "--generate-html" << std::endl; + os << "--generate-cxx Generate C++ code." << ::std::endl; - os << "--stdout" << std::endl; + os << "--generate-man Generate documentation in the man page format." << ::std::endl; - os << "--option-length Indent option description characters when" << ::std::endl - << " printing usage." << ::std::endl; + os << "--generate-html Generate documentation in the HTML format." << ::std::endl; + + os << "--man-prologue Insert the content of at the beginning of" << ::std::endl + << " the man page file." << ::std::endl; + + os << "--man-epilogue Insert the content of at the end of the man" << ::std::endl + << " page file." << ::std::endl; + + os << "--html-prologue Insert the content of at the beginning of" << ::std::endl + << " the HTML file." << ::std::endl; + + os << "--html-epilogue Insert the content of at the end of the HTML" << ::std::endl + << " file." << ::std::endl; + + os << "--class Generate the man page or HTML documentation only" << ::std::endl + << " for the options class." << ::std::endl; + + os << "--stdout Write output to STDOUT instead of a file." << ::std::endl; os << "--hxx-suffix Use instead of the default '.hxx' to" << ::std::endl << " construct the name of the generated header file." << ::std::endl; @@ -386,6 +433,9 @@ print_usage (::std::ostream& os) os << "--cxx-suffix Use instead of the default '.cxx' to" << ::std::endl << " construct the name of the generated source file." << ::std::endl; + os << "--man-suffix Use instead of the default '.1' to" << ::std::endl + << " construct the name of the generated man page file." << ::std::endl; + os << "--html-suffix Use instead of the default '.html' to" << ::std::endl << " construct the name of the generated HTML file." << ::std::endl; @@ -396,16 +446,17 @@ print_usage (::std::ostream& os) << " optional separator between options and arguments." << ::std::endl; os << "--include-with-brackets Use angle brackets (<>) instead of quotes (\"\") in" << ::std::endl - << " generated #include directives." << ::std::endl; + << " the generated '#include' directives." << ::std::endl; - os << "--include-prefix Add to generated #include directive paths." << ::std::endl; + os << "--include-prefix Add to the generated '#include' directive" << ::std::endl + << " paths." << ::std::endl; - os << "--guard-prefix Add to generated header inclusion guards." << ::std::endl; + os << "--guard-prefix Add to the generated header inclusion" << ::std::endl + << " guards." << ::std::endl; - os << "--reserved-name = Add to the list of names that should not be" << ::std::endl - << " used as identifiers. The name can optionally be" << ::std::endl - << " followed by '=' and the replacement name that" << ::std::endl - << " should be used instead." << ::std::endl; + os << "--reserved-name = Add with an optional replacement to" << ::std::endl + << " the list of names that should not be used as" << ::std::endl + << " identifiers." << ::std::endl; } typedef @@ -430,22 +481,36 @@ struct _cli_options_map_init &::cli::thunk< options, bool, &options::suppress_inline_ >; _cli_options_map_["--suppress-usage"] = &::cli::thunk< options, bool, &options::suppress_usage_ >; + _cli_options_map_["--long-usage"] = + &::cli::thunk< options, bool, &options::long_usage_ >; + _cli_options_map_["--option-length"] = + &::cli::thunk< options, std::size_t, &options::option_length_ >; _cli_options_map_["--generate-cxx"] = &::cli::thunk< options, bool, &options::generate_cxx_ >; _cli_options_map_["--generate-man"] = &::cli::thunk< options, bool, &options::generate_man_ >; _cli_options_map_["--generate-html"] = &::cli::thunk< options, bool, &options::generate_html_ >; + _cli_options_map_["--man-prologue"] = + &::cli::thunk< options, std::string, &options::man_prologue_ >; + _cli_options_map_["--man-epilogue"] = + &::cli::thunk< options, std::string, &options::man_epilogue_ >; + _cli_options_map_["--html-prologue"] = + &::cli::thunk< options, std::string, &options::html_prologue_ >; + _cli_options_map_["--html-epilogue"] = + &::cli::thunk< options, std::string, &options::html_epilogue_ >; + _cli_options_map_["--class"] = + &::cli::thunk< options, std::string, &options::class__ >; _cli_options_map_["--stdout"] = &::cli::thunk< options, bool, &options::stdout_ >; - _cli_options_map_["--option-length"] = - &::cli::thunk< options, std::size_t, &options::option_length_ >; _cli_options_map_["--hxx-suffix"] = &::cli::thunk< options, std::string, &options::hxx_suffix_ >; _cli_options_map_["--ixx-suffix"] = &::cli::thunk< options, std::string, &options::ixx_suffix_ >; _cli_options_map_["--cxx-suffix"] = &::cli::thunk< options, std::string, &options::cxx_suffix_ >; + _cli_options_map_["--man-suffix"] = + &::cli::thunk< options, std::string, &options::man_suffix_ >; _cli_options_map_["--html-suffix"] = &::cli::thunk< options, std::string, &options::html_suffix_ >; _cli_options_map_["--option-prefix"] = diff --git a/cli/options.hxx b/cli/options.hxx index 96f50e6..fdf7627 100644 --- a/cli/options.hxx +++ b/cli/options.hxx @@ -190,6 +190,12 @@ class options suppress_usage () const; const bool& + long_usage () const; + + const std::size_t& + option_length () const; + + const bool& generate_cxx () const; const bool& @@ -198,12 +204,24 @@ class options const bool& generate_html () const; + const std::string& + man_prologue () const; + + const std::string& + man_epilogue () const; + + const std::string& + html_prologue () const; + + const std::string& + html_epilogue () const; + + const std::string& + class_ () const; + const bool& stdout () const; - const std::size_t& - option_length () const; - const std::string& hxx_suffix () const; @@ -214,6 +232,9 @@ class options cxx_suffix () const; const std::string& + man_suffix () const; + + const std::string& html_suffix () const; const std::string& @@ -253,14 +274,21 @@ class options std::string output_dir_; bool suppress_inline_; bool suppress_usage_; + bool long_usage_; + std::size_t option_length_; bool generate_cxx_; bool generate_man_; bool generate_html_; + std::string man_prologue_; + std::string man_epilogue_; + std::string html_prologue_; + std::string html_epilogue_; + std::string class__; bool stdout_; - std::size_t option_length_; std::string hxx_suffix_; std::string ixx_suffix_; std::string cxx_suffix_; + std::string man_suffix_; std::string html_suffix_; std::string option_prefix_; std::string option_separator_; diff --git a/cli/options.ixx b/cli/options.ixx index 6cf5899..1745d4a 100644 --- a/cli/options.ixx +++ b/cli/options.ixx @@ -119,6 +119,18 @@ suppress_usage () const } inline const bool& options:: +long_usage () const +{ + return long_usage_; +} + +inline const std::size_t& options:: +option_length () const +{ + return option_length_; +} + +inline const bool& options:: generate_cxx () const { return generate_cxx_; @@ -136,16 +148,40 @@ generate_html () const return generate_html_; } -inline const bool& options:: -stdout () const +inline const std::string& options:: +man_prologue () const { - return stdout_; + return man_prologue_; } -inline const std::size_t& options:: -option_length () const +inline const std::string& options:: +man_epilogue () const { - return option_length_; + return man_epilogue_; +} + +inline const std::string& options:: +html_prologue () const +{ + return html_prologue_; +} + +inline const std::string& options:: +html_epilogue () const +{ + return html_epilogue_; +} + +inline const std::string& options:: +class_ () const +{ + return class__; +} + +inline const bool& options:: +stdout () const +{ + return stdout_; } inline const std::string& options:: @@ -167,6 +203,12 @@ cxx_suffix () const } inline const std::string& options:: +man_suffix () const +{ + return man_suffix_; +} + +inline const std::string& options:: html_suffix () const { return html_suffix_; diff --git a/cli/source.cxx b/cli/source.cxx index 4cfd425..f473665 100644 --- a/cli/source.cxx +++ b/cli/source.cxx @@ -188,7 +188,7 @@ namespace // If we have both the long and the short descriptions, use // the short one. Otherwise, use the first sentence from the - // long one. + // long one ubless --long-usage was specified. // string d; @@ -197,14 +197,14 @@ namespace if (doc.size () > 1) d = doc[0]; else if (doc.size () > 0) - d = frist_sentence (doc[0]); + d = options.long_usage () ? doc[0] : first_sentence (doc[0]); } else { if (doc.size () > 2) d = doc[1]; else if (doc.size () > 1) - d = frist_sentence (doc[1]); + d = options.long_usage () ? doc[1] : first_sentence (doc[1]); } // Format the documentation string. @@ -281,7 +281,7 @@ namespace } string - frist_sentence (string const& s) + first_sentence (string const& s) { size_t p (s.find ('.')); diff --git a/doc/cli-epilogue.1 b/doc/cli-epilogue.1 new file mode 100644 index 0000000..cd2348d --- /dev/null +++ b/doc/cli-epilogue.1 @@ -0,0 +1,21 @@ +.\" +.\" DIAGNOSTICS +.\" +.SH DIAGNOSTICS +If the input file is not a valid CLI definition, +.B cli +will issue diagnostic messages to STDERR and exit with non-zero exit code. +.\" +.\" BUGS +.\" +.SH BUGS +Send bug reports to the cli-users@codesynthesis.com mailing list. +.\" +.\" COPYRIGHT +.\" +.SH COPYRIGHT +Copyright (c) 2009 Code Synthesis Tools CC. + +Permission is granted to copy, distribute and/or modify this document under +the terms of the MIT License. Copy of this license can be obtained from +http://www.codesynthesis.com/licenses/mit.txt diff --git a/doc/cli-epilogue.xhtml b/doc/cli-epilogue.xhtml new file mode 100644 index 0000000..ea0a34a --- /dev/null +++ b/doc/cli-epilogue.xhtml @@ -0,0 +1,24 @@ +

DIAGNOSTICS

+ +

If the input file is not a valid CLI definition, cli + will issue diagnostic messages to STDERR and exit with non-zero exit + code.

+ +

BUGS

+ +

Send bug reports to the + cli-users@codesynthesis.com mailing list.

+ + + + + + diff --git a/doc/cli-prologue.1 b/doc/cli-prologue.1 new file mode 100644 index 0000000..5d3953b --- /dev/null +++ b/doc/cli-prologue.1 @@ -0,0 +1,59 @@ +.\" Process this file with +.\" groff -man -Tascii cli.1 +.\" +.TH CLI 1 "October 2009" "CLI 1.0.0" +.SH NAME +cli \- command line interface compiler for C++ +.\" +.\" +.\" +.\"-------------------------------------------------------------------- +.SH SYNOPSIS +.\"-------------------------------------------------------------------- +.B cli +.B [ +.I options +.B ] +.I file +.\" +.\" +.\" +.\"-------------------------------------------------------------------- +.SH DESCRIPTION +.\"-------------------------------------------------------------------- +.B cli +generates C++ implementation and documentation in various formats for a +command line interface defined in the CLI language. For an input file in +the form +.B name.cli +the following is generated. By default or if the +.B --generate-cxx +option is specified, the following C++ files are generated: +.B name.hxx +(header file), +.B name.ixx +(inline file, generated unless the +.B --suppress-inline +option is specified), and +.B name.cxx (source file). +If the +.B --generate-html +option is specified, then the +.B name.html +HTML documentation file is generated. If the +.B --generate-man +option is specified, then the +.B name.1 +man page file is generated. When +.B --generate-html +or +.B --generate-man +is specified, the +.B --stdout +option can be used to redirect the output to STDOUT instead of a file. +.\" +.\" +.\" +.\"-------------------------------------------------------------------- +.SH OPTIONS +.\"-------------------------------------------------------------------- diff --git a/doc/cli-prologue.xhtml b/doc/cli-prologue.xhtml new file mode 100644 index 0000000..71108e1 --- /dev/null +++ b/doc/cli-prologue.xhtml @@ -0,0 +1,79 @@ + + + + + CLI 1.0.0 Compiler Command Line Manual + + + + + + + + + + + +
+
+ +

NAME

+ +

cli - command line interface compiler for C++

+ +

SYNOPSIS

+ +
+
cli [options] file
+
+ +

DESCRIPTION

+ +

cli generates C++ implementation and + documentation in various formats for a command line interface + defined in the CLI language. For an input file in the form + name.cli the following is generated. By + default or if the --generate-cxx option is + specified, the following C++ files are generated: + name.hxx (header file), name.ixx + (inline file, generated unless the --suppress-inline + option is specified), and name.cxx (source file). + If the --generate-html option is specified, then + the name.html HTML documentation file is generated. + If the --generate-man option is specified, then + the name.1 man page file is generated. When + --generate-html or --generate-man + is specified, the --stdout option can be used to + redirect the output to STDOUT instead of a file.

+ +

OPTIONS

diff --git a/doc/cli.1 b/doc/cli.1 index 80e3bc6..2e9f180 100644 --- a/doc/cli.1 +++ b/doc/cli.1 @@ -22,10 +22,13 @@ cli \- command line interface compiler for C++ .SH DESCRIPTION .\"-------------------------------------------------------------------- .B cli -generates a C++ implementation for a command line interface defined in the CLI -language. For an input file in the form +generates C++ implementation and documentation in various formats for a +command line interface defined in the CLI language. For an input file in +the form .B name.cli -the following C++ files are generated: +the following is generated. By default or if the +.B --generate-cxx +option is specified, the following C++ files are generated: .B name.hxx (header file), .B name.ixx @@ -33,95 +36,139 @@ the following C++ files are generated: .B --suppress-inline option is specified), and .B name.cxx (source file). +If the +.B --generate-html +option is specified, then the +.B name.html +HTML documentation file is generated. If the +.B --generate-man +option is specified, then the +.B name.1 +man page file is generated. When +.B --generate-html +or +.B --generate-man +is specified, the +.B --stdout +option can be used to redirect the output to STDOUT instead of a file. .\" .\" .\" .\"-------------------------------------------------------------------- .SH OPTIONS .\"-------------------------------------------------------------------- -.IP "\fB\--help\fR" -Print usage information and exit. - -.IP "\fB\--version\fR" -Print version and exit. - -.IP "\fB\--output-dir\fR | \fB\-o \fIdir\fR" -Write generated files to -.I dir -instead of the current directory. - -.IP "\fB\--suppress-inline\fR" -Generate all functions non-inline. By default simple functions are made -inline. This option suppresses creation of the inline file. - -.IP "\fB\--hxx-suffix \fIsuffix\fR" -Use the provided -.I suffix -instead of the default -.B .hxx -to construct the name of the generated header file. - -.IP "\fB\--ixx-suffix \fIsuffix\fR" -Use the provided -.I suffix -instead of the default -.B .ixx -to construct the name of the generated inline file. - -.IP "\fB\--cxx-suffix \fIsuffix\fR" -Use the provided -.I suffix -instead of the default -.B .cxx -to construct the name of the generated source file. - -.IP "\fB\--option-prefix \fIprefix\fR" -Use the provided -.I prefix -instead of the default -.B - -as an option prefix. Unknown command line arguments that start with this -prefix are treated as unknown options. If you set the option prefix to the -empty value, then all the unknown command line arguments will be treated as -program arguments. - -.IP "\fB\--option-separator \fIseparator\fR" -Use the provided -.I separator -instead of the default -.B -- -as an optional separator between options and arguments. All the command line -arguments that are parsed after this separator are treated as program -arguments. Set the option separator to the empty value if you don't want -this functionality. - -.IP "\fB\--include-with-brackets\fR" -Use angle brackets (<>) instead of quotes ("") in generated -.B #include -directives. - -.IP "\fB\--include-prefix \fIprefix\fR" -Add -.I prefix -to generated -.B #include -directive paths. - -.IP "\fB\--guard-prefix \fIprefix\fR" -Add -.I prefix -to generated header inclusion guards. The prefix is transformed to upper -case and characters that are illegal in a preprocessor macro name are -replaced with underscores. - -.IP "\fB\--reserved-name \fIname\fR[\fB=\fIrep\fR]" -Add -.I name -to the list of names that should not be used as identifiers. The name -can optionally be followed by -.B = -and the replacement name that should be used instead. All C++ keywords -are already in this list. +.\" +.\" The following documentation was generated by CLI, a command +.\" line interface compiler for C++. +.\" +.IP "\fB--help\fP" +Print usage information and exit\. + +.IP "\fB--version\fP" +Print version and exit\. + +.IP "\fB--output-dir\fP|\fB-o\fP \fIdir\fP" +Write the generated files to \fIdir\fP instead of the current directory\. + +.IP "\fB--suppress-inline\fP" +Generate all functions non-inline\. By default simple functions are made +inline\. This option suppresses creation of the inline file\. + +.IP "\fB--suppress-usage\fP" +Suppress the generation of the usage printing code\. + +.IP "\fB--long-usage\fP" +If no short documentation string is provided, use the complete long +documentation string in usage\. By default, in this situation only the first +sentence from the long string is used\. + +.IP "\fB--option-length\fP \fIlen\fP" +Indent option descriptions \fIlen\fP characters when printing usage\. This +is useful when you have multiple options classes, potentially in separate +files, and would like their usage to have the same indentation level\. + +.IP "\fB--generate-cxx\fP" +Generate C++ code\. If neither \fB--generate-man\fP nor +\fB--generate-html\fP is specified, this mode is assumed by default\. + +.IP "\fB--generate-man\fP" +Generate documentation in the man page format\. + +.IP "\fB--generate-html\fP" +Generate documentation in the HTML format\. + +.IP "\fB--man-prologue\fP \fIfile\fP" +Insert the content of \fIfile\fP at the beginning of the man page file\. + +.IP "\fB--man-epilogue\fP \fIfile\fP" +Insert the content of \fIfile\fP at the end of the man page file\. + +.IP "\fB--html-prologue\fP \fIfile\fP" +Insert the content of \fIfile\fP at the beginning of the HTML file\. + +.IP "\fB--html-epilogue\fP \fIfile\fP" +Insert the content of \fIfile\fP at the end of the HTML file\. + +.IP "\fB--class\fP \fIfq-name\fP" +Generate the man page or HTML documentation only for the \fIfq-name\fP +options class\. The \fIfq-name\fP name should be a fully-qualified options +class name, for example, \fBapp::options\fP\. This functionality is useful +if you need to insert custom documentation between options belonging to +different classes\. + +.IP "\fB--stdout\fP" +Write output to STDOUT instead of a file\. This option is not valid when +generating C++ code and is normally used to combine generated documentation +for several option classes in a single file\. + +.IP "\fB--hxx-suffix\fP \fIsuffix\fP" +Use \fIsuffix\fP instead of the default \fB\.hxx\fP to construct the name of +the generated header file\. + +.IP "\fB--ixx-suffix\fP \fIsuffix\fP" +Use \fIsuffix\fP instead of the default \fB\.ixx\fP to construct the name of +the generated inline file\. + +.IP "\fB--cxx-suffix\fP \fIsuffix\fP" +Use \fIsuffix\fP instead of the default \fB\.cxx\fP to construct the name of +the generated source file\. + +.IP "\fB--man-suffix\fP \fIsuffix\fP" +Use \fIsuffix\fP instead of the default \fB\.1\fP to construct the name of +the generated man page file\. + +.IP "\fB--html-suffix\fP \fIsuffix\fP" +Use \fIsuffix\fP instead of the default \fB\.html\fP to construct the name +of the generated HTML file\. + +.IP "\fB--option-prefix\fP \fIprefix\fP" +Use \fIprefix\fP instead of the default \fB-\fP as an option prefix\. +Unknown command line arguments that start with this prefix are treated as +unknown options\. If you set the option prefix to the empty value, then all +the unknown command line arguments will be treated as program arguments\. + +.IP "\fB--option-separator\fP \fIsep\fP" +Use \fIsep\fP instead of the default \fB--\fP as an optional separator +between options and arguments\. All the command line arguments that are +parsed after this separator are treated as program arguments\. Set the +option separator to the empty value if you don't want this functionality\. + +.IP "\fB--include-with-brackets\fP" +Use angle brackets (<>) instead of quotes ("") in the generated +\fB#include\fP directives\. + +.IP "\fB--include-prefix\fP \fIprefix\fP" +Add \fIprefix\fP to the generated \fB#include\fP directive paths\. + +.IP "\fB--guard-prefix\fP \fIprefix\fP" +Add \fIprefix\fP to the generated header inclusion guards\. The prefix is +transformed to upper case and characters that are illegal in a preprocessor +macro name are replaced with underscores\. + +.IP "\fB--reserved-name\fP \fIname\fP=\fIrep\fP" +Add \fIname\fP with an optional \fIrep\fP replacement to the list of names +that should not be used as identifiers\. If provided, the replacement name +is used instead\. All C++ keywords are already in this list\. .\" .\" DIAGNOSTICS @@ -129,9 +176,7 @@ are already in this list. .SH DIAGNOSTICS If the input file is not a valid CLI definition, .B cli -will issue diagnostic messages to -.B STDERR -and exit with non-zero exit code. +will issue diagnostic messages to STDERR and exit with non-zero exit code. .\" .\" BUGS .\" diff --git a/doc/cli.xhtml b/doc/cli.xhtml index b026b6e..85dd4b9 100644 --- a/doc/cli.xhtml +++ b/doc/cli.xhtml @@ -59,81 +59,144 @@

DESCRIPTION

-

cli generates a C++ implementation for a command - line interface defined in the CLI language. For an input file in the - form name.cli the following C++ files are generated: +

cli generates C++ implementation and + documentation in various formats for a command line interface + defined in the CLI language. For an input file in the form + name.cli the following is generated. By + default or if the --generate-cxx option is + specified, the following C++ files are generated: name.hxx (header file), name.ixx (inline file, generated unless the --suppress-inline - option is specified), and name.cxx (source file).

+ option is specified), and name.cxx (source file). + If the --generate-html option is specified, then + the name.html HTML documentation file is generated. + If the --generate-man option is specified, then + the name.1 man page file is generated. When + --generate-html or --generate-man + is specified, the --stdout option can be used to + redirect the output to STDOUT instead of a file.

OPTIONS

-
-
--help
-
Print usage information and exit.
- -
--version
-
Print version and exit.
- -
--output-dir | -o dir
-
Write generated files to dir instead of - the current directory.
- -
--suppress-inline
-
Generate all functions non-inline. By default simple functions - are made inline. This option suppresses creation of the inline - file.
- -
--hxx-suffix suffix
-
Use the provided suffix instead of the default - .hxx to construct the name of the generated header - file.
- -
--ixx-suffix suffix
-
Use the provided suffix instead of the default - .ixx to construct the name of the generated inline - file.
- -
--cxx-suffix suffix
-
Use the provided suffix instead of the default - .cxx to construct the name of the generated source - file.
- -
--option-prefix prefix
-
Use the provided prefix instead of the default - - as an option prefix. Unknown command line - arguments that start with this prefix are treated as unknown options. - If you set the option prefix to the empty value, then all the unknown - command line arguments will be treated as program arguments.
- -
--option-separator separator
-
Use the provided separator instead of the default - -- as an optional separator between options and - arguments. All the command line arguments that are parsed after this - separator are treated as program arguments. Set the option separator - to the empty value if you don't want this functionality.
- -
--include-with-brackets
-
Use angle brackets (<>) instead of quotes ("") in generated - #include directives.
- -
--include-prefix prefix
-
Add prefix to generated #include - directive paths.
- -
--guard-prefix prefix
-
Add prefix to generated header inclusion guards. - The prefix is transformed to upper case and characters that are - illegal in a preprocessor macro name are replaced with underscores.
- -
--reserved-name name[=rep]
-
Add name to the list of names that should not - be used as identifiers. The name can optionally be followed by - = and the replacement name that should be - used instead. All C++ keywords are already in this list.
+ -
+
+
--help
+
Print usage information and exit.
+
--version
+
Print version and exit.
+ +
--output-dir|-o dir
+
Write the generated files to dir instead of the current directory.
+ +
--suppress-inline
+
Generate all functions non-inline. By default simple functions are made + inline. This option suppresses creation of the inline file.
+ +
--suppress-usage
+
Suppress the generation of the usage printing code.
+ +
--long-usage
+
If no short documentation string is provided, use the complete long + documentation string in usage. By default, in this situation only the first + sentence from the long string is used.
+ +
--option-length len
+
Indent option descriptions len characters when printing usage. This + is useful when you have multiple options classes, potentially in separate + files, and would like their usage to have the same indentation level.
+ +
--generate-cxx
+
Generate C++ code. If neither --generate-man nor + --generate-html is specified, this mode is assumed by + default.
+ +
--generate-man
+
Generate documentation in the man page format.
+ +
--generate-html
+
Generate documentation in the HTML format.
+ +
--man-prologue file
+
Insert the content of file at the beginning of the man page file.
+ +
--man-epilogue file
+
Insert the content of file at the end of the man page file.
+ +
--html-prologue file
+
Insert the content of file at the beginning of the HTML file.
+ +
--html-epilogue file
+
Insert the content of file at the end of the HTML file.
+ +
--class fq-name
+
Generate the man page or HTML documentation only for the fq-name + options class. The fq-name name should be a fully-qualified options + class name, for example, app::options. This + functionality is useful if you need to insert custom documentation between + options belonging to different classes.
+ +
--stdout
+
Write output to STDOUT instead of a file. This option is not valid when + generating C++ code and is normally used to combine generated documentation + for several option classes in a single file.
+ +
--hxx-suffix suffix
+
Use suffix instead of the default .hxx to + construct the name of the generated header file.
+ +
--ixx-suffix suffix
+
Use suffix instead of the default .ixx to + construct the name of the generated inline file.
+ +
--cxx-suffix suffix
+
Use suffix instead of the default .cxx to + construct the name of the generated source file.
+ +
--man-suffix suffix
+
Use suffix instead of the default .1 to construct + the name of the generated man page file.
+ +
--html-suffix suffix
+
Use suffix instead of the default .html to + construct the name of the generated HTML file.
+ +
--option-prefix prefix
+
Use prefix instead of the default - as an option + prefix. Unknown command line arguments that start with this prefix are + treated as unknown options. If you set the option prefix to the empty value, + then all the unknown command line arguments will be treated as program + arguments.
+ +
--option-separator sep
+
Use sep instead of the default -- as an optional + separator between options and arguments. All the command line arguments that + are parsed after this separator are treated as program arguments. Set the + option separator to the empty value if you don't want this functionality.
+ +
--include-with-brackets
+
Use angle brackets (<>) instead of quotes ("") in the generated + #include directives.
+ +
--include-prefix prefix
+
Add prefix to the generated #include directive + paths.
+ +
--guard-prefix prefix
+
Add prefix to the generated header inclusion guards. The prefix is + transformed to upper case and characters that are illegal in a preprocessor + macro name are replaced with underscores.
+ +
--reserved-name name=rep
+
Add name with an optional rep replacement to the list of names + that should not be used as identifiers. If provided, the replacement name is + used instead. All C++ keywords are already in this list.
+ +

DIAGNOSTICS

If the input file is not a valid CLI definition, cli diff --git a/doc/guide/index.xhtml b/doc/guide/index.xhtml index 24d76fa..a7ae6e7 100644 --- a/doc/guide/index.xhtml +++ b/doc/guide/index.xhtml @@ -125,6 +125,17 @@ padding : 0em 0 0em 0.7em; text-align : left; } + + /* Sample options documentation. */ + .options dt { + padding-top : 0.4em; + } + + .options dd { + padding-top : 0.1em; + padding-bottom : 0.4em; + padding-left : 1.4em; + } @@ -168,6 +179,7 @@ 2.2Translating CLI Definitions to C++ 2.3Implementing Application Logic 2.4Compiling and Running + 2.5Adding Documentation @@ -177,8 +189,9 @@ - - + + +
3.1Options Class Definition
3.2Option Definition
3.3Include Directive
3.4Namespace Definition
3.3Option Documentation
3.4Include Directive
3.5Namespace Definition
@@ -332,10 +345,9 @@ using namespace std; void usage () { - cerr << "usage: driver <options> <names>" << endl - << " [--help]" << endl - << " [--greeting <string>]" << endl - << " [--exclamations <integer>]" << endl; + cerr << "usage: driver [options] <names>" << endl + << "options:" << endl; + options::print_usage (cerr); } int @@ -411,19 +423,110 @@ Hi, Jane!!!

 $ ./driver -n 3 Jane
 unknown option '-n'
-usage: driver <options> <names>
-  [--help]
-  [--greeting <string>]
-  [--exclamations <integer>]
+usage: driver [options] <names>
+options:
+--help
+--greeting <arg>
+--exclamations <arg>
 
 $ ./driver --exclamations abc Jane
 invalid value 'abc' for option '--exclamations'
-usage: driver <options> <names>
-  [--help]
-  [--greeting <string>]
-  [--exclamations <integer>]
+usage: driver [options] <names>
+options:
+--help
+--greeting <arg>
+--exclamations <arg>
+  
+ +

2.5 Adding Documentation

+ +

As we have seen in the previous sections, the options + C++ class provides the print_usage() function which we + can use to display the application usage information. Right now this + information is very basic and does not include any description of + the purpose of each option:

+ +
+$ ./driver --help
+usage: driver [options] <names>
+options:
+--help
+--greeting <arg>
+--exclamations <arg>
+  
+ +

To make the usage information more descriptive we can document each + option in the command line interface definition. This information can + also be used to automatically generate program documentation in various + formats, such as HTML and man page. For example:

+ +
+include <string>;
+
+class options
+{
+  bool --help {"Print usage information and exit."};
+
+  std::string --greeting = "Hello"
+  {
+    "<text>",
+    "Use <text> as a greeting phrase instead of the default \"Hello\"."
+  };
+
+  unsigned int --exclamations = 1
+  {
+    "<num>",
+    "Print <num> exclamation marks instead of 1 by default."
+  };
+};
   
+

If we now save this updated command line interface to + hello.cli and recompile our application, the usage + information printed by the program will look like this:

+ +
+usage: driver [options] <names>
+options:
+--help               Print usage information and exit.
+--greeting <text>    Use <text> as a greeting phrase instead of the
+                     default "Hello".
+--exclamations <num> Print <num> exclamation marks instead of 1 by
+                     default.
+  
+ +

We can also generate the program documentation in the HTML + (--generate-html CLI option) and man page + (--generate-man CLI option) formats. For example:

+ +
+$ cli --generate-html hello.cli
+  
+ +

The resulting hello.html file contains the following + documentation:

+ +
+
--help
+
Print usage information and exit.
+ +
--greeting text
+
Use text as a greeting phrase instead of the default "Hello".
+ +
--exclamations num
+
Print num exclamation marks instead of 1 by default.
+ +
+ +

This HTML fragment can be combined with custom prologue and epilogue + to create a complete program documentation + (--html-prologue/--html-epilogue options for the HTML + output, --man-prologue/--man-epilogue options for the + man page output). For an example of such complete documentation see + the CLI + Compiler Command Line Manual and the cli(1) man + page. For more information on the option documentation syntax, + see Section 3.3, Option Documentation.

@@ -431,8 +534,8 @@ usage: driver <options> <names>

3 CLI Language

This chapter describes the CLI language and its mapping to C++. - A CLI file consists of zero or more Include - Directives followed by one or more Namespace Definitions + A CLI file consists of zero or more Include + Directives followed by one or more Namespace Definitions or Option Class Definitions. C and C++-style comments can be used anywhere in the CLI file except in character and string literals.

@@ -488,6 +591,10 @@ public: operator= (const options&); public: + static void + print_usage (std::ostream&); + +public: bool help () const; @@ -500,7 +607,10 @@ public:

An option class is mapped to a C++ class with the same name. The C++ class defines a set of public overloaded constructors, a public copy constructor and an assignment operator, as well as a set of public - accessor functions corresponding to option definitions.

+ accessor functions corresponding to option definitions. It also + defines a public static print_usage() function that + can be used to print the usage information for the options + defined by the class.

The argc/argv arguments in the overloaded constructors are used to pass the command line arguments array, normally as passed @@ -654,12 +764,12 @@ namespace cli

3.2 Option Definition

-

An option definition consists of there components: type, - name, and default value. An option type can be any - C++ type as long as its string representation can be parsed using - the std::istream interface. If the option type is - user-defined then you will need to include its declaration using - the Include Directive.

+

An option definition consists of four components: type, + name, default value, and documentation. + An option type can be any C++ type as long as its string representation + can be parsed using the std::istream interface. If the option + type is user-defined then you will need to include its declaration using + the Include Directive.

An option of a type other than bool is expected to have a value. An option of type bool is treated as @@ -672,7 +782,7 @@ namespace cli by |. The C++ accessor function name is derived from the first name by removing any leading special characters, such as -, /, etc., and replacing special characters - in other places with underscore. For example, the following option + in other places with underscores. For example, the following option definition:

@@ -705,7 +815,7 @@ class options
 };
   
-

The final component of the option definition is the optional default +

The following component of the option definition is the optional default value. If the default value is not specified, then the option is initialized with the default constructor. In particular, this means that a bool option will be initialized to false, @@ -797,7 +907,127 @@ class options -m =B (key is an empty string), -m c= (value is an empty string), or -m d (same as -m d=).

-

3.3 Include Directive

+

The last component in the option definition is optional documentation. + It is discussed in the next section.

+ +

3.3 Option Documentation

+ +

Option documentation mimics C++ string array initialization: + it is enclosed in {} and consists of one or more + documentation strings separated by a comma, for example:

+ +
+class options
+{
+  int --compression = 5
+  {
+    "<level>",
+    "Set compression to <level> instead of 5 by default.
+
+     With the higher compression levels the program may produce a
+     smaller output but may also take longer and use more memory."
+  };
+};
+  
+ +

The option documentation consists of a maximum of three documentation + strings. The first string is the value documentation string. + It describes the option value and is only applicable to options + with types other than bool (options of type + bool are flags and don't have an explicit value). + The second string (or the first string for options of type + bool) is the short documentation string. It + provides a brief description of the option. The last entry + in the option documentation is the long documentation string. + It provides a detailed description of the option. The short + documentation string is optional. If only two strings are + present in the option documentation (one string for options + of type bool), then the second (first) string is + assumed to be the long documentation string.

+ +

Option documentation is used to print the usage information + as well as to generate program documentation in the HTML and + man page formats. For usage information the short documentation + string is used if provided. If only the long string is available, + then, by default, only the first sentence from the long string + is used. You can override this behavior and include the complete + long string in the usage information by specifying the + --long-usage CLI compiler option. When generating + the program documentation, the long documentation strings are + always used.

+ +

The value documentation string can contain text enclosed in + <> which is automatically recognized by the CLI + compiler and typeset according to the selected output in all three + documentation strings. For example, in usage the level + value for the --compression option presented above + will be displayed as <level> while in the HTML and + man page output it will be typeset in italic as + level. Here is another example using the + std::map type:

+ +
+include <map>;
+include <string>;
+
+class options
+{
+  std::map<std::string, std::string> --map
+  {
+    "<key>=<value>",
+    "Add the <key>, <value> pair to the map."
+  };
+};
+  
+ +

The resulting HTML output for this option would look like this:

+ +
+
--map key=value
+
Add the key, value pair to the map.
+
+ +

As you might have noticed from the examples presented so far, the + documentation strings can span multiple lines which is not possible + in C++. Also, all three documentation strings support the following + basic formatting mechanisms. The start of a new paragraph is indicated + by a blank line. A fragment of text can be typeset in monospace font + (normally used for code fragments) by enclosing it in the + \c{} block. Similarly, text can be typeset in bold or + italic fonts using the \b{} and \i{} blocks, + respectively. You can also combine several font properties in a single + block, for example, \cb{bold code}. If you need to include + literal } in a formatting block, you can use the + \} escape sequence, for example, + \c{int a[] = {1, 2\}}. The following example shows how we + can use these mechanisms:

+ +
+class options
+{
+  int --compression = 5
+  {
+    "<level>",
+    "Set compression to <level> instead of 5 by default.
+
+     With the higher compression levels the program \i{may}
+     produce a smaller output but may also \b{take longer}
+     and \b{use more memory}."
+  };
+};
+  
+ +

The resulting HTML output for this option would look like this:

+ +
+
--compression level
+
Set compression to level instead of 5 by default. + +

With the higher compression levels the program may produce a + smaller output but may also take longer and use more memory.

+
+ +

3.4 Include Directive

If you are using user-defined types in your option definitions, you will need to include their declarations with the include @@ -843,7 +1073,7 @@ class options and name_type types in the options class would be undeclared and result in compilation errors.

-

3.4 Namespace Definition

+

3.5 Namespace Definition

Option classes can be placed into namespaces which are translated directly to C++ namespaces. For example:

diff --git a/doc/makefile b/doc/makefile index 7b97ba2..c9267a7 100644 --- a/doc/makefile +++ b/doc/makefile @@ -9,14 +9,32 @@ default := $(out_base)/ install := $(out_base)/.install cleandoc := $(out_base)/.cleandoc -$(default): $(out_base)/guide/ +$(default): $(out_base)/cli.xhtml $(out_base)/cli.1 $(out_base)/guide/ + +# Generated documentation. +# +$(out_base)/cli.xhtml: $(src_root)/cli/options.cli \ + $(src_base)/cli-prologue.xhtml \ + $(src_base)/cli-epilogue.xhtml + $(call message,cli-html $<,$(out_root)/cli/cli --generate-html \ +--output-dir $(out_base) --html-prologue $(src_base)/cli-prologue.xhtml \ +--html-epilogue $(src_base)/cli-epilogue.xhtml $<) + $(call message,,mv $(out_base)/options.html $(out_base)/cli.xhtml) + +$(out_base)/cli.1: $(src_root)/cli/options.cli \ + $(src_base)/cli-prologue.1 \ + $(src_base)/cli-epilogue.1 + $(call message,cli-man $<,$(out_root)/cli/cli --generate-man \ +--output-dir $(out_base) --man-prologue $(src_base)/cli-prologue.1 \ +--man-epilogue $(src_base)/cli-epilogue.1 $<) + $(call message,,mv $(out_base)/options.1 $(out_base)/cli.1) # Install. # -$(install): $(out_base)/guide/.install +$(install): $(out_base)/guide/.install $(out_base)/cli.xhtml $(out_base)/cli.1 $(call install-data,$(src_base)/default.css,$(install_doc_dir)/cli/default.css) - $(call install-data,$(src_base)/cli.xhtml,$(install_doc_dir)/cli/cli.xhtml) - $(call install-data,$(src_base)/cli.1,$(install_man_dir)/man1/cli.1) + $(call install-data,$(out_base)/cli.xhtml,$(install_doc_dir)/cli/cli.xhtml) + $(call install-data,$(out_base)/cli.1,$(install_man_dir)/man1/cli.1) # Clean. # diff --git a/examples/hello/driver.cxx b/examples/hello/driver.cxx index b08e6be..bb82d22 100644 --- a/examples/hello/driver.cxx +++ b/examples/hello/driver.cxx @@ -12,10 +12,9 @@ using namespace std; void usage () { - cerr << "usage: driver " << endl - << " [--help]" << endl - << " [--greeting ]" << endl - << " [--exclamations ]" << endl; + cerr << "usage: driver [options] " << endl + << "options:" << endl; + options::print_usage (cerr); } int diff --git a/examples/hello/hello.cli b/examples/hello/hello.cli index 9907510..b75e1b8 100644 --- a/examples/hello/hello.cli +++ b/examples/hello/hello.cli @@ -2,7 +2,17 @@ include ; class options { - bool --help; - std::string --greeting = "Hello"; - unsigned int --exclamations = 1; + bool --help {"Print usage information and exit."}; + + std::string --greeting = "Hello" + { + "", + "Use as a greeting phrase instead of the default \"Hello\"." + }; + + unsigned int --exclamations = 1 + { + "", + "Print exclamation marks instead of 1 by default." + }; }; -- cgit v1.1