diff options
author | Karen Arutyunov <karen@codesynthesis.com> | 2019-08-14 14:36:44 +0300 |
---|---|---|
committer | Karen Arutyunov <karen@codesynthesis.com> | 2019-08-16 14:53:54 +0300 |
commit | ed437dbd3483baa3d15d1d86d8f057d9112653b1 (patch) | |
tree | cbf3974e8b1774ccccd818442cf042e501190b09 | |
parent | 80ee886ca0bd3e41434621a056125c92f31b1aea (diff) |
Add support for default options files
-rw-r--r-- | bpkg/auth.cxx | 4 | ||||
-rw-r--r-- | bpkg/bpkg.cxx | 159 | ||||
-rw-r--r-- | bpkg/buildfile | 4 | ||||
-rw-r--r-- | bpkg/cfg-create.cxx | 48 | ||||
-rw-r--r-- | bpkg/cfg-create.hxx | 9 | ||||
-rw-r--r-- | bpkg/common.cli | 5 | ||||
-rw-r--r-- | bpkg/database.cxx | 6 | ||||
-rw-r--r-- | bpkg/diagnostics.hxx | 19 | ||||
-rw-r--r-- | bpkg/options-types.hxx | 2 | ||||
-rw-r--r-- | bpkg/package.cxx | 4 | ||||
-rw-r--r-- | bpkg/pkg-drop.cli | 2 | ||||
-rw-r--r-- | bpkg/pkg-fetch.cxx | 27 | ||||
-rw-r--r-- | bpkg/pkg-fetch.hxx | 4 | ||||
-rw-r--r-- | bpkg/pkg-unpack.cxx | 27 | ||||
-rw-r--r-- | bpkg/pkg-unpack.hxx | 4 | ||||
-rw-r--r-- | bpkg/rep-create.cxx | 45 | ||||
-rw-r--r-- | bpkg/rep-create.hxx | 9 | ||||
-rw-r--r-- | bpkg/rep-fetch.cxx | 2 | ||||
-rw-r--r-- | bpkg/rep-info.cxx | 47 | ||||
-rw-r--r-- | bpkg/rep-info.hxx | 9 | ||||
-rw-r--r-- | bpkg/types-parsers.hxx | 15 | ||||
-rw-r--r-- | bpkg/types-parsers.txx | 15 | ||||
-rw-r--r-- | bpkg/types.hxx | 9 | ||||
-rw-r--r-- | bpkg/utility.cxx | 38 | ||||
-rw-r--r-- | bpkg/utility.hxx | 25 | ||||
-rw-r--r-- | tests/rep-create.testscript | 17 | ||||
-rw-r--r-- | tests/rep-info.testscript | 45 |
27 files changed, 561 insertions, 39 deletions
diff --git a/bpkg/auth.cxx b/bpkg/auth.cxx index 3fcb24b..d667c01 100644 --- a/bpkg/auth.cxx +++ b/bpkg/auth.cxx @@ -635,7 +635,7 @@ namespace bpkg fail << "--trust-yes and --trust-no are mutually exclusive"; if (conf != nullptr && conf->empty ()) - conf = dir_exists (bpkg_dir) ? ¤t_dir : nullptr; + conf = exists (bpkg_dir) ? ¤t_dir : nullptr; assert (conf == nullptr || !conf->empty ()); @@ -682,7 +682,7 @@ namespace bpkg tracer trace ("authenticate_repository"); if (conf != nullptr && conf->empty ()) - conf = dir_exists (bpkg_dir) ? ¤t_dir : nullptr; + conf = exists (bpkg_dir) ? ¤t_dir : nullptr; assert (conf == nullptr || !conf->empty ()); diff --git a/bpkg/bpkg.cxx b/bpkg/bpkg.cxx index fdcb8bb..be38297 100644 --- a/bpkg/bpkg.cxx +++ b/bpkg/bpkg.cxx @@ -7,7 +7,8 @@ #endif #include <iostream> -#include <exception> // set_terminate(), terminate_handler +#include <exception> // set_terminate(), terminate_handler +#include <type_traits> // enable_if, is_base_of #include <libbutl/backtrace.mxx> // backtrace() @@ -52,6 +53,91 @@ using namespace bpkg; namespace bpkg { + // Deduce the default options files and the directory to start searching + // from based on the command line options and arguments. + // + // default_options_files + // options_files (const char* cmd, const xxx_options&, const strings& args); + + // Return the default options files and the configuration directory as a + // search start directory for commands that operate on a configuration (and + // thus have their options derived from configuration_options). + // + // Note that we don't support package-level default options files. + // + static inline default_options_files + options_files (const char* cmd, + const configuration_options& o, + const strings&) + { + // bpkg.options + // bpkg-<cmd>.options + + return default_options_files { + {path ("bpkg.options"), path (string ("bpkg-") + cmd + ".options")}, + normalize (o.directory (), "configuration")}; + } + + // Return the default options files without search start directory for + // commands that don't operate on a configuration (and thus their options + // are not derived from configuration_options). + // + template <typename O> + static inline typename enable_if<!is_base_of<configuration_options, + O>::value, + default_options_files>::type + options_files (const char* cmd, + const O&, + const strings&) + { + // bpkg.options + // bpkg-<cmd>.options + + return default_options_files { + {path ("bpkg.options"), path (string ("bpkg-") + cmd + ".options")}, + nullopt /* start_dir */}; + } + + // Merge the default options and the command line options. Fail if options + // used to deduce the default options files or the start directory appear in + // an options file (or for other good reasons). + // + // xxx_options + // merge_options (const default_options<xxx_options>&, const xxx_options&); + + // Merge the default options and the command line options for commands + // that operate on configuration. Fail if --directory|-d appears in the + // options file to avoid the chicken and egg problem. + // + template <typename O> + static inline typename enable_if<is_base_of<configuration_options, + O>::value, + O>::type + merge_options (const default_options<O>& defs, const O& cmd) + { + return merge_default_options ( + defs, + cmd, + [] (const default_options_entry<O>& e, const O&) + { + if (e.options.directory_specified ()) + fail (e.file) << "--directory|-d in default options file"; + }); + } + + // Merge the default options and the command line options for commands that + // allow any option in the options files (and thus their options are not + // derived from configuration_options). + // + template <typename O> + static inline typename enable_if<!is_base_of<configuration_options, + O>::value, + O>::type + merge_options (const default_options<O>& defs, const O& cmd) + { + return merge_default_options (defs, cmd); + } + int main (int argc, char* argv[]); } @@ -77,9 +163,12 @@ static O init (const common_options& co, cli::group_scanner& scan, strings& args, + const char* cmd, bool keep_sep, bool tmp) { + tracer trace ("init"); + O o; static_cast<common_options&> (o) = co; @@ -126,14 +215,48 @@ init (const common_options& co, } } + // Note that the diagnostics verbosity level can only be calculated after + // default options are loaded and merged (see below). Thus, to trace the + // default options files search, we refer to the verbosity level specified + // on the command line. + // + auto verbosity = [&o] () + { + return o.verbose_specified () + ? o.verbose () + : o.V () ? 3 : o.v () ? 2 : o.quiet () ? 0 : 1; + }; + + // Handle default options files. + // + // Note: don't need to use group_scaner (no arguments in options files). + // + if (!o.no_default_options ()) // Command line option. + try + { + o = merge_options ( + load_default_options<O, cli::argv_file_scanner, cli::unknown_mode> ( + nullopt /* sys_dir */, + path::home_directory (), + options_files (cmd, o, args), + [&trace, &verbosity] (const path& f, bool remote) + { + if (verbosity () >= 3) + trace << "loading " << (remote ? "remote " : "local ") << f; + }), + o); + } + catch (const system_error& e) + { + fail << "unable to load default options files: " << e; + } + // Global initializations. // // Diagnostics verbosity. // - verb = o.verbose_specified () - ? o.verbose () - : o.V () ? 3 : o.v () ? 2 : o.quiet () ? 0 : 1; + verb = verbosity (); // Temporary directory. // @@ -231,6 +354,7 @@ try return help (init<help_options> (co, scan, argsv, + "help", false /* keep_sep */, false /* tmp */), "", @@ -263,6 +387,7 @@ try ho = init<help_options> (co, scan, argsv, + "help", false /* keep_sep */, false /* tmp */); @@ -310,6 +435,7 @@ try // r = pkg_verify (init<pkg_verify_options> (co, // scan, // argsv, + // "pkg-verify", // false /* keep_sep */, // true /* tmp */), // args); @@ -317,16 +443,21 @@ try // break; // } // -#define COMMAND_IMPL(NP, SP, CMD, SEP, TMP) \ - if (cmd.NP##CMD ()) \ - { \ - if (h) \ - r = help (ho, SP#CMD, print_bpkg_##NP##CMD##_usage); \ - else \ - r = NP##CMD (init<NP##CMD##_options> (co, scan, argsv, SEP, TMP), \ - args); \ - \ - break; \ +#define COMMAND_IMPL(NP, SP, CMD, SEP, TMP) \ + if (cmd.NP##CMD ()) \ + { \ + if (h) \ + r = help (ho, SP#CMD, print_bpkg_##NP##CMD##_usage); \ + else \ + r = NP##CMD (init<NP##CMD##_options> (co, \ + scan, \ + argsv, \ + SP#CMD, \ + SEP, \ + TMP), \ + args); \ + \ + break; \ } // cfg-* commands diff --git a/bpkg/buildfile b/bpkg/buildfile index adb6d3b..be4a754 100644 --- a/bpkg/buildfile +++ b/bpkg/buildfile @@ -156,8 +156,8 @@ if $cli.configured --guard-prefix BPKG --cxx-prologue "#include <bpkg/types-parsers.hxx>" \ --cli-namespace bpkg::cli --generate-vector-scanner --generate-file-scanner \ --generate-group-scanner --keep-separator --generate-specifier \ ---generate-parse --page-usage 'bpkg::print_$name$_' --ansi-color \ ---include-base-last --option-length 24 +--generate-parse --generate-merge --page-usage 'bpkg::print_$name$_' \ +--ansi-color --include-base-last --option-length 24 cli.cxx{common-options}: cli.options += --short-usage --long-usage # Both. cli.cxx{bpkg-options}: cli.options += --short-usage --suppress-undocumented diff --git a/bpkg/cfg-create.cxx b/bpkg/cfg-create.cxx index 14fb982..1bc1a57 100644 --- a/bpkg/cfg-create.cxx +++ b/bpkg/cfg-create.cxx @@ -161,7 +161,8 @@ namespace bpkg if (verb && !o.no_result ()) { - c.complete ().normalize (); + normalize (c, "configuration"); + if (o.existing ()) text << "initialized existing configuration in " << c; else @@ -170,4 +171,49 @@ namespace bpkg return 0; } + + default_options_files + options_files (const char*, const cfg_create_options& o, const strings&) + { + // bpkg.options + // bpkg-cfg-create.options + + // Use the configuration parent directory as a start directory. + // + optional<dir_path> start_dir; + + // Let cfg_create() complain later for the root directory used as a + // configuration directory. + // + dir_path d (normalize (o.directory (), "configuration")); + if (!d.root ()) + start_dir = d.directory (); + + return default_options_files { + {path ("bpkg.options"), path ("bpkg-cfg-create.options")}, + move (start_dir)}; + } + + cfg_create_options + merge_options (const default_options<cfg_create_options>& defs, + const cfg_create_options& cmd) + { + return merge_default_options ( + defs, + cmd, + [] (const default_options_entry<cfg_create_options>& e, + const cfg_create_options&) + { + const cfg_create_options& o (e.options); + + auto forbid = [&e] (const char* opt, bool specified) + { + if (specified) + fail (e.file) << opt << " in default options file"; + }; + + forbid ("--directory|-d", o.directory_specified ()); + forbid ("--wipe", o.wipe ()); // Dangerous. + }); + } } diff --git a/bpkg/cfg-create.hxx b/bpkg/cfg-create.hxx index 888335c..ca5522c 100644 --- a/bpkg/cfg-create.hxx +++ b/bpkg/cfg-create.hxx @@ -14,6 +14,15 @@ namespace bpkg { int cfg_create (const cfg_create_options&, cli::scanner& args); + + default_options_files + options_files (const char* cmd, + const cfg_create_options&, + const strings& args); + + cfg_create_options + merge_options (const default_options<cfg_create_options>&, + const cfg_create_options&); } #endif // BPKG_CFG_CREATE_HXX diff --git a/bpkg/common.cli b/bpkg/common.cli index 1fbb0e9..9b5df37 100644 --- a/bpkg/common.cli +++ b/bpkg/common.cli @@ -366,5 +366,10 @@ namespace bpkg the shell escaping and quoting is not required. Repeat this option to specify more than one options file." } + + bool --no-default-options + { + "Don't load default options files." + } }; } diff --git a/bpkg/database.cxx b/bpkg/database.cxx index ba4ab10..a66b3a3 100644 --- a/bpkg/database.cxx +++ b/bpkg/database.cxx @@ -30,11 +30,7 @@ namespace bpkg public: conn_factory (const dir_path& d) { - dir_path v (d); - v.complete (); - v.normalize (); - - setenv (open_name, v.string ()); + setenv (open_name, normalize (d, "configuration").string ()); } virtual diff --git a/bpkg/diagnostics.hxx b/bpkg/diagnostics.hxx index 5d0fbd1..2d3ee7f 100644 --- a/bpkg/diagnostics.hxx +++ b/bpkg/diagnostics.hxx @@ -5,7 +5,7 @@ #ifndef BPKG_DIAGNOSTICS_HXX #define BPKG_DIAGNOSTICS_HXX -#include <utility> // forward() +#include <utility> // move(), forward() #include <odb/tracer.hxx> @@ -98,8 +98,8 @@ namespace bpkg // Zero lines or columns are not printed. // explicit - location (string f, uint64_t l, uint64_t c) - : file (move (f)), line (l), column (c) {} + location (string f, uint64_t l = 0, uint64_t c = 0) + : file (std::move (f)), line (l), column (c) {} location () = default; @@ -118,6 +118,11 @@ namespace bpkg const location& l) : type_ (type), name_ (name), loc_ (l) {} + location_prologue_base (const char* type, + const char* name, + const path& f) + : type_ (type), name_ (name), loc_ (f.string ()) {} + void operator() (const diag_record& r) const; @@ -151,10 +156,18 @@ namespace bpkg return location_prologue (epilogue_, type_, name_, l); } + location_prologue + operator() (const path& f) const + { + return location_prologue (epilogue_, type_, name_, f); + } + template <typename L> location_prologue operator() (const L& l) const { + // get_location() is the user-supplied ADL-searched function. + // return location_prologue ( epilogue_, type_, name_, get_location (l, data_)); } diff --git a/bpkg/options-types.hxx b/bpkg/options-types.hxx index ebe2734..b6d9756 100644 --- a/bpkg/options-types.hxx +++ b/bpkg/options-types.hxx @@ -21,7 +21,7 @@ namespace bpkg // Qualified options. // // An option that uses this type can have its values qualified using the - // <qualifier>:<value> form, for example, '--option foo:bar' An unqualified + // <qualifier>:<value> form, for example, '--option foo:bar'. An unqualified // value that contains a colon can be specified as qualified with an empty // qualifier, for example, '--option :http://example.org'. Unqualified // values apply to all the qualifiers in the order specified. diff --git a/bpkg/package.cxx b/bpkg/package.cxx index be03c2c..0ac2577 100644 --- a/bpkg/package.cxx +++ b/bpkg/package.cxx @@ -330,9 +330,9 @@ namespace bpkg // generally be completed against the configuration directory (unlikely // but possible), that can be relative and/or not normalized. // - src_root.complete ().normalize (); + normalize (src_root, "package source"); - changed = src_root != dir_path (d).complete ().normalize (); + changed = src_root != normalize (d, "package source"); } return !changed diff --git a/bpkg/pkg-drop.cli b/bpkg/pkg-drop.cli index 50a8da4..7002d57 100644 --- a/bpkg/pkg-drop.cli +++ b/bpkg/pkg-drop.cli @@ -21,7 +21,7 @@ namespace bpkg The \cb{pkg-drop} command drops one or more packages from the configuration. If the packages being dropped still have dependent - packages, then those will have to be drop as well and you will be + packages, then those will have to be dropped as well and you will be prompted to confirm. And if the packages being dropped have dependency packages that would otherwise no longer be used, then they will be dropped as well unless the \c{\b{--keep-unused}|\b{-K}} option is diff --git a/bpkg/pkg-fetch.cxx b/bpkg/pkg-fetch.cxx index 28e2fac..b86ac85 100644 --- a/bpkg/pkg-fetch.cxx +++ b/bpkg/pkg-fetch.cxx @@ -43,8 +43,8 @@ namespace bpkg // If the archive is inside the configuration, use the relative path. // This way we can move the configuration around. // - c.complete ().normalize (); - a.complete ().normalize (); + normalize (c, "configuration"); + normalize (a, "archive"); if (a.sub (c)) a = a.leaf (c); @@ -346,4 +346,27 @@ namespace bpkg return 0; } + + pkg_fetch_options + merge_options (const default_options<pkg_fetch_options>& defs, + const pkg_fetch_options& cmd) + { + return merge_default_options ( + defs, + cmd, + [] (const default_options_entry<pkg_fetch_options>& e, + const pkg_fetch_options&) + { + const pkg_fetch_options& o (e.options); + + auto forbid = [&e] (const char* opt, bool specified) + { + if (specified) + fail (e.file) << opt << " in default options file"; + }; + + forbid ("--directory|-d", o.directory_specified ()); + forbid ("--purge|-p", o.purge ()); // Dangerous. + }); + } } diff --git a/bpkg/pkg-fetch.hxx b/bpkg/pkg-fetch.hxx index 91ef172..cdd5256 100644 --- a/bpkg/pkg-fetch.hxx +++ b/bpkg/pkg-fetch.hxx @@ -43,6 +43,10 @@ namespace bpkg version, bool replace, bool simulate); + + pkg_fetch_options + merge_options (const default_options<pkg_fetch_options>&, + const pkg_fetch_options&); } #endif // BPKG_PKG_FETCH_HXX diff --git a/bpkg/pkg-unpack.cxx b/bpkg/pkg-unpack.cxx index f289c2f..801496d 100644 --- a/bpkg/pkg-unpack.cxx +++ b/bpkg/pkg-unpack.cxx @@ -90,8 +90,8 @@ namespace bpkg // If the package is inside the configuration, use the relative path. // This way we can move the configuration around. // - c.complete ().normalize (); - d.complete ().normalize (); + normalize (c, "configuration"); + normalize (d, "package"); if (d.sub (c)) d = d.leaf (c); @@ -428,4 +428,27 @@ namespace bpkg return 0; } + + pkg_unpack_options + merge_options (const default_options<pkg_unpack_options>& defs, + const pkg_unpack_options& cmd) + { + return merge_default_options ( + defs, + cmd, + [] (const default_options_entry<pkg_unpack_options>& e, + const pkg_unpack_options&) + { + const pkg_unpack_options& o (e.options); + + auto forbid = [&e] (const char* opt, bool specified) + { + if (specified) + fail (e.file) << opt << " in default options file"; + }; + + forbid ("--directory|-d", o.directory_specified ()); + forbid ("--purge|-p", o.purge ()); // Dangerous. + }); + } } diff --git a/bpkg/pkg-unpack.hxx b/bpkg/pkg-unpack.hxx index 1ae3806..5f8f4b4 100644 --- a/bpkg/pkg-unpack.hxx +++ b/bpkg/pkg-unpack.hxx @@ -52,6 +52,10 @@ namespace bpkg version, bool replace, bool simulate); + + pkg_unpack_options + merge_options (const default_options<pkg_unpack_options>&, + const pkg_unpack_options&); } #endif // BPKG_PKG_UNPACK_HXX diff --git a/bpkg/rep-create.cxx b/bpkg/rep-create.cxx index f9d239b..f0df1c0 100644 --- a/bpkg/rep-create.cxx +++ b/bpkg/rep-create.cxx @@ -246,8 +246,7 @@ namespace bpkg if (verb && !o.no_result ()) { - d.complete (); - d.normalize (); + normalize (d, "repository"); text << pm.size () << " package(s) in " << d; } @@ -257,4 +256,46 @@ namespace bpkg { fail << "invalid path: '" << e.path << "'" << endf; } + + default_options_files + options_files (const char*, const rep_create_options&, const strings& args) + { + // bpkg.options + // bpkg-rep-create.options + + // Use the repository directory as a start directory. + // + optional<dir_path> start_dir; + + // Let rep_create() complain later for invalid repository directory. + // + try + { + dir_path d (!args.empty () ? args[0] : "."); + if (!d.empty ()) + start_dir = move (normalize (d, "repository")); + } + catch (const invalid_path&) {} + + return default_options_files { + {path ("bpkg.options"), path ("bpkg-rep-create.options")}, + move (start_dir)}; + } + + rep_create_options + merge_options (const default_options<rep_create_options>& defs, + const rep_create_options& cmd) + { + return merge_default_options ( + defs, + cmd, + [] (const default_options_entry<rep_create_options>& e, + const rep_create_options&) + { + // For security reason. + // + if (e.options.key_specified () && e.remote) + fail (e.file) << "--key <name> in remote default options file"; + }); + } } diff --git a/bpkg/rep-create.hxx b/bpkg/rep-create.hxx index df98dba..44febb7 100644 --- a/bpkg/rep-create.hxx +++ b/bpkg/rep-create.hxx @@ -14,6 +14,15 @@ namespace bpkg { int rep_create (const rep_create_options&, cli::scanner& args); + + default_options_files + options_files (const char* cmd, + const rep_create_options&, + const strings& args); + + rep_create_options + merge_options (const default_options<rep_create_options>&, + const rep_create_options&); } #endif // BPKG_REP_CREATE_HXX diff --git a/bpkg/rep-fetch.cxx b/bpkg/rep-fetch.cxx index fd47198..413358a 100644 --- a/bpkg/rep-fetch.cxx +++ b/bpkg/rep-fetch.cxx @@ -362,7 +362,7 @@ namespace bpkg bool ev) { if (conf != nullptr && conf->empty ()) - conf = dir_exists (bpkg_dir) ? ¤t_dir : nullptr; + conf = exists (bpkg_dir) ? ¤t_dir : nullptr; assert (conf == nullptr || !conf->empty ()); diff --git a/bpkg/rep-info.cxx b/bpkg/rep-info.cxx index 0f64565..eeb93fe 100644 --- a/bpkg/rep-info.cxx +++ b/bpkg/rep-info.cxx @@ -374,4 +374,51 @@ namespace bpkg return 0; } + + default_options_files + options_files (const char*, const rep_info_options& o, const strings&) + { + // bpkg.options + // bpkg-rep-info.options + + // If bpkg-rep-info operates in the configuration directory, then use it + // as a search start directory. + // + optional<dir_path> start_dir; + + // Let rep_info() complain later for invalid configuration directory. + // + try + { + dir_path d; + + if (o.directory_specified ()) + d = dir_path (o.directory ()); + else if (exists (bpkg_dir)) + d = current_dir; + + if (!d.empty ()) + start_dir = move (normalize (d, "configuration")); + } + catch (const invalid_path&) {} + + return default_options_files { + {path ("bpkg.options"), path ("bpkg-rep-info.options")}, + move (start_dir)}; + } + + rep_info_options + merge_options (const default_options<rep_info_options>& defs, + const rep_info_options& cmd) + { + return merge_default_options ( + defs, + cmd, + [] (const default_options_entry<rep_info_options>& e, + const rep_info_options&) + { + if (e.options.directory_specified ()) + fail (e.file) << "--directory|-d in default options file"; + }); + } } diff --git a/bpkg/rep-info.hxx b/bpkg/rep-info.hxx index cb82c0c..4f62a1e 100644 --- a/bpkg/rep-info.hxx +++ b/bpkg/rep-info.hxx @@ -14,6 +14,15 @@ namespace bpkg { int rep_info (const rep_info_options&, cli::scanner& args); + + default_options_files + options_files (const char* cmd, + const rep_info_options&, + const strings& args); + + rep_info_options + merge_options (const default_options<rep_info_options>&, + const rep_info_options&); } #endif // BPKG_REP_INFO_HXX diff --git a/bpkg/types-parsers.hxx b/bpkg/types-parsers.hxx index 409f1a9..343714c 100644 --- a/bpkg/types-parsers.hxx +++ b/bpkg/types-parsers.hxx @@ -24,6 +24,9 @@ namespace bpkg { static void parse (path&, bool&, scanner&); + + static void + merge (path& b, const path& a) {b = a;} }; template <> @@ -31,6 +34,9 @@ namespace bpkg { static void parse (dir_path&, bool&, scanner&); + + static void + merge (dir_path& b, const dir_path& a) {b = a;} }; template <> @@ -38,6 +44,9 @@ namespace bpkg { static void parse (auth&, bool&, scanner&); + + static void + merge (auth& b, const auth& a) {b = a;} }; template <> @@ -45,6 +54,9 @@ namespace bpkg { static void parse (repository_type&, bool&, scanner&); + + static void + merge (repository_type& b, const repository_type& a) {b = a;} }; template <const char* const* Q, typename V> @@ -52,6 +64,9 @@ namespace bpkg { static void parse (qualified_option<Q, V>&, bool&, scanner&); + + static void + merge (qualified_option<Q, V>&, const qualified_option<Q, V>&); }; } } diff --git a/bpkg/types-parsers.txx b/bpkg/types-parsers.txx index df6796f..f187fc7 100644 --- a/bpkg/types-parsers.txx +++ b/bpkg/types-parsers.txx @@ -65,5 +65,20 @@ namespace bpkg } } } + + template <const char* const* Q, typename V> + void parser<qualified_option<Q, V>>:: + merge (qualified_option<Q, V>& b, const qualified_option<Q, V>& a) + { + for (const auto& o: a) + { + auto i (b.find (o.first)); + + if (i != b.end ()) + i->second = o.second; + else + b.emplace (o.first, o.second); + } + } } } diff --git a/bpkg/types.hxx b/bpkg/types.hxx index eaf07d2..667ed76 100644 --- a/bpkg/types.hxx +++ b/bpkg/types.hxx @@ -24,10 +24,11 @@ #include <libbutl/path.mxx> #include <libbutl/process.mxx> -#include <libbutl/utility.mxx> // compare_reference_target +#include <libbutl/utility.mxx> // compare_reference_target #include <libbutl/optional.mxx> #include <libbutl/fdstream.mxx> #include <libbutl/small-vector.mxx> +#include <libbutl/default-options.mxx> namespace bpkg { @@ -108,6 +109,12 @@ namespace bpkg using butl::ifdstream; using butl::ofdstream; using butl::fdstream_mode; + + // <libbutl/default-options.mxx> + // + using butl::default_options_files; + using butl::default_options_entry; + using butl::default_options; } // In order to be found (via ADL) these have to be either in std:: or in diff --git a/bpkg/utility.cxx b/bpkg/utility.cxx index 91fc49e..3cb9fb5 100644 --- a/bpkg/utility.cxx +++ b/bpkg/utility.cxx @@ -83,6 +83,44 @@ namespace bpkg } } + path& + normalize (path& f, const char* what) + { + try + { + f.complete ().normalize (); + } + catch (const invalid_path& e) + { + fail << "invalid " << what << " path " << e.path; + } + catch (const system_error& e) + { + fail << "unable to obtain current directory: " << e; + } + + return f; + } + + dir_path& + normalize (dir_path& d, const char* what) + { + try + { + d.complete ().normalize (); + } + catch (const invalid_path& e) + { + fail << "invalid " << what << " directory " << e.path; + } + catch (const system_error& e) + { + fail << "unable to obtain current directory: " << e; + } + + return d; + } + bool stderr_term; bool diff --git a/bpkg/utility.hxx b/bpkg/utility.hxx index 8bb8fd8..1e82f44 100644 --- a/bpkg/utility.hxx +++ b/bpkg/utility.hxx @@ -88,6 +88,31 @@ namespace bpkg void clean_tmp (bool ignore_errors); + // Path. + // + // Normalize a path. Also make the relative path absolute using the current + // directory. + // + path& + normalize (path&, const char* what); + + inline path + normalize (const path& f, const char* what) + { + path r (f); + return move (normalize (r, what)); + } + + dir_path& + normalize (dir_path&, const char* what); + + inline dir_path + normalize (const dir_path& d, const char* what) + { + dir_path r (d); + return move (normalize (r, what)); + } + // Progress. // extern bool stderr_term; // True if stderr is a terminal. diff --git a/tests/rep-create.testscript b/tests/rep-create.testscript index 487acf0..a7b9e19 100644 --- a/tests/rep-create.testscript +++ b/tests/rep-create.testscript @@ -147,6 +147,23 @@ EOO } + : remote-key-option-failure + : + : Test that bpkg-rep-create fails for the remote --key option. + : + { + $clone_rep; + + touch .git; # Pretend we are in the git repository. + + mkdir .build2; + echo "--key $key" >=.build2/bpkg-rep-create.options; + + $* 1/stable/ 2>>/~%EOE% != 0 + %.+/bpkg-rep-create.options: error: --key <name> in remote default options file% + EOE + } + : without-key : $clone_rep; diff --git a/tests/rep-info.testscript b/tests/rep-info.testscript index 822dada..ed1f1d3 100644 --- a/tests/rep-info.testscript +++ b/tests/rep-info.testscript @@ -353,3 +353,48 @@ else %fragment: \.+% EOO } + +: default-options-files +: +{ + : specified-dir + : + { + $cfg_create -d cfg 2>! &cfg/***; + + mkdir cfg/.build2; + + echo '--directory .' >= cfg/.build2/bpkg-rep-info.options; + + $* $rep/testing -d cfg 2>>/~%EOE%d != 0; + %\.+/specified-dir/cfg/.build2/bpkg-rep-info.options: error: --directory\|-d in default options file% + EOE + + # Disable default options files loading. + # + $* --no-default-options --name $rep/testing -d cfg >>"EOO" + pkg:build2.org/rep-info/testing ($rep/testing) + EOO + } + + : current-dir + : + { + mkdir .bpkg; # Pretend we are in the configuration directory. + mkdir .build2; + + echo '--directory .' >= .build2/bpkg-rep-info.options; + + # Load options from the current (configuration) directory. + # + $* $rep/testing 2>>/~%EOE%d != 0; + %\.+/current-dir/.build2/bpkg-rep-info.options: error: --directory\|-d in default options file% + EOE + + # Disable loading options from the current (configuration) directory. + # + $* --name -d '' $rep/testing >>"EOO" + pkg:build2.org/rep-info/testing ($rep/testing) + EOO + } +} |