From 871273b374f75306a6c79c4ec067f2e4ce338172 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Mon, 7 Dec 2020 22:13:06 +0300 Subject: Add proper support for option files option to load_default_options() --- libbutl/default-options.mxx | 11 ++++--- libbutl/default-options.txx | 18 +++++++---- tests/default-options/driver.cxx | 64 ++++++++++++++++++++++------------------ tests/default-options/testscript | 33 +++++++++++++++++++++ 4 files changed, 88 insertions(+), 38 deletions(-) diff --git a/libbutl/default-options.mxx b/libbutl/default-options.mxx index aeb246d..11f7bb2 100644 --- a/libbutl/default-options.mxx +++ b/libbutl/default-options.mxx @@ -61,10 +61,12 @@ LIBBUTL_MODEXPORT namespace butl using default_options = small_vector, 4>; // Search for and load the specified list of options files in the specified - // directories returning a vector of option class instances (O). If args is - // false, only options are allowed and are parsed using scanner S in the - // U::fail mode. If args is true, then both options and arguments are - // allowed in any order with options parsed in the U::stop mode. + // directories returning a vector of option class instances (O). Read + // additional options from files referenced by the specified option + // (normally --options-file). If args is false, only options are allowed and + // are parsed using scanner S in the U::fail mode. If args is true, then + // both options and arguments are allowed in any order with options parsed + // in the U::stop mode. // // Pass each default options file path to the specified function prior to // load (can be used for tracing, etc). The function signature is: @@ -112,6 +114,7 @@ LIBBUTL_MODEXPORT namespace butl const optional& extra_dir, const default_options_files&, F&&, + const std::string& option, bool args = false); // Merge the default options/arguments and the command line diff --git a/libbutl/default-options.txx b/libbutl/default-options.txx index 5245bd6..eaf4235 100644 --- a/libbutl/default-options.txx +++ b/libbutl/default-options.txx @@ -31,6 +31,7 @@ LIBBUTL_MODEXPORT namespace butl //@@ MOD Clang needs this for some reason. template bool load_default_options_files (const dir_path& d, + const std::string& opt, bool args, bool remote, const small_vector& fs, @@ -43,7 +44,8 @@ LIBBUTL_MODEXPORT namespace butl //@@ MOD Clang needs this for some reason. bool r (true); - auto load = [args, &fs, &fn, &def_ops, &r] (const dir_path& d, bool rem) + auto load = [&opt, args, &fs, &fn, &def_ops, &r] + (const dir_path& d, bool rem) { using namespace std; @@ -57,7 +59,7 @@ LIBBUTL_MODEXPORT namespace butl //@@ MOD Clang needs this for some reason. { fn (p, rem, false /* overwrite */); - S s (p.string ()); + S s (p.string (), opt); // @@ Note that the potentially thrown exceptions (unknown option, // unexpected argument, etc) will not contain any location @@ -66,7 +68,7 @@ LIBBUTL_MODEXPORT namespace butl //@@ MOD Clang needs this for some reason. // this in CLI. // O o; - small_vector as; + small_vector as; if (args) { @@ -88,9 +90,9 @@ LIBBUTL_MODEXPORT namespace butl //@@ MOD Clang needs this for some reason. rem}); } } - catch (std::system_error& e) + catch (system_error& e) { - throw std::make_pair (move (p), std::move (e)); + throw make_pair (move (p), move (e)); } } }; @@ -116,6 +118,7 @@ LIBBUTL_MODEXPORT namespace butl //@@ MOD Clang needs this for some reason. const optional& extra_dir, const default_options_files& ofs, F&& fn, + const std::string& opt, bool args) { if (sys_dir) @@ -206,6 +209,7 @@ LIBBUTL_MODEXPORT namespace butl //@@ MOD Clang needs this for some reason. if (load_extra && extra_dir->sub (d)) { load = load_default_options_files (*extra_dir, + opt, args, false /* remote */, ofs.files, @@ -219,6 +223,7 @@ LIBBUTL_MODEXPORT namespace butl //@@ MOD Clang needs this for some reason. if (load && options_dir_exists (od)) load = load_default_options_files (od, + opt, args, remote, ofs.files, @@ -235,6 +240,7 @@ LIBBUTL_MODEXPORT namespace butl //@@ MOD Clang needs this for some reason. if (load && load_extra) load = load_default_options_files (*extra_dir, + opt, args, false /* remote */, ofs.files, @@ -247,6 +253,7 @@ LIBBUTL_MODEXPORT namespace butl //@@ MOD Clang needs this for some reason. if (options_dir_exists (d)) load = load_default_options_files (d, + opt, args, false /* remote */, ofs.files, @@ -256,6 +263,7 @@ LIBBUTL_MODEXPORT namespace butl //@@ MOD Clang needs this for some reason. if (load && sys_dir && options_dir_exists (*sys_dir)) load_default_options_files (*sys_dir, + opt, args, false /* remote */, ofs.files, diff --git a/tests/default-options/driver.cxx b/tests/default-options/driver.cxx index 574e002..b3f66bf 100644 --- a/tests/default-options/driver.cxx +++ b/tests/default-options/driver.cxx @@ -35,7 +35,7 @@ using namespace std; using namespace butl; // Usage: argv[0] [-f ] [-d ] [-s ] [-h ] -// [-x ] [-e] [-t] +// [-x ] [-a] [-e] [-t] // // Parse default options files, merge them with the command line options, and // print the resulting options to STDOUT one per line. Note that the options @@ -78,56 +78,61 @@ main (int argc, const char* argv[]) class scanner { public: - scanner (const string& f): ifs_ (f, fdopen_mode::in, ifdstream::badbit) {} + scanner (const string& f, const string& option) + : option_ (option) {load (path (f));} bool more () { - if (peeked_) - return true; - - if (!eof_) - eof_ = ifs_.peek () == ifdstream::traits_type::eof (); - - return !eof_; + return i_ < args_.size (); } string peek () { assert (more ()); - - if (peeked_) - return *peeked_; - - string s; - getline (ifs_, s); - - peeked_ = move (s); - return *peeked_; + return args_[i_]; } string next () { assert (more ()); + return args_[i_++]; + } + + private: + void + load (const path& f) + { + ifdstream is (f, fdopen_mode::in, ifdstream::badbit); - string s; - if (peeked_) + for (string l; !eof (getline (is, l)); ) { - s = move (*peeked_); - peeked_ = nullopt; - } - else - getline (ifs_, s); + if (option_ && *option_ == l) + { + assert (!eof (getline (is, l))); - return s; + // If the path of the file being parsed is not simple and the path + // of the file that needs to be loaded is relative, then complete + // the latter using the former as a base. + // + path p (l); + + if (!f.simple () && p.relative ()) + p = f.directory () / p; + + load (p); + } + else + args_.push_back (move (l)); + } } private: - ifdstream ifs_; - bool eof_ = false; - optional peeked_; + optional option_; + vector args_; + size_t i_ = 0; }; enum class unknow_mode @@ -264,6 +269,7 @@ main (int argc, const char* argv[]) cerr << (overwrite ? "overwriting " : "loading ") << (remote ? "remote " : "local ") << f << endl; }, + "--options-file", args); } catch (const invalid_argument& e) diff --git a/tests/default-options/testscript b/tests/default-options/testscript index b168ca9..09bb2ec 100644 --- a/tests/default-options/testscript +++ b/tests/default-options/testscript @@ -345,3 +345,36 @@ EOO } } + +: options-file +: +{ + d = work/.build2; + mkdir -p work/.build2; + + cat <=$d/foo; + --foo + --options-file + bar + --fox + EOI + + cat <=$d/bar; + --bar + --options-file + baz + --box + EOI + + cat <=$d/baz; + --baz + EOI + + $* -d $~/work -f foo >>EOO + --foo + --bar + --baz + --box + --fox + EOO +} -- cgit v1.1