summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--LICENSE2
-rw-r--r--cli-examples/build/root.build3
-rw-r--r--cli-examples/features/driver.cxx25
-rw-r--r--cli-examples/features/options.cli9
-rw-r--r--cli-examples/manifest6
-rw-r--r--cli-tests/build/root.build3
-rw-r--r--cli-tests/combined/driver.cxx3
-rw-r--r--cli-tests/ctor/driver.cxx3
-rw-r--r--cli-tests/erase/driver.cxx4
-rw-r--r--cli-tests/file/driver.cxx3
-rw-r--r--cli-tests/group/driver.cxx6
-rw-r--r--cli-tests/inheritance/driver.cxx4
-rw-r--r--cli-tests/manifest6
-rw-r--r--cli-tests/merge/driver.cxx4
-rw-r--r--cli-tests/position/driver.cxx3
-rw-r--r--cli-tests/specifier/driver.cxx4
-rw-r--r--cli/NEWS17
-rw-r--r--cli/build/root.build11
-rw-r--r--cli/cli/.gitignore2
-rw-r--r--cli/cli/buildfile145
-rw-r--r--cli/cli/cli.cxx8
-rw-r--r--cli/cli/context.cxx128
-rw-r--r--cli/cli/context.hxx13
-rw-r--r--cli/cli/generator.cxx144
-rw-r--r--cli/cli/generator.hxx7
-rw-r--r--cli/cli/header.cxx6
-rw-r--r--cli/cli/html.cxx10
-rw-r--r--cli/cli/lexer.test.cxx3
-rw-r--r--cli/cli/man.cxx10
-rw-r--r--cli/cli/options.cli40
-rw-r--r--cli/cli/parser.cxx33
-rw-r--r--cli/cli/parser.hxx21
-rw-r--r--cli/cli/parser.test.cxx5
-rw-r--r--cli/cli/pregenerated/cli/options.cxx (renamed from cli/cli/bootstrap/cli/options.cxx)222
-rw-r--r--cli/cli/pregenerated/cli/options.hxx (renamed from cli/cli/bootstrap/cli/options.hxx)71
-rw-r--r--cli/cli/pregenerated/cli/options.ixx (renamed from cli/cli/bootstrap/cli/options.ixx)126
-rw-r--r--cli/cli/runtime-header.cxx81
-rw-r--r--cli/cli/runtime-source.cxx146
-rw-r--r--cli/cli/source.cxx19
-rw-r--r--cli/cli/txt.cxx5
-rw-r--r--cli/doc/.gitignore6
-rw-r--r--cli/doc/buildfile236
-rw-r--r--cli/doc/cli-guide.xhtml6
-rw-r--r--cli/doc/pregenerated/cli.1 (renamed from cli/doc/bootstrap/cli.1)26
-rw-r--r--cli/doc/pregenerated/cli.xhtml (renamed from cli/doc/bootstrap/cli.xhtml)38
-rw-r--r--cli/manifest6
-rw-r--r--cli/tests/ascii-tree/buildfile8
-rw-r--r--cli/tests/ascii-tree/testscript43
48 files changed, 1388 insertions, 342 deletions
diff --git a/LICENSE b/LICENSE
index 0d59d21..313757d 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2009-2021 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/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 43771e8..a7f30dd 100644
--- a/cli-examples/manifest
+++ b/cli-examples/manifest
@@ -1,6 +1,6 @@
: 1
name: cli-examples
-version: 1.2.0-b.8+1
+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.14.0-
-depends: * bpkg >= 0.14.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 01abf23..9a3c710 100644
--- a/cli-tests/group/driver.cxx
+++ b/cli-tests/group/driver.cxx
@@ -6,11 +6,13 @@
//
#include <string>
-#include <cassert>
#include <iostream>
#include "test.hxx"
+#undef NDEBUG
+#include <cassert>
+
using namespace std;
int
@@ -50,7 +52,7 @@ main (int argc, char* argv[])
s.peek ();
assert (pos == s.position ());
- const char* a;
+ const char* a (0);
if (!sa)
{
a = s.next ();
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 08ccb26..4c3626b 100644
--- a/cli-tests/manifest
+++ b/cli-tests/manifest
@@ -1,6 +1,6 @@
: 1
name: cli-tests
-version: 1.2.0-b.8+1
+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.14.0-
-depends: * bpkg >= 0.14.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/driver.cxx b/cli-tests/position/driver.cxx
index eebb5d0..df45f5b 100644
--- a/cli-tests/position/driver.cxx
+++ b/cli-tests/position/driver.cxx
@@ -8,6 +8,9 @@
#include "test.hxx"
+#undef NDEBUG
+#include <cassert>
+
using namespace std;
int
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
diff --git a/cli/NEWS b/cli/NEWS
index a1fe25e..07da75f 100644
--- a/cli/NEWS
+++ b/cli/NEWS
@@ -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 6132356..ceeaf6c 100644
--- a/cli/build/root.build
+++ b/cli/build/root.build
@@ -4,10 +4,12 @@
# 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 the final cli to generate the man pages.
+# "for-run"). Nor can we run such final cli to generate the man pages.
#
config [bool] config.cli.develop ?= false
+develop = $config.cli.develop
+
define cli: file
cli{*}: extension = cli
@@ -34,6 +36,7 @@ test.target = $cxx.target
#
# Note that cat is a builtin which means this is both portable and fast.
#
-copyright = $process.run_regex(cat $src_root/LICENSE, \
- 'Copyright \(c\) (.+)\.', \
- '\1')
+if ($build.mode != 'skeleton')
+ copyright = $process.run_regex(cat $src_root/LICENSE, \
+ 'Copyright \(c\) (.+)\.', \
+ '\1')
diff --git a/cli/cli/.gitignore b/cli/cli/.gitignore
index 79562f1..614e56f 100644
--- a/cli/cli/.gitignore
+++ b/cli/cli/.gitignore
@@ -1,5 +1,5 @@
/cli
-/bootstrap/cli/cli
+/pregenerated/cli/cli
/version.hxx
/options.?xx
diff --git a/cli/cli/buildfile b/cli/cli/buildfile
index dc5d75b..92c3e6e 100644
--- a/cli/cli/buildfile
+++ b/cli/cli/buildfile
@@ -25,91 +25,118 @@ libue{cli}: $hdr $src $all_s $all_t {hxx}{version} $libs
hxx{version}: in{version} $src_root/manifest
-# Build options (both bootstrap and final version).
+# Build options (apply to both bootstrap and final version).
#
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
-# bootstrap/cli/.
+# pregenerated/cli/.
#
-# 2. Use that to re-generate options.?xx. If the result differs from the
-# saved version, copy it over and fail the build, asking for a restart.
+# 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.
+# 3. Otherwise, proceed to build the final version of cli using regenerated
+# options.?xx.
#
-if $config.cli.develop
+pregenerated/
{
- libue{cli}: {hxx ixx cxx}{options}
+ # This should only apply to the bootstrap build.
+ #
+ cxx.poptions =+ "-I$src_base" # Note: must come first.
- bootstrap/
+ cli/
{
- # This should only apply to the bootstrap build.
+ # Note: semantics/ and traversal/ do not include options.hxx so we can
+ # share their object files.
#
- cxx.poptions =+ "-I$src_base" # Note: must come first.
+ exe{cli}: obj{cli $name($src) options} ../../{$all_s $all_t} $libs
- 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
- for s: cli $name($src)
- obj{$s}: ../../cxx{$s} $libs
+ obj{options}: {hxx ixx cxx}{options}
- obj{options}: {hxx ixx cxx}{options}
-
- obj{cli}: cxx.poptions += -DCLI_BOOTSTRAP
- }
+ obj{cli}: cxx.poptions += -DCLI_BOOTSTRAP
}
+}
- <{hxx ixx cxx}{options}>: cli{options} bootstrap/cli/exe{cli}
- {
- options = --include-with-brackets --include-prefix cli \
- --guard-prefix CLI --generate-file-scanner \
- --generate-specifier --generate-modifier \
- --suppress-undocumented --reserved-name stdout
-
- # Symlink the generated code in src for convenience of development.
- #
- backlink = true
- }
- {{
- diag cli ($<[0])
- ($<[1]) $options -o $out_base $path($<[0])
+# 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}
+{
+ dist = ($develop ? pregenerated/cli/ : false)
- # If the result differs from the bootstrap version, copy it over and
- # request the build restart.
- #
- if diff $src_base/bootstrap/cli/options.hxx $path($>[0]) >- && \
- diff $src_base/bootstrap/cli/options.ixx $path($>[1]) >- && \
- diff $src_base/bootstrap/cli/options.cxx $path($>[2]) >-
- exit
- end
-
- cp $path($>[0]) $src_base/bootstrap/cli/options.hxx
- cp $path($>[1]) $src_base/bootstrap/cli/options.ixx
- cp $path($>[2]) $src_base/bootstrap/cli/options.cxx
-
- exit "bootstrap options.?xx have changed, restart the build"
- }}
+ # Symlink the generated code in src for convenience of development.
+ #
+ backlink = true
}
-else
-{
- # Use bootstrap options.?xx to build the final version of cli.
+%
+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.
#
- libue{cli}: bootstrap/cli/{hxx ixx cxx}{options}
- cxx.poptions =+ "-I($src_base/bootstrap)" # Note: must come first.
+ 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
- ./: cli{options} # Keep in distribution.
-}
+ 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 (final version only).
#
+##
# Pass the copyright notice extracted from the LICENSE file.
#
diff --git a/cli/cli/cli.cxx b/cli/cli/cli.cxx
index f1196de..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>
@@ -121,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.
//
@@ -143,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 9273845..211e01f 100644
--- a/cli/cli/options.cli
+++ b/cli/cli/options.cli
@@ -171,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},
@@ -192,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
@@ -257,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
@@ -572,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/bootstrap/cli/options.cxx b/cli/cli/pregenerated/cli/options.cxx
index 56dd24f..9f0be5f 100644
--- a/cli/cli/bootstrap/cli/options.cxx
+++ b/cli/cli/pregenerated/cli/options.cxx
@@ -542,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;
}
};
@@ -655,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)
@@ -662,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)
@@ -671,7 +750,6 @@ namespace cli
}
#include <map>
-#include <cstring>
// options
//
@@ -702,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_ (),
@@ -715,6 +796,7 @@ options ()
page_usage_specified_ (false),
option_length_ (0),
option_length_specified_ (false),
+ ascii_tree_ (),
ansi_color_ (),
exclude_base_ (),
include_base_last_ (),
@@ -794,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_ ("--"),
@@ -843,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_ (),
@@ -856,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_ (),
@@ -935,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_ ("--"),
@@ -987,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_ (),
@@ -1000,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_ (),
@@ -1079,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_ ("--"),
@@ -1131,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_ (),
@@ -1144,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_ (),
@@ -1223,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_ ("--"),
@@ -1277,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_ (),
@@ -1290,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_ (),
@@ -1369,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_ ("--"),
@@ -1419,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_ (),
@@ -1432,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_ (),
@@ -1511,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_ ("--"),
@@ -1584,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;
@@ -1593,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
@@ -1613,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;
@@ -1741,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;
@@ -1791,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_ >;
@@ -1810,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_ >;
@@ -1881,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_ >;
@@ -1983,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_ >;
@@ -1990,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/bootstrap/cli/options.hxx b/cli/cli/pregenerated/cli/options.hxx
index 35108fa..f44258f 100644
--- a/cli/cli/bootstrap/cli/options.hxx
+++ b/cli/cli/pregenerated/cli/options.hxx
@@ -673,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;
@@ -710,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&
@@ -785,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&
@@ -1385,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&
@@ -1553,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_;
@@ -1566,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_;
@@ -1645,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/bootstrap/cli/options.ixx b/cli/cli/pregenerated/cli/options.ixx
index ee4cbdb..54fa54a 100644
--- a/cli/cli/bootstrap/cli/options.ixx
+++ b/cli/cli/pregenerated/cli/options.ixx
@@ -672,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
{
@@ -745,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__;
@@ -895,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_;
@@ -2095,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 2148941..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_;"
@@ -322,7 +329,7 @@ generate_runtime_header (context& ctx)
<< "// position of the previous scanner should be used as the" << endl
<< "// start position of the next." << endl
<< "//" << endl
- << "class scanner"
+ << "class " << exp << "scanner"
<< "{"
<< "public:" << endl
<< "virtual" << endl
@@ -346,7 +353,7 @@ generate_runtime_header (context& ctx)
// argv_scanner
//
- os << "class argv_scanner: public scanner"
+ os << "class " << exp << "argv_scanner: public scanner"
<< "{"
<< "public:" << endl
<< "argv_scanner (int& argc," << endl
@@ -390,7 +397,7 @@ 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>&," << endl
@@ -429,7 +436,7 @@ 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
@@ -549,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&);"
@@ -632,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
@@ -662,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-source.cxx b/cli/cli/runtime-source.cxx
index d5334a0..11cd746 100644
--- a/cli/cli/runtime-source.cxx
+++ b/cli/cli/runtime-source.cxx
@@ -17,11 +17,11 @@ generate_runtime_source (context& ctx, bool complete)
<< "#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;
@@ -31,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
<< "{"
<< "}"
@@ -47,7 +51,7 @@ generate_runtime_source (context& ctx, bool complete)
<< "}"
<< "const char* unknown_option::" << endl
- << "what () const throw ()"
+ << "what () const " << nothrow
<< "{"
<< "return \"unknown option\";"
<< "}";
@@ -57,7 +61,7 @@ generate_runtime_source (context& ctx, bool complete)
os << "// unknown_argument" << endl
<< "//" << endl
<< "unknown_argument::" << endl
- << "~unknown_argument () throw ()"
+ << "~unknown_argument () " << nothrow
<< "{"
<< "}"
@@ -68,7 +72,7 @@ generate_runtime_source (context& ctx, bool complete)
<< "}"
<< "const char* unknown_argument::" << endl
- << "what () const throw ()"
+ << "what () const " << nothrow
<< "{"
<< "return \"unknown argument\";"
<< "}";
@@ -78,7 +82,7 @@ generate_runtime_source (context& ctx, bool complete)
os << "// missing_value" << endl
<< "//" << endl
<< "missing_value::" << endl
- << "~missing_value () throw ()"
+ << "~missing_value () " << nothrow
<< "{"
<< "}"
@@ -89,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\";"
<< "}";
@@ -99,7 +103,7 @@ generate_runtime_source (context& ctx, bool complete)
os << "// invalid_value" << endl
<< "//" << endl
<< "invalid_value::" << endl
- << "~invalid_value () throw ()"
+ << "~invalid_value () " << nothrow
<< "{"
<< "}"
@@ -115,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\";"
<< "}";
@@ -131,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\";"
<< "}";
@@ -143,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
<< "{"
<< "}"
@@ -155,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\";"
<< "}";
@@ -165,7 +169,7 @@ generate_runtime_source (context& ctx, bool complete)
os << "// unmatched_quote" << endl
<< "//" << endl
<< "unmatched_quote::" << endl
- << "~unmatched_quote () throw ()"
+ << "~unmatched_quote () " << nothrow
<< "{"
<< "}"
@@ -177,7 +181,7 @@ generate_runtime_source (context& ctx, bool complete)
<< "}"
<< "const char* unmatched_quote::" << endl
- << "what () const throw ()"
+ << "what () const " << nothrow
<< "{"
<< "return \"unmatched quote\";"
<< "}";
@@ -191,7 +195,7 @@ generate_runtime_source (context& ctx, bool complete)
<< "//" << endl
<< "unexpected_group::" << endl
- << "~unexpected_group () throw ()"
+ << "~unexpected_group () " << nothrow
<< "{"
<< "}"
@@ -203,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\";"
<< "}";
@@ -214,7 +218,7 @@ generate_runtime_source (context& ctx, bool complete)
<< "//" << endl
<< "group_separator::" << endl
- << "~group_separator () throw ()"
+ << "~group_separator () " << nothrow
<< "{"
<< "}"
@@ -238,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 ());"
@@ -1011,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
@@ -1194,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
@@ -1203,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 93bbd2d..84c93ab 100644
--- a/cli/doc/.gitignore
+++ b/cli/doc/.gitignore
@@ -1,5 +1,5 @@
+/cli.1
+/cli.xhtml
+
*.ps
*.pdf
-
-/cli.xhtml
-/cli.1
diff --git a/cli/doc/buildfile b/cli/doc/buildfile
index 09202ad..753b620 100644
--- a/cli/doc/buildfile
+++ b/cli/doc/buildfile
@@ -16,15 +16,37 @@ pdf{*}: extension = pdf
define html2ps: file
html2ps{*}: extension = html2ps
-./: css{default} xhtml{cli-guide} bootstrap/{man1 xhtml}{cli}
+./: css{default} xhtml{cli-guide}
-if $config.cli.develop
+# 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"
+ doc_version = [string] "$version.major.$version.minor.$version.patch"
if $version.pre_release
doc_version += "-$version.pre_release_string"
- # Let's take the last for-digit number to cover 2000-2021,2022.
+ # 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')
@@ -33,87 +55,145 @@ if $config.cli.develop
# We use the cli version we've built to generate the documentation.
#
- # Note: avoid cleaning it through this dependency.
- #
include ../cli/
+}
+
+# Note: avoid cleaning exe{cli} through this dependency.
+#
+{man1 xhtml}{cli}: ../cli/exe{cli}: clean = false
- {xhtml man1}{cli}: ../cli/exe{cli}: clean = false
-
- ./: man1{cli}: ../cli/cli{options} file{cli-prologue.1 cli-epilogue.1}
- {{
- 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 bootstrap version, copy it over. Unlike
- # the bootstrap cli case, here we don't need to cause a build restart.
- #
- if! diff $src_base/bootstrap/cli.1 $path($>) >-
- cp $path($>) $src_base/bootstrap/cli.1
- end
- }}
-
- ./: xhtml{cli}: $src_root/cli/cli{options} \
- file{cli-prologue.xhtml cli-epilogue.xhtml}
- {{
- 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/bootstrap/cli.xhtml $path($>) >-
- cp $path($>) $src_base/bootstrap/cli.xhtml
- end
- }}
+# 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}
- if ($html2ps != [null] && $ps2pdf != [null])
- {
- # Note that we include these generated files into the distribution and
- # don't remove them when cleaning in src (so that clean results in a state
- # identical to distributed).
- #
- ./: ps{cli-guide}: xhtml{cli-guide} html2ps{guide} $html2ps
- {
- options =
-
- dist = true
- clean = ($src_root != $out_root)
- }
- {{
- diag html2ps ($<[0])
- $html2ps $options -f $path($<[1]) -o $path($>) $path($<[0])
- }}
-
- ./: pdf{cli-guide}: ps{cli-guide} $ps2pdf
- {
- options = -dOptimize=true -dEmbedAllFonts=true
-
- dist = true
- clean = ($src_root != $out_root)
- }
- {{
- diag ps2pdf ($<[0])
- $ps2pdf $options $path($<[0]) $path($>)
- }}
- }
- else
- {
+ html2pdf = ($html2ps != [null] && $ps2pdf != [null])
+
+ if! $html2pdf
warn "html2ps and/or ps2pdf14 are not available, not generating .ps and .pdf documentation"
- ./: html2ps{guide} # Note: not keeping ps/pdf (could be outdated).
- }
}
-else
- ./: file{cli-prologue* cli-epilogue*} html2ps{guide} {ps pdf}{+cli-guide}
+
+./: {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/cli-guide.xhtml b/cli/doc/cli-guide.xhtml
index 675db03..94c006c 100644
--- a/cli/doc/cli-guide.xhtml
+++ b/cli/doc/cli-guide.xhtml
@@ -1084,12 +1084,12 @@ include &lt;string>;
class options
{
- std::map&lt;std::string, std::string> --map | -m;
+ std::map&lt;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/bootstrap/cli.1 b/cli/doc/pregenerated/cli.1
index d684d30..d452ca4 100644
--- a/cli/doc/bootstrap/cli.1
+++ b/cli/doc/pregenerated/cli.1
@@ -1,7 +1,7 @@
.\" Process this file with
.\" groff -man -Tascii cli.1
.\"
-.TH CLI 1 "January 2021" "CLI 1.2.0-b.8"
+.TH CLI 1 "January 2023" "CLI 1.2.0"
.SH NAME
cli \- command line interface compiler for C++
.\"
@@ -157,6 +157,9 @@ 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\.
@@ -166,6 +169,15 @@ Generate documentation in the man page format\.
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
@@ -206,6 +218,10 @@ the long usage function has the \fB*long_usage()\fR suffix\.
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
@@ -341,6 +357,12 @@ 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
@@ -409,7 +431,7 @@ Send bug reports to the cli-users@codesynthesis.com mailing list.
.\" COPYRIGHT
.\"
.SH COPYRIGHT
-Copyright (c) 2009-2021 Code Synthesis Tools CC.
+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
diff --git a/cli/doc/bootstrap/cli.xhtml b/cli/doc/pregenerated/cli.xhtml
index b62bf57..2a4f751 100644
--- a/cli/doc/bootstrap/cli.xhtml
+++ b/cli/doc/pregenerated/cli.xhtml
@@ -2,9 +2,9 @@
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
- <title>CLI 1.2.0-b.8 Compiler Command Line Manual</title>
+ <title>CLI 1.2.0 Compiler Command Line Manual</title>
- <meta name="copyright" content="&#169; 2009-2021 Code Synthesis Tools CC"/>
+ <meta name="copyright" content="&#169; 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"/>
@@ -192,6 +192,11 @@ arg+{ --foo } # 'arg+{' ...</pre>
<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
@@ -208,6 +213,18 @@ arg+{ --foo } # 'arg+{' ...</pre>
<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
@@ -258,6 +275,12 @@ arg+{ --foo } # 'arg+{' ...</pre>
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
@@ -474,6 +497,15 @@ arg+{ --foo } # 'arg+{' ...</pre>
<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
@@ -560,7 +592,7 @@ arg+{ --foo } # 'arg+{' ...</pre>
</div>
<div id="footer">
- Copyright &#169; 2009-2021 Code Synthesis Tools CC.
+ Copyright &#169; 2009-2023 Code Synthesis Tools CC.
<div id="terms">
Permission is granted to copy, distribute and/or modify this
diff --git a/cli/manifest b/cli/manifest
index 242ac9b..a90072b 100644
--- a/cli/manifest
+++ b/cli/manifest
@@ -1,6 +1,6 @@
: 1
name: cli
-version: 1.2.0-b.8+1
+version: 1.2.0
summary: Command line interface (CLI) compiler for C++
license: MIT
topics: C++, command line interface, source code generation, \
@@ -14,8 +14,8 @@ email: cli-users@codesynthesis.com ; Mailing list
build-warning-email: builds@codesynthesis.com
requires: c++14
requires: host
-depends: * build2 >= 0.14.0-
-depends: * bpkg >= 0.14.0-
+depends: * build2 >= 0.16.0
+depends: * bpkg >= 0.16.0
depends: libcutl ^1.11.0-
tests: * cli-tests == $
examples: * cli-examples == $
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