aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2019-08-14 14:36:44 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2019-08-16 14:53:54 +0300
commited437dbd3483baa3d15d1d86d8f057d9112653b1 (patch)
treecbf3974e8b1774ccccd818442cf042e501190b09
parent80ee886ca0bd3e41434621a056125c92f31b1aea (diff)
Add support for default options files
-rw-r--r--bpkg/auth.cxx4
-rw-r--r--bpkg/bpkg.cxx159
-rw-r--r--bpkg/buildfile4
-rw-r--r--bpkg/cfg-create.cxx48
-rw-r--r--bpkg/cfg-create.hxx9
-rw-r--r--bpkg/common.cli5
-rw-r--r--bpkg/database.cxx6
-rw-r--r--bpkg/diagnostics.hxx19
-rw-r--r--bpkg/options-types.hxx2
-rw-r--r--bpkg/package.cxx4
-rw-r--r--bpkg/pkg-drop.cli2
-rw-r--r--bpkg/pkg-fetch.cxx27
-rw-r--r--bpkg/pkg-fetch.hxx4
-rw-r--r--bpkg/pkg-unpack.cxx27
-rw-r--r--bpkg/pkg-unpack.hxx4
-rw-r--r--bpkg/rep-create.cxx45
-rw-r--r--bpkg/rep-create.hxx9
-rw-r--r--bpkg/rep-fetch.cxx2
-rw-r--r--bpkg/rep-info.cxx47
-rw-r--r--bpkg/rep-info.hxx9
-rw-r--r--bpkg/types-parsers.hxx15
-rw-r--r--bpkg/types-parsers.txx15
-rw-r--r--bpkg/types.hxx9
-rw-r--r--bpkg/utility.cxx38
-rw-r--r--bpkg/utility.hxx25
-rw-r--r--tests/rep-create.testscript17
-rw-r--r--tests/rep-info.testscript45
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) ? &current_dir : nullptr;
+ conf = exists (bpkg_dir) ? &current_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) ? &current_dir : nullptr;
+ conf = exists (bpkg_dir) ? &current_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) ? &current_dir : nullptr;
+ conf = exists (bpkg_dir) ? &current_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
+ }
+}