aboutsummaryrefslogtreecommitdiff
path: root/libbutl
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2019-08-17 20:35:59 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2019-08-21 18:53:32 +0300
commit375c7c9770c5407af33058170d13d9801a508b30 (patch)
tree375e54b5c740d32e8ab3f4008706b3681bddf5f4 /libbutl
parent44b929bb1611ce98e926b2f4846565338f344d48 (diff)
Don't load default options from directories which subdirectory contains --no-default-options
Diffstat (limited to 'libbutl')
-rw-r--r--libbutl/default-options.cxx74
-rw-r--r--libbutl/default-options.mxx25
-rw-r--r--libbutl/default-options.txx184
3 files changed, 205 insertions, 78 deletions
diff --git a/libbutl/default-options.cxx b/libbutl/default-options.cxx
new file mode 100644
index 0000000..69b9c42
--- /dev/null
+++ b/libbutl/default-options.cxx
@@ -0,0 +1,74 @@
+// file : libbutl/default-options.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef __cpp_modules_ts
+#include <libbutl/default-options.mxx>
+#endif
+
+#include <cassert>
+
+#ifndef __cpp_lib_modules_ts
+#include <vector>
+#endif
+
+// Other includes.
+
+#ifdef __cpp_modules_ts
+module butl.default_options;
+
+// Only imports additional to interface.
+#ifdef __clang__
+#ifdef __cpp_lib_modules_ts
+import std.core;
+#endif
+import butl.path;
+import butl.optional;
+import butl.small_vector;
+#endif
+
+#endif
+
+using namespace std;
+
+namespace butl
+{
+ optional<dir_path>
+ default_options_start (const optional<dir_path>& home,
+ const vector<dir_path>& dirs)
+ {
+ if (home)
+ assert (home->absolute () && home->normalized ());
+
+ if (dirs.empty ())
+ return nullopt;
+
+ // Use the first directory as a start.
+ //
+ auto i (dirs.begin ());
+ dir_path d (*i);
+
+ // Try to find a common prefix for each subsequent directory.
+ //
+ for (++i; i != dirs.end (); ++i)
+ {
+ bool p (false);
+
+ for (;
+ !(d.root () || (home && d == *home));
+ d = d.directory ())
+ {
+ if (i->sub (d))
+ {
+ p = true;
+ break;
+ }
+ }
+
+ if (!p)
+ return nullopt;
+ }
+
+ return d;
+ }
+}
diff --git a/libbutl/default-options.mxx b/libbutl/default-options.mxx
index 62c7f92..403df47 100644
--- a/libbutl/default-options.mxx
+++ b/libbutl/default-options.mxx
@@ -7,7 +7,10 @@
#endif
#ifndef __cpp_lib_modules_ts
+#include <vector>
+
#include <utility> // move(), forward(), make_pair()
+#include <algorithm> // reverse()
#include <system_error>
#endif
@@ -42,7 +45,7 @@ LIBBUTL_MODEXPORT namespace butl
struct default_options_files
{
small_vector<path, 2> files;
- optional<dir_path> start_dir;
+ optional<dir_path> start;
};
template <typename O>
@@ -62,7 +65,11 @@ LIBBUTL_MODEXPORT namespace butl
// Pass each default options file path to the specified function prior to
// load (can be used for tracing, etc). The function signature is:
//
- // void (const path&, bool remote)
+ // void (const path&, bool remote, bool overwrite)
+ //
+ // Note that the function may be called for the same file twice if it was
+ // later discovered that it is in fact remote. In the second call the
+ // overwrite flag will be true.
//
// Throw `pair<path, system_error>` on the underlying OS error with the
// first half referring the filesystem entry the error relates to and pass
@@ -78,13 +85,16 @@ LIBBUTL_MODEXPORT namespace butl
// .build2/local/ subdirectories of each directory. For sys_dir they are
// looked for in the directory itself (e.g., /etc/build2/).
//
- // Note that all the directories should be absolute and normalized.
+ // Note that the search is stopped at the directory containing a file with
+ // --no-default-options.
+ //
+ // Also note that all the directories should be absolute and normalized.
//
// The presence of the .git filesystem entry causes the options files in
// this directory and any of its subdirectories to be considered remote
// (note that in the current implementation this is the case even for files
// from the .build2/local/ subdirectory since the mere location is not a
- // sufficient ground to definititevly conclude that the file is not remote;
+ // sufficient ground to definitively conclude that the file is not remote;
// to be sure we would need to query the VCS or some such).
//
template <typename O, typename S, typename U, typename F>
@@ -118,6 +128,13 @@ LIBBUTL_MODEXPORT namespace butl
template <typename O, typename F>
O
merge_default_options (const default_options<O>&, const O&, F&&);
+
+ // Find a common start (parent) directory stopping at home or root
+ // (excluding).
+ //
+ LIBBUTL_SYMEXPORT optional<dir_path>
+ default_options_start (const optional<dir_path>& home_dir,
+ const std::vector<dir_path>&);
}
#include <libbutl/default-options.ixx>
diff --git a/libbutl/default-options.txx b/libbutl/default-options.txx
index 996ee33..276fa63 100644
--- a/libbutl/default-options.txx
+++ b/libbutl/default-options.txx
@@ -16,8 +16,9 @@ LIBBUTL_MODEXPORT namespace butl //@@ MOD Clang needs this for some reason.
}
// Search for and parse the options files in the specified directory and
- // its local/ subdirectory, if exists, and append the options to the
- // resulting list.
+ // its local/ subdirectory, if exists, in the reverse order and append the
+ // options to the resulting list. Return false if --no-default-options is
+ // encountered.
//
// Note that we check for the local/ subdirectory even if we don't think it
// belongs to the remote directory; the user may move things around or it
@@ -29,18 +30,20 @@ LIBBUTL_MODEXPORT namespace butl //@@ MOD Clang needs this for some reason.
// remote (see load_default_options() for details).
//
template <typename O, typename S, typename U, typename F>
- void
+ bool
load_default_options_files (const dir_path& d,
bool remote,
const small_vector<path, 2>& fs,
F&& fn,
- default_options<O>& r)
+ default_options<O>& def_ops)
{
- auto load = [&fs, &fn, &r] (const dir_path& d, bool remote)
+ bool r (true);
+
+ auto load = [&fs, &fn, &def_ops, &r] (const dir_path& d, bool remote)
{
using namespace std;
- for (const path& f: fs)
+ for (const path& f: reverse_iterate (fs))
{
path p (d / f);
@@ -48,7 +51,7 @@ LIBBUTL_MODEXPORT namespace butl //@@ MOD Clang needs this for some reason.
{
if (file_exists (p)) // Follows symlinks.
{
- fn (p, remote);
+ fn (p, remote, false /* overwrite */);
S s (p.string ());
@@ -61,9 +64,12 @@ LIBBUTL_MODEXPORT namespace butl //@@ MOD Clang needs this for some reason.
O o;
o.parse (s, U::fail, U::fail);
- r.push_back (default_options_entry<O> {move (p),
- move (o),
- remote});
+ if (o.no_default_options ())
+ r = false;
+
+ def_ops.push_back (default_options_entry<O> {move (p),
+ move (o),
+ remote});
}
}
catch (std::system_error& e)
@@ -73,55 +79,18 @@ LIBBUTL_MODEXPORT namespace butl //@@ MOD Clang needs this for some reason.
}
};
- load (d, remote);
-
dir_path ld (d / dir_path ("local"));
if (options_dir_exists (ld))
load (ld, remote);
- }
-
- // Search for and parse the options files in the specified and outer
- // directories until root/home directory (excluding) and append the options
- // to the resulting list. Return true if the directory is "remote" (i.e.,
- // belongs to a VCS repository).
- //
- template <typename O, typename S, typename U, typename F>
- bool
- load_default_options_files (const dir_path& start_dir,
- const optional<dir_path>& home_dir,
- const small_vector<path, 2>& fs,
- F&& fn,
- default_options<O>& r)
- {
- if (start_dir.root () || (home_dir && start_dir == *home_dir))
- return false;
- bool remote (load_default_options_files<O, S, U> (start_dir.directory (),
- home_dir,
- fs,
- std::forward<F> (fn),
- r));
- if (!remote)
- try
- {
- remote = git_repository (start_dir);
- }
- catch (std::system_error& e)
- {
- throw std::make_pair (start_dir / ".git", std::move (e));
- }
-
- dir_path d (start_dir / dir_path (".build2"));
-
- if (options_dir_exists (d))
- load_default_options_files<O, S, U> (d,
- remote,
- fs,
- std::forward<F> (fn),
- r);
+ // Don't load options from .build2/ if --no-default-options is encountered
+ // in .build2/local/.
+ //
+ if (r)
+ load (d, remote);
- return remote;
+ return r;
}
template <typename O, typename S, typename U, typename F>
@@ -133,42 +102,109 @@ LIBBUTL_MODEXPORT namespace butl //@@ MOD Clang needs this for some reason.
{
default_options<O> r;
- if (sys_dir)
+ // Search for and parse the options files in the specified and outer
+ // directories until root/home directory and in the system directory,
+ // stopping if --no-default-options is encountered and reversing the
+ // resulting options entry list in the end.
+ //
+ bool load (true);
+
+ if (ofs.start)
{
- assert (sys_dir->absolute () && sys_dir->normalized ());
+ assert (ofs.start->absolute () && ofs.start->normalized ());
- if (options_dir_exists (*sys_dir))
- load_default_options_files<O, S, U> (*sys_dir,
- false /* remote */,
- ofs.files,
- std::forward<F> (fn),
- r);
+ for (dir_path d (*ofs.start);
+ !(d.root () || (home_dir && d == *home_dir));
+ d = d.directory ())
+ {
+ bool remote;
+
+ try
+ {
+ remote = git_repository (d);
+ }
+ catch (std::system_error& e)
+ {
+ throw std::make_pair (d / ".git", std::move (e));
+ }
+
+ // If the directory is remote, then mark all the previously collected
+ // local entries (that belong to its subdirectories) as remote too.
+ //
+ // @@ Note that currently the local/ subdirectory of a remote
+ // directory is considered remote (see above for details). When
+ // changing that, skip entries from directories with the `local`
+ // name.
+ //
+ if (remote)
+ {
+ // We could optimize this, iterating in the reverse order until the
+ // fist remote entry. However, let's preserve the function calls
+ // order for entries being overwritten.
+ //
+ for (default_options_entry<O>& e: r)
+ {
+ if (!e.remote)
+ {
+ e.remote = true;
+
+ fn (e.file, true /* remote */, true /* overwrite */);
+ }
+ }
+ }
+
+ // If --no-default-options is encountered, then stop the files search
+ // but continue the directory traversal until the remote directory is
+ // encountered and, if that's the case, mark the already collected
+ // local entries as remote.
+ //
+ if (load)
+ {
+ dir_path od (d / dir_path (".build2"));
+
+ if (options_dir_exists (od))
+ load = load_default_options_files<O, S, U> (od,
+ remote,
+ ofs.files,
+ std::forward<F> (fn),
+ r);
+ }
+
+ if (!load && remote)
+ break;
+ }
}
if (home_dir)
{
assert (home_dir->absolute () && home_dir->normalized ());
- dir_path d (*home_dir / dir_path (".build2"));
+ if (load)
+ {
+ dir_path d (*home_dir / dir_path (".build2"));
+
+ if (options_dir_exists (d))
+ load = load_default_options_files<O, S, U> (d,
+ false /* remote */,
+ ofs.files,
+ std::forward<F> (fn),
+ r);
+ }
+ }
- if (options_dir_exists (d))
- load_default_options_files<O, S, U> (d,
+ if (sys_dir)
+ {
+ assert (sys_dir->absolute () && sys_dir->normalized ());
+
+ if (load && options_dir_exists (*sys_dir))
+ load_default_options_files<O, S, U> (*sys_dir,
false /* remote */,
ofs.files,
std::forward<F> (fn),
r);
}
- if (ofs.start_dir)
- {
- assert (ofs.start_dir->absolute () && ofs.start_dir->normalized ());
-
- load_default_options_files<O, S, U> (*ofs.start_dir,
- home_dir,
- ofs.files,
- std::forward<F> (fn),
- r);
- }
+ std::reverse (r.begin (), r.end ());
return r;
}