From 375c7c9770c5407af33058170d13d9801a508b30 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Sat, 17 Aug 2019 20:35:59 +0300 Subject: Don't load default options from directories which subdirectory contains --no-default-options --- libbutl/default-options.cxx | 74 ++++++++++++++++++ libbutl/default-options.mxx | 25 +++++- libbutl/default-options.txx | 184 ++++++++++++++++++++++++++------------------ 3 files changed, 205 insertions(+), 78 deletions(-) create mode 100644 libbutl/default-options.cxx (limited to 'libbutl') 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 +#endif + +#include + +#ifndef __cpp_lib_modules_ts +#include +#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 + default_options_start (const optional& home, + const vector& 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 + #include // move(), forward(), make_pair() +#include // reverse() #include #endif @@ -42,7 +45,7 @@ LIBBUTL_MODEXPORT namespace butl struct default_options_files { small_vector files; - optional start_dir; + optional start; }; template @@ -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` 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 @@ -118,6 +128,13 @@ LIBBUTL_MODEXPORT namespace butl template O merge_default_options (const default_options&, const O&, F&&); + + // Find a common start (parent) directory stopping at home or root + // (excluding). + // + LIBBUTL_SYMEXPORT optional + default_options_start (const optional& home_dir, + const std::vector&); } #include 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 - void + bool load_default_options_files (const dir_path& d, bool remote, const small_vector& fs, F&& fn, - default_options& r) + default_options& 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 {move (p), - move (o), - remote}); + if (o.no_default_options ()) + r = false; + + def_ops.push_back (default_options_entry {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 - bool - load_default_options_files (const dir_path& start_dir, - const optional& home_dir, - const small_vector& fs, - F&& fn, - default_options& r) - { - if (start_dir.root () || (home_dir && start_dir == *home_dir)) - return false; - bool remote (load_default_options_files (start_dir.directory (), - home_dir, - fs, - std::forward (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 (d, - remote, - fs, - std::forward (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 @@ -133,42 +102,109 @@ LIBBUTL_MODEXPORT namespace butl //@@ MOD Clang needs this for some reason. { default_options 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 (*sys_dir, - false /* remote */, - ofs.files, - std::forward (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& 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 (od, + remote, + ofs.files, + std::forward (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 (d, + false /* remote */, + ofs.files, + std::forward (fn), + r); + } + } - if (options_dir_exists (d)) - load_default_options_files (d, + if (sys_dir) + { + assert (sys_dir->absolute () && sys_dir->normalized ()); + + if (load && options_dir_exists (*sys_dir)) + load_default_options_files (*sys_dir, false /* remote */, ofs.files, std::forward (fn), r); } - if (ofs.start_dir) - { - assert (ofs.start_dir->absolute () && ofs.start_dir->normalized ()); - - load_default_options_files (*ofs.start_dir, - home_dir, - ofs.files, - std::forward (fn), - r); - } + std::reverse (r.begin (), r.end ()); return r; } -- cgit v1.1