diff options
73 files changed, 3182 insertions, 531 deletions
diff --git a/INSTALL b/INSTALL deleted file mode 100644 index 19bc883..0000000 --- a/INSTALL +++ /dev/null @@ -1,30 +0,0 @@ -CLI uses itself for command line options parsing which makes it a bit tricky -to develop. Below is one way to setup the development environment: - -$ git clone .../cli.git -$ cd cli -$ bdep init -C ../builds/main @main cc # Main build. -$ bdep update # Using pre-geneared code. -$ bdep init -d cli/ -C ../builds/save @save cc # "Saved" build. -$ bdep update @save - -# @@ This does not currently work because bdep (bpkg) will drop it on next -# sync (reconfigure). -# -#$ b configure: ../builds/main/cli/ \ -# config.cli="$(realpath ../builds/save/cli/cli/cli)" - -$ echo >>../builds/main/build/config.build \ - "cli/ config.cli=$(realpath ../builds/save/cli/cli/cli)" - -$ bdep update # Regenerate code. - -Then, when making changes that affect the generated code, perform the -following sequence of steps (the key thing to keep in mind is that the saved -configuration will use generated code from source directory that is -backlinked during the main build): - -$ b cli/ # Regenerate using old saved. -$ bdep update @save # Update saved. -$ b cli/ # Regenerate using new saved. -$ bdep update @save # Update saved. @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2009-2020 Code Synthesis Tools CC. +Copyright (c) 2009-2023 Code Synthesis Tools CC. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md new file mode 100644 index 0000000..03e9d19 --- /dev/null +++ b/README.md @@ -0,0 +1,36 @@ +# CLI + +CLI is a command line interface compiler for C++. + +The development setup for CLI uses two configurations, for example: + +``` +git clone .../cli.git +cd cli + +bdep init --empty + +bdep config create @host ../cli-host --type host cc config.cxx=g++ +bdep config create @target ../cli-target cc config.cxx=g++ + +bdep init @host -d cli +bdep init @target -d cli-tests -d cli-examples + +``` + +To generate the documentation in the `.ps` and `.pdf` formats, `html2ps` and +`ps2pdf14` programs are required (the latter is from `ghostscript`). A warning +is issued in the development mode if these programs are not available. + +To test installation of the CLI compiler, create a separate target +configuration (we must use a non-development build due to the bootstrap +process): + +``` +bdep config create @install ../cli-install --type target cc config.cxx=g++ \ + config.install.root=/tmp/install + +bdep init @install -d cli config.cli.develop=false + +b install: ../cli-install/cli/ +``` @@ -1,7 +1,6 @@ -# file : buildfile -# license : MIT; see accompanying LICENSE file +# Glue buildfile that "pulls" all the packages in the project. +# +import pkgs = [dir_paths] $process.run_regex(\ + cat $src_root/packages.manifest, '\s*location\s*:\s*(\S+)\s*', '\1') -# Glue buildfile that "pulls" all the packages. - -import pkgs = */ ./: $pkgs diff --git a/cli-examples/build/root.build b/cli-examples/build/root.build index 5f4b348..90a319a 100644 --- a/cli-examples/build/root.build +++ b/cli-examples/build/root.build @@ -16,7 +16,8 @@ if ($cxx.target.system == 'win32-msvc') if ($cxx.class == 'msvc') cxx.coptions += /wd4251 /wd4275 /wd4800 -using cli +if ($build.mode != 'skeleton') + using cli # Every exe{} in this project is by default a test. # diff --git a/cli-examples/features/driver.cxx b/cli-examples/features/driver.cxx index c14b5c7..fc364a0 100644 --- a/cli-examples/features/driver.cxx +++ b/cli-examples/features/driver.cxx @@ -2,6 +2,7 @@ // author : Boris Kolpackov <boris@codesynthesis.com> // license : MIT; see accompanying LICENSE file +#include <utility> // pair #include <iostream> #include <iterator> #include <algorithm> @@ -45,13 +46,27 @@ main (int argc, char* argv[]) // --map | -m // - typedef map<std::string, std::string> str_map; - const str_map& m = o.map (); - str_map::const_iterator i (m.find ("a")); + { + typedef map<string, bool> str_map; + const str_map& m = o.map (); + str_map::const_iterator i (m.find ("a")); - if (i != m.end ()) - cerr << "value for the 'a' key: " << i->second << endl; + if (i != m.end ()) + cerr << "value for the 'a' map key: " << i->second << endl; + } + // --multimap + // + { + typedef multimap<string, int> str_multimap; + const str_multimap& m = o.multimap (); + + pair<str_multimap::const_iterator, str_multimap::const_iterator> r ( + m.equal_range ("a")); + + for (str_multimap::const_iterator i (r.first); i != r.second; ++i) + cerr << "value for the 'a' multimap key: " << i->second << endl; + } } catch (const cli::exception& e) { diff --git a/cli-examples/features/options.cli b/cli-examples/features/options.cli index ea055b3..d1e4b0c 100644 --- a/cli-examples/features/options.cli +++ b/cli-examples/features/options.cli @@ -30,10 +30,11 @@ namespace features std::vector<int> --vector | -v; std::set<int> --set | -s; - // We can also use maps. In this case the option value is expected to have - // two parts: the key and the value, separated by '='. For example: -m a=A - // -m =B -m c= -m d (same as -m d=). + // We can also use maps and multimaps. In this case the option value is + // expected to have two parts: the key and the value, separated by '='. + // For example: -m a=1 -m =true -m c= -m d (same as -m d=). // - std::map<std::string, std::string> --map | -m; + std::map<std::string, bool> --map | -m; + std::multimap<std::string, int> --multimap; }; } diff --git a/cli-examples/manifest b/cli-examples/manifest index da9cc56..a7f30dd 100644 --- a/cli-examples/manifest +++ b/cli-examples/manifest @@ -1,6 +1,6 @@ : 1 name: cli-examples -version: 1.2.0-b.7.z +version: 1.2.0 project: cli summary: Examples of using the CLI language and compiler for C++ license: MIT @@ -10,5 +10,5 @@ doc-url: https://www.codesynthesis.com/projects/cli/doc/guide/ src-url: https://git.codesynthesis.com/cgit/cli/cli/tree/cli-examples/ email: cli-users@codesynthesis.com ; Mailing list requires: c++14 -depends: * build2 >= 0.13.0 -depends: * bpkg >= 0.13.0 +depends: * build2 >= 0.16.0 +depends: * bpkg >= 0.16.0 diff --git a/cli-tests/build/root.build b/cli-tests/build/root.build index 5f4b348..90a319a 100644 --- a/cli-tests/build/root.build +++ b/cli-tests/build/root.build @@ -16,7 +16,8 @@ if ($cxx.target.system == 'win32-msvc') if ($cxx.class == 'msvc') cxx.coptions += /wd4251 /wd4275 /wd4800 -using cli +if ($build.mode != 'skeleton') + using cli # Every exe{} in this project is by default a test. # diff --git a/cli-tests/combined/driver.cxx b/cli-tests/combined/driver.cxx index dcbdd34..3e96f0c 100644 --- a/cli-tests/combined/driver.cxx +++ b/cli-tests/combined/driver.cxx @@ -9,6 +9,9 @@ #include "test.hxx" +#undef NDEBUG +#include <cassert> + using namespace std; int diff --git a/cli-tests/ctor/driver.cxx b/cli-tests/ctor/driver.cxx index ed306f4..72c1954 100644 --- a/cli-tests/ctor/driver.cxx +++ b/cli-tests/ctor/driver.cxx @@ -4,6 +4,9 @@ #include "test.hxx" +#undef NDEBUG +#include <cassert> + int main (int argc, char* argv[]) { diff --git a/cli-tests/erase/driver.cxx b/cli-tests/erase/driver.cxx index af35836..a46df20 100644 --- a/cli-tests/erase/driver.cxx +++ b/cli-tests/erase/driver.cxx @@ -6,10 +6,12 @@ // #include <string> -#include <cassert> #include "test.hxx" +#undef NDEBUG +#include <cassert> + using namespace std; int diff --git a/cli-tests/file/driver.cxx b/cli-tests/file/driver.cxx index eef7ef1..fe923cc 100644 --- a/cli-tests/file/driver.cxx +++ b/cli-tests/file/driver.cxx @@ -10,6 +10,9 @@ #include "test.hxx" +#undef NDEBUG +#include <cassert> + using namespace std; int diff --git a/cli-tests/group/driver.cxx b/cli-tests/group/driver.cxx index 68f6107..9a3c710 100644 --- a/cli-tests/group/driver.cxx +++ b/cli-tests/group/driver.cxx @@ -5,10 +5,14 @@ // Test group_scanner. // +#include <string> #include <iostream> #include "test.hxx" +#undef NDEBUG +#include <cassert> + using namespace std; int @@ -25,27 +29,58 @@ main (int argc, char* argv[]) // string m (argv[1]); + bool sa (m.find ('s') != string::npos); + bool sg (m.find ('g') != string::npos); + argv_scanner as (--argc, ++argv); group_scanner s (as); + // Verify previous two args are still valid for good measure. + // + const char* prev_a (0); + string prev_s; + + // Verify position. + // + size_t pos (0); // argv_scanner starts from 1. + while (s.more ()) { - if (m.find ('s') == string::npos) + assert (pos < s.position ()); + pos = s.position (); + + s.peek (); + assert (pos == s.position ()); + + const char* a (0); + if (!sa) { - const char* a (s.next ()); + a = s.next (); cout << "'" << a << "'"; } else s.skip (); - if (m.find ('g') == string::npos) + if (!sg) { scanner& gs (s.group ()); while (gs.more ()) cout << " '" << gs.next () << "'"; } - cout << endl; + if (!sa || !sg) + cout << endl; + + if (!sa && !sg) + { + s.more (); + + if (prev_a != 0) + assert (prev_a == prev_s); + + prev_a = a; + prev_s = a; + } } return 0; diff --git a/cli-tests/group/testscript b/cli-tests/group/testscript index 6269ca2..0b1d939 100644 --- a/cli-tests/group/testscript +++ b/cli-tests/group/testscript @@ -19,6 +19,14 @@ $* '' { --foo --bar }+ arg1 arg2 >>EOO 'arg2' EOO +: group-pre-pack +: +$* '' { --foo --bar }+ { arg1 arg2 } arg3 >>EOO +'arg1' '--foo' '--bar' +'arg2' '--foo' '--bar' +'arg3' +EOO + : group-pre-multi : $* '' { --foo }+ { --bar }+ arg1 arg2 >>EOO @@ -26,6 +34,14 @@ $* '' { --foo }+ { --bar }+ arg1 arg2 >>EOO 'arg2' EOO +: group-pre-multi-pack +: +$* '' { --foo }+ { --bar }+ { arg1 arg2 } arg3 >>EOO +'arg1' '--foo' '--bar' +'arg2' '--foo' '--bar' +'arg3' +EOO + : group-post : $* '' arg1 arg2 +{ foo bar } >>EOO @@ -33,6 +49,14 @@ $* '' arg1 arg2 +{ foo bar } >>EOO 'arg2' 'foo' 'bar' EOO +: group-post-pack +: +$* '' arg1 { arg2 arg3 } +{ foo bar } >>EOO +'arg1' +'arg2' 'foo' 'bar' +'arg3' 'foo' 'bar' +EOO + : group-post-multi : $* '' arg1 arg2 +{ foo } +{ bar } >>EOO @@ -40,6 +64,14 @@ $* '' arg1 arg2 +{ foo } +{ bar } >>EOO 'arg2' 'foo' 'bar' EOO +: group-post-multi-pack +: +$* '' arg1 { arg2 arg3 } +{ foo } +{ bar } >>EOO +'arg1' +'arg2' 'foo' 'bar' +'arg3' 'foo' 'bar' +EOO + : group-both : $* '' arg1 { --foo --bar }+ arg2 +{ foo bar } arg3 >>EOO @@ -48,6 +80,15 @@ $* '' arg1 { --foo --bar }+ arg2 +{ foo bar } arg3 >>EOO 'arg3' EOO +: group-both-pack +: +$* '' arg1 { --foo --bar }+ { arg2 arg3 } +{ foo bar } arg4 >>EOO +'arg1' +'arg2' '--foo' '--bar' 'foo' 'bar' +'arg3' '--foo' '--bar' 'foo' 'bar' +'arg4' +EOO + : group-both-multi : $* '' arg1 { --foo }+ { --bar }+ arg2 +{ foo } +{ bar } arg3 >>EOO @@ -56,6 +97,15 @@ $* '' arg1 { --foo }+ { --bar }+ arg2 +{ foo } +{ bar } arg3 >>EOO 'arg3' EOO +: group-both-multi-pack +: +$* '' arg1 { --foo }+ { --bar }+ { arg2 arg3 } +{ foo } +{ bar } arg4 >>EOO +'arg1' +'arg2' '--foo' '--bar' 'foo' 'bar' +'arg3' '--foo' '--bar' 'foo' 'bar' +'arg4' +EOO + : multi-group : $* '' { --foo }+ arg1 arg2 +{ bar } >>EOO @@ -63,6 +113,15 @@ $* '' { --foo }+ arg1 arg2 +{ bar } >>EOO 'arg2' 'bar' EOO +: multi-group-pack +: +$* '' { --foo }+ { arg1 arg2 } { arg3 arg4 } +{ bar } >>EOO +'arg1' '--foo' +'arg2' '--foo' +'arg3' 'bar' +'arg4' 'bar' +EOO + : empty-group : $* '' { }+ arg1 arg2 +{ } >>EOO @@ -70,6 +129,13 @@ $* '' { }+ arg1 arg2 +{ } >>EOO 'arg2' EOO +: empty-group-pack +: +$* '' { }+ { arg1 arg2 } +{ } >>EOO +'arg1' +'arg2' +EOO + : escape-arg : $* '' '\{' '\}' '\+{' '\}+' '{x' '}x' >>EOO @@ -87,10 +153,16 @@ $* '' { '\{' '\}' '\+{' '\}+' '{x' '}x' }+ arg >>EOO 'arg' '{' '}' '+{' '}+' '{x' '}x' EOO -: not-group +: pack-no-group : -$* '' { --foo } 2>>EOE != 0 -expected group separator '}+' instead of '}', use '\}' to escape +$* '' { --foo } { arg2 }+ 2>>EOE != 0 +unexpected group separator '{', use '\{' to escape +EOE + +: empty-pack +: +$* '' { --foo }+ { } 2>>EOE != 0 +unexpected group separator '{', use '\{' to escape EOE : no-arg-pre @@ -135,6 +207,8 @@ EOE : unhandled-group-skip : -$* 'sg' { --foo }+ arg +{ bar } >>EOO +$* 'sg' { --foo }+ arg +{ bar } -EOO +: unhandled-group-skip-pack +: +$* 'sg' { --foo }+ { arg1 arg2 } +{ bar } diff --git a/cli-tests/inheritance/driver.cxx b/cli-tests/inheritance/driver.cxx index 4acab0d..f0860fd 100644 --- a/cli-tests/inheritance/driver.cxx +++ b/cli-tests/inheritance/driver.cxx @@ -6,11 +6,13 @@ // #include <string> -#include <cassert> #include <iostream> #include "test.hxx" +#undef NDEBUG +#include <cassert> + using namespace std; int diff --git a/cli-tests/manifest b/cli-tests/manifest index 0ff0825..4c3626b 100644 --- a/cli-tests/manifest +++ b/cli-tests/manifest @@ -1,6 +1,6 @@ : 1 name: cli-tests -version: 1.2.0-b.7.z +version: 1.2.0 project: cli summary: Tests for the CLI compiler for C++ license: MIT @@ -10,5 +10,5 @@ doc-url: https://www.codesynthesis.com/projects/cli/doc/guide/ src-url: https://git.codesynthesis.com/cgit/cli/cli/tree/cli-tests/ email: cli-users@codesynthesis.com ; Mailing list requires: c++14 -depends: * build2 >= 0.13.0 -depends: * bpkg >= 0.13.0 +depends: * build2 >= 0.16.0 +depends: * bpkg >= 0.16.0 diff --git a/cli-tests/merge/driver.cxx b/cli-tests/merge/driver.cxx index 43b1c59..94dc16e 100644 --- a/cli-tests/merge/driver.cxx +++ b/cli-tests/merge/driver.cxx @@ -6,10 +6,12 @@ // #include <string> -#include <cassert> #include "test.hxx" +#undef NDEBUG +#include <cassert> + using namespace std; template <typename T, int N1, int N2> diff --git a/cli-tests/position/buildfile b/cli-tests/position/buildfile new file mode 100644 index 0000000..371cc54 --- /dev/null +++ b/cli-tests/position/buildfile @@ -0,0 +1,9 @@ +# file : position/buildfile +# license : MIT; see accompanying LICENSE file + +exe{driver}: {hxx cxx}{* -test} cli.cxx{test} testscript + +cxx.poptions =+ "-I$out_base" + +cli.cxx{test}: cli{test} +cli.options = --generate-file-scanner --generate-specifier diff --git a/cli-tests/position/driver.cxx b/cli-tests/position/driver.cxx new file mode 100644 index 0000000..df45f5b --- /dev/null +++ b/cli-tests/position/driver.cxx @@ -0,0 +1,47 @@ +// file : position/driver.cxx +// author : Boris Kolpackov <boris@codesynthesis.com> +// license : MIT; see accompanying LICENSE file + +// Test argument/option position. +// +#include <iostream> + +#include "test.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; + +int +main (int argc, char* argv[]) +{ + try + { + cli::argv_file_scanner scan (argc, argv, "--file"); + options ops (scan); + + if (ops.a_specified ()) + cout << ops.a ().second << ": " << "-a " << ops.a ().first << endl; + + for (const pair<int, size_t>& b: ops.b ()) + { + cout << b.second << ": " << "-b " << b.first << endl; + } + + while (scan.more ()) + { + // Note that calling position() inside `cout << ...` depends on order of + // argument evaluation. + // + size_t p (scan.position ()); + cout << p << ": " << scan.next () << endl; + } + + cout << "max: " << scan.position () << endl; + } + catch (const cli::exception& e) + { + cerr << e << endl; + } +} diff --git a/cli-tests/position/test.cli b/cli-tests/position/test.cli new file mode 100644 index 0000000..7fc8655 --- /dev/null +++ b/cli-tests/position/test.cli @@ -0,0 +1,13 @@ +// file : position/test.cli +// author : Boris Kolpackov <boris@codesynthesis.com> +// license : MIT; see accompanying LICENSE file + +include <vector>; +include <utility>; +include <cstddef>; + +class options +{ + std::pair<int, std::size_t> -a; + std::vector<std::pair<int, std::size_t> > -b; +}; diff --git a/cli-tests/position/testscript b/cli-tests/position/testscript new file mode 100644 index 0000000..568e571 --- /dev/null +++ b/cli-tests/position/testscript @@ -0,0 +1,36 @@ +# file : position/testscript +# license : MIT; see accompanying LICENSE file + +: basics +: +$* -b 1 -a 2 -b 3 foo bar baz >>EOO +3: -a 2 +1: -b 1 +5: -b 3 +7: foo +8: bar +9: baz +max: 10 +EOO + +: override +: +$* -a 1 -a 2 >>EOO +3: -a 2 +max: 5 +EOO + +: file +: +cat <<EOI >=test.ops; +-a 2 +EOI +$* -b 1 --file test.ops -b 2 foo bar baz >>EOO +5: -a 2 +1: -b 1 +7: -b 2 +9: foo +10: bar +11: baz +max: 12 +EOO diff --git a/cli-tests/specifier/driver.cxx b/cli-tests/specifier/driver.cxx index 50b9cf5..15b9695 100644 --- a/cli-tests/specifier/driver.cxx +++ b/cli-tests/specifier/driver.cxx @@ -6,10 +6,12 @@ // #include <string> -#include <cassert> #include "test.hxx" +#undef NDEBUG +#include <cassert> + using namespace std; int @@ -4,8 +4,8 @@ Version 1.2.0 function which can be used to merge several already parsed options class instances, for example, to implement option appending/overriding. - * New option, --generate-specifier, triggers the generation of functions - for determining whether the option was specified on the command line. + * New option, --generate-specifier, triggers the generation of functions for + determining whether the option was specified on the command line. * New option, --suppress-undocumented, suppresses the generation of documentation entries for undocumented options. @@ -13,12 +13,17 @@ Version 1.2.0 * New option, --cli-namespace, allows changing of the namespace for the generated CLI support types. - * The argv_file_scanner now supports double and single-quoting option - values in option files. This is useful to preserve leading and trailing + * The argv_file_scanner now supports double and single-quoting option values + in option files. This is useful to preserve leading and trailing whitespaces as well as to specify empty values. - * The argv_file_scanner now supports multiple file options as well as - file search callbacks. + * The argv_file_scanner now supports multiple file options as well as file + search callbacks. + + * New option, --generate-dep, triggers the generation of the make dependency + information. Other related new options: --dep-suffix, --dep-file. + + * Support for std::multimap as an option type in addition to std::map. Version 1.1.0 diff --git a/cli/build/root.build b/cli/build/root.build index 476b192..ceeaf6c 100644 --- a/cli/build/root.build +++ b/cli/build/root.build @@ -1,24 +1,17 @@ # file : build/root.build # license : MIT; see accompanying LICENSE file -# Regenerate the options parsing code (included into the repository). +# Note that we cannot install the development build. This is due to both the +# bootstrap cli (which we need to run) and the final cli (which we need to +# install) depending on libcutl (so we need it to be both "for-install" and +# "for-run"). Nor can we run such final cli to generate the man pages. # -# Note that this is the same variable as what's used by the import machinery -# when we import cli%exe{cli}. Here, however, we require it to be explicitly -# specified (i.e., no default search in PATH) in order for the regeneration -# to be enabled. -# -# Normally, in the development build, this variable will be set only in the -# cli package configuration to point to its own binary (or a binary in another -# build configuration if you want to play it safe). -# -# Note, though, that currently referring to the cli target in the project -# itself ends up with the 'dependency cycle detected' error. In the future we -# will fix that by using an ad hoc recipe instead of the cli module. But for -# now you can workaround this issue by pointing to a binary in another -# configuration. -# -config [path] config.cli +config [bool] config.cli.develop ?= false + +develop = $config.cli.develop + +define cli: file +cli{*}: extension = cli cxx.std = latest @@ -35,20 +28,15 @@ if ($cxx.target.system == 'win32-msvc') if ($cxx.class == 'msvc') cxx.coptions += /wd4251 /wd4275 /wd4800 -cxx.poptions =+ "-I$out_root" "-I$src_root" - -# Load the cli module only if explicitly requested. This way a distribution -# that includes pre-generated files can be built without installing cli. This -# is also the reason why we need to explicitly spell out individual source -# file prerequisites instead of using the cli.cxx{} group (it won't be there -# unless the module is configured). -# -# @@ Replace with import when the parsing code regenerating is implemented -# via an ad hoc recipe. -# -if ($config.cli != [null] && $config.cli != false) - using cli - # Specify the test target for cross-testing. # test.target = $cxx.target + +# Extract the copyright notice from the LICENSE file. +# +# Note that cat is a builtin which means this is both portable and fast. +# +if ($build.mode != 'skeleton') + copyright = $process.run_regex(cat $src_root/LICENSE, \ + 'Copyright \(c\) (.+)\.', \ + '\1') diff --git a/cli/buildfile b/cli/buildfile index 5cbb37a..c9e2e50 100644 --- a/cli/buildfile +++ b/cli/buildfile @@ -3,6 +3,7 @@ ./: {*/ -build/} doc{INSTALL NEWS README} legal{LICENSE} manifest -# Don't install the INSTALL file. +# Don't install tests or the INSTALL file. # +tests/: install = false doc{INSTALL}@./: install = false diff --git a/cli/cli/.gitignore b/cli/cli/.gitignore index 903d015..614e56f 100644 --- a/cli/cli/.gitignore +++ b/cli/cli/.gitignore @@ -1,5 +1,7 @@ -cli -version.hxx +/cli +/pregenerated/cli/cli +/version.hxx +/options.?xx # Unit test executables and Testscript output directories (can be symlinks). # diff --git a/cli/cli/buildfile b/cli/cli/buildfile index 2385a7d..92c3e6e 100644 --- a/cli/cli/buildfile +++ b/cli/cli/buildfile @@ -15,63 +15,146 @@ exe{cli}: cli.checksum = $version } -libue{cli}: {hxx ixx txx cxx}{** -cli -version -options -**.test...} \ - {hxx}{version} {hxx ixx cxx}{options} \ - $libs +hdr = {hxx ixx txx}{* -cli -version -options -*.test...} +src = cxx{* -cli -version -options -*.test...} + +all_s = semantics/{hxx ixx txx cxx}{*} +all_t = traversal/{hxx ixx txx cxx}{*} + +libue{cli}: $hdr $src $all_s $all_t {hxx}{version} $libs hxx{version}: in{version} $src_root/manifest -# Unit tests. +# Build options (apply to both bootstrap and final version). # -exe{*.test}: +cxx.poptions =+ "-I$out_root" "-I$src_root" + +# CLI uses its own generated code to handle the command line. To solve the +# chicken and egg problem that this poses we keep pregenerated source code in +# pregenerated/. +# +# In the consumption build ($config.cli.develop == false), we just use this +# pregenerated source code to build the final version of the compiler. In the +# development build ($config.cli.develop == true) we use this pregenerated +# source code to build a bootstrap version of the compiler which we then use +# to regenerate the source code and build that final version from that. + +## Consumption build ($develop == false). +# + +# Use pregenerated versions to build the final version of cli. +# +libue{cli}: pregenerated/{hxx ixx cxx}{**}: include = (!$develop) + +if! $develop + cxx.poptions =+ "-I($src_base/pregenerated)" # Note: must come first. + +# Distribute pregenerated versions only in the consumption build. +# +pregenerated/{hxx ixx cxx}{*}: dist = (!$develop) + +# +## + +## Development build ($develop == true). +# +libue{cli}: {hxx ixx cxx}{options}: include = $develop + +# Bootstrap. +# +# The plan is as follows: +# +# 1. Build the bootstrap version of cli using a copy of options.?xx saved in +# pregenerated/cli/. +# +# 2. Use that to regenerate options.?xx. If the result differs from the saved +# version, copy it over and fail the build, asking for a restart. +# +# 3. Otherwise, proceed to build the final version of cli using regenerated +# options.?xx. +# +pregenerated/ { - test = true - install = false + # This should only apply to the bootstrap build. + # + cxx.poptions =+ "-I$src_base" # Note: must come first. + + cli/ + { + # Note: semantics/ and traversal/ do not include options.hxx so we can + # share their object files. + # + exe{cli}: obj{cli $name($src) options} ../../{$all_s $all_t} $libs + + for s: cli $name($src) + obj{$s}: ../../cxx{$s} $libs + + obj{options}: {hxx ixx cxx}{options} + + obj{cli}: cxx.poptions += -DCLI_BOOTSTRAP + } } -for t: cxx{**.test...} +# In the development build distribute regenerated {hxx ixx cxx}{options}, +# remapping their locations to the paths of the pregenerated versions (which +# are only distributed in the consumption build; see above). This way we make +# sure that the distributed files are always up-to-date. +# +<{hxx ixx cxx}{options}>: cli{options} pregenerated/cli/exe{cli} { - d = $directory($t) - n = $name($t)... + dist = ($develop ? pregenerated/cli/ : false) - ./: $d/exe{$n}: $t $d/{hxx ixx txx}{+$n} $d/testscript{+$n} - $d/exe{$n}: libue{cli}: bin.whole = false + # Symlink the generated code in src for convenience of development. + # + backlink = true } +% +if $develop +{{ + options = --include-with-brackets --include-prefix cli \ + --guard-prefix CLI --generate-file-scanner \ + --generate-specifier --generate-modifier \ + --suppress-undocumented --reserved-name stdout + + diag cli ($<[0]) -> $> + ($<[1]) $options -o $out_base $path($<[0]) + + # If the result differs from the bootstrap version, copy it over and + # request the build restart. + # + if diff $src_base/pregenerated/cli/options.hxx $path($>[0]) >- && \ + diff $src_base/pregenerated/cli/options.ixx $path($>[1]) >- && \ + diff $src_base/pregenerated/cli/options.cxx $path($>[2]) >- + exit + end + + cp $path($>[0]) $src_base/pregenerated/cli/options.hxx + cp $path($>[1]) $src_base/pregenerated/cli/options.ixx + cp $path($>[2]) $src_base/pregenerated/cli/options.cxx + + exit "bootstrap options.?xx have changed, restart the build" +}} -# Build options. # +## + # Pass the copyright notice extracted from the LICENSE file. # -copyright = $process.run_regex(cat $src_root/LICENSE, \ - 'Copyright \(c\) (.+)\.', \ - '\1') - obj{cli}: cxx.poptions += -DCLI_COPYRIGHT=\"$copyright\" -# Generated options parsing code. -# -# @@ This will eventually be replaced with an ah hoc recipe. +# Unit tests. # -if ($config.cli != [null] && $config.cli != false) +exe{*.test}: { - cli.cxx{options}: cli{options} - - cli.options += --include-with-brackets --include-prefix cli \ ---guard-prefix CLI --generate-file-scanner --generate-specifier \ ---generate-modifier --suppress-undocumented --reserved-name stdout + test = true + install = false +} - cli.cxx{*}: - { - # Include the generated cli files into the distribution and don't remove - # them when cleaning in src (so that clean results in a state identical to - # distributed). - # - dist = true - clean = ($src_root != $out_root) +for t: cxx{**.test...} +{ + d = $directory($t) + n = $name($t)... - # We keep the generated code in the repository so copy it back to src - # in case of a forwarded configuration. - # - backlink = overwrite - } + ./: $d/exe{$n}: $t $d/{hxx ixx txx}{+$n} $d/testscript{+$n} + $d/exe{$n}: libue{cli}: bin.whole = false } diff --git a/cli/cli/cli.cxx b/cli/cli/cli.cxx index 11cf6c0..d56f9e2 100644 --- a/cli/cli/cli.cxx +++ b/cli/cli/cli.cxx @@ -6,6 +6,7 @@ #include <string> #include <memory> // unique_ptr #include <fstream> +#include <utility> // move() #include <iostream> #include <libcutl/compiler/code-stream.hxx> @@ -16,7 +17,13 @@ #include <cli/parser.hxx> #include <cli/generator.hxx> -#include <cli/version.hxx> +#ifndef CLI_BOOTSTRAP +# include <cli/version.hxx> +#else +# define CLI_VERSION_ID 0 +# define CLI_VERSION_FULL 0 +# define CLI_COPYRIGHT "" +#endif using namespace std; using namespace cutl; @@ -115,8 +122,9 @@ main (int argc, char* argv[]) // Parse and generate. // - parser p (include_paths); - unique_ptr<semantics::cli_unit> unit (p.parse (ifs, path)); + parser p (include_paths, ops.generate_dep ()); + parser::parse_result r (p.parse (ifs, path)); + unique_ptr<semantics::cli_unit>& unit (r.unit); // Merge documentation variables from the command line. // @@ -137,7 +145,7 @@ main (int argc, char* argv[]) } generator g; - g.generate (ops, *unit, path); + g.generate (ops, move (*unit), move (r.dependencies), path); } catch (cli::exception const& ex) { diff --git a/cli/cli/context.cxx b/cli/cli/context.cxx index 54bb988..2c6a733 100644 --- a/cli/cli/context.cxx +++ b/cli/cli/context.cxx @@ -7,6 +7,7 @@ #include <cstring> // strncmp() #include <fstream> #include <sstream> +#include <utility> // move() #include <iostream> #include <cli/context.hxx> @@ -114,6 +115,8 @@ context (ostream& os_, opt_prefix (options.option_prefix ()), opt_sep (options.option_separator ()), cli (data_->cli_), + exp (data_->exp_), + exp_inl (data_->exp_inl_), reserved_name_map (options.reserved_name ()), keyword_set (data_->keyword_set_), link_regex (data_->link_regex_), @@ -141,6 +144,16 @@ context (ostream& os_, if (!cli.empty () && cli[0] != ':') data_->cli_ = "::" + data_->cli_; + data_->exp_ = options.export_symbol (); + + if (!exp.empty ()) + { + data_->exp_ += ' '; + + if (options.suppress_inline ()) + data_->exp_inl_ = data_->exp_; + } + for (size_t i (0); i < sizeof (keywords) / sizeof (char*); ++i) data_->keyword_set_.insert (keywords[i]); @@ -179,6 +192,8 @@ context (context& c) opt_prefix (c.opt_prefix), opt_sep (c.opt_sep), cli (c.cli), + exp (c.exp), + exp_inl (c.exp_inl), reserved_name_map (c.reserved_name_map), keyword_set (c.keyword_set), link_regex (c.link_regex), @@ -296,6 +311,59 @@ process_link_target (const string& tg) return found ? r : tg; } +void context:: +preprocess_ascii_tree (string& s) +{ + // tree --charset=UTF-8 uses the following box-drawing characters (see + // color.c): + // + // CHAR UTF-8 ASCII + //---------------------------- + // + // | E29482 | + // + // -- E29480 - + // + // |- E2949C | + // + // |_ E29494 ` + // + // <nbspace> C2A0 <space> + // + // Note that here we rely on the fact that neither E2 nor C2 can appear as + // continuation bytes. + // + for (size_t i (0); i != s.size (); ++i) + { + i = s.find_first_of ("\xE2\xC2", i); + + if (i == string::npos) + break; + + if (s[i] == '\xE2') + { + if (s[i + 1] == '\x94') + { + const char* r; + switch (s[i + 2]) + { + case '\x80': r = "-"; break; + case '\x82': + case '\x9c': r = "|"; break; + case '\x94': r = "`"; break; + default: continue; + } + + s.replace (i, 3, r); + } + } + else + { + if (s[i + 1] == '\xA0') + s.replace (i, 2, " "); + } + } +} string context:: translate_arg (string const& s, std::set<string>& set) @@ -1270,15 +1338,32 @@ format (semantics::scope& scope, string const& s, bool para) stack<block> blocks; blocks.push (block (block::text, para, "")); // Top-level. - // Number of li in ol. Since we don't support nested lists, we don't - // need to push it into the stack. + // Number of li in ol. Since we don't support nested lists (except in HTML + // where this is unused), we don't need to push it into the stack. // size_t ol_count (0); // Mapping of \h to HTML tag. By default it is <h1> until we encounter - // \h0 or \h1 at which point we change it to <h2>. + // \h0 or \h1 at which point we change it to <h2>. It can also be mapped + // with --html-heading-map. // - char html_h ('1'); + char html_h ('\0'); + + typedef map<char, string> html_hmap; + if (ot == ot_html) + { + const html_hmap& hm (options.html_heading_map ()); + html_hmap::const_iterator mi (hm.find ('h')); + + if (mi != hm.end ()) + { + // Note: this mapping back is necessary for TOC to function correctly + // with multiple string fragments. + // + if (mi->second == "h1") html_h = '1'; + else if (mi->second == "h2") html_h = '2'; + } + } bool last (false); for (size_t b (0), e; !last; b = e + 1) @@ -1532,7 +1617,10 @@ format (semantics::scope& scope, string const& s, bool para) case block::dl: good = (k == block::li); break; case block::li: good = (k == block::note || k == block::text || - k == block::pre ); break; + k == block::pre || + (ot == ot_html && (k == block::ul || + k == block::ol || + k == block::dl))); break; case block::note: good = (k == block::text || k == block::pre || (ot == ot_html && (k == block::ul || @@ -1644,7 +1732,7 @@ format (semantics::scope& scope, string const& s, bool para) { case block::h: blocks.push (block (k, false, id, header)); break; case block::ul: - case block::ol: ol_count = 0; // Fall through. + case block::ol: if (ot != ot_html) ol_count = 0; // Fall through. case block::dl: blocks.push (block (k, true, id)); break; case block::li: { @@ -1652,9 +1740,12 @@ format (semantics::scope& scope, string const& s, bool para) { case block::ol: { - ostringstream os; - os << ++ol_count; - header = os.str (); + if (ot != ot_html) + { + ostringstream os; + os << ++ol_count; + header = os.str (); + } break; } case block::dl: @@ -1919,7 +2010,7 @@ format (semantics::scope& scope, string const& s, bool para) case '1': break; // Always unwind. case 'h': { - pop = html_h == '1' || e.type == 'h' || e.type == '2'; + pop = html_h != '2' || e.type == 'h' || e.type == '2'; break; } case '2': pop = e.type == '2'; break; @@ -2016,9 +2107,10 @@ format (semantics::scope& scope, string const& s, bool para) // Same as in non-TOC mode below. // - // @@ This only works for a single string fragment. + // This only works automatically for a single string fragment. + // For multiple string fragments use --html-heading-map. // - if (t == '0' || t == '1') + if (html_h == '\0' && (t == '0' || t == '1')) html_h = '2'; break; @@ -2136,9 +2228,8 @@ format (semantics::scope& scope, string const& s, bool para) string h; string c; - typedef map<char, string> map; - const map& hm (options.html_heading_map ()); - map::const_iterator mi (hm.find (t)); + const html_hmap& hm (options.html_heading_map ()); + html_hmap::const_iterator mi (hm.find (t)); if (mi == hm.end ()) { @@ -2147,7 +2238,7 @@ format (semantics::scope& scope, string const& s, bool para) case '0': h = "h1"; c = "preface"; break; case 'H': h = "h1"; c = "part"; break; case '1': h = "h1"; break; - case 'h': h = html_h == '1' ? "h1" : "h2"; break; + case 'h': h = html_h != '2' ? "h1" : "h2"; break; case '2': h = "h3"; break; } } @@ -2173,9 +2264,10 @@ format (semantics::scope& scope, string const& s, bool para) v += "</" + h + '>'; - // @@ This only works for a single string fragment. + // This only works automatically for a single string fragment. + // For multiple string fragments use --html-heading-map. // - if (t == '0' || t == '1') + if (html_h == '\0' && (t == '0' || t == '1')) html_h = '2'; break; diff --git a/cli/cli/context.hxx b/cli/cli/context.hxx index 66dcb24..c5802ed 100644 --- a/cli/cli/context.hxx +++ b/cli/cli/context.hxx @@ -92,6 +92,8 @@ public: string const& opt_prefix; string const& opt_sep; string const& cli; + string const& exp; + string const& exp_inl; // Export symbol if inline is suppressed. typedef std::map<string, string> reserved_name_map_type; reserved_name_map_type const& reserved_name_map; @@ -135,6 +137,8 @@ private: { string inl_; string cli_; + string exp_; + string exp_inl_; keyword_set_type keyword_set_; regex_mapping link_regex_; id_set_type id_set_; @@ -153,10 +157,13 @@ public: string process_link_target (const string&); - // Translate and format the documentation string. Translate converts - // the <arg>-style constructs to \i{arg}. Format converts the string - // to the output format. + // Preprocess, translate, and format the documentation string. Translate + // converts the <arg>-style constructs to \i{arg}. Format converts the + // string to the output format. // + static void + preprocess_ascii_tree (string&); + static string translate_arg (string const&, std::set<string>&); diff --git a/cli/cli/generator.cxx b/cli/cli/generator.cxx index b4e6640..623ac67 100644 --- a/cli/cli/generator.cxx +++ b/cli/cli/generator.cxx @@ -2,9 +2,10 @@ // author : Boris Kolpackov <boris@codesynthesis.com> // license : MIT; see accompanying LICENSE file -#include <cctype> // std::toupper, std::is{alpha,upper,lower} +#include <cctype> // toupper, is{alpha,upper,lower} #include <string> #include <fstream> +#include <utility> // move() #include <iostream> #include <libcutl/fs/auto-remove.hxx> @@ -98,7 +99,10 @@ namespace } void - append (context& ctx, vector<string> const& text, string const& file) + append (context& ctx, + vector<string> const& text, + string const& file, + vector<path>* pdeps) { for (vector<string>::const_iterator i (text.begin ()); i != text.end (); ++i) @@ -111,7 +115,8 @@ namespace ifstream ifs; open (ifs, file); - path d (path (file).directory ()); + path p (file); + path d (p.directory ()); // getline() will set the failbit if it failed to extract anything, // not even the delimiter and eofbit if it reached eof before seeing @@ -119,6 +124,9 @@ namespace // for (string s; getline (ifs, s); ) append (ctx, s, &d); + + if (pdeps != nullptr) + pdeps->push_back (move (p.normalize ())); } } } @@ -129,7 +137,10 @@ generator () } void generator:: -generate (options& ops, semantics::cli_unit& unit, path const& p) +generate (options& ops, + semantics::cli_unit&& unit, + vector<path>&& deps, + path const& p) { if (ops.generate_group_scanner ()) ops.generate_vector_scanner (true); @@ -168,8 +179,48 @@ generate (options& ops, semantics::cli_unit& unit, path const& p) } } + bool gen_dep (ops.generate_dep ()); + vector<path>* pdeps (gen_dep ? &deps : nullptr); + vector<path> depts; // Dependents. + fs::auto_removes auto_rm; + // gen_dep + // + // Make sure that we remove the potentially outdated dependency file if we + // fail to generate any source/documentation file. + // + // Note that we will write the dependency file content later, when all the + // dependents and dependencies are determined. + // + ofstream dep; + + if (gen_dep) + { + path dep_path; + + if (ops.dep_file ().empty ()) + { + dep_path = path (pfx + base + sfx + ops.dep_suffix ()); + + if (!ops.output_dir ().empty ()) + dep_path = path (ops.output_dir ()) / dep_path; + } + else + dep_path = path (ops.dep_file ()); + + dep.open (dep_path.string ().c_str (), ios_base::out); + + if (!dep.is_open ()) + { + cerr << "error: unable to open '" << dep_path << "' in write mode" + << endl; + throw failed (); + } + + auto_rm.add (dep_path); + } + // C++ output. // if (gen_cxx) @@ -235,6 +286,9 @@ generate (options& ops, semantics::cli_unit& unit, path const& p) auto_rm.add (hxx_path); + if (gen_dep) + depts.push_back (move (hxx_path.normalize ())); + // // ofstream ixx; @@ -251,6 +305,9 @@ generate (options& ops, semantics::cli_unit& unit, path const& p) } auto_rm.add (ixx_path); + + if (gen_dep) + depts.push_back (move (ixx_path.normalize ())); } // @@ -266,6 +323,9 @@ generate (options& ops, semantics::cli_unit& unit, path const& p) auto_rm.add (cxx_path); + if (gen_dep) + depts.push_back (move (cxx_path.normalize ())); + // Print headers. // hxx << cxx_header; @@ -304,7 +364,7 @@ generate (options& ops, semantics::cli_unit& unit, path const& p) // hxx << "// Begin prologue." << endl << "//" << endl; - append (ctx, ops.hxx_prologue (), ops.hxx_prologue_file ()); + append (ctx, ops.hxx_prologue (), ops.hxx_prologue_file (), pdeps); hxx << "//" << endl << "// End prologue." << endl << endl; @@ -331,7 +391,7 @@ generate (options& ops, semantics::cli_unit& unit, path const& p) // hxx << "// Begin epilogue." << endl << "//" << endl; - append (ctx, ops.hxx_epilogue (), ops.hxx_epilogue_file ()); + append (ctx, ops.hxx_epilogue (), ops.hxx_epilogue_file (), pdeps); hxx << "//" << endl << "// End epilogue." << endl << endl; @@ -349,7 +409,7 @@ generate (options& ops, semantics::cli_unit& unit, path const& p) // ixx << "// Begin prologue." << endl << "//" << endl; - append (ctx, ops.ixx_prologue (), ops.ixx_prologue_file ()); + append (ctx, ops.ixx_prologue (), ops.ixx_prologue_file (), pdeps); ixx << "//" << endl << "// End prologue." << endl << endl; @@ -369,7 +429,7 @@ generate (options& ops, semantics::cli_unit& unit, path const& p) // ixx << "// Begin epilogue." << endl << "//" << endl; - append (ctx, ops.ixx_epilogue (), ops.ixx_epilogue_file ()); + append (ctx, ops.ixx_epilogue (), ops.ixx_epilogue_file (), pdeps); ixx << "//" << endl << "// End epilogue." << endl; } @@ -383,7 +443,7 @@ generate (options& ops, semantics::cli_unit& unit, path const& p) // cxx << "// Begin prologue." << endl << "//" << endl; - append (ctx, ops.cxx_prologue (), ops.cxx_prologue_file ()); + append (ctx, ops.cxx_prologue (), ops.cxx_prologue_file (), pdeps); cxx << "//" << endl << "// End prologue." << endl << endl; @@ -413,7 +473,7 @@ generate (options& ops, semantics::cli_unit& unit, path const& p) // cxx << "// Begin epilogue." << endl << "//" << endl; - append (ctx, ops.cxx_epilogue (), ops.cxx_epilogue_file ()); + append (ctx, ops.cxx_epilogue (), ops.cxx_epilogue_file (), pdeps); cxx << "//" << endl << "// End epilogue." << endl << endl; @@ -443,6 +503,9 @@ generate (options& ops, semantics::cli_unit& unit, path const& p) } auto_rm.add (man_path); + + if (gen_dep) + depts.push_back (move (man_path.normalize ())); } // The explicit cast helps VC++ 8.0 overcome its issues. @@ -452,9 +515,9 @@ generate (options& ops, semantics::cli_unit& unit, path const& p) for (bool first (true); first || ctx.toc; first = false) { - append (ctx, ops.man_prologue (), ops.man_prologue_file ()); + append (ctx, ops.man_prologue (), ops.man_prologue_file (), pdeps); generate_man (ctx); - append (ctx, ops.man_epilogue (), ops.man_epilogue_file ()); + append (ctx, ops.man_epilogue (), ops.man_epilogue_file (), pdeps); if (ctx.toc) { @@ -492,6 +555,9 @@ generate (options& ops, semantics::cli_unit& unit, path const& p) } auto_rm.add (html_path); + + if (gen_dep) + depts.push_back (move (html_path.normalize ())); } // The explicit cast helps VC++ 8.0 overcome its issues. @@ -501,9 +567,9 @@ generate (options& ops, semantics::cli_unit& unit, path const& p) for (bool first (true); first || ctx.toc; first = false) { - append (ctx, ops.html_prologue (), ops.html_prologue_file ()); + append (ctx, ops.html_prologue (), ops.html_prologue_file (), pdeps); generate_html (ctx); - append (ctx, ops.html_epilogue (), ops.html_epilogue_file ()); + append (ctx, ops.html_epilogue (), ops.html_epilogue_file (), pdeps); if (ctx.toc) { @@ -538,6 +604,9 @@ generate (options& ops, semantics::cli_unit& unit, path const& p) } auto_rm.add (txt_path); + + if (gen_dep) + depts.push_back (move (txt_path.normalize ())); } // The explicit cast helps VC++ 8.0 overcome its issues. @@ -547,9 +616,9 @@ generate (options& ops, semantics::cli_unit& unit, path const& p) for (bool first (true); first || ctx.toc; first = false) { - append (ctx, ops.txt_prologue (), ops.txt_prologue_file ()); + append (ctx, ops.txt_prologue (), ops.txt_prologue_file (), pdeps); generate_txt (ctx); - append (ctx, ops.txt_epilogue (), ops.txt_epilogue_file ()); + append (ctx, ops.txt_epilogue (), ops.txt_epilogue_file (), pdeps); if (ctx.toc) { @@ -561,6 +630,49 @@ generate (options& ops, semantics::cli_unit& unit, path const& p) ctx.verify_id_ref (); } + // gen_dep + // + if (gen_dep) + { + // Write the specified path to the dependencies file stream, escaping + // colons and backslashes. + // + auto write = [&dep] (path const& p) + { + for (char c: p.string ()) + { + if (c == ':' || c == '\\') + dep << '\\'; + + dep << c; + } + }; + + // Note that we don't add the dependency file as a dependent, but in the + // future may invent some option which triggers that. + // + bool first (true); + for (const auto& p: depts) + { + if (!first) + dep << " \\" << endl; + else + first = false; + + write (p); + } + + dep << ':'; + + for (const auto& p: deps) + { + dep << " \\" << endl + << " "; write (p); + } + + dep << endl; + } + auto_rm.cancel (); } catch (const generation_failed&) diff --git a/cli/cli/generator.hxx b/cli/cli/generator.hxx index f567528..3deb3f3 100644 --- a/cli/cli/generator.hxx +++ b/cli/cli/generator.hxx @@ -5,6 +5,8 @@ #ifndef CLI_GENERATOR_HXX #define CLI_GENERATOR_HXX +#include <vector> + #include <cli/options.hxx> #include <cli/semantics/unit.hxx> @@ -16,7 +18,10 @@ public: class failed {}; void - generate (options&, semantics::cli_unit&, semantics::path const&); + generate (options&, + semantics::cli_unit&&, + std::vector<semantics::path>&& dependencies, + semantics::path const&); private: generator (generator const&); diff --git a/cli/cli/header.cxx b/cli/cli/header.cxx index a2a3ccd..87ff259 100644 --- a/cli/cli/header.cxx +++ b/cli/cli/header.cxx @@ -115,7 +115,7 @@ namespace string name (escape (c.name ())); string um (cli + "::unknown_mode"); - os << "class " << name; + os << "class " << exp << name; { base b (*this); @@ -367,13 +367,13 @@ generate_header (context& ctx) string up (ctx.cli + "::usage_para"); string const& ost (ctx.options.ostream_type ()); - os << up << endl + os << ctx.exp << up << endl << n << "usage (" << ost << "&," << endl << up << " = " << up << "::none);" << endl; if (ctx.gen_usage == ut_both) - os << up << endl + os << ctx.exp << up << endl << n << "long_usage (" << ost << "&," << endl << up << " = " << up << "::none);" << endl; diff --git a/cli/cli/html.cxx b/cli/cli/html.cxx index b374b91..48eb5e3 100644 --- a/cli/cli/html.cxx +++ b/cli/cli/html.cxx @@ -127,13 +127,16 @@ namespace // n > 2 - arg string, short string, long string // size_t n (ds.size ()); - const string& d ( + string d ( n == 1 ? (cd_ == cd_short ? first_sentence (ds[0]) : ds[0]) : (n == 2 ? (cd_ == cd_short ? first_sentence (ds[1]) : ds[1]) : ds[cd_ == cd_short ? 1 : 2])); + if (options.ascii_tree ()) + preprocess_ascii_tree (d); + std::set<string> arg_set; if (n > 1) translate_arg (ds[0], arg_set); @@ -211,6 +214,8 @@ namespace if (type != "bool" || doc.size () >= 3) { + // Note: we naturally assume this doesn't need --ascii-tree treatment. + // string s ( translate_arg ( doc.size () > 0 ? doc[0] : string ("<arg>"), arg_set)); @@ -236,6 +241,9 @@ namespace d = (cd_ == cd_short ? first_sentence (doc[1]) : doc[1]); } + if (options.ascii_tree ()) + preprocess_ascii_tree (d); + // Format the documentation string. // d = format (o.scope (), escape_html (translate (d, arg_set)), false); diff --git a/cli/cli/lexer.test.cxx b/cli/cli/lexer.test.cxx index 0eb4dcb..a5cc2f3 100644 --- a/cli/cli/lexer.test.cxx +++ b/cli/cli/lexer.test.cxx @@ -8,6 +8,9 @@ #include <cli/token.hxx> #include <cli/lexer.hxx> +#undef NDEBUG +#include <cassert> + using namespace std; const char* keywords[] = diff --git a/cli/cli/man.cxx b/cli/cli/man.cxx index df703e8..d446b2a 100644 --- a/cli/cli/man.cxx +++ b/cli/cli/man.cxx @@ -91,13 +91,16 @@ namespace // n > 2 - arg string, short string, long string // size_t n (ds.size ()); - const string& d ( + string d ( n == 1 ? (cd_ == cd_short ? first_sentence (ds[0]) : ds[0]) : (n == 2 ? (cd_ == cd_short ? first_sentence (ds[1]) : ds[1]) : ds[cd_ == cd_short ? 1 : 2])); + if (options.ascii_tree ()) + preprocess_ascii_tree (d); + std::set<string> arg_set; if (n > 1) translate_arg (ds[0], arg_set); @@ -149,6 +152,8 @@ namespace if (type != "bool" || doc.size () >= 3) { + // Note: we naturally assume this doesn't need --ascii-tree treatment. + // string s ( translate_arg ( doc.size () > 0 ? doc[0] : string ("<arg>"), arg_set)); @@ -174,6 +179,9 @@ namespace d = (cd_ == cd_short ? first_sentence (doc[1]) : doc[1]); } + if (options.ascii_tree ()) + preprocess_ascii_tree (d); + // Format the documentation string. // d = format (o.scope (), translate (d, arg_set), false); diff --git a/cli/cli/options.cli b/cli/cli/options.cli index ff462d3..211e01f 100644 --- a/cli/cli/options.cli +++ b/cli/cli/options.cli @@ -108,11 +108,15 @@ class options { -f }+ { -b }+ arg +{ f=1 } +{ b=2 } # 'arg' with '-f' 'b' 'f=1' 'b=2' \ - Note that the group applies to a single argument only. For example: + The group applies to a single argument only unless multiple arguments + are themselves grouped with '\cb{{}' and '\cb{\}}'. For example: \ - { --foo }+ arg1 arg2 +{ --bar } # 'arg1' with '--foo' and - # 'arg2' with '--bar' + { --foo }+ arg1 arg2 +{ --bar } # 'arg1' with '--foo' + # 'arg2' with '--bar' + + { --foo }+ { arg1 arg2 } +{ --bar } # 'arg1' with '--foo' '--bar' + # 'arg2' with '--foo' '--bar' \ The group separators ('\cb{{}', '\cb{\}+'}, etc) must be separate command @@ -167,6 +171,13 @@ class options should be used to print usage and exception information." }; + std::string --export-symbol + { + "<symbol>", + "Insert <symbol> in places where DLL export/import control statements + (\cb{__declspec(dllexport/dllimport)}) are necessary." + }; + bool --generate-cxx { "Generate C++ code. If neither \cb{--generate-man}, \cb{--generate-html}, @@ -188,6 +199,18 @@ class options "Generate documentation in the plain text format, similar to usage." }; + bool --generate-dep + { + "Generate \cb{make} dependency information. This option triggers the + creation of the \cb{.d} file containing the dependencies of the generated + files on the main \cb{.cli} file as well as all the \cb{.cli} files that + it includes or sources, transitively. Paths specified with the + \cb{--*-prologue-file} and \cb{--*-epilogue-file} options are also + added as dependencies. Note, however, that paths specified with the + \cb{--options-file} option are not added (since they may or may not + contain options that affect the output)." + }; + bool --stdout { "Write output to STDOUT instead of a file. This option is not valid @@ -253,6 +276,13 @@ class options files, and would like their usage to have the same indentation level." }; + bool --ascii-tree + { + "Convert UTF-8 \cb{tree(1)} output to ASCII. Specifically, box-drawing + characters used in the \cb{--charset=UTF-8} output are replaced with + ASCII characters used in the \cb{--charset=ASCII} output." + }; + bool --ansi-color { "Use ANSI color escape sequences when printing usage. By \"color\" we @@ -568,6 +598,20 @@ class options the generated text file." }; + std::string --dep-suffix = ".d" + { + "<suffix>", + "Use <suffix> instead of the default \cb{.d} to construct the name of the + generated dependency file. See also \cb{--dep-file}." + }; + + std::string --dep-file + { + "<path>", + "Use <path> as the generated dependency file path instead of deriving it + from the input file name." + }; + std::string --option-prefix = "-" { "<prefix>", diff --git a/cli/cli/parser.cxx b/cli/cli/parser.cxx index 4685edc..e2efca5 100644 --- a/cli/cli/parser.cxx +++ b/cli/cli/parser.cxx @@ -17,6 +17,7 @@ #include <fstream> #include <sstream> +#include <utility> // move() #include <iostream> #include <cli/token.hxx> @@ -66,7 +67,7 @@ const char* punctuation[] = { // Output the token type and value in a format suitable for diagnostics. // -std::ostream& +static std::ostream& operator<< (std::ostream& os, token const& t) { switch (t.type ()) @@ -184,16 +185,19 @@ recover (token& t) } } -unique_ptr<cli_unit> parser:: +parser::parse_result parser:: parse (std::istream& is, path const& p) { unique_ptr<cli_unit> unit (new cli_unit (p, 1, 1)); { path ap (p); - ap.absolute (); + ap.complete (); ap.normalize (); - include_map_[ap] = unit.get (); + include_map_[move (ap)] = unit.get (); + + if (collect_dependencies_) + dependencies_.push_back (path (p).normalize ()); } root_ = cur_ = unit.get (); @@ -211,7 +215,7 @@ parse (std::istream& is, path const& p) if (!valid_ || !l.valid ()) throw invalid_input (); - return unit; + return parse_result {move (unit), move (dependencies_)}; } void parser:: @@ -350,6 +354,16 @@ source_decl () if (valid_) { + if (collect_dependencies_) + { + path ap (p); + ap.complete (); + ap.normalize (); + + if (include_map_.emplace (move (ap), nullptr).second) + dependencies_.push_back (p); + } + auto_restore<path const> new_path (path_, &p); ifstream ifs (p.string ().c_str ()); @@ -468,15 +482,18 @@ include_decl () // Detect and ignore multiple inclusions. // path ap (p); - ap.absolute (); + ap.complete (); ap.normalize (); include_map::iterator it (include_map_.find (ap)); - if (it == include_map_.end ()) + if (it == include_map_.end () || it->second == nullptr) { + if (collect_dependencies_ && it == include_map_.end ()) + dependencies_.push_back (p); + cli_unit& n (root_->new_node<cli_unit> (p, 1, 1)); root_->new_edge<cli_includes> (*cur_, n, ik, f); - include_map_[ap] = &n; + include_map_[move (ap)] = &n; auto_restore<cli_unit> new_cur (cur_, &n); auto_restore<path const> new_path (path_, &p); diff --git a/cli/cli/parser.hxx b/cli/cli/parser.hxx index 326768e..960e74b 100644 --- a/cli/cli/parser.hxx +++ b/cli/cli/parser.hxx @@ -23,11 +23,23 @@ class parser public: typedef std::vector<semantics::path> paths; - parser (paths const& include_paths): include_paths_ (include_paths) {} + parser (paths const& include_paths, bool collect_dependencies) + : include_paths_ (include_paths), + collect_dependencies_ (collect_dependencies) {} struct invalid_input {}; - std::unique_ptr<semantics::cli_unit> + struct parse_result + { + std::unique_ptr<semantics::cli_unit> unit; + + // Normalized paths of the main CLI file and files it includes and sources + // recursively, with the duplicates suppressed. + // + paths dependencies; + }; + + parse_result parse (std::istream& is, semantics::path const& path); private: @@ -72,6 +84,7 @@ private: private: paths const include_paths_; + bool collect_dependencies_; bool valid_; semantics::path const* path_; @@ -84,8 +97,12 @@ private: std::size_t doc_count_; // Scope doc counter, see scope_doc() for details. + // If the entry's value is NULL, then the key refers to a sourced file. + // typedef std::map<semantics::path, semantics::cli_unit*> include_map; include_map include_map_; + + paths dependencies_; }; #endif // CLI_PARSER_HXX diff --git a/cli/cli/parser.test.cxx b/cli/cli/parser.test.cxx index 6ef0d11..44109b3 100644 --- a/cli/cli/parser.test.cxx +++ b/cli/cli/parser.test.cxx @@ -9,6 +9,9 @@ #include <cli/semantics.hxx> #include <cli/traversal.hxx> +#undef NDEBUG +#include <cassert> + using namespace std; int @@ -29,7 +32,7 @@ main (int argc, char* argv[]) ifs.open (path.string ().c_str ()); parser::paths include_paths; - parser p (include_paths); + parser p (include_paths, true /* collect_dependencies */); p.parse (ifs, path); } catch (semantics::invalid_path const& e) diff --git a/cli/cli/options.cxx b/cli/cli/pregenerated/cli/options.cxx index 0f0604a..9f0be5f 100644 --- a/cli/cli/options.cxx +++ b/cli/cli/pregenerated/cli/options.cxx @@ -15,6 +15,7 @@ #include <set> #include <string> #include <vector> +#include <utility> #include <ostream> #include <sstream> #include <cstring> @@ -196,6 +197,7 @@ namespace cli else ++i_; + ++start_position_; return r; } else @@ -206,11 +208,20 @@ namespace cli skip () { if (i_ < argc_) + { ++i_; + ++start_position_; + } else throw eos_reached (); } + std::size_t argv_scanner:: + position () + { + return start_position_; + } + // argv_file_scanner // int argv_file_scanner::zero_argc_ = 0; @@ -321,6 +332,7 @@ namespace cli { hold_[i_ == 0 ? ++i_ : --i_].swap (args_.front ().value); args_.pop_front (); + ++start_position_; return hold_[i_].c_str (); } } @@ -334,7 +346,10 @@ namespace cli if (args_.empty ()) return base::skip (); else + { args_.pop_front (); + ++start_position_; + } } const argv_file_scanner::option_info* argv_file_scanner:: @@ -347,6 +362,12 @@ namespace cli return 0; } + std::size_t argv_file_scanner:: + position () + { + return start_position_; + } + void argv_file_scanner:: load (const std::string& file) { @@ -521,10 +542,31 @@ namespace cli struct parser<bool> { static void - parse (bool& x, scanner& s) + parse (bool& x, bool& xs, scanner& s) { - s.next (); - x = true; + const char* o (s.next ()); + + if (s.more ()) + { + const char* v (s.next ()); + + if (std::strcmp (v, "1") == 0 || + std::strcmp (v, "true") == 0 || + std::strcmp (v, "TRUE") == 0 || + std::strcmp (v, "True") == 0) + x = true; + else if (std::strcmp (v, "0") == 0 || + std::strcmp (v, "false") == 0 || + std::strcmp (v, "FALSE") == 0 || + std::strcmp (v, "False") == 0) + x = false; + else + throw invalid_value (o, v); + } + else + throw missing_value (o); + + xs = true; } }; @@ -546,6 +588,17 @@ namespace cli }; template <typename X> + struct parser<std::pair<X, std::size_t> > + { + static void + parse (std::pair<X, std::size_t>& x, bool& xs, scanner& s) + { + x.second = s.position (); + parser<X>::parse (x.first, xs, s); + } + }; + + template <typename X> struct parser<std::vector<X> > { static void @@ -583,6 +636,7 @@ namespace cli if (s.more ()) { + std::size_t pos (s.position ()); std::string ov (s.next ()); std::string::size_type p = ov.find ('='); @@ -602,14 +656,14 @@ namespace cli if (!kstr.empty ()) { av[1] = const_cast<char*> (kstr.c_str ()); - argv_scanner s (0, ac, av); + argv_scanner s (0, ac, av, false, pos); parser<K>::parse (k, dummy, s); } if (!vstr.empty ()) { av[1] = const_cast<char*> (vstr.c_str ()); - argv_scanner s (0, ac, av); + argv_scanner s (0, ac, av, false, pos); parser<V>::parse (v, dummy, s); } @@ -622,6 +676,56 @@ namespace cli } }; + template <typename K, typename V, typename C> + struct parser<std::multimap<K, V, C> > + { + static void + parse (std::multimap<K, V, C>& m, bool& xs, scanner& s) + { + const char* o (s.next ()); + + if (s.more ()) + { + std::size_t pos (s.position ()); + std::string ov (s.next ()); + std::string::size_type p = ov.find ('='); + + K k = K (); + V v = V (); + std::string kstr (ov, 0, p); + std::string vstr (ov, (p != std::string::npos ? p + 1 : ov.size ())); + + int ac (2); + char* av[] = + { + const_cast<char*> (o), + 0 + }; + + bool dummy; + if (!kstr.empty ()) + { + av[1] = const_cast<char*> (kstr.c_str ()); + argv_scanner s (0, ac, av, false, pos); + parser<K>::parse (k, dummy, s); + } + + if (!vstr.empty ()) + { + av[1] = const_cast<char*> (vstr.c_str ()); + argv_scanner s (0, ac, av, false, pos); + parser<V>::parse (v, dummy, s); + } + + m.insert (typename std::multimap<K, V, C>::value_type (k, v)); + } + else + throw missing_value (o); + + xs = true; + } + }; + template <typename X, typename T, T X::*M> void thunk (X& x, scanner& s) @@ -629,6 +733,14 @@ namespace cli parser<T>::parse (x.*M, s); } + template <typename X, bool X::*M> + void + thunk (X& x, scanner& s) + { + s.next (); + x.*M = true; + } + template <typename X, typename T, T X::*M, bool X::*S> void thunk (X& x, scanner& s) @@ -638,7 +750,6 @@ namespace cli } #include <map> -#include <cstring> // options // @@ -669,10 +780,13 @@ options () cli_namespace_specified_ (false), ostream_type_ ("::std::ostream"), ostream_type_specified_ (false), + export_symbol_ (), + export_symbol_specified_ (false), generate_cxx_ (), generate_man_ (), generate_html_ (), generate_txt_ (), + generate_dep_ (), stdout__ (), suppress_undocumented_ (), suppress_usage_ (), @@ -682,6 +796,7 @@ options () page_usage_specified_ (false), option_length_ (0), option_length_specified_ (false), + ascii_tree_ (), ansi_color_ (), exclude_base_ (), include_base_last_ (), @@ -761,6 +876,10 @@ options () html_suffix_specified_ (false), txt_suffix_ (".txt"), txt_suffix_specified_ (false), + dep_suffix_ (".d"), + dep_suffix_specified_ (false), + dep_file_ (), + dep_file_specified_ (false), option_prefix_ ("-"), option_prefix_specified_ (false), option_separator_ ("--"), @@ -810,10 +929,13 @@ options (int& argc, cli_namespace_specified_ (false), ostream_type_ ("::std::ostream"), ostream_type_specified_ (false), + export_symbol_ (), + export_symbol_specified_ (false), generate_cxx_ (), generate_man_ (), generate_html_ (), generate_txt_ (), + generate_dep_ (), stdout__ (), suppress_undocumented_ (), suppress_usage_ (), @@ -823,6 +945,7 @@ options (int& argc, page_usage_specified_ (false), option_length_ (0), option_length_specified_ (false), + ascii_tree_ (), ansi_color_ (), exclude_base_ (), include_base_last_ (), @@ -902,6 +1025,10 @@ options (int& argc, html_suffix_specified_ (false), txt_suffix_ (".txt"), txt_suffix_specified_ (false), + dep_suffix_ (".d"), + dep_suffix_specified_ (false), + dep_file_ (), + dep_file_specified_ (false), option_prefix_ ("-"), option_prefix_specified_ (false), option_separator_ ("--"), @@ -954,10 +1081,13 @@ options (int start, cli_namespace_specified_ (false), ostream_type_ ("::std::ostream"), ostream_type_specified_ (false), + export_symbol_ (), + export_symbol_specified_ (false), generate_cxx_ (), generate_man_ (), generate_html_ (), generate_txt_ (), + generate_dep_ (), stdout__ (), suppress_undocumented_ (), suppress_usage_ (), @@ -967,6 +1097,7 @@ options (int start, page_usage_specified_ (false), option_length_ (0), option_length_specified_ (false), + ascii_tree_ (), ansi_color_ (), exclude_base_ (), include_base_last_ (), @@ -1046,6 +1177,10 @@ options (int start, html_suffix_specified_ (false), txt_suffix_ (".txt"), txt_suffix_specified_ (false), + dep_suffix_ (".d"), + dep_suffix_specified_ (false), + dep_file_ (), + dep_file_specified_ (false), option_prefix_ ("-"), option_prefix_specified_ (false), option_separator_ ("--"), @@ -1098,10 +1233,13 @@ options (int& argc, cli_namespace_specified_ (false), ostream_type_ ("::std::ostream"), ostream_type_specified_ (false), + export_symbol_ (), + export_symbol_specified_ (false), generate_cxx_ (), generate_man_ (), generate_html_ (), generate_txt_ (), + generate_dep_ (), stdout__ (), suppress_undocumented_ (), suppress_usage_ (), @@ -1111,6 +1249,7 @@ options (int& argc, page_usage_specified_ (false), option_length_ (0), option_length_specified_ (false), + ascii_tree_ (), ansi_color_ (), exclude_base_ (), include_base_last_ (), @@ -1190,6 +1329,10 @@ options (int& argc, html_suffix_specified_ (false), txt_suffix_ (".txt"), txt_suffix_specified_ (false), + dep_suffix_ (".d"), + dep_suffix_specified_ (false), + dep_file_ (), + dep_file_specified_ (false), option_prefix_ ("-"), option_prefix_specified_ (false), option_separator_ ("--"), @@ -1244,10 +1387,13 @@ options (int start, cli_namespace_specified_ (false), ostream_type_ ("::std::ostream"), ostream_type_specified_ (false), + export_symbol_ (), + export_symbol_specified_ (false), generate_cxx_ (), generate_man_ (), generate_html_ (), generate_txt_ (), + generate_dep_ (), stdout__ (), suppress_undocumented_ (), suppress_usage_ (), @@ -1257,6 +1403,7 @@ options (int start, page_usage_specified_ (false), option_length_ (0), option_length_specified_ (false), + ascii_tree_ (), ansi_color_ (), exclude_base_ (), include_base_last_ (), @@ -1336,6 +1483,10 @@ options (int start, html_suffix_specified_ (false), txt_suffix_ (".txt"), txt_suffix_specified_ (false), + dep_suffix_ (".d"), + dep_suffix_specified_ (false), + dep_file_ (), + dep_file_specified_ (false), option_prefix_ ("-"), option_prefix_specified_ (false), option_separator_ ("--"), @@ -1386,10 +1537,13 @@ options (::cli::scanner& s, cli_namespace_specified_ (false), ostream_type_ ("::std::ostream"), ostream_type_specified_ (false), + export_symbol_ (), + export_symbol_specified_ (false), generate_cxx_ (), generate_man_ (), generate_html_ (), generate_txt_ (), + generate_dep_ (), stdout__ (), suppress_undocumented_ (), suppress_usage_ (), @@ -1399,6 +1553,7 @@ options (::cli::scanner& s, page_usage_specified_ (false), option_length_ (0), option_length_specified_ (false), + ascii_tree_ (), ansi_color_ (), exclude_base_ (), include_base_last_ (), @@ -1478,6 +1633,10 @@ options (::cli::scanner& s, html_suffix_specified_ (false), txt_suffix_ (".txt"), txt_suffix_specified_ (false), + dep_suffix_ (".d"), + dep_suffix_specified_ (false), + dep_file_ (), + dep_file_specified_ (false), option_prefix_ ("-"), option_prefix_specified_ (false), option_separator_ ("--"), @@ -1551,6 +1710,10 @@ print_usage (::std::ostream& os, ::cli::usage_para p) << " std::ostream that should be used to print usage" << ::std::endl << " and exception information." << ::std::endl; + os << "--export-symbol <symbol> Insert <symbol> in places where DLL export/import" << ::std::endl + << " control statements" << ::std::endl + << " (__declspec(dllexport/dllimport)) are necessary." << ::std::endl; + os << "--generate-cxx Generate C++ code." << ::std::endl; os << "--generate-man Generate documentation in the man page format." << ::std::endl; @@ -1560,6 +1723,8 @@ print_usage (::std::ostream& os, ::cli::usage_para p) os << "--generate-txt Generate documentation in the plain text format," << ::std::endl << " similar to usage." << ::std::endl; + os << "--generate-dep Generate make dependency information." << ::std::endl; + os << "--stdout Write output to STDOUT instead of a file." << ::std::endl; os << "--suppress-undocumented Suppress the generation of documentation entries" << ::std::endl @@ -1580,6 +1745,8 @@ print_usage (::std::ostream& os, ::cli::usage_para p) os << "--option-length <len> Indent option descriptions <len> characters when" << ::std::endl << " printing usage." << ::std::endl; + os << "--ascii-tree Convert UTF-8 tree(1) output to ASCII." << ::std::endl; + os << "--ansi-color Use ANSI color escape sequences when printing" << ::std::endl << " usage." << ::std::endl; @@ -1708,6 +1875,13 @@ print_usage (::std::ostream& os, ::cli::usage_para p) os << "--txt-suffix <suffix> Use <suffix> instead of the default .txt to" << ::std::endl << " construct the name of the generated text file." << ::std::endl; + os << "--dep-suffix <suffix> Use <suffix> instead of the default .d to" << ::std::endl + << " construct the name of the generated dependency" << ::std::endl + << " file." << ::std::endl; + + os << "--dep-file <path> Use <path> as the generated dependency file path" << ::std::endl + << " instead of deriving it from the input file name." << ::std::endl; + os << "--option-prefix <prefix> Use <prefix> instead of the default '-' as an" << ::std::endl << " option prefix." << ::std::endl; @@ -1758,9 +1932,9 @@ struct _cli_options_map_init &::cli::thunk< options, std::uint64_t, &options::build2_metadata_, &options::build2_metadata_specified_ >; _cli_options_map_["--help"] = - &::cli::thunk< options, bool, &options::help_ >; + &::cli::thunk< options, &options::help_ >; _cli_options_map_["--version"] = - &::cli::thunk< options, bool, &options::version_ >; + &::cli::thunk< options, &options::version_ >; _cli_options_map_["--include-path"] = &::cli::thunk< options, std::vector<std::string>, &options::include_path_, &options::include_path_specified_ >; @@ -1777,61 +1951,68 @@ struct _cli_options_map_init &::cli::thunk< options, cxx_version, &options::std_, &options::std_specified_ >; _cli_options_map_["--generate-modifier"] = - &::cli::thunk< options, bool, &options::generate_modifier_ >; + &::cli::thunk< options, &options::generate_modifier_ >; _cli_options_map_["--generate-specifier"] = - &::cli::thunk< options, bool, &options::generate_specifier_ >; + &::cli::thunk< options, &options::generate_specifier_ >; _cli_options_map_["--generate-parse"] = - &::cli::thunk< options, bool, &options::generate_parse_ >; + &::cli::thunk< options, &options::generate_parse_ >; _cli_options_map_["--generate-merge"] = - &::cli::thunk< options, bool, &options::generate_merge_ >; + &::cli::thunk< options, &options::generate_merge_ >; _cli_options_map_["--generate-description"] = - &::cli::thunk< options, bool, &options::generate_description_ >; + &::cli::thunk< options, &options::generate_description_ >; _cli_options_map_["--generate-file-scanner"] = - &::cli::thunk< options, bool, &options::generate_file_scanner_ >; + &::cli::thunk< options, &options::generate_file_scanner_ >; _cli_options_map_["--generate-vector-scanner"] = - &::cli::thunk< options, bool, &options::generate_vector_scanner_ >; + &::cli::thunk< options, &options::generate_vector_scanner_ >; _cli_options_map_["--generate-group-scanner"] = - &::cli::thunk< options, bool, &options::generate_group_scanner_ >; + &::cli::thunk< options, &options::generate_group_scanner_ >; _cli_options_map_["--suppress-inline"] = - &::cli::thunk< options, bool, &options::suppress_inline_ >; + &::cli::thunk< options, &options::suppress_inline_ >; _cli_options_map_["--suppress-cli"] = - &::cli::thunk< options, bool, &options::suppress_cli_ >; + &::cli::thunk< options, &options::suppress_cli_ >; _cli_options_map_["--cli-namespace"] = &::cli::thunk< options, std::string, &options::cli_namespace_, &options::cli_namespace_specified_ >; _cli_options_map_["--ostream-type"] = &::cli::thunk< options, std::string, &options::ostream_type_, &options::ostream_type_specified_ >; + _cli_options_map_["--export-symbol"] = + &::cli::thunk< options, std::string, &options::export_symbol_, + &options::export_symbol_specified_ >; _cli_options_map_["--generate-cxx"] = - &::cli::thunk< options, bool, &options::generate_cxx_ >; + &::cli::thunk< options, &options::generate_cxx_ >; _cli_options_map_["--generate-man"] = - &::cli::thunk< options, bool, &options::generate_man_ >; + &::cli::thunk< options, &options::generate_man_ >; _cli_options_map_["--generate-html"] = - &::cli::thunk< options, bool, &options::generate_html_ >; + &::cli::thunk< options, &options::generate_html_ >; _cli_options_map_["--generate-txt"] = - &::cli::thunk< options, bool, &options::generate_txt_ >; + &::cli::thunk< options, &options::generate_txt_ >; + _cli_options_map_["--generate-dep"] = + &::cli::thunk< options, &options::generate_dep_ >; _cli_options_map_["--stdout"] = - &::cli::thunk< options, bool, &options::stdout__ >; + &::cli::thunk< options, &options::stdout__ >; _cli_options_map_["--suppress-undocumented"] = - &::cli::thunk< options, bool, &options::suppress_undocumented_ >; + &::cli::thunk< options, &options::suppress_undocumented_ >; _cli_options_map_["--suppress-usage"] = - &::cli::thunk< options, bool, &options::suppress_usage_ >; + &::cli::thunk< options, &options::suppress_usage_ >; _cli_options_map_["--long-usage"] = - &::cli::thunk< options, bool, &options::long_usage_ >; + &::cli::thunk< options, &options::long_usage_ >; _cli_options_map_["--short-usage"] = - &::cli::thunk< options, bool, &options::short_usage_ >; + &::cli::thunk< options, &options::short_usage_ >; _cli_options_map_["--page-usage"] = &::cli::thunk< options, std::string, &options::page_usage_, &options::page_usage_specified_ >; _cli_options_map_["--option-length"] = &::cli::thunk< options, std::size_t, &options::option_length_, &options::option_length_specified_ >; + _cli_options_map_["--ascii-tree"] = + &::cli::thunk< options, &options::ascii_tree_ >; _cli_options_map_["--ansi-color"] = - &::cli::thunk< options, bool, &options::ansi_color_ >; + &::cli::thunk< options, &options::ansi_color_ >; _cli_options_map_["--exclude-base"] = - &::cli::thunk< options, bool, &options::exclude_base_ >; + &::cli::thunk< options, &options::exclude_base_ >; _cli_options_map_["--include-base-last"] = - &::cli::thunk< options, bool, &options::include_base_last_ >; + &::cli::thunk< options, &options::include_base_last_ >; _cli_options_map_["--class-doc"] = &::cli::thunk< options, std::map<std::string, std::string>, &options::class_doc_, &options::class_doc_specified_ >; @@ -1848,12 +2029,12 @@ struct _cli_options_map_init &::cli::thunk< options, std::vector<std::string>, &options::link_regex_, &options::link_regex_specified_ >; _cli_options_map_["--link-regex-trace"] = - &::cli::thunk< options, bool, &options::link_regex_trace_ >; + &::cli::thunk< options, &options::link_regex_trace_ >; _cli_options_map_["--html-heading-map"] = &::cli::thunk< options, std::map<char, std::string>, &options::html_heading_map_, &options::html_heading_map_specified_ >; _cli_options_map_["--omit-link-check"] = - &::cli::thunk< options, bool, &options::omit_link_check_ >; + &::cli::thunk< options, &options::omit_link_check_ >; _cli_options_map_["--hxx-prologue"] = &::cli::thunk< options, std::vector<std::string>, &options::hxx_prologue_, &options::hxx_prologue_specified_ >; @@ -1950,6 +2131,12 @@ struct _cli_options_map_init _cli_options_map_["--txt-suffix"] = &::cli::thunk< options, std::string, &options::txt_suffix_, &options::txt_suffix_specified_ >; + _cli_options_map_["--dep-suffix"] = + &::cli::thunk< options, std::string, &options::dep_suffix_, + &options::dep_suffix_specified_ >; + _cli_options_map_["--dep-file"] = + &::cli::thunk< options, std::string, &options::dep_file_, + &options::dep_file_specified_ >; _cli_options_map_["--option-prefix"] = &::cli::thunk< options, std::string, &options::option_prefix_, &options::option_prefix_specified_ >; @@ -1957,13 +2144,13 @@ struct _cli_options_map_init &::cli::thunk< options, std::string, &options::option_separator_, &options::option_separator_specified_ >; _cli_options_map_["--keep-separator"] = - &::cli::thunk< options, bool, &options::keep_separator_ >; + &::cli::thunk< options, &options::keep_separator_ >; _cli_options_map_["--no-combined-flags"] = - &::cli::thunk< options, bool, &options::no_combined_flags_ >; + &::cli::thunk< options, &options::no_combined_flags_ >; _cli_options_map_["--no-combined-values"] = - &::cli::thunk< options, bool, &options::no_combined_values_ >; + &::cli::thunk< options, &options::no_combined_values_ >; _cli_options_map_["--include-with-brackets"] = - &::cli::thunk< options, bool, &options::include_with_brackets_ >; + &::cli::thunk< options, &options::include_with_brackets_ >; _cli_options_map_["--include-prefix"] = &::cli::thunk< options, std::string, &options::include_prefix_, &options::include_prefix_specified_ >; diff --git a/cli/cli/options.hxx b/cli/cli/pregenerated/cli/options.hxx index b54d81f..f44258f 100644 --- a/cli/cli/options.hxx +++ b/cli/cli/pregenerated/cli/options.hxx @@ -236,6 +236,14 @@ namespace cli // for the two previous arguments up until a call to a third // peek() or next(). // + // The position() function returns a monotonically-increasing + // number which, if stored, can later be used to determine the + // relative position of the argument returned by the following + // call to next(). Note that if multiple scanners are used to + // extract arguments from multiple sources, then the end + // position of the previous scanner should be used as the + // start position of the next. + // class scanner { public: @@ -253,13 +261,24 @@ namespace cli virtual void skip () = 0; + + virtual std::size_t + position () = 0; }; class argv_scanner: public scanner { public: - argv_scanner (int& argc, char** argv, bool erase = false); - argv_scanner (int start, int& argc, char** argv, bool erase = false); + argv_scanner (int& argc, + char** argv, + bool erase = false, + std::size_t start_position = 0); + + argv_scanner (int start, + int& argc, + char** argv, + bool erase = false, + std::size_t start_position = 0); int end () const; @@ -276,7 +295,11 @@ namespace cli virtual void skip (); - private: + virtual std::size_t + position (); + + protected: + std::size_t start_position_; int i_; int& argc_; char** argv_; @@ -289,16 +312,19 @@ namespace cli argv_file_scanner (int& argc, char** argv, const std::string& option, - bool erase = false); + bool erase = false, + std::size_t start_position = 0); argv_file_scanner (int start, int& argc, char** argv, const std::string& option, - bool erase = false); + bool erase = false, + std::size_t start_position = 0); argv_file_scanner (const std::string& file, - const std::string& option); + const std::string& option, + std::size_t start_position = 0); struct option_info { @@ -315,18 +341,21 @@ namespace cli char** argv, const option_info* options, std::size_t options_count, - bool erase = false); + bool erase = false, + std::size_t start_position = 0); argv_file_scanner (int start, int& argc, char** argv, const option_info* options, std::size_t options_count, - bool erase = false); + bool erase = false, + std::size_t start_position = 0); argv_file_scanner (const std::string& file, const option_info* options = 0, - std::size_t options_count = 0); + std::size_t options_count = 0, + std::size_t start_position = 0); virtual bool more (); @@ -340,6 +369,9 @@ namespace cli virtual void skip (); + virtual std::size_t + position (); + // Return the file path if the peeked at argument came from a file and // the empty string otherwise. The reference is guaranteed to be valid // till the end of the scanner lifetime. @@ -641,6 +673,21 @@ class options void ostream_type_specified (bool); + const std::string& + export_symbol () const; + + std::string& + export_symbol (); + + void + export_symbol (const std::string&); + + bool + export_symbol_specified () const; + + void + export_symbol_specified (bool); + const bool& generate_cxx () const; @@ -678,6 +725,15 @@ class options generate_txt (const bool&); const bool& + generate_dep () const; + + bool& + generate_dep (); + + void + generate_dep (const bool&); + + const bool& stdout_ () const; bool& @@ -753,6 +809,15 @@ class options option_length_specified (bool); const bool& + ascii_tree () const; + + bool& + ascii_tree (); + + void + ascii_tree (const bool&); + + const bool& ansi_color () const; bool& @@ -1353,6 +1418,36 @@ class options txt_suffix_specified (bool); const std::string& + dep_suffix () const; + + std::string& + dep_suffix (); + + void + dep_suffix (const std::string&); + + bool + dep_suffix_specified () const; + + void + dep_suffix_specified (bool); + + const std::string& + dep_file () const; + + std::string& + dep_file (); + + void + dep_file (const std::string&); + + bool + dep_file_specified () const; + + void + dep_file_specified (bool); + + const std::string& option_prefix () const; std::string& @@ -1521,10 +1616,13 @@ class options bool cli_namespace_specified_; std::string ostream_type_; bool ostream_type_specified_; + std::string export_symbol_; + bool export_symbol_specified_; bool generate_cxx_; bool generate_man_; bool generate_html_; bool generate_txt_; + bool generate_dep_; bool stdout__; bool suppress_undocumented_; bool suppress_usage_; @@ -1534,6 +1632,7 @@ class options bool page_usage_specified_; std::size_t option_length_; bool option_length_specified_; + bool ascii_tree_; bool ansi_color_; bool exclude_base_; bool include_base_last_; @@ -1613,6 +1712,10 @@ class options bool html_suffix_specified_; std::string txt_suffix_; bool txt_suffix_specified_; + std::string dep_suffix_; + bool dep_suffix_specified_; + std::string dep_file_; + bool dep_file_specified_; std::string option_prefix_; bool option_prefix_specified_; std::string option_separator_; diff --git a/cli/cli/options.ixx b/cli/cli/pregenerated/cli/options.ixx index 4461340..54fa54a 100644 --- a/cli/cli/options.ixx +++ b/cli/cli/pregenerated/cli/options.ixx @@ -141,14 +141,29 @@ namespace cli // argv_scanner // inline argv_scanner:: - argv_scanner (int& argc, char** argv, bool erase) - : i_ (1), argc_ (argc), argv_ (argv), erase_ (erase) + argv_scanner (int& argc, + char** argv, + bool erase, + std::size_t sp) + : start_position_ (sp + 1), + i_ (1), + argc_ (argc), + argv_ (argv), + erase_ (erase) { } inline argv_scanner:: - argv_scanner (int start, int& argc, char** argv, bool erase) - : i_ (start), argc_ (argc), argv_ (argv), erase_ (erase) + argv_scanner (int start, + int& argc, + char** argv, + bool erase, + std::size_t sp) + : start_position_ (sp + static_cast<std::size_t> (start)), + i_ (start), + argc_ (argc), + argv_ (argv), + erase_ (erase) { } @@ -164,8 +179,9 @@ namespace cli argv_file_scanner (int& argc, char** argv, const std::string& option, - bool erase) - : argv_scanner (argc, argv, erase), + bool erase, + std::size_t sp) + : argv_scanner (argc, argv, erase, sp), option_ (option), options_ (&option_info_), options_count_ (1), @@ -181,8 +197,9 @@ namespace cli int& argc, char** argv, const std::string& option, - bool erase) - : argv_scanner (start, argc, argv, erase), + bool erase, + std::size_t sp) + : argv_scanner (start, argc, argv, erase, sp), option_ (option), options_ (&option_info_), options_count_ (1), @@ -195,8 +212,9 @@ namespace cli inline argv_file_scanner:: argv_file_scanner (const std::string& file, - const std::string& option) - : argv_scanner (0, zero_argc_, 0), + const std::string& option, + std::size_t sp) + : argv_scanner (0, zero_argc_, 0, sp), option_ (option), options_ (&option_info_), options_count_ (1), @@ -214,8 +232,9 @@ namespace cli char** argv, const option_info* options, std::size_t options_count, - bool erase) - : argv_scanner (argc, argv, erase), + bool erase, + std::size_t sp) + : argv_scanner (argc, argv, erase, sp), options_ (options), options_count_ (options_count), i_ (1), @@ -229,8 +248,9 @@ namespace cli char** argv, const option_info* options, std::size_t options_count, - bool erase) - : argv_scanner (start, argc, argv, erase), + bool erase, + std::size_t sp) + : argv_scanner (start, argc, argv, erase, sp), options_ (options), options_count_ (options_count), i_ (1), @@ -241,8 +261,9 @@ namespace cli inline argv_file_scanner:: argv_file_scanner (const std::string& file, const option_info* options, - std::size_t options_count) - : argv_scanner (0, zero_argc_, 0), + std::size_t options_count, + std::size_t sp) + : argv_scanner (0, zero_argc_, 0, sp), options_ (options), options_count_ (options_count), i_ (1), @@ -651,6 +672,36 @@ ostream_type_specified (bool x) this->ostream_type_specified_ = x; } +inline const std::string& options:: +export_symbol () const +{ + return this->export_symbol_; +} + +inline std::string& options:: +export_symbol () +{ + return this->export_symbol_; +} + +inline void options:: +export_symbol (const std::string& x) +{ + this->export_symbol_ = x; +} + +inline bool options:: +export_symbol_specified () const +{ + return this->export_symbol_specified_; +} + +inline void options:: +export_symbol_specified (bool x) +{ + this->export_symbol_specified_ = x; +} + inline const bool& options:: generate_cxx () const { @@ -724,6 +775,24 @@ generate_txt (const bool& x) } inline const bool& options:: +generate_dep () const +{ + return this->generate_dep_; +} + +inline bool& options:: +generate_dep () +{ + return this->generate_dep_; +} + +inline void options:: +generate_dep (const bool& x) +{ + this->generate_dep_ = x; +} + +inline const bool& options:: stdout_ () const { return this->stdout__; @@ -874,6 +943,24 @@ option_length_specified (bool x) } inline const bool& options:: +ascii_tree () const +{ + return this->ascii_tree_; +} + +inline bool& options:: +ascii_tree () +{ + return this->ascii_tree_; +} + +inline void options:: +ascii_tree (const bool& x) +{ + this->ascii_tree_ = x; +} + +inline const bool& options:: ansi_color () const { return this->ansi_color_; @@ -2074,6 +2161,66 @@ txt_suffix_specified (bool x) } inline const std::string& options:: +dep_suffix () const +{ + return this->dep_suffix_; +} + +inline std::string& options:: +dep_suffix () +{ + return this->dep_suffix_; +} + +inline void options:: +dep_suffix (const std::string& x) +{ + this->dep_suffix_ = x; +} + +inline bool options:: +dep_suffix_specified () const +{ + return this->dep_suffix_specified_; +} + +inline void options:: +dep_suffix_specified (bool x) +{ + this->dep_suffix_specified_ = x; +} + +inline const std::string& options:: +dep_file () const +{ + return this->dep_file_; +} + +inline std::string& options:: +dep_file () +{ + return this->dep_file_; +} + +inline void options:: +dep_file (const std::string& x) +{ + this->dep_file_ = x; +} + +inline bool options:: +dep_file_specified () const +{ + return this->dep_file_specified_; +} + +inline void options:: +dep_file_specified (bool x) +{ + this->dep_file_specified_ = x; +} + +inline const std::string& options:: option_prefix () const { return this->option_prefix_; diff --git a/cli/cli/runtime-header.cxx b/cli/cli/runtime-header.cxx index adf70dd..1003e91 100644 --- a/cli/cli/runtime-header.cxx +++ b/cli/cli/runtime-header.cxx @@ -43,10 +43,13 @@ generate_runtime_header (context& ctx) ctx.ns_open (ctx.cli); + string const& exp (ctx.exp); + string const& exp_inl (ctx.exp_inl); + // usage_para // if (!ctx.options.suppress_usage ()) - os << "class usage_para" + os << "class " << exp_inl << "usage_para" << "{" << "public:" << endl << "enum value" @@ -64,7 +67,7 @@ generate_runtime_header (context& ctx) // unknown_mode // - os << "class unknown_mode" + os << "class " << exp_inl << "unknown_mode" << "{" << "public:" << endl << "enum value" @@ -85,26 +88,30 @@ generate_runtime_header (context& ctx) string const& os_type (ctx.options.ostream_type ()); + const char* nothrow (ctx.options.std () < cxx_version::cxx11 + ? "throw ()" + : "noexcept"); + os << "// Exceptions." << endl << "//" << endl << endl; - os << "class exception: public std::exception" + os << "class " << exp << "exception: public std::exception" << "{" << "public:" << endl << "virtual void" << endl << "print (" << os_type << "&) const = 0;" << "};"; - os << os_type << "&" << endl + os << exp_inl << os_type << "&" << endl << "operator<< (" << os_type << "&, const exception&);" << endl; - os << "class unknown_option: public exception" + os << "class " << exp << "unknown_option: public exception" << "{" << "public:" << endl << "virtual" << endl - << "~unknown_option () throw ();" + << "~unknown_option () " << nothrow << ';' << endl << "unknown_option (const std::string& option);" << endl @@ -115,17 +122,17 @@ generate_runtime_header (context& ctx) << "print (" << os_type << "&) const;" << endl << "virtual const char*" << endl - << "what () const throw ();" + << "what () const " << nothrow << ';' << endl << "private:" << endl << "std::string option_;" << "};"; - os << "class unknown_argument: public exception" + os << "class " << exp << "unknown_argument: public exception" << "{" << "public:" << endl << "virtual" << endl - << "~unknown_argument () throw ();" + << "~unknown_argument () " << nothrow << ';' << endl << "unknown_argument (const std::string& argument);" << endl @@ -136,17 +143,17 @@ generate_runtime_header (context& ctx) << "print (" << os_type << "&) const;" << endl << "virtual const char*" << endl - << "what () const throw ();" + << "what () const " << nothrow << ';' << endl << "private:" << endl << "std::string argument_;" << "};"; - os << "class missing_value: public exception" + os << "class " << exp << "missing_value: public exception" << "{" << "public:" << endl << "virtual" << endl - << "~missing_value () throw ();" + << "~missing_value () " << nothrow << ';' << endl << "missing_value (const std::string& option);" << endl @@ -157,17 +164,17 @@ generate_runtime_header (context& ctx) << "print (" << os_type << "&) const;" << endl << "virtual const char*" << endl - << "what () const throw ();" + << "what () const " << nothrow << ';' << endl << "private:" << endl << "std::string option_;" << "};"; - os << "class invalid_value: public exception" + os << "class " << exp << "invalid_value: public exception" << "{" << "public:" << endl << "virtual" << endl - << "~invalid_value () throw ();" + << "~invalid_value () " << nothrow << ';' << endl << "invalid_value (const std::string& option," << endl << "const std::string& value," << endl @@ -186,7 +193,7 @@ generate_runtime_header (context& ctx) << "print (" << os_type << "&) const;" << endl << "virtual const char*" << endl - << "what () const throw ();" + << "what () const " << nothrow << ';' << endl << "private:" << endl << "std::string option_;" @@ -194,23 +201,23 @@ generate_runtime_header (context& ctx) << "std::string message_;" << "};"; - os << "class eos_reached: public exception" + os << "class " << exp << "eos_reached: public exception" << "{" << "public:" << endl << "virtual void" << endl << "print (" << os_type << "&) const;" << endl << "virtual const char*" << endl - << "what () const throw ();" + << "what () const " << nothrow << ';' << "};"; if (ctx.options.generate_file_scanner ()) { - os << "class file_io_failure: public exception" + os << "class " << exp << "file_io_failure: public exception" << "{" << "public:" << endl << "virtual" << endl - << "~file_io_failure () throw ();" + << "~file_io_failure () " << nothrow << ';' << endl << "file_io_failure (const std::string& file);" << endl @@ -221,17 +228,17 @@ generate_runtime_header (context& ctx) << "print (" << os_type << "&) const;" << endl << "virtual const char*" << endl - << "what () const throw ();" + << "what () const " << nothrow << ';' << endl << "private:" << endl << "std::string file_;" << "};"; - os << "class unmatched_quote: public exception" + os << "class " << exp << "unmatched_quote: public exception" << "{" << "public:" << endl << "virtual" << endl - << "~unmatched_quote () throw ();" + << "~unmatched_quote () " << nothrow << ';' << endl << "unmatched_quote (const std::string& argument);" << endl @@ -242,7 +249,7 @@ generate_runtime_header (context& ctx) << "print (" << os_type << "&) const;" << endl << "virtual const char*" << endl - << "what () const throw ();" + << "what () const " << nothrow << ';' << endl << "private:" << endl << "std::string argument_;" @@ -251,11 +258,11 @@ generate_runtime_header (context& ctx) if (ctx.options.generate_group_scanner ()) { - os << "class unexpected_group: public exception" + os << "class " << exp << "unexpected_group: public exception" << "{" << "public:" << endl << "virtual" << endl - << "~unexpected_group () throw ();" + << "~unexpected_group () " << nothrow << ';' << endl << "unexpected_group (const std::string& argument," << endl << "const std::string& group);" @@ -270,18 +277,18 @@ generate_runtime_header (context& ctx) << "print (std::ostream&) const;" << endl << "virtual const char*" << endl - << "what () const throw ();" + << "what () const " << nothrow << ';' << endl << "private:" << endl << "std::string argument_;" << "std::string group_;" << "};"; - os << "class group_separator: public exception" << endl + os << "class " << exp << "group_separator: public exception" << endl << "{" << "public:" << endl << "virtual" << endl - << "~group_separator () throw ();" + << "~group_separator () " << nothrow << ';' << endl << "// Note: either (but not both) can be empty." << endl << "//" << endl @@ -298,7 +305,7 @@ generate_runtime_header (context& ctx) << "print (std::ostream&) const;" << endl << "virtual const char*" << endl - << "what () const throw ();" + << "what () const " << nothrow << ';' << endl << "private:" << endl << "std::string encountered_;" @@ -308,13 +315,21 @@ generate_runtime_header (context& ctx) // scanner // - os << "// Command line argument scanner interface." << endl - << "//" << endl - << "// The values returned by next() are guaranteed to be valid" << endl - << "// for the two previous arguments up until a call to a third" << endl - << "// peek() or next()." << endl - << "//" << endl - << "class scanner" + os << "// Command line argument scanner interface." << endl + << "//" << endl + << "// The values returned by next() are guaranteed to be valid" << endl + << "// for the two previous arguments up until a call to a third" << endl + << "// peek() or next()." << endl + << "//" << endl + << "// The position() function returns a monotonically-increasing" << endl + << "// number which, if stored, can later be used to determine the"<< endl + << "// relative position of the argument returned by the following"<< endl + << "// call to next(). Note that if multiple scanners are used to" << endl + << "// extract arguments from multiple sources, then the end" << endl + << "// position of the previous scanner should be used as the" << endl + << "// start position of the next." << endl + << "//" << endl + << "class " << exp << "scanner" << "{" << "public:" << endl << "virtual" << endl @@ -331,15 +346,26 @@ generate_runtime_header (context& ctx) << endl << "virtual void" << endl << "skip () = 0;" + << endl + << "virtual std::size_t" << endl + << "position () = 0;" << "};"; // argv_scanner // - os << "class argv_scanner: public scanner" + os << "class " << exp << "argv_scanner: public scanner" << "{" << "public:" << endl - << "argv_scanner (int& argc, char** argv, bool erase = false);" - << "argv_scanner (int start, int& argc, char** argv, bool erase = false);" + << "argv_scanner (int& argc," << endl + << "char** argv," << endl + << "bool erase = false," << endl + << "std::size_t start_position = 0);" + << endl + << "argv_scanner (int start," << endl + << "int& argc," << endl + << "char** argv," << endl + << "bool erase = false," << endl + << "std::size_t start_position = 0);" << endl << "int" << endl << "end () const;" @@ -356,7 +382,11 @@ generate_runtime_header (context& ctx) << "virtual void" << endl << "skip ();" << endl - << "private:" << endl + << "virtual std::size_t" << endl + << "position ();" + << endl + << "protected:" << endl + << "std::size_t start_position_;" << "int i_;" << "int& argc_;" << "char** argv_;" @@ -367,17 +397,18 @@ generate_runtime_header (context& ctx) // if (ctx.options.generate_vector_scanner ()) { - os << "class vector_scanner: public scanner" + os << "class " << exp << "vector_scanner: public scanner" << "{" << "public:" << endl - << "vector_scanner (const std::vector<std::string>&, " << - "std::size_t start = 0);" + << "vector_scanner (const std::vector<std::string>&," << endl + << "std::size_t start = 0," << endl + << "std::size_t start_position = 0);" << endl << "std::size_t" << endl << "end () const;" << endl << "void" << endl - << "reset (std::size_t start = 0);" + << "reset (std::size_t start = 0, std::size_t start_position = 0);" << endl << "virtual bool" << endl << "more ();" @@ -391,7 +422,11 @@ generate_runtime_header (context& ctx) << "virtual void" << endl << "skip ();" << endl + << "virtual std::size_t" << endl + << "position ();" + << endl << "private:" << endl + << "std::size_t start_position_;" << "const std::vector<std::string>& v_;" << "std::size_t i_;" << "};"; @@ -401,22 +436,25 @@ generate_runtime_header (context& ctx) // if (ctx.options.generate_file_scanner ()) { - os << "class argv_file_scanner: public argv_scanner" + os << "class " << exp << "argv_file_scanner: public argv_scanner" << "{" << "public:" << endl << "argv_file_scanner (int& argc," << endl << "char** argv," << endl << "const std::string& option," << endl - << "bool erase = false);" + << "bool erase = false," << endl + << "std::size_t start_position = 0);" << endl << "argv_file_scanner (int start," << endl << "int& argc," << endl << "char** argv," << endl << "const std::string& option," << endl - << "bool erase = false);" + << "bool erase = false," << endl + << "std::size_t start_position = 0);" << endl << "argv_file_scanner (const std::string& file," << endl - << "const std::string& option);" + << "const std::string& option," << endl + << "std::size_t start_position = 0);" << endl << "struct option_info" << "{" @@ -432,18 +470,21 @@ generate_runtime_header (context& ctx) << "char** argv," << endl << "const option_info* options," << endl << "std::size_t options_count," << endl - << "bool erase = false);" + << "bool erase = false," << endl + << "std::size_t start_position = 0);" << endl << "argv_file_scanner (int start," << endl << "int& argc," << endl << "char** argv," << endl << "const option_info* options," << endl << "std::size_t options_count," << endl - << "bool erase = false);" + << "bool erase = false," << endl + << "std::size_t start_position = 0);" << endl << "argv_file_scanner (const std::string& file," << endl << "const option_info* options = 0," << endl - << "std::size_t options_count = 0);" + << "std::size_t options_count = 0," << endl + << "std::size_t start_position = 0);" << endl << "virtual bool" << endl << "more ();" @@ -457,6 +498,9 @@ generate_runtime_header (context& ctx) << "virtual void" << endl << "skip ();" << endl + << "virtual std::size_t" << endl + << "position ();" + << endl << "// Return the file path if the peeked at argument came from a file and" << endl << "// the empty string otherwise. The reference is guaranteed to be valid" << endl << "// till the end of the scanner lifetime." << endl @@ -512,7 +556,7 @@ generate_runtime_header (context& ctx) // if (ctx.options.generate_group_scanner ()) { - os << "class group_scanner: public scanner" + os << "class " << exp << "group_scanner: public scanner" << "{" << "public:" << endl << "group_scanner (scanner&);" @@ -529,12 +573,18 @@ generate_runtime_header (context& ctx) << "virtual void" << endl << "skip ();" << endl + << "virtual std::size_t" << endl + << "position ();" + << endl << "// The group is only available after the call to next()" << endl << "// (and skip() -- in case one needs to make sure the group" << endl << "// was empty, or some such) and is only valid (and must be" << endl << "// handled) until the next call to any of the scanner" << endl << "// functions (including more())." << endl << "//" << endl + << "// Note also that argument positions within each group start"<< endl + << "// from 0." << endl + << "//" << endl << "scanner&" << endl << "group ();" << endl @@ -562,21 +612,20 @@ generate_runtime_header (context& ctx) << "static separator" << endl << "sense (const char*);" << endl - << "// If the state is scanned or skipped, then scan the" << endl - << "// leading groups and save the next (unescaped) argument in" << endl - << "// arg_. If the state is peeked, then scan the trailing" << endl - << "// groups. In both cases set the new state." << endl + + << "// Scan the leading groups, the next argument/argument pack,"<< endl + << "// and the trailing groups." << endl << "//" << endl << "void" << endl - << "scan_group (state);" + << "scan_group ();" << endl << "scanner& scan_;" << "state state_;" << endl << "// Circular buffer of two arguments." << endl << "//" << endl - << "std::string arg_[2];" - << "std::size_t i_;" + << "std::vector<std::string> arg_[2];" + << "std::size_t i_, j_, pos_;" << endl << "std::vector<std::string> group_;" << "vector_scanner group_scan_;" @@ -590,7 +639,7 @@ generate_runtime_header (context& ctx) os << "typedef std::vector<std::string> option_names;" << endl; - os << "class option" + os << "class " << exp_inl << "option" << "{" << "public:" << endl << endl @@ -620,7 +669,7 @@ generate_runtime_header (context& ctx) << "std::string default_value_;" << "};"; - os << "class options: public std::vector<option>" + os << "class " << exp << "options: public std::vector<option>" << "{" << "public:" << endl << "typedef std::vector<option> container_type;" diff --git a/cli/cli/runtime-inline.cxx b/cli/cli/runtime-inline.cxx index 8f0e84c..ce18e92 100644 --- a/cli/cli/runtime-inline.cxx +++ b/cli/cli/runtime-inline.cxx @@ -232,14 +232,29 @@ generate_runtime_inline (context& ctx) << "//" << endl; os << inl << "argv_scanner::" << endl - << "argv_scanner (int& argc, char** argv, bool erase)" << endl - << ": i_ (1), argc_ (argc), argv_ (argv), erase_ (erase)" + << "argv_scanner (int& argc," << endl + << "char** argv," << endl + << "bool erase," << endl + << "std::size_t sp)" << endl + << ": start_position_ (sp + 1)," << endl + << " i_ (1)," << endl + << " argc_ (argc)," << endl + << " argv_ (argv)," << endl + << " erase_ (erase)" << "{" << "}"; os << inl << "argv_scanner::" << endl - << "argv_scanner (int start, int& argc, char** argv, bool erase)" << endl - << ": i_ (start), argc_ (argc), argv_ (argv), erase_ (erase)" + << "argv_scanner (int start," << endl + << "int& argc," << endl + << "char** argv," << endl + << "bool erase," << endl + << "std::size_t sp)" << endl + << ": start_position_ (sp + static_cast<std::size_t> (start))," << endl + << " i_ (start)," << endl + << " argc_ (argc)," << endl + << " argv_ (argv)," << endl + << " erase_ (erase)" << "{" << "}"; @@ -257,9 +272,10 @@ generate_runtime_inline (context& ctx) << "//" << endl; os << inl << "vector_scanner::" << endl - << "vector_scanner (const std::vector<std::string>& v, " << - "std::size_t i)" << endl - << ": v_ (v), i_ (i)" + << "vector_scanner (const std::vector<std::string>& v," << endl + << "std::size_t i," << endl + << "std::size_t sp)" << endl + << ": start_position_ (sp), v_ (v), i_ (i)" << "{" << "}"; @@ -270,9 +286,10 @@ generate_runtime_inline (context& ctx) << "}"; os << inl << "void vector_scanner::" << endl - << "reset (std::size_t i)" + << "reset (std::size_t i, std::size_t sp)" << "{" << "i_ = i;" + << "start_position_ = sp;" << "}"; } @@ -289,8 +306,9 @@ generate_runtime_inline (context& ctx) << "argv_file_scanner (int& argc," << endl << "char** argv," << endl << "const std::string& option," << endl - << "bool erase)" << endl - << ": argv_scanner (argc, argv, erase)," << endl + << "bool erase," << endl + << "std::size_t sp)" << endl + << ": argv_scanner (argc, argv, erase, sp)," << endl << " option_ (option)," << endl << " options_ (&option_info_)," << endl << " options_count_ (1)," << endl @@ -308,8 +326,9 @@ generate_runtime_inline (context& ctx) << "int& argc," << endl << "char** argv," << endl << "const std::string& option," << endl - << "bool erase)" << endl - << ": argv_scanner (start, argc, argv, erase)," << endl + << "bool erase," << endl + << "std::size_t sp)" << endl + << ": argv_scanner (start, argc, argv, erase, sp)," << endl << " option_ (option)," << endl << " options_ (&option_info_)," << endl << " options_count_ (1)," << endl @@ -324,8 +343,9 @@ generate_runtime_inline (context& ctx) os << inl << "argv_file_scanner::" << endl << "argv_file_scanner (const std::string& file," << endl - << "const std::string& option)" << endl - << ": argv_scanner (0, zero_argc_, 0)," << endl + << "const std::string& option," << endl + << "std::size_t sp)" << endl + << ": argv_scanner (0, zero_argc_, 0, sp)," << endl << " option_ (option)," << endl << " options_ (&option_info_)," << endl << " options_count_ (1)," << endl @@ -345,8 +365,9 @@ generate_runtime_inline (context& ctx) << "char** argv," << endl << "const option_info* options," << endl << "std::size_t options_count," << endl - << "bool erase)" << endl - << ": argv_scanner (argc, argv, erase)," << endl + << "bool erase," << endl + << "std::size_t sp)" << endl + << ": argv_scanner (argc, argv, erase, sp)," << endl << " options_ (options)," << endl << " options_count_ (options_count)," << endl << " i_ (1)"; @@ -362,8 +383,9 @@ generate_runtime_inline (context& ctx) << "char** argv," << endl << "const option_info* options," << endl << "std::size_t options_count," << endl - << "bool erase)" << endl - << ": argv_scanner (start, argc, argv, erase)," << endl + << "bool erase," << endl + << "std::size_t sp)" << endl + << ": argv_scanner (start, argc, argv, erase, sp)," << endl << " options_ (options)," << endl << " options_count_ (options_count)," << endl << " i_ (1)"; @@ -376,8 +398,9 @@ generate_runtime_inline (context& ctx) os << inl << "argv_file_scanner::" << endl << "argv_file_scanner (const std::string& file," << endl << "const option_info* options," << endl - << "std::size_t options_count)" << endl - << ": argv_scanner (0, zero_argc_, 0)," << endl + << "std::size_t options_count," << endl + << "std::size_t sp)" << endl + << ": argv_scanner (0, zero_argc_, 0, sp)," << endl << " options_ (options)," << endl << " options_count_ (options_count)," << endl << " i_ (1)"; @@ -398,7 +421,7 @@ generate_runtime_inline (context& ctx) << inl << "group_scanner::" << endl << "group_scanner (scanner& s)" << endl - << ": scan_ (s), state_ (skipped), i_ (1), group_scan_ (group_)" + << ": scan_ (s), state_ (skipped), i_ (1), j_ (0), group_scan_ (group_)" << "{" << "}" diff --git a/cli/cli/runtime-source.cxx b/cli/cli/runtime-source.cxx index 3864163..11cd746 100644 --- a/cli/cli/runtime-source.cxx +++ b/cli/cli/runtime-source.cxx @@ -15,12 +15,13 @@ generate_runtime_source (context& ctx, bool complete) << "#include <set>" << endl << "#include <string>" << endl << "#include <vector>" << endl + << "#include <utility>" << endl // pair << "#include <ostream>" << endl - << "#include <sstream>" << endl; + << "#include <sstream>" << endl + << "#include <cstring>" << endl; if (complete && ctx.options.generate_file_scanner ()) - os << "#include <cstring>" << endl - << "#include <fstream>" << endl; + os << "#include <fstream>" << endl; os << endl; @@ -30,12 +31,16 @@ generate_runtime_source (context& ctx, bool complete) { string const& os_type (ctx.options.ostream_type ()); + const char* nothrow (ctx.options.std () < cxx_version::cxx11 + ? "throw ()" + : "noexcept"); + // unknown_option // os << "// unknown_option" << endl << "//" << endl << "unknown_option::" << endl - << "~unknown_option () throw ()" + << "~unknown_option () " << nothrow << "{" << "}" @@ -46,7 +51,7 @@ generate_runtime_source (context& ctx, bool complete) << "}" << "const char* unknown_option::" << endl - << "what () const throw ()" + << "what () const " << nothrow << "{" << "return \"unknown option\";" << "}"; @@ -56,7 +61,7 @@ generate_runtime_source (context& ctx, bool complete) os << "// unknown_argument" << endl << "//" << endl << "unknown_argument::" << endl - << "~unknown_argument () throw ()" + << "~unknown_argument () " << nothrow << "{" << "}" @@ -67,7 +72,7 @@ generate_runtime_source (context& ctx, bool complete) << "}" << "const char* unknown_argument::" << endl - << "what () const throw ()" + << "what () const " << nothrow << "{" << "return \"unknown argument\";" << "}"; @@ -77,7 +82,7 @@ generate_runtime_source (context& ctx, bool complete) os << "// missing_value" << endl << "//" << endl << "missing_value::" << endl - << "~missing_value () throw ()" + << "~missing_value () " << nothrow << "{" << "}" @@ -88,7 +93,7 @@ generate_runtime_source (context& ctx, bool complete) << "}" << "const char* missing_value::" << endl - << "what () const throw ()" + << "what () const " << nothrow << "{" << "return \"missing option value\";" << "}"; @@ -98,7 +103,7 @@ generate_runtime_source (context& ctx, bool complete) os << "// invalid_value" << endl << "//" << endl << "invalid_value::" << endl - << "~invalid_value () throw ()" + << "~invalid_value () " << nothrow << "{" << "}" @@ -114,7 +119,7 @@ generate_runtime_source (context& ctx, bool complete) << "}" << "const char* invalid_value::" << endl - << "what () const throw ()" + << "what () const " << nothrow << "{" << "return \"invalid option value\";" << "}"; @@ -130,7 +135,7 @@ generate_runtime_source (context& ctx, bool complete) << "}" << "const char* eos_reached::" << endl - << "what () const throw ()" + << "what () const " << nothrow << "{" << "return \"end of argument stream reached\";" << "}"; @@ -142,7 +147,7 @@ generate_runtime_source (context& ctx, bool complete) os << "// file_io_failure" << endl << "//" << endl << "file_io_failure::" << endl - << "~file_io_failure () throw ()" + << "~file_io_failure () " << nothrow << "{" << "}" @@ -154,7 +159,7 @@ generate_runtime_source (context& ctx, bool complete) << "}" << "const char* file_io_failure::" << endl - << "what () const throw ()" + << "what () const " << nothrow << "{" << "return \"unable to open file or read failure\";" << "}"; @@ -164,7 +169,7 @@ generate_runtime_source (context& ctx, bool complete) os << "// unmatched_quote" << endl << "//" << endl << "unmatched_quote::" << endl - << "~unmatched_quote () throw ()" + << "~unmatched_quote () " << nothrow << "{" << "}" @@ -176,7 +181,7 @@ generate_runtime_source (context& ctx, bool complete) << "}" << "const char* unmatched_quote::" << endl - << "what () const throw ()" + << "what () const " << nothrow << "{" << "return \"unmatched quote\";" << "}"; @@ -190,7 +195,7 @@ generate_runtime_source (context& ctx, bool complete) << "//" << endl << "unexpected_group::" << endl - << "~unexpected_group () throw ()" + << "~unexpected_group () " << nothrow << "{" << "}" @@ -202,7 +207,7 @@ generate_runtime_source (context& ctx, bool complete) << "}" << "const char* unexpected_group::" << endl - << "what () const throw ()" + << "what () const " << nothrow << "{" << "return \"unexpected grouped argument\";" << "}"; @@ -213,7 +218,7 @@ generate_runtime_source (context& ctx, bool complete) << "//" << endl << "group_separator::" << endl - << "~group_separator () throw ()" + << "~group_separator () " << nothrow << "{" << "}" @@ -237,7 +242,7 @@ generate_runtime_source (context& ctx, bool complete) << "}" << "const char* group_separator::" << endl - << "what () const throw ()" + << "what () const " << nothrow << "{" << "bool ex (!expected_.empty ());" << "bool en (!encountered_.empty ());" @@ -259,6 +264,10 @@ generate_runtime_source (context& ctx, bool complete) // argv_scanner // + // Note that due to the erase logic we cannot just add i_ to + // start_position and so have to increment it instead. See also + // argv_file_scanner that continues with this logic. + // os << "// argv_scanner" << endl << "//" << endl @@ -295,6 +304,7 @@ generate_runtime_source (context& ctx, bool complete) << "else" << endl << "++i_;" << endl + << "++start_position_;" << "return r;" << "}" << "else" << endl @@ -304,10 +314,19 @@ generate_runtime_source (context& ctx, bool complete) << "void argv_scanner::" << endl << "skip ()" << "{" - << "if (i_ < argc_)" << endl + << "if (i_ < argc_)" + << "{" << "++i_;" + << "++start_position_;" + << "}" << "else" << endl << "throw eos_reached ();" + << "}" + + << "std::size_t argv_scanner::" << endl + << "position ()" + << "{" + << "return start_position_;" << "}"; // vector_scanner @@ -348,11 +367,19 @@ generate_runtime_source (context& ctx, bool complete) << "++i_;" << "else" << endl << "throw eos_reached ();" + << "}" + + << "std::size_t vector_scanner::" << endl + << "position ()" + << "{" + << "return start_position_ + i_;" << "}"; } // argv_file_scanner // + // Note that we continue incrementing start_position like argv_scanner. + // if (ctx.options.generate_file_scanner ()) { bool sep (!ctx.opt_sep.empty ()); @@ -486,6 +513,7 @@ generate_runtime_source (context& ctx, bool complete) << "{" << "hold_[i_ == 0 ? ++i_ : --i_].swap (args_.front ().value);" << "args_.pop_front ();" + << "++start_position_;" << "return hold_[i_].c_str ();" << "}" << "}" @@ -498,8 +526,11 @@ generate_runtime_source (context& ctx, bool complete) << endl << "if (args_.empty ())" << endl << "return base::skip ();" - << "else" << endl + << "else" + << "{" << "args_.pop_front ();" + << "++start_position_;" + << "}" << "}" << "const argv_file_scanner::option_info* argv_file_scanner::" << endl @@ -512,6 +543,12 @@ generate_runtime_source (context& ctx, bool complete) << "return 0;" << "}" + << "std::size_t argv_file_scanner::" << endl + << "position ()" + << "{" + << "return start_position_;" + << "}" + << "void argv_file_scanner::" << endl << "load (const std::string& file)" << "{" @@ -699,43 +736,50 @@ generate_runtime_source (context& ctx, bool complete) << "if (state_ == scanned)" << "{" << "if (group_scan_.end () != group_.size ())" << endl - << "throw unexpected_group (arg_[i_], group_scan_.next ());" + << "throw unexpected_group (arg_[i_][j_], group_scan_.next ());" << "}" - << "return scan_.more ();" + << "return j_ != 0 || scan_.more ();" << "}" << "const char* group_scanner::" << endl << "peek ()" << "{" - << "if (state_ != peeked)" << endl - << "scan_group (peeked);" - << "scan_.peek ();" + << "if (state_ != peeked)" + << "{" + << "scan_group ();" + << "state_ = peeked;" + << "}" << "// Return unescaped." << endl - << "return arg_[i_].c_str ();" + << "return arg_[i_][j_ - 1].c_str ();" << "}" << "const char* group_scanner::" << endl << "next ()" << "{" << "if (state_ != peeked)" << endl - << "scan_group (peeked);" - << "scan_.next ();" - << "scan_group (scanned);" + << "scan_group ();" + << "state_ = scanned;" << "// Return unescaped." << endl - << "return arg_[i_].c_str ();" + << "return arg_[i_][--j_].c_str ();" << "}" << "void group_scanner::" << endl << "skip ()" << "{" << "if (state_ != peeked)" << endl - << "scan_group (peeked);" - << "scan_.skip ();" - << "scan_group (skipped);" + << "scan_group ();" + << "state_ = skipped;" + << "--j_;" + << "}" + + << "std::size_t group_scanner::" << endl + << "position ()" + << "{" + << "return j_ == 0 ? scan_.position () : pos_ + (arg_[i_].size () - j_);" << "}" << "void group_scanner::" << endl - << "scan_group (state st)" + << "scan_group ()" << "{" << "// If the previous argument has been scanned, then make" << endl << "// sure the group has been scanned (handled) as well." << endl @@ -743,53 +787,66 @@ generate_runtime_source (context& ctx, bool complete) << "if (state_ == scanned)" << "{" << "if (group_scan_.end () != group_.size ())" << endl - << "throw unexpected_group (arg_[i_], group_scan_.next ());" + << "throw unexpected_group (arg_[i_][j_], group_scan_.next ());" << "}" - << "if (state_ != peeked)" + << "// If we still have arguments in the pack, rewind the group." << endl + << "//" << endl + << "if (j_ != 0)" << "{" - << "arg_[i_ == 0 ? ++i_ : --i_].clear ();" - << "group_.clear ();" << "group_scan_.reset ();" + << "return;" << "}" - << "// We recognize all group sequences both before and " << endl - << "// after the argument and diagnose any misuse. We may" << endl - << "// also have multiple groups:" << endl - << "//" << endl - << "// { -x }+ { -y }+ arg" << endl - << "//" << endl + // Position must remain the same from before the first call to peek() + // (comes directly from the scanner) and until next(). + // + // Note that while it may seem like a good idea to pass + // scan_.position() to reset() below, the trailing group positions + // will overlap with the argument's. So it seems best to start + // positions of each argument in a group from 0. + // + // Note also that we try hard not to throw away allocated memory in + // arg_[][0]. + // + << "i_ += (i_ == 0 ? 1 : -1);" + << "group_.clear ();" + << "group_scan_.reset ();" + << "pos_ = scan_.position ();" << endl - << "// Using group_ won't cover empty groups." << endl + + << "// Note: using group_ won't cover empty groups and using" << endl + << "// j_ won't cover single-argument packs." << endl << "//" << endl - << "bool g (false);" + << "bool group (false), pack (false);" << endl - << "while (scan_.more ())" + << "do" << "{" - << "const char* a (scan_.peek ());" + << "const char* a (scan_.next ());" << "size_t i (*a == '\\\\' ? 1 : 0);" << "separator s (sense (a + i));" << endl << "if (s == none || i != 0)" << "{" - << "if (state_ != peeked)" << endl - << "arg_[i_] = a + (s != none ? i : 0);" + << "if (arg_[i_].size () != 1)" << endl + << "arg_[i_].resize (1);" + << endl + << "arg_[i_][0] = a + (s != none ? i : 0);" + << "j_ = 1;" << "break;" << "}" - << "// Start of a leading group for the next argument." << endl + << "// Start of a leading group for the next argument or" << endl + << "// argument pack. We will only know which once we see" << endl + << "// the closing separator." << endl << "//" << endl - << "if (s == open && state_ == peeked)" << endl - << "break;" - << endl - << "if (s != (state_ == peeked ? open_plus : open))" << endl + << "if (s != open)" << endl << "throw group_separator (a, \"\");" << endl - << "g = true;" + << "size_t n (group_.size ());" << endl << "// Scan the group until the closing separator." << endl << "//" << endl - << "scan_.next ();" << "s = none;" << "while (s == none && scan_.more ())" << "{" @@ -804,20 +861,88 @@ generate_runtime_source (context& ctx, bool complete) << "}" << "}" - << "if (s != (state_ == peeked ? close : close_plus))" + << "if (s == close)" << "{" - << "throw group_separator ((s != none ? a : \"\")," << endl - << "(state_ == peeked ? \"}\" : \"}+\"));" + << "size_t m (group_.size ());" + << endl + << "j_ = m - n;" + << "if (j_ == 0)" << endl + << "throw group_separator (\"{\", \"\");" + << endl + << "if (arg_[i_].size () != j_)" << endl + << "arg_[i_].resize (j_);" + << endl + << "// Move from group_ to arg_. Add in reverse for ease " << endl + << "// of iteration." << endl + << "//" << endl + << "for (size_t j (0); j != j_; ++j)" << endl + << "arg_[i_][j] = group_[m - j - 1];" + << "group_.resize (n);" + << endl + << "pack = true;" + << "break;" << "}" + << "else if (s == close_plus)" << endl + << "group = true;" + << "else" << endl + << "throw group_separator ((s != none ? a : \"\"), \"}+\");" << "}" + << "while (scan_.more ());" + << endl << "// Handle the case where we have seen the leading group" << endl << "// but there are no more arguments." << endl << "//" << endl - << "if (g && state_ != peeked && !scan_.more ())" << endl + << "if (group && j_ == 0)" << endl << "throw group_separator (\"{\", \"\");" << endl - << "state_ = st;" + << "// Handle trailing groups, if any." << endl + << "//" << endl + + << "while (scan_.more ())" + << "{" + << "const char* a (scan_.peek ());" + << "size_t i (*a == '\\\\' ? 1 : 0);" + << "separator s (sense (a + i));" + << endl + + << "// Next argument, argument pack, or leading group." << endl + << "//" << endl + << "if (s == none || s == open || i != 0)" << endl + << "break;" + << endl + << "if (s != open_plus)" << endl + << "throw group_separator (a, \"\");" + << endl + << "group = true;" + << endl + << "// Scan the group until the closing separator." << endl + << "//" << endl + << "scan_.next ();" + << "s = none;" + << "while (s == none && scan_.more ())" + << "{" + << "a = scan_.next ();" + << "i = (*a == '\\\\' ? 1 : 0);" + << "s = sense (a + i);" + << endl + << "if (s == none || i != 0)" + << "{" + << "group_.push_back (a + (s != none ? i : 0));" + << "s = none;" + << "}" + << "}" + + << "if (s != close)" << endl + << "throw group_separator ((s != none ? a : \"\"), \"}\");" + << "}" + + << "// Handle the case where we have seen the argument pack" << endl + << "// without leading or trailing group." << endl + << "//" << endl + << "if (pack && !group)" << endl + << "throw group_separator (\"{\", \"\");" + << "}"; } @@ -890,11 +1015,33 @@ generate_runtime_source (context& ctx, bool complete) << "{"; os << "static void" << endl - << "parse (bool& x, scanner& s)" + << "parse (bool& x, " << (sp ? "bool& xs, " : "") << "scanner& s)" << "{" - << "s.next ();" - << "x = true;" - << "}"; + << "const char* o (s.next ());" + << endl + << "if (s.more ())" + << "{" + << "const char* v (s.next ());" + << endl + << "if (std::strcmp (v, \"1\") == 0 ||" << endl + << "std::strcmp (v, \"true\") == 0 ||" << endl + << "std::strcmp (v, \"TRUE\") == 0 ||" << endl + << "std::strcmp (v, \"True\") == 0)" << endl + << "x = true;" + << "else if (std::strcmp (v, \"0\") == 0 ||" << endl + << "std::strcmp (v, \"false\") == 0 ||" << endl + << "std::strcmp (v, \"FALSE\") == 0 ||" << endl + << "std::strcmp (v, \"False\") == 0)" << endl + << "x = false;" + << "else" << endl + << "throw invalid_value (o, v);" + << "}" + << "else" << endl + << "throw missing_value (o);"; + if (sp) + os << endl + << "xs = true;"; + os << "}"; if (gen_merge) os << "static void" << endl @@ -934,6 +1081,28 @@ generate_runtime_source (context& ctx, bool complete) os << "};"; + // parser<std::pair<X, std::size_t>> + // + os << "template <typename X>" << endl + << "struct parser<std::pair<X, std::size_t> >" + << "{"; + + os << "static void" << endl + << "parse (std::pair<X, std::size_t>& x, " << (sp ? "bool& xs, " : "") << "scanner& s)" + << "{" + << "x.second = s.position ();" + << "parser<X>::parse (x.first, " << (sp ? "xs, " : "") << "s);" + << "}"; + + if (gen_merge) + os << "static void" << endl + << "merge (std::pair<X, std::size_t>& b, const std::pair<X, std::size_t>& a)" + << "{" + << "b = a;" + << "}"; + + os << "};"; + // parser<std::vector<X>> // os << "template <typename X>" << endl @@ -1001,6 +1170,7 @@ generate_runtime_source (context& ctx, bool complete) << endl << "if (s.more ())" << "{" + << "std::size_t pos (s.position ());" << "std::string ov (s.next ());" << "std::string::size_type p = ov.find ('=');" << endl @@ -1020,13 +1190,13 @@ generate_runtime_source (context& ctx, bool complete) os << "if (!kstr.empty ())" << "{" << "av[1] = const_cast<char*> (kstr.c_str ());" - << "argv_scanner s (0, ac, av);" + << "argv_scanner s (0, ac, av, false, pos);" << "parser<K>::parse (k, " << (sp ? "dummy, " : "") << "s);" << "}" << "if (!vstr.empty ())" << "{" << "av[1] = const_cast<char*> (vstr.c_str ());" - << "argv_scanner s (0, ac, av);" + << "argv_scanner s (0, ac, av, false, pos);" << "parser<V>::parse (v, " << (sp ? "dummy, " : "") << "s);" << "}" << "m[k] = v;" @@ -1050,6 +1220,70 @@ generate_runtime_source (context& ctx, bool complete) os << "};"; + // parser<std::multimap<K,V,C>> + // + os << "template <typename K, typename V, typename C>" << endl + << "struct parser<std::multimap<K, V, C> >" + << "{"; + + os << "static void" << endl + << "parse (std::multimap<K, V, C>& m, " << (sp ? "bool& xs, " : "") << "scanner& s)" + << "{" + << "const char* o (s.next ());" + << endl + << "if (s.more ())" + << "{" + << "std::size_t pos (s.position ());" + << "std::string ov (s.next ());" + << "std::string::size_type p = ov.find ('=');" + << endl + << "K k = K ();" + << "V v = V ();" + << "std::string kstr (ov, 0, p);" + << "std::string vstr (ov, (p != std::string::npos ? p + 1 : ov.size ()));" + << endl + << "int ac (2);" + << "char* av[] =" + << "{" + << "const_cast<char*> (o)," << endl + << "0" + << "};"; + if (sp) + os << "bool dummy;"; + os << "if (!kstr.empty ())" + << "{" + << "av[1] = const_cast<char*> (kstr.c_str ());" + << "argv_scanner s (0, ac, av, false, pos);" + << "parser<K>::parse (k, " << (sp ? "dummy, " : "") << "s);" + << "}" + << "if (!vstr.empty ())" + << "{" + << "av[1] = const_cast<char*> (vstr.c_str ());" + << "argv_scanner s (0, ac, av, false, pos);" + << "parser<V>::parse (v, " << (sp ? "dummy, " : "") << "s);" + << "}" + << "m.insert (typename std::multimap<K, V, C>::value_type (k, v));" + << "}" + << "else" << endl + << "throw missing_value (o);"; + if (sp) + os << endl + << "xs = true;"; + os << "}"; + + if (gen_merge) + os << "static void" << endl + << "merge (std::multimap<K, V, C>& b, const std::multimap<K, V, C>& a)" + << "{" + << "for (typename std::multimap<K, V, C>::const_iterator i (a.begin ()); " << endl + << "i != a.end (); " << endl + << "++i)" << endl + << "b.insert (typename std::multimap<K, V, C>::value_type (i->first," << endl + << "i->second));" << endl + << "}"; + + os << "};"; + // Parser thunk. // os << "template <typename X, typename T, T X::*M>" << endl @@ -1059,6 +1293,14 @@ generate_runtime_source (context& ctx, bool complete) << "parser<T>::parse (x.*M, s);" << "}"; + os << "template <typename X, bool X::*M>" << endl + << "void" << endl + << "thunk (X& x, scanner& s)" + << "{" + << "s.next ();" + << "x.*M = true;" + << "}"; + if (ctx.gen_specifier) os << "template <typename X, typename T, T X::*M, bool X::*S>" << endl << "void" << endl diff --git a/cli/cli/source.cxx b/cli/cli/source.cxx index b6df839..3c04d1a 100644 --- a/cli/cli/source.cxx +++ b/cli/cli/source.cxx @@ -110,8 +110,12 @@ namespace for (names::name_iterator i (n.name_begin ()); i != n.name_end (); ++i) { os << "_cli_" << scope << "_map_[\"" << *i << "\"] =" << endl - << "&" << cli << "::thunk< " << scope << ", " << type << ", " << - "&" << scope << "::" << member; + << "&" << cli << "::thunk< " << scope; + + if (type != "bool") + os << ", " << type; + + os << ", " << "&" << scope << "::" << member; if (gen_specifier && type != "bool") os << "," << endl @@ -278,6 +282,9 @@ namespace : (n == 1 ? ds[0] : ds[1]); // Else, use common (no first sentence). } + if (options.ascii_tree ()) + preprocess_ascii_tree (d); + std::set<string> arg_set; if (n > 1 && options.ansi_color ()) translate_arg (ds[0], arg_set); @@ -345,6 +352,8 @@ namespace { l++; // ' ' seperator + // Note: we naturally assume this doesn't need --ascii-tree treatment. + // string s (doc.size () > 0 ? doc[0] : string ("<arg>")); if (options.ansi_color ()) @@ -439,6 +448,8 @@ namespace os << ' '; l++; + // Note: we naturally assume this doesn't need --ascii-tree treatment. + // string s (doc.size () > 0 ? doc[0] : string ("<arg>")); if (color) @@ -475,6 +486,9 @@ namespace } } + if (options.ascii_tree ()) + preprocess_ascii_tree (d); + // Format the documentation string. // if (color) @@ -1262,7 +1276,6 @@ generate_source (context& ctx) ostream& os (ctx.os); os << "#include <map>" << endl - << "#include <cstring>" << endl << endl; traversal::cli_unit unit; diff --git a/cli/cli/txt.cxx b/cli/cli/txt.cxx index 16de45a..1e9094d 100644 --- a/cli/cli/txt.cxx +++ b/cli/cli/txt.cxx @@ -164,13 +164,16 @@ namespace // n > 2 - arg string, short string, long string // size_t n (ds.size ()); - const string& d ( + string d ( n == 1 ? (cd_ == cd_short ? first_sentence (ds[0]) : ds[0]) : (n == 2 ? (cd_ == cd_short ? first_sentence (ds[1]) : ds[1]) : ds[cd_ == cd_short ? 1 : 2])); + if (options.ascii_tree ()) + preprocess_ascii_tree (d); + std::set<string> arg_set; if (n > 1 && options.ansi_color ()) translate_arg (ds[0], arg_set); diff --git a/cli/doc/.gitignore b/cli/doc/.gitignore index 562ecbd..84c93ab 100644 --- a/cli/doc/.gitignore +++ b/cli/doc/.gitignore @@ -1,2 +1,5 @@ -cli.xhtml -cli.1 +/cli.1 +/cli.xhtml + +*.ps +*.pdf diff --git a/cli/doc/buildfile b/cli/doc/buildfile index f47adad..753b620 100644 --- a/cli/doc/buildfile +++ b/cli/doc/buildfile @@ -7,14 +7,193 @@ css{*}: extension = css define xhtml: doc xhtml{*}: extension = xhtml -./: {man1 xhtml}{cli} \ - css{default} \ - file{cli-*} +define ps: doc +ps{*}: extension = ps -./: guide/doc{cli-guide*} \ - guide/xhtml{index} \ - guide/file{*.html2ps} +define pdf: doc +pdf{*}: extension = pdf -doc{*}: install.subdirs = true +define html2ps: file +html2ps{*}: extension = html2ps -./: file{doc.sh} +./: css{default} xhtml{cli-guide} + +# Man pages. +# + +## Consumption build ($develop == false). +# + +# Use pregenerated versions in the consumption build. +# +./: pregenerated/{man1 xhtml}{*}: include = (!$develop) + +# Distribute pregenerated versions only in the consumption build. +# +pregenerated/{man1 xhtml}{*}: dist = (!$develop) + +# +## + +## Development build ($develop == true). +# + +./: {man1 xhtml}{cli}: include = $develop + +if $develop +{ + doc_version = [string] "$version.major.$version.minor.$version.patch" + if $version.pre_release + doc_version += "-$version.pre_release_string" + + # Let's take the last four-digit number to cover 2000-2021,2022. + # + doc_year = $regex.replace($copyright, '.+[-, ]([0-9][0-9][0-9][0-9]) .+', '\1') + + man_options = -v project="CLI" -v version="$doc_version" \ + -v copyright="$copyright" --suppress-undocumented + + # We use the cli version we've built to generate the documentation. + # + include ../cli/ +} + +# Note: avoid cleaning exe{cli} through this dependency. +# +{man1 xhtml}{cli}: ../cli/exe{cli}: clean = false + +# In the development build distribute regenerated versions, remapping their +# locations to the paths of the pregenerated versions (which are only +# distributed in the consumption build; see above). This way we make sure that +# the distributed files are always up-to-date. +# +{man1 xhtml}{cli}: dist = ($develop ? pregenerated/ : false) + +man1{cli}: ../cli/cli{options} file{cli-prologue.1 cli-epilogue.1} +% +if $develop +{{ + diag 'cli --man' ($<[1]) -> $> + + # Use the copyright year to approximate the last authoring date. + # + ($<[0]) --generate-man $man_options \ + -v date="January $doc_year" \ + --man-prologue-file $path($<[2]) \ + --man-epilogue-file $path($<[3]) \ + --stdout $path($<[1]) >$path($>) + + # If the result differs from the pregenerated version, copy it over. Unlike + # the bootstrap compiler case, here we don't need to cause a build restart + # (since nothing depends on it). + # + if! diff $src_base/pregenerated/cli.1 $path($>) >- + cp $path($>) $src_base/pregenerated/cli.1 + end +}} + +xhtml{cli}: ../cli/cli{options} file{cli-prologue.xhtml cli-epilogue.xhtml} +% +if $develop +{{ + diag 'cli --html' ($<[1]) -> $> + + ($<[0]) --generate-html $man_options \ + --html-prologue-file $path($<[2]) \ + --html-epilogue-file $path($<[3]) \ + --stdout $path($<[1]) >$path($>) + + if! diff $src_base/pregenerated/cli.xhtml $path($>) >- + cp $path($>) $src_base/pregenerated/cli.xhtml + end +}} + +# +## + +# Manual. +# +# This case is slightly more involved because we make the generation of the +# manual's ps/pdf optional and also don't keep the result in the repository. +# Specifically: +# +# 1. In the consumption build we will install/redistribute ps/pdf if present. +# +# 2. In the development build we will generate ps/pdf if we are able to import +# the needed tools, issuing a warning otherwise. + +## Consumption build ($develop == false). +# + +# Use pregenerated versions, if exist, in the consumption build. +# +./: pregenerated/{ps pdf}{*}: include = (!$develop) + +# Distribute pregenerated versions only in the consumption build. +# +pregenerated/{ps pdf}{*}: dist = (!$develop) + +# +## + +## Development build ($develop == true). +# + +html2pdf = false + +if $develop +{ + # Import the html2ps and ps2pdf programs from the system, if available. + # + import? html2ps = html2ps%exe{html2ps} + import? ps2pdf = ps2pdf14%exe{ps2pdf14} + + html2pdf = ($html2ps != [null] && $ps2pdf != [null]) + + if! $html2pdf + warn "html2ps and/or ps2pdf14 are not available, not generating .ps and .pdf documentation" +} + +./: {ps pdf}{cli-guide}: include = $html2pdf + +# In the development build distribute regenerated versions, remapping their +# locations to the paths of the pregenerated versions (which are only +# distributed in the consumption build; see above). This way we make sure that +# the distributed files are always up-to-date. +# +{ps pdf}{cli-guide}: dist = ($html2pdf ? pregenerated/ : false) + +# Note: the pregenerated files may not exist, thus --no-cleanup option is +# required for the cp builtin call. Strictly speaking we don't really need +# to copy them since they are not stored in the repository, but let's do +# that for consistency with the distributed source tree. +# +# @@ TMP Note that cli-guide.xhtml and guide.html2ps still have copyright +# years hard-coded. +# +ps{cli-guide}: xhtml{cli-guide} html2ps{guide} $html2ps +% +if $html2pdf +{{ + options = + + diag html2ps ($<[0]) -> $> + $html2ps $options -f $path($<[1]) -o $path($>) $path($<[0]) + + cp --no-cleanup $path($>) $src_base/pregenerated/cli-guide.ps +}} + +pdf{cli-guide}: ps{cli-guide} $ps2pdf +% +if $html2pdf +{{ + options = -dOptimize=true -dEmbedAllFonts=true + + diag ps2pdf ($<[0]) -> $> + $ps2pdf $options $path($<[0]) $path($>) + + cp --no-cleanup $path($>) $src_base/pregenerated/cli-guide.pdf +}} + +# +## diff --git a/cli/doc/guide/index.xhtml b/cli/doc/cli-guide.xhtml index 675db03..94c006c 100644 --- a/cli/doc/guide/index.xhtml +++ b/cli/doc/cli-guide.xhtml @@ -1084,12 +1084,12 @@ include <string>; class options { - std::map<std::string, std::string> --map | -m; + std::map<std::string, bool> --map | -m; }; </pre> - <p>The possible option values for this interface are: <code>-m a=A</code>, - <code>-m =B</code> (key is an empty string), <code>-m c=</code> (value + <p>The possible option values for this interface are: <code>-m a=1</code>, + <code>-m =true</code> (key is an empty string), <code>-m c=</code> (value is an empty string), or <code>-m d</code> (same as <code>-m d=</code>).</p> <p>The last component in the option definition is optional documentation. diff --git a/cli/doc/cli-prologue.1 b/cli/doc/cli-prologue.1 index 165cd1a..2b34fee 100644 --- a/cli/doc/cli-prologue.1 +++ b/cli/doc/cli-prologue.1 @@ -1,7 +1,7 @@ .\" Process this file with .\" groff -man -Tascii cli.1 .\" -.TH CLI 1 "December 2009" "CLI 1.2.0" +.TH CLI 1 "$date$" "$project$ $version$" .SH NAME cli \- command line interface compiler for C++ .\" diff --git a/cli/doc/cli-prologue.xhtml b/cli/doc/cli-prologue.xhtml index 9a57f0e..386c4f0 100644 --- a/cli/doc/cli-prologue.xhtml +++ b/cli/doc/cli-prologue.xhtml @@ -2,7 +2,7 @@ <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"> <head> - <title>CLI 1.2.0 Compiler Command Line Manual</title> + <title>$project$ $version$ Compiler Command Line Manual</title> <meta name="copyright" content="© $copyright$"/> <meta name="keywords" content="cli,command,line,interface,compiler,c++"/> diff --git a/cli/doc/doc.sh b/cli/doc/doc.sh deleted file mode 100755 index dde9aca..0000000 --- a/cli/doc/doc.sh +++ /dev/null @@ -1,78 +0,0 @@ -#! /usr/bin/env bash - -version=1.2.0-b.6 - -trap 'exit 1' ERR -set -o errtrace # Trap in functions. - -function info () { echo "$*" 1>&2; } -function error () { info "$*"; exit 1; } - -date="$(date +"%B %Y")" -copyright="$(sed -n -re 's%^Copyright \(c\) (.+)\.$%\1%p' ../LICENSE)" - -while [ $# -gt 0 ]; do - case $1 in - --clean) - rm -f cli.xhtml cli.1 - rm -f guide/cli-guide.ps guide/cli-guide.pdf - exit 0 - ;; - *) - error "unexpected $1" - ;; - esac -done - -function compile () # <input-name> <output-name> -{ - local i=$1; shift - local o=$1; shift - - # Use a bash array to handle empty arguments. - # - local ops=() - while [ $# -gt 0 ]; do - ops=("${ops[@]}" "$1") - shift - done - - # --html-suffix .xhtml - ../cli/cli -I .. \ --v project="cli" \ --v version="$version" \ --v date="$date" \ --v copyright="$copyright" \ -"${ops[@]}" --generate-html --stdout \ ---html-prologue-file cli-prologue.xhtml \ ---html-epilogue-file cli-epilogue.xhtml \ -"../cli/$i.cli" >"$o.xhtml" - - # --man-suffix .1 - ../cli/cli -I .. \ --v project="cli" \ --v version="$version" \ --v date="$date" \ --v copyright="$copyright" \ -"${ops[@]}" --generate-man --stdout \ ---man-prologue-file cli-prologue.1 \ ---man-epilogue-file cli-epilogue.1 \ -"../cli/$i.cli" >"$o.1" -} - -compile options cli --suppress-undocumented - -# Manual. -# - -#function compile_doc () -#{ -# html2ps -f doc.html2ps:a4.html2ps -o "$n-a4.ps" "$n.xhtml" -# ps2pdf14 -sPAPERSIZE=a4 -dOptimize=true -dEmbedAllFonts=true "$n-a4.ps" "$n-a4.pdf" -# -# html2ps -f doc.html2ps:letter.html2ps -o "$n-letter.ps" "$n.xhtml" -# ps2pdf14 -sPAPERSIZE=letter -dOptimize=true -dEmbedAllFonts=true "$n-letter.ps" "$n-letter.pdf" -#} - -html2ps -f guide/guide.html2ps -o guide/cli-guide.ps guide/index.xhtml -ps2pdf14 -dOptimize=true -dEmbedAllFonts=true guide/cli-guide.ps guide/cli-guide.pdf diff --git a/cli/doc/guide/guide.html2ps b/cli/doc/guide.html2ps index a691002..a691002 100644 --- a/cli/doc/guide/guide.html2ps +++ b/cli/doc/guide.html2ps diff --git a/cli/doc/guide/.gitignore b/cli/doc/guide/.gitignore deleted file mode 100644 index 239cc7f..0000000 --- a/cli/doc/guide/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*.ps -*.pdf diff --git a/cli/doc/pregenerated/cli.1 b/cli/doc/pregenerated/cli.1 new file mode 100644 index 0000000..d452ca4 --- /dev/null +++ b/cli/doc/pregenerated/cli.1 @@ -0,0 +1,438 @@ +.\" Process this file with +.\" groff -man -Tascii cli.1 +.\" +.TH CLI 1 "January 2023" "CLI 1.2.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 +.\"-------------------------------------------------------------------- +.IP "\fB--help\fR" +Print usage information and exit\. +.IP "\fB--version\fR" +Print version and exit\. +.IP "\fB--include-path\fR|\fB-I\fR \fIdir\fR" +Search \fIdir\fR for bracket-included (\fB<>\fR) options files\. +.IP "\fB--output-dir\fR|\fB-o\fR \fIdir\fR" +Write the generated files to \fIdir\fR instead of the current directory\. +.IP "\fB--std\fR \fIversion\fR" +Specify the C++ standard that should be used during compilation\. Valid values +are \fBc++98\fR (default), \fBc++11\fR, and \fBc++14\fR\. +.IP "\fB--generate-modifier\fR" +Generate option value modifiers in addition to accessors\. +.IP "\fB--generate-specifier\fR" +Generate functions for determining whether the option was specified on the +command line\. +.IP "\fB--generate-parse\fR" +Generate \fBparse()\fR functions instead of parsing constructors\. This is +primarily useful for being able to parse into an already initialized options +class instance, for example, to implement option appending/overriding\. +.IP "\fB--generate-merge\fR" +Generate \fBmerge()\fR functions\. This is primarily useful for being able to +merge several already parsed options class instances, for example, to +implement option appending/overriding\. Note that this option forces +\fB--generate-specifier\fR\. +.IP "\fB--generate-description\fR" +Generate the option description list that can be examined at runtime\. +.IP "\fB--generate-file-scanner\fR" +Generate the \fBargv_file_scanner\fR implementation\. This scanner is capable +of reading command line arguments from the \fBargv\fR array as well as files +specified with command line options\. +.IP "\fB--generate-vector-scanner\fR" +Generate the \fBvector_scanner\fR implementation\. This scanner is capable of +reading command line arguments from \fBvector<string>\fR\. +.IP "\fB--generate-group-scanner\fR" +Generate the \fBgroup_scanner\fR implementation\. This scanner supports +grouping of arguments (usually options) to apply only to a certain argument\. + +Groups can be specified before (leading) and/or after (trailing) the argument +they apply to\. A leading group starts with '\fB{\fR' and ends with '\fB}+\fR' +while a trailing group starts with '\fB+{\fR' and ends with '\fB}\fR'\. For +example: + +.nf +{ --foo --bar }+ arg # 'arg' with '--foo' '--bar' +arg +{ fox=1 baz=2 } # 'arg' with 'fox=1' 'baz=2' +.fi + +Multiple leading and/or trailing groups can be specified for the same +argument\. For example: + +.nf +{ -f }+ { -b }+ arg +{ f=1 } +{ b=2 } # 'arg' with '-f' 'b' 'f=1' 'b=2' +.fi + +The group applies to a single argument only unless multiple arguments are +themselves grouped with '\fB{\fR' and '\fB}\fR'\. For example: + +.nf +{ --foo }+ arg1 arg2 +{ --bar } # 'arg1' with '--foo' + # 'arg2' with '--bar' + +{ --foo }+ { arg1 arg2 } +{ --bar } # 'arg1' with '--foo' '--bar' + # 'arg2' with '--foo' '--bar' +.fi + +The group separators ('\fB{\fR', '\fB}+'\fR, etc) must be separate command +line arguments\. In particular, they must not be adjacent either to the +arguments inside the group nor to the argument they apply to\. All such cases +will be treated as ordinary arguments\. For example: + +.nf +{--foo}+ arg # '{--foo}+' \.\.\. +arg+{ --foo } # 'arg+{' \.\.\. +.fi + +If one of the group separators needs to be specified as an argument verbatim, +then it must be escaped with '\fB\e\fR'\. For example: + +.nf +} # error: unexpected group separator +}x # '}x' +\\} # '}' +{ \\}+ }+ arg # 'arg' with '}+' +.fi +.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--suppress-cli\fR" +Do not generate the CLI support types (scanners, parser, etc)\. Normally, the +support types are generated unless another \fB\.cli\fR was included, in which +case the support types are expected to be provided by its generated code\. +.IP "\fB--cli-namespace\fR \fIns\fR" +Generate the CLI support types in the \fIns\fR namespace (\fBcli\fR by +default)\. The namespace can be nested, for example \fBdetails::cli\fR\. If +the namespace is empty, then the support types are generated in the global +namespace\. +.IP "\fB--ostream-type\fR \fItype\fR" +Output stream type instead of the default \fBstd::ostream\fR that should be +used to print usage and exception information\. +.IP "\fB--export-symbol\fR \fIsymbol\fR" +Insert \fIsymbol\fR in places where DLL export/import control statements +(\fB__declspec(dllexport/dllimport)\fR) are necessary\. +.IP "\fB--generate-cxx\fR" +Generate C++ code\. If neither \fB--generate-man\fR, \fB--generate-html\fR, +nor \fB--generate-txt\fR is specified, this mode is assumed by default\. +.IP "\fB--generate-man\fR" +Generate documentation in the man page format\. +.IP "\fB--generate-html\fR" +Generate documentation in the HTML format\. +.IP "\fB--generate-txt\fR" +Generate documentation in the plain text format, similar to usage\. +.IP "\fB--generate-dep\fR" +Generate \fBmake\fR dependency information\. This option triggers the creation +of the \fB\.d\fR file containing the dependencies of the generated files on +the main \fB\.cli\fR file as well as all the \fB\.cli\fR files that it +includes or sources, transitively\. Paths specified with the +\fB--*-prologue-file\fR and \fB--*-epilogue-file\fR options are also added as +dependencies\. Note, however, that paths specified with the +\fB--options-file\fR option are not added (since they may or may not contain +options that affect the output)\. +.IP "\fB--stdout\fR" +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--suppress-undocumented\fR" +Suppress the generation of documentation entries for undocumented options\. +.IP "\fB--suppress-usage\fR" +Suppress the generation of the usage printing code\. +.IP "\fB--long-usage\fR" +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--short-usage\fR" +If specified together with \fB--long-usage\fR, generate both short and long +usage versions\. In this mode, the long usage printing function is called +\fBprint_long_usage()\fR and in its implementation the long documentation +string is always used, even if the short version is provided\. +.IP "\fB--page-usage\fR \fIname\fR" +Generate the combined usage printing code for the entire page\. Specifically, +this will include all the namespace-level documentation as well as usage for +all the options classes printed in the order they are defined in the main +translation unit (documentation/classes from included units are ignored except +for base classes)\. + +The \fIname\fR argument is used as a prefix to form the name of the usage +printing function\. It can include the namespace qualification as well as +documentation variable expansion, for example: + +.nf +--page-usage print_ # print_usage() in global namespace +--page-usage app::print_ # print_usage() in app namespace +--page-usage print_$name$_ # print_foo_usage() if name is foo +.fi + +If both \fB--long-usage\fR and \fB--short-usage\fR options are specified, then +the long usage function has the \fB*long_usage()\fR suffix\. +.IP "\fB--option-length\fR \fIlen\fR" +Indent option descriptions \fIlen\fR 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--ascii-tree\fR" +Convert UTF-8 \fBtree(1)\fR output to ASCII\. Specifically, box-drawing +characters used in the \fB--charset=UTF-8\fR output are replaced with ASCII +characters used in the \fB--charset=ASCII\fR output\. +.IP "\fB--ansi-color\fR" +Use ANSI color escape sequences when printing usage\. By "color" we really +only mean the bold and underline modifiers\. Note that Windows console does +not recognize ANSI escape sequences and will display them as garbage\. +However, if you pipe such output through \fBless(1)\fR, it will display them +correctly\. +.IP "\fB--exclude-base\fR" +Exclude base class information from usage and documentation\. +.IP "\fB--include-base-last\fR" +Include base class information after derived for usage and documentation\. By +default, base classes are included first\. +.IP "\fB--class-doc\fR \fIname\fR=\fIkind\fR" +Specify the documentation \fIkind\fR that should be used for the options class +\fIname\fR\. The \fIname\fR value should be a fully-qualified class name, for +example, \fBapp::options\fR\. The \fIkind\fR value can be \fBshort\fR, +\fBlong\fR, \fBexclude\fR, or \fBexclude-base\fR\. If the value is +\fBexclude\fR, then the class documentation is excluded from usage and +man/HTML/text output\. If it is \fBexclude-base\fR, then it is only excluded +when used as a base\. For usage, the \fBshort\fR and \fBlong\fR values +determine which usage function will be called when the class is used as base +or as part of the page usage (see \fB--page-usage\fR)\. For man/HTML/text, +these values determine which documentation strings are used in the output\. +.IP "\fB--class\fR \fIname\fR" +Generate the man page, HTML, or text documentation only for the options class +\fIname\fR\. The \fIname\fR value should be a fully-qualified options class +name, for example, \fBapp::options\fR\. To generate documentation for multiple +classes, repeat this option and the documentation will be produced in the +order specified\. This functionality is useful if you need to assemble +documentation from multiple classes in a specific order or to insert custom +documentation between options belonging to different classes\. +.IP "\fB--docvar\fR|\fB-v\fR \fIname\fR=\fIval\fR" +Set documentation variable \fIname\fR to the value \fIval\fR\. Documentation +variables can be substituted in prologues and epilogues (see +\fB--*-prologue*\fR and \fB--*-epilogue*\fR options) using the +\fB$\fR\fIname\fR\fB$\fR expansion syntax (use \fB$$\fR to escape expansion)\. +They can also be defined in \fB\.cli\fR files using the +\&"\e\fIname\fR=\fIval\fR"\fR syntax\. +.IP "\fB--link-regex\fR \fIregex\fR" +Add \fIregex\fR to the list of regular expressions used to transform link +targets in the generated documentation\. The argument to this option is a +Perl-like regular expression in the form +\fB/\fR\fIpattern\fR\fB/\fR\fIreplacement\fR\fB/\fR\fR\. Any character can be +used as a delimiter instead of '\fB/\fR' and the delimiter can be escaped +inside \fIpattern\fR and \fIreplacement\fR with a backslash (\fB\e\fR)\. You +can specify multiple regular expressions by repeating this option\. All the +regular expressions are tried in the order specified and the first expression +that matches is used\. Use the \fB--link-regex-trace\fR option to debug link +transformation\. +.IP "\fB--link-regex-trace\fR" +Trace the process of applying regular expressions specified with the +\fB--link-regex\fR option\. Use this option to find out why your regular +expressions don't do what you expected them to do\. +.IP "\fB--html-heading-map\fR \fIc\fR=\fIh\fR" +Map CLI heading \fIc\fR (valid values: '\fBH\fR', '\fB0\fR', '\fB1\fR', +\&'\fBh\fR', and '\fB2\fR') to HTML heading \fIh\fR (for example, '\fBh1\fR', +\&'\fBh2\fR', etc)\. +.IP "\fB--omit-link-check\fR" +Don't check that local fragment link references (\el{#ref \.\.\.}) resolve to +ids\. +.IP "\fB--hxx-prologue\fR \fItext\fR" +Insert \fItext\fR at the beginning of the generated C++ header file\. +.IP "\fB--ixx-prologue\fR \fItext\fR" +Insert \fItext\fR at the beginning of the generated C++ inline file\. +.IP "\fB--cxx-prologue\fR \fItext\fR" +Insert \fItext\fR at the beginning of the generated C++ source file\. +.IP "\fB--man-prologue\fR \fItext\fR" +Insert \fItext\fR at the beginning of the generated man page file\. +.IP "\fB--html-prologue\fR \fItext\fR" +Insert \fItext\fR at the beginning of the generated HTML file\. +.IP "\fB--txt-prologue\fR \fItext\fR" +Insert \fItext\fR at the beginning of the generated text file\. +.IP "\fB--hxx-epilogue\fR \fItext\fR" +Insert \fItext\fR at the end of the generated C++ header file\. +.IP "\fB--ixx-epilogue\fR \fItext\fR" +Insert \fItext\fR at the end of the generated C++ inline file\. +.IP "\fB--cxx-epilogue\fR \fItext\fR" +Insert \fItext\fR at the end of the generated C++ source file\. +.IP "\fB--man-epilogue\fR \fItext\fR" +Insert \fItext\fR at the end of the generated man page file\. +.IP "\fB--html-epilogue\fR \fItext\fR" +Insert \fItext\fR at the end of the generated HTML file\. +.IP "\fB--txt-epilogue\fR \fItext\fR" +Insert \fItext\fR at the end of the generated text file\. +.IP "\fB--hxx-prologue-file\fR \fIfile\fR" +Insert the content of \fIfile\fR at the beginning of the generated C++ header +file\. +.IP "\fB--ixx-prologue-file\fR \fIfile\fR" +Insert the content of \fIfile\fR at the beginning of the generated C++ inline +file\. +.IP "\fB--cxx-prologue-file\fR \fIfile\fR" +Insert the content of \fIfile\fR at the beginning of the generated C++ source +file\. +.IP "\fB--man-prologue-file\fR \fIfile\fR" +Insert the content of \fIfile\fR at the beginning of the generated man page +file\. +.IP "\fB--html-prologue-file\fR \fIfile\fR" +Insert the content of \fIfile\fR at the beginning of the generated HTML file\. +.IP "\fB--txt-prologue-file\fR \fIfile\fR" +Insert the content of \fIfile\fR at the beginning of the generated text file\. +.IP "\fB--hxx-epilogue-file\fR \fIfile\fR" +Insert the content of \fIfile\fR at the end of the generated C++ header file\. +.IP "\fB--ixx-epilogue-file\fR \fIfile\fR" +Insert the content of \fIfile\fR at the end of the generated C++ inline file\. +.IP "\fB--cxx-epilogue-file\fR \fIfile\fR" +Insert the content of \fIfile\fR at the end of the generated C++ source file\. +.IP "\fB--man-epilogue-file\fR \fIfile\fR" +Insert the content of \fIfile\fR at the end of the generated man page file\. +.IP "\fB--html-epilogue-file\fR \fIfile\fR" +Insert the content of \fIfile\fR at the end of the generated HTML file\. +.IP "\fB--txt-epilogue-file\fR \fIfile\fR" +Insert the content of \fIfile\fR at the end of the generated text file\. +.IP "\fB--output-prefix\fR \fIprefix\fR" +Add \fIprefix\fR at the beginning of the generated output file name(s)\. +.IP "\fB--output-suffix\fR \fIsuffix\fR" +Add \fIsuffix\fR at the end of the generated output file name(s)\. Note that +it is added before any file type-specific suffixes; see \fB--*-suffix\fR +below\. +.IP "\fB--hxx-suffix\fR \fIsuffix\fR" +Use \fIsuffix\fR instead of the default \fB\.hxx\fR to construct the name of +the generated header file\. +.IP "\fB--ixx-suffix\fR \fIsuffix\fR" +Use \fIsuffix\fR instead of the default \fB\.ixx\fR to construct the name of +the generated inline file\. +.IP "\fB--cxx-suffix\fR \fIsuffix\fR" +Use \fIsuffix\fR instead of the default \fB\.cxx\fR to construct the name of +the generated source file\. +.IP "\fB--man-suffix\fR \fIsuffix\fR" +Use \fIsuffix\fR instead of the default \fB\.1\fR to construct the name of the +generated man page file\. +.IP "\fB--html-suffix\fR \fIsuffix\fR" +Use \fIsuffix\fR instead of the default \fB\.html\fR to construct the name of +the generated HTML file\. +.IP "\fB--txt-suffix\fR \fIsuffix\fR" +Use \fIsuffix\fR instead of the default \fB\.txt\fR to construct the name of +the generated text file\. +.IP "\fB--dep-suffix\fR \fIsuffix\fR" +Use \fIsuffix\fR instead of the default \fB\.d\fR to construct the name of the +generated dependency file\. See also \fB--dep-file\fR\. +.IP "\fB--dep-file\fR \fIpath\fR" +Use \fIpath\fR as the generated dependency file path instead of deriving it +from the input file name\. +.IP "\fB--option-prefix\fR \fIprefix\fR" +Use \fIprefix\fR instead of the default '\fB-\fR' 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\fR \fIsep\fR" +Use \fIsep\fR instead of the default '\fB--\fR' 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--keep-separator\fR" +Leave the option separator in the scanner\. This is primarily useful for +incremental option parsing\. +.IP "\fB--no-combined-flags\fR" +Disable support for combining multiple single-character flags into a single +argument (the \fB-xyz\fR form that is equivalent to \fB-x\fR \fB-y\fR +\fB-z\fR)\. An argument is considered a combination of flags if it starts with +a single option prefix (\fB--option-prefix\fR) and only contains letters and +digits\. Note that an option with a value may not be part of such a +combination, not even if it is specified last\. +.IP "\fB--no-combined-values\fR" +Disable support for combining an option and its value into a single argument +with the assignment sign (the \fIoption\fR\fB=\fR\fIvalue\fR\fR form)\. This +functionality requires a non-empty option prefix (\fB--option-prefix\fR)\. +.IP "\fB--include-with-brackets\fR" +Use angle brackets (\fB<>\fR) instead of quotes (\fB""\fR) in the generated +\fB#include\fR directives\. +.IP "\fB--include-prefix\fR \fIprefix\fR" +Add \fIprefix\fR to the generated \fB#include\fR directive paths\. +.IP "\fB--guard-prefix\fR \fIprefix\fR" +Add \fIprefix\fR 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\fR \fIname\fR=\fIrep\fR" +Add \fIname\fR with an optional \fIrep\fR 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\. +.IP "\fB--options-file\fR \fIfile\fR" +Read additional options from \fIfile\fR\. Each option should appear on a +separate line optionally followed by space or equal sign (\fB=\fR) and an +option value\. Empty lines and lines starting with \fB#\fR are ignored\. +Option values can be enclosed in double (\fB"\fR) or single (\fB'\fR) quotes +to preserve leading and trailing whitespaces as well as to specify empty +values\. If the value itself contains trailing or leading quotes, enclose it +with an extra pair of quotes, for example \fB'"x"'\fR\. Non-leading and +non-trailing quotes are interpreted as being part of the option value\. + +The semantics of providing options in a file is equivalent to providing the +same set of options in the same order on the command line at the point where +the \fB--options-file\fR option is specified except that the shell escaping +and quoting is not required\. Repeat this option to specify more than one +options file\. +.\" +.\" 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-2023 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/cli/doc/pregenerated/cli.xhtml b/cli/doc/pregenerated/cli.xhtml new file mode 100644 index 0000000..2a4f751 --- /dev/null +++ b/cli/doc/pregenerated/cli.xhtml @@ -0,0 +1,605 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"> + +<head> + <title>CLI 1.2.0 Compiler Command Line Manual</title> + + <meta name="copyright" content="© 2009-2023 Code Synthesis Tools CC"/> + <meta name="keywords" content="cli,command,line,interface,compiler,c++"/> + <meta name="description" content="CLI Compiler Command Line Manual"/> + + <link rel="stylesheet" type="text/css" href="default.css" /> + +<style type="text/css"> + + #synopsis { + list-style-type: none; + } + + #synopsis li { + padding-top : 0.0em; + padding-bottom : 0.0em; + } + + .options { + margin: 1em 0 1em 0; + } + + .options dt { + margin: 1em 0 0 0; + } + + .options dd { + margin: .1em 0 0 4.5em; + } + +</style> +</head> + +<body> +<div id="container"> + <div id="content"> + + <h1>NAME</h1> + + <p>cli - command line interface compiler for C++</p> + + <h1>SYNOPSIS</h1> + + <dl id="synopsis"> + <dt><code><b>cli</b> [<i>options</i>] <i>file</i></code></dt> + </dl> + + <h1>DESCRIPTION</h1> + + <p><code><b>cli</b></code> 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 + <code><b>name.cli</b></code> the following is generated. By + default or if the <code><b>--generate-cxx</b></code> option is + specified, the following C++ files are generated: + <code><b>name.hxx</b></code> (header file), <code><b>name.ixx</b></code> + (inline file, generated unless the <code><b>--suppress-inline</b></code> + option is specified), and <code><b>name.cxx</b></code> (source file). + If the <code><b>--generate-html</b></code> option is specified, then + the <code><b>name.html</b></code> HTML documentation file is generated. + If the <code><b>--generate-man</b></code> option is specified, then + the <code><b>name.1</b></code> man page file is generated. When + <code><b>--generate-html</b></code> or <code><b>--generate-man</b></code> + is specified, the <code><b>--stdout</b></code> option can be used to + redirect the output to STDOUT instead of a file.</p> + + <h1>OPTIONS</h1> + <dl class="options"> + <dt><code><b>--help</b></code></dt> + <dd>Print usage information and exit.</dd> + + <dt><code><b>--version</b></code></dt> + <dd>Print version and exit.</dd> + + <dt><code><b>--include-path</b></code>|<code><b>-I</b></code> <code><i>dir</i></code></dt> + <dd>Search <code><i>dir</i></code> for bracket-included + (<code><b><></b></code>) options files.</dd> + + <dt><code><b>--output-dir</b></code>|<code><b>-o</b></code> <code><i>dir</i></code></dt> + <dd>Write the generated files to <code><i>dir</i></code> instead of the + current directory.</dd> + + <dt><code><b>--std</b></code> <code><i>version</i></code></dt> + <dd>Specify the C++ standard that should be used during compilation. Valid + values are <code><b>c++98</b></code> (default), <code><b>c++11</b></code>, + and <code><b>c++14</b></code>.</dd> + + <dt><code><b>--generate-modifier</b></code></dt> + <dd>Generate option value modifiers in addition to accessors.</dd> + + <dt><code><b>--generate-specifier</b></code></dt> + <dd>Generate functions for determining whether the option was specified on + the command line.</dd> + + <dt><code><b>--generate-parse</b></code></dt> + <dd>Generate <code><b>parse()</b></code> functions instead of parsing + constructors. This is primarily useful for being able to parse into an + already initialized options class instance, for example, to implement + option appending/overriding.</dd> + + <dt><code><b>--generate-merge</b></code></dt> + <dd>Generate <code><b>merge()</b></code> functions. This is primarily + useful for being able to merge several already parsed options class + instances, for example, to implement option appending/overriding. Note + that this option forces <code><b>--generate-specifier</b></code>.</dd> + + <dt><code><b>--generate-description</b></code></dt> + <dd>Generate the option description list that can be examined at + runtime.</dd> + + <dt><code><b>--generate-file-scanner</b></code></dt> + <dd>Generate the <code><b>argv_file_scanner</b></code> implementation. + This scanner is capable of reading command line arguments from the + <code><b>argv</b></code> array as well as files specified with command + line options.</dd> + + <dt><code><b>--generate-vector-scanner</b></code></dt> + <dd>Generate the <code><b>vector_scanner</b></code> implementation. This + scanner is capable of reading command line arguments from + <code><b>vector<string></b></code>.</dd> + + <dt><code><b>--generate-group-scanner</b></code></dt> + <dd>Generate the <code><b>group_scanner</b></code> implementation. This + scanner supports grouping of arguments (usually options) to apply only to + a certain argument. + + <p>Groups can be specified before (leading) and/or after (trailing) the + argument they apply to. A leading group starts with + '<code><b>{</b></code>' and ends with '<code><b>}+</b></code>' while a + trailing group starts with '<code><b>+{</b></code>' and ends with + '<code><b>}</b></code>'. For example:</p> + + <pre>{ --foo --bar }+ arg # 'arg' with '--foo' '--bar' +arg +{ fox=1 baz=2 } # 'arg' with 'fox=1' 'baz=2'</pre> + + <p>Multiple leading and/or trailing groups can be specified for the same + argument. For example:</p> + + <pre>{ -f }+ { -b }+ arg +{ f=1 } +{ b=2 } # 'arg' with '-f' 'b' 'f=1' 'b=2'</pre> + + <p>The group applies to a single argument only unless multiple arguments + are themselves grouped with '<code><b>{</b></code>' and + '<code><b>}</b></code>'. For example:</p> + + <pre>{ --foo }+ arg1 arg2 +{ --bar } # 'arg1' with '--foo' + # 'arg2' with '--bar' + +{ --foo }+ { arg1 arg2 } +{ --bar } # 'arg1' with '--foo' '--bar' + # 'arg2' with '--foo' '--bar'</pre> + + <p>The group separators ('<code><b>{</b></code>', + '<code><b>}+'</b></code>, etc) must be separate command line arguments. In + particular, they must not be adjacent either to the arguments inside the + group nor to the argument they apply to. All such cases will be treated as + ordinary arguments. For example:</p> + + <pre>{--foo}+ arg # '{--foo}+' ... +arg+{ --foo } # 'arg+{' ...</pre> + + <p>If one of the group separators needs to be specified as an argument + verbatim, then it must be escaped with '<code><b>\</b></code>'. For + example:</p> + + <pre>} # error: unexpected group separator +}x # '}x' +\} # '}' +{ \}+ }+ arg # 'arg' with '}+'</pre></dd> + + <dt><code><b>--suppress-inline</b></code></dt> + <dd>Generate all functions non-inline. By default simple functions are + made inline. This option suppresses creation of the inline file.</dd> + + <dt><code><b>--suppress-cli</b></code></dt> + <dd>Do not generate the CLI support types (scanners, parser, etc). + Normally, the support types are generated unless another + <code><b>.cli</b></code> was included, in which case the support types are + expected to be provided by its generated code.</dd> + + <dt><code><b>--cli-namespace</b></code> <code><i>ns</i></code></dt> + <dd>Generate the CLI support types in the <code><i>ns</i></code> namespace + (<code><b>cli</b></code> by default). The namespace can be nested, for + example <code><b>details::cli</b></code>. If the namespace is empty, then + the support types are generated in the global namespace.</dd> + + <dt><code><b>--ostream-type</b></code> <code><i>type</i></code></dt> + <dd>Output stream type instead of the default + <code><b>std::ostream</b></code> that should be used to print usage and + exception information.</dd> + + <dt><code><b>--export-symbol</b></code> <code><i>symbol</i></code></dt> + <dd>Insert <code><i>symbol</i></code> in places where DLL export/import + control statements (<code><b>__declspec(dllexport/dllimport)</b></code>) + are necessary.</dd> + + <dt><code><b>--generate-cxx</b></code></dt> + <dd>Generate C++ code. If neither <code><b>--generate-man</b></code>, + <code><b>--generate-html</b></code>, nor + <code><b>--generate-txt</b></code> is specified, this mode is assumed by + default.</dd> + + <dt><code><b>--generate-man</b></code></dt> + <dd>Generate documentation in the man page format.</dd> + + <dt><code><b>--generate-html</b></code></dt> + <dd>Generate documentation in the HTML format.</dd> + + <dt><code><b>--generate-txt</b></code></dt> + <dd>Generate documentation in the plain text format, similar to + usage.</dd> + + <dt><code><b>--generate-dep</b></code></dt> + <dd>Generate <code><b>make</b></code> dependency information. This option + triggers the creation of the <code><b>.d</b></code> file containing the + dependencies of the generated files on the main <code><b>.cli</b></code> + file as well as all the <code><b>.cli</b></code> files that it includes or + sources, transitively. Paths specified with the + <code><b>--*-prologue-file</b></code> and + <code><b>--*-epilogue-file</b></code> options are also added as + dependencies. Note, however, that paths specified with the + <code><b>--options-file</b></code> option are not added (since they may or + may not contain options that affect the output).</dd> + + <dt><code><b>--stdout</b></code></dt> + <dd>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.</dd> + + <dt><code><b>--suppress-undocumented</b></code></dt> + <dd>Suppress the generation of documentation entries for undocumented + options.</dd> + + <dt><code><b>--suppress-usage</b></code></dt> + <dd>Suppress the generation of the usage printing code.</dd> + + <dt><code><b>--long-usage</b></code></dt> + <dd>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.</dd> + + <dt><code><b>--short-usage</b></code></dt> + <dd>If specified together with <code><b>--long-usage</b></code>, generate + both short and long usage versions. In this mode, the long usage printing + function is called <code><b>print_long_usage()</b></code> and in its + implementation the long documentation string is always used, even if the + short version is provided.</dd> + + <dt><code><b>--page-usage</b></code> <code><i>name</i></code></dt> + <dd>Generate the combined usage printing code for the entire page. + Specifically, this will include all the namespace-level documentation as + well as usage for all the options classes printed in the order they are + defined in the main translation unit (documentation/classes from included + units are ignored except for base classes). + + <p>The <code><i>name</i></code> argument is used as a prefix to form the + name of the usage printing function. It can include the namespace + qualification as well as documentation variable expansion, for + example:</p> + + <pre>--page-usage print_ # print_usage() in global namespace +--page-usage app::print_ # print_usage() in app namespace +--page-usage print_$name$_ # print_foo_usage() if name is foo</pre> + + <p>If both <code><b>--long-usage</b></code> and + <code><b>--short-usage</b></code> options are specified, then the long + usage function has the <code><b>*long_usage()</b></code> suffix.</p></dd> + + <dt><code><b>--option-length</b></code> <code><i>len</i></code></dt> + <dd>Indent option descriptions <code><i>len</i></code> 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.</dd> + + <dt><code><b>--ascii-tree</b></code></dt> + <dd>Convert UTF-8 <code><b>tree(1)</b></code> output to ASCII. + Specifically, box-drawing characters used in the + <code><b>--charset=UTF-8</b></code> output are replaced with ASCII + characters used in the <code><b>--charset=ASCII</b></code> output.</dd> + + <dt><code><b>--ansi-color</b></code></dt> + <dd>Use ANSI color escape sequences when printing usage. By "color" we + really only mean the bold and underline modifiers. Note that Windows + console does not recognize ANSI escape sequences and will display them as + garbage. However, if you pipe such output through + <code><b>less(1)</b></code>, it will display them correctly.</dd> + + <dt><code><b>--exclude-base</b></code></dt> + <dd>Exclude base class information from usage and documentation.</dd> + + <dt><code><b>--include-base-last</b></code></dt> + <dd>Include base class information after derived for usage and + documentation. By default, base classes are included first.</dd> + + <dt><code><b>--class-doc</b></code> <code><i>name</i></code>=<code><i>kind</i></code></dt> + <dd>Specify the documentation <code><i>kind</i></code> that should be used + for the options class <code><i>name</i></code>. The + <code><i>name</i></code> value should be a fully-qualified class name, for + example, <code><b>app::options</b></code>. The <code><i>kind</i></code> + value can be <code><b>short</b></code>, <code><b>long</b></code>, + <code><b>exclude</b></code>, or <code><b>exclude-base</b></code>. If the + value is <code><b>exclude</b></code>, then the class documentation is + excluded from usage and man/HTML/text output. If it is + <code><b>exclude-base</b></code>, then it is only excluded when used as a + base. For usage, the <code><b>short</b></code> and + <code><b>long</b></code> values determine which usage function will be + called when the class is used as base or as part of the page usage (see + <code><b>--page-usage</b></code>). For man/HTML/text, these values + determine which documentation strings are used in the output.</dd> + + <dt><code><b>--class</b></code> <code><i>name</i></code></dt> + <dd>Generate the man page, HTML, or text documentation only for the + options class <code><i>name</i></code>. The <code><i>name</i></code> value + should be a fully-qualified options class name, for example, + <code><b>app::options</b></code>. To generate documentation for multiple + classes, repeat this option and the documentation will be produced in the + order specified. This functionality is useful if you need to assemble + documentation from multiple classes in a specific order or to insert + custom documentation between options belonging to different classes.</dd> + + <dt><code><b>--docvar</b></code>|<code><b>-v</b></code> <code><i>name</i></code>=<code><i>val</i></code></dt> + <dd>Set documentation variable <code><i>name</i></code> to the value + <code><i>val</i></code>. Documentation variables can be substituted in + prologues and epilogues (see <code><b>--*-prologue*</b></code> and + <code><b>--*-epilogue*</b></code> options) using the + <code><b>$</b></code><code><i>name</i></code><code><b>$</b></code> + expansion syntax (use <code><b>$$</b></code> to escape expansion). They + can also be defined in <code><b>.cli</b></code> files using the + <code>"\<code><i>name</i></code>=<code><i>val</i></code>"</code> + syntax.</dd> + + <dt><code><b>--link-regex</b></code> <code><i>regex</i></code></dt> + <dd>Add <code><i>regex</i></code> to the list of regular expressions used + to transform link targets in the generated documentation. The argument to + this option is a Perl-like regular expression in the form + <code><b>/</b><i>pattern</i><b>/</b><i>replacement</i><b>/</b></code>. Any + character can be used as a delimiter instead of '<code><b>/</b></code>' + and the delimiter can be escaped inside <code><i>pattern</i></code> and + <code><i>replacement</i></code> with a backslash (<code><b>\</b></code>). + You can specify multiple regular expressions by repeating this option. All + the regular expressions are tried in the order specified and the first + expression that matches is used. Use the + <code><b>--link-regex-trace</b></code> option to debug link + transformation.</dd> + + <dt><code><b>--link-regex-trace</b></code></dt> + <dd>Trace the process of applying regular expressions specified with the + <code><b>--link-regex</b></code> option. Use this option to find out why + your regular expressions don't do what you expected them to do.</dd> + + <dt><code><b>--html-heading-map</b></code> <code><i>c</i></code>=<code><i>h</i></code></dt> + <dd>Map CLI heading <code><i>c</i></code> (valid values: + '<code><b>H</b></code>', '<code><b>0</b></code>', '<code><b>1</b></code>', + '<code><b>h</b></code>', and '<code><b>2</b></code>') to HTML heading + <code><i>h</i></code> (for example, '<code><b>h1</b></code>', + '<code><b>h2</b></code>', etc).</dd> + + <dt><code><b>--omit-link-check</b></code></dt> + <dd>Don't check that local fragment link references (\l{#ref ...}) resolve + to ids.</dd> + + <dt><code><b>--hxx-prologue</b></code> <code><i>text</i></code></dt> + <dd>Insert <code><i>text</i></code> at the beginning of the generated C++ + header file.</dd> + + <dt><code><b>--ixx-prologue</b></code> <code><i>text</i></code></dt> + <dd>Insert <code><i>text</i></code> at the beginning of the generated C++ + inline file.</dd> + + <dt><code><b>--cxx-prologue</b></code> <code><i>text</i></code></dt> + <dd>Insert <code><i>text</i></code> at the beginning of the generated C++ + source file.</dd> + + <dt><code><b>--man-prologue</b></code> <code><i>text</i></code></dt> + <dd>Insert <code><i>text</i></code> at the beginning of the generated man + page file.</dd> + + <dt><code><b>--html-prologue</b></code> <code><i>text</i></code></dt> + <dd>Insert <code><i>text</i></code> at the beginning of the generated HTML + file.</dd> + + <dt><code><b>--txt-prologue</b></code> <code><i>text</i></code></dt> + <dd>Insert <code><i>text</i></code> at the beginning of the generated text + file.</dd> + + <dt><code><b>--hxx-epilogue</b></code> <code><i>text</i></code></dt> + <dd>Insert <code><i>text</i></code> at the end of the generated C++ header + file.</dd> + + <dt><code><b>--ixx-epilogue</b></code> <code><i>text</i></code></dt> + <dd>Insert <code><i>text</i></code> at the end of the generated C++ inline + file.</dd> + + <dt><code><b>--cxx-epilogue</b></code> <code><i>text</i></code></dt> + <dd>Insert <code><i>text</i></code> at the end of the generated C++ source + file.</dd> + + <dt><code><b>--man-epilogue</b></code> <code><i>text</i></code></dt> + <dd>Insert <code><i>text</i></code> at the end of the generated man page + file.</dd> + + <dt><code><b>--html-epilogue</b></code> <code><i>text</i></code></dt> + <dd>Insert <code><i>text</i></code> at the end of the generated HTML + file.</dd> + + <dt><code><b>--txt-epilogue</b></code> <code><i>text</i></code></dt> + <dd>Insert <code><i>text</i></code> at the end of the generated text + file.</dd> + + <dt><code><b>--hxx-prologue-file</b></code> <code><i>file</i></code></dt> + <dd>Insert the content of <code><i>file</i></code> at the beginning of the + generated C++ header file.</dd> + + <dt><code><b>--ixx-prologue-file</b></code> <code><i>file</i></code></dt> + <dd>Insert the content of <code><i>file</i></code> at the beginning of the + generated C++ inline file.</dd> + + <dt><code><b>--cxx-prologue-file</b></code> <code><i>file</i></code></dt> + <dd>Insert the content of <code><i>file</i></code> at the beginning of the + generated C++ source file.</dd> + + <dt><code><b>--man-prologue-file</b></code> <code><i>file</i></code></dt> + <dd>Insert the content of <code><i>file</i></code> at the beginning of the + generated man page file.</dd> + + <dt><code><b>--html-prologue-file</b></code> <code><i>file</i></code></dt> + <dd>Insert the content of <code><i>file</i></code> at the beginning of the + generated HTML file.</dd> + + <dt><code><b>--txt-prologue-file</b></code> <code><i>file</i></code></dt> + <dd>Insert the content of <code><i>file</i></code> at the beginning of the + generated text file.</dd> + + <dt><code><b>--hxx-epilogue-file</b></code> <code><i>file</i></code></dt> + <dd>Insert the content of <code><i>file</i></code> at the end of the + generated C++ header file.</dd> + + <dt><code><b>--ixx-epilogue-file</b></code> <code><i>file</i></code></dt> + <dd>Insert the content of <code><i>file</i></code> at the end of the + generated C++ inline file.</dd> + + <dt><code><b>--cxx-epilogue-file</b></code> <code><i>file</i></code></dt> + <dd>Insert the content of <code><i>file</i></code> at the end of the + generated C++ source file.</dd> + + <dt><code><b>--man-epilogue-file</b></code> <code><i>file</i></code></dt> + <dd>Insert the content of <code><i>file</i></code> at the end of the + generated man page file.</dd> + + <dt><code><b>--html-epilogue-file</b></code> <code><i>file</i></code></dt> + <dd>Insert the content of <code><i>file</i></code> at the end of the + generated HTML file.</dd> + + <dt><code><b>--txt-epilogue-file</b></code> <code><i>file</i></code></dt> + <dd>Insert the content of <code><i>file</i></code> at the end of the + generated text file.</dd> + + <dt><code><b>--output-prefix</b></code> <code><i>prefix</i></code></dt> + <dd>Add <code><i>prefix</i></code> at the beginning of the generated + output file name(s).</dd> + + <dt><code><b>--output-suffix</b></code> <code><i>suffix</i></code></dt> + <dd>Add <code><i>suffix</i></code> at the end of the generated output file + name(s). Note that it is added before any file type-specific suffixes; see + <code><b>--*-suffix</b></code> below.</dd> + + <dt><code><b>--hxx-suffix</b></code> <code><i>suffix</i></code></dt> + <dd>Use <code><i>suffix</i></code> instead of the default + <code><b>.hxx</b></code> to construct the name of the generated header + file.</dd> + + <dt><code><b>--ixx-suffix</b></code> <code><i>suffix</i></code></dt> + <dd>Use <code><i>suffix</i></code> instead of the default + <code><b>.ixx</b></code> to construct the name of the generated inline + file.</dd> + + <dt><code><b>--cxx-suffix</b></code> <code><i>suffix</i></code></dt> + <dd>Use <code><i>suffix</i></code> instead of the default + <code><b>.cxx</b></code> to construct the name of the generated source + file.</dd> + + <dt><code><b>--man-suffix</b></code> <code><i>suffix</i></code></dt> + <dd>Use <code><i>suffix</i></code> instead of the default + <code><b>.1</b></code> to construct the name of the generated man page + file.</dd> + + <dt><code><b>--html-suffix</b></code> <code><i>suffix</i></code></dt> + <dd>Use <code><i>suffix</i></code> instead of the default + <code><b>.html</b></code> to construct the name of the generated HTML + file.</dd> + + <dt><code><b>--txt-suffix</b></code> <code><i>suffix</i></code></dt> + <dd>Use <code><i>suffix</i></code> instead of the default + <code><b>.txt</b></code> to construct the name of the generated text + file.</dd> + + <dt><code><b>--dep-suffix</b></code> <code><i>suffix</i></code></dt> + <dd>Use <code><i>suffix</i></code> instead of the default + <code><b>.d</b></code> to construct the name of the generated dependency + file. See also <code><b>--dep-file</b></code>.</dd> + + <dt><code><b>--dep-file</b></code> <code><i>path</i></code></dt> + <dd>Use <code><i>path</i></code> as the generated dependency file path + instead of deriving it from the input file name.</dd> + + <dt><code><b>--option-prefix</b></code> <code><i>prefix</i></code></dt> + <dd>Use <code><i>prefix</i></code> instead of the default + '<code><b>-</b></code>' 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.</dd> + + <dt><code><b>--option-separator</b></code> <code><i>sep</i></code></dt> + <dd>Use <code><i>sep</i></code> instead of the default + '<code><b>--</b></code>' 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.</dd> + + <dt><code><b>--keep-separator</b></code></dt> + <dd>Leave the option separator in the scanner. This is primarily useful + for incremental option parsing.</dd> + + <dt><code><b>--no-combined-flags</b></code></dt> + <dd>Disable support for combining multiple single-character flags into a + single argument (the <code><b>-xyz</b></code> form that is equivalent to + <code><b>-x</b></code> <code><b>-y</b></code> <code><b>-z</b></code>). An + argument is considered a combination of flags if it starts with a single + option prefix (<code><b>--option-prefix</b></code>) and only contains + letters and digits. Note that an option with a value may not be part of + such a combination, not even if it is specified last.</dd> + + <dt><code><b>--no-combined-values</b></code></dt> + <dd>Disable support for combining an option and its value into a single + argument with the assignment sign (the + <code><i>option</i><b>=</b><i>value</i></code> form). This functionality + requires a non-empty option prefix + (<code><b>--option-prefix</b></code>).</dd> + + <dt><code><b>--include-with-brackets</b></code></dt> + <dd>Use angle brackets (<code><b><></b></code>) instead of quotes + (<code><b>""</b></code>) in the generated <code><b>#include</b></code> + directives.</dd> + + <dt><code><b>--include-prefix</b></code> <code><i>prefix</i></code></dt> + <dd>Add <code><i>prefix</i></code> to the generated + <code><b>#include</b></code> directive paths.</dd> + + <dt><code><b>--guard-prefix</b></code> <code><i>prefix</i></code></dt> + <dd>Add <code><i>prefix</i></code> 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.</dd> + + <dt><code><b>--reserved-name</b></code> <code><i>name</i></code>=<code><i>rep</i></code></dt> + <dd>Add <code><i>name</i></code> with an optional <code><i>rep</i></code> + 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.</dd> + + <dt><code><b>--options-file</b></code> <code><i>file</i></code></dt> + <dd>Read additional options from <code><i>file</i></code>. Each option + should appear on a separate line optionally followed by space or equal + sign (<code><b>=</b></code>) and an option value. Empty lines and lines + starting with <code><b>#</b></code> are ignored. Option values can be + enclosed in double (<code><b>"</b></code>) or single + (<code><b>'</b></code>) quotes to preserve leading and trailing + whitespaces as well as to specify empty values. If the value itself + contains trailing or leading quotes, enclose it with an extra pair of + quotes, for example <code><b>'"x"'</b></code>. Non-leading and + non-trailing quotes are interpreted as being part of the option value. + + <p>The semantics of providing options in a file is equivalent to providing + the same set of options in the same order on the command line at the point + where the <code><b>--options-file</b></code> option is specified except + that the shell escaping and quoting is not required. Repeat this option to + specify more than one options file.</p></dd> + </dl> + + <h1>DIAGNOSTICS</h1> + + <p>If the input file is not a valid CLI definition, <code><b>cli</b></code> + will issue diagnostic messages to STDERR and exit with non-zero exit + code.</p> + + <h1>BUGS</h1> + + <p>Send bug reports to the + <a href="mailto:cli-users@codesynthesis.com">cli-users@codesynthesis.com</a> mailing list.</p> + + </div> + <div id="footer"> + Copyright © 2009-2023 Code Synthesis Tools CC. + + <div id="terms"> + Permission is granted to copy, distribute and/or modify this + document under the terms of the + <a href="http://www.codesynthesis.com/licenses/mit.txt">MIT License</a>. + </div> + </div> +</div> +</body> +</html> diff --git a/cli/manifest b/cli/manifest index 7205efd..a90072b 100644 --- a/cli/manifest +++ b/cli/manifest @@ -1,6 +1,6 @@ : 1 name: cli -version: 1.2.0-b.7.z +version: 1.2.0 summary: Command line interface (CLI) compiler for C++ license: MIT topics: C++, command line interface, source code generation, \ @@ -12,10 +12,10 @@ doc-url: https://www.codesynthesis.com/projects/cli/doc/guide/ src-url: https://git.codesynthesis.com/cgit/cli/cli/tree/cli/ email: cli-users@codesynthesis.com ; Mailing list build-warning-email: builds@codesynthesis.com -builds: all requires: c++14 -depends: * build2 >= 0.13.0 -depends: * bpkg >= 0.13.0 +requires: host +depends: * build2 >= 0.16.0 +depends: * bpkg >= 0.16.0 depends: libcutl ^1.11.0- -tests: cli-tests == $ -examples: cli-examples == $ +tests: * cli-tests == $ +examples: * cli-examples == $ diff --git a/cli/tests/.gitignore b/cli/tests/.gitignore new file mode 100644 index 0000000..d4a788b --- /dev/null +++ b/cli/tests/.gitignore @@ -0,0 +1,2 @@ +test +test-* diff --git a/cli/tests/ascii-tree/buildfile b/cli/tests/ascii-tree/buildfile new file mode 100644 index 0000000..6450286 --- /dev/null +++ b/cli/tests/ascii-tree/buildfile @@ -0,0 +1,8 @@ +# file : tests/ascii-tree/buildfile +# license : MIT; see accompanying LICENSE file + +import! [metadata] cli = cli%exe{cli} + +./: testscript $cli + +testscript{*}: test = $cli diff --git a/cli/tests/ascii-tree/testscript b/cli/tests/ascii-tree/testscript new file mode 100644 index 0000000..f0546f6 --- /dev/null +++ b/cli/tests/ascii-tree/testscript @@ -0,0 +1,43 @@ +# file : tests/ascii-tree/testscript +# license : MIT; see accompanying LICENSE file + +: basics +: +cat <<EOI >=test.cli; +" +\ +hello/ +├── build/ +│ ├── bootstrap.build +│ └── root.build +├── hello/ +│ ├── sub/ +│ │ ├── bar +│ │ └── foo +│ ├── buildfile +│ ├── hello.cxx +│ └── testscript +├── buildfile +├── manifest +├── README.md +└── repositories.manifest +\ +" +EOI +$* --generate-txt --ascii-tree --stdout test.cli >>EOO +hello/ +|-- build/ +| |-- bootstrap.build +| `-- root.build +|-- hello/ +| |-- sub/ +| | |-- bar +| | `-- foo +| |-- buildfile +| |-- hello.cxx +| `-- testscript +|-- buildfile +|-- manifest +|-- README.md +`-- repositories.manifest +EOO diff --git a/cli/tests/build/.gitignore b/cli/tests/build/.gitignore new file mode 100644 index 0000000..4a730a3 --- /dev/null +++ b/cli/tests/build/.gitignore @@ -0,0 +1,3 @@ +config.build +root/ +bootstrap/ diff --git a/cli/tests/build/bootstrap.build b/cli/tests/build/bootstrap.build new file mode 100644 index 0000000..bb305a2 --- /dev/null +++ b/cli/tests/build/bootstrap.build @@ -0,0 +1,8 @@ +# file : tests/build/bootstrap.build +# license : MIT; see accompanying LICENSE file + +project = + +using config +using dist +using test diff --git a/cli/tests/build/root.build b/cli/tests/build/root.build new file mode 100644 index 0000000..cb9ba03 --- /dev/null +++ b/cli/tests/build/root.build @@ -0,0 +1,8 @@ +# file : tests/build/root.build +# license : MIT; see accompanying LICENSE file + +# Configure C++ module and specify the test target for cross-testing. +# +using cxx.config + +test.target = $cxx.target diff --git a/cli/tests/buildfile b/cli/tests/buildfile new file mode 100644 index 0000000..556ed55 --- /dev/null +++ b/cli/tests/buildfile @@ -0,0 +1,4 @@ +# file : tests/buildfile +# license : MIT; see accompanying LICENSE file + +./: {*/ -build/} diff --git a/cli-tests/note/buildfile b/cli/tests/headings/buildfile index c8cf49b..aa665c7 100644 --- a/cli-tests/note/buildfile +++ b/cli/tests/headings/buildfile @@ -1,6 +1,8 @@ -# file : note/buildfile +# file : tests/headings/buildfile # license : MIT; see accompanying LICENSE file +import! [metadata] cli = cli%exe{cli} + ./: testscript $cli testscript{*}: test = $cli diff --git a/cli-tests/headings/testscript b/cli/tests/headings/testscript index 0efb7ae..163ba5f 100644 --- a/cli-tests/headings/testscript +++ b/cli/tests/headings/testscript @@ -1,4 +1,4 @@ -# file : headings/testscript +# file : tests/headings/testscript # license : MIT; see accompanying LICENSE file : auto-headings diff --git a/cli-tests/headings/buildfile b/cli/tests/note/buildfile index af3ae75..b35a911 100644 --- a/cli-tests/headings/buildfile +++ b/cli/tests/note/buildfile @@ -1,6 +1,8 @@ -# file : headings/buildfile +# file : tests/note/buildfile # license : MIT; see accompanying LICENSE file +import! [metadata] cli = cli%exe{cli} + ./: testscript $cli testscript{*}: test = $cli diff --git a/cli-tests/note/testscript b/cli/tests/note/testscript index 9f00811..52bcecc 100644 --- a/cli-tests/note/testscript +++ b/cli/tests/note/testscript @@ -1,4 +1,4 @@ -# file : note/testscript +# file : tests/note/testscript # license : MIT; see accompanying LICENSE file : block-basics diff --git a/cli-tests/toc/buildfile b/cli/tests/toc/buildfile index 3be6359..50a9ec0 100644 --- a/cli-tests/toc/buildfile +++ b/cli/tests/toc/buildfile @@ -1,6 +1,8 @@ -# file : toc/buildfile +# file : tests/toc/buildfile # license : MIT; see accompanying LICENSE file +import! [metadata] cli = cli%exe{cli} + ./: testscript $cli testscript{*}: test = $cli diff --git a/cli-tests/toc/testscript b/cli/tests/toc/testscript index 70e9065..6b3ff3e 100644 --- a/cli-tests/toc/testscript +++ b/cli/tests/toc/testscript @@ -1,4 +1,4 @@ -# file : toc/testscript +# file : tests/toc/testscript # license : MIT; see accompanying LICENSE file : toc |