[vcpkg] Make Filesystem::remove_all faster #7570

I added benchmarks to measure how fast the parallel remove_all code was
-- it turns out, about 3x slower than stdfs::remove_all. Since this was
the case, I removed all of the parallelism and rewrote it serially, and
ended up about 30% faster than stdfs::remove_all (in addition to
supporting symlinks).

In addition, I did the following three orthogonal changes:
  - simplified the work queue, basing it on Billy O'Neal's idea
  - Fix warnings on older versions of compilers in tests, by splitting
    the pragmas out of pch.h.
  - Ran clang-format on some files

In fixing up remove_all, the following changes were made:
  - On Windows, regular symlinks and directory symlinks are distinct;
    as an example, to remove directory symlinks (and junctions, for that
    matter), one must use RemoveDirectory. Only on Windows, I added new
    `file_type` and `file_status` types, with `file_type` including a new
    `directory_symlink` enumerator, and `file_status` being exactly the
    same as the old one except using the new `file_type`. On Unix, I
    didn't make that change since they don't make a distinction.
  - I added new `symlink_status` and `status` functions which use the
    new `file_status` on Windows.
  - I made `Filesystem::exists` call `fs::exists(status(p))`, as opposed
    to the old version which called `stdfs::exists` directly.
  - Added benchmarks to `vcpkg-test/files.cpp`. They test the
    performance of `remove_all` on small directories (~20 files), with
    symlinks and without, and on large directories (~2000 files), with
    symlinks and without.
This commit is contained in:
Nicole Mazzuca 2019-08-02 16:49:45 -07:00 committed by nicole mazzuca
parent 65cb5cd00c
commit e79f0dc532
33 changed files with 812 additions and 686 deletions

2
.gitignore vendored
View File

@ -11,7 +11,7 @@
*.userosscache
*.sln.docstates
toolsrc/out
toolsrc/out*
toolsrc/CMakeSettings.json
# User-specific files (MonoDevelop/Xamarin Studio)

View File

@ -3,6 +3,7 @@ cmake_minimum_required(VERSION 3.3)
project(vcpkg C CXX)
OPTION(BUILD_TESTING "Option for enabling testing" ON)
OPTION(VCPKG_BUILD_BENCHMARKING "Option for enabling benchmarking" OFF)
OPTION(DEFINE_DISABLE_METRICS "Option for disabling metrics" OFF)
OPTION(VCPKG_ALLOW_APPLE_CLANG "Option for allowing apple clang" OFF)
@ -77,6 +78,10 @@ if (BUILD_TESTING)
$<TARGET_OBJECTS:vcpkglib>)
add_test(NAME default COMMAND vcpkg-test [${TEST_NAME}])
if (VCPKG_BUILD_BENCHMARKING)
target_compile_options(vcpkg-test PRIVATE -DCATCH_CONFIG_ENABLE_BENCHMARKING)
endif()
endif()
if(MSVC)

View File

@ -1,14 +1,6 @@
#pragma once
#if defined(_MSC_VER) && _MSC_VER < 1911
// [[nodiscard]] is not recognized before VS 2017 version 15.3
#pragma warning(disable : 5030)
#endif
#if defined(__GNUC__) && __GNUC__ < 7
// [[nodiscard]] is not recognized before GCC version 7
#pragma GCC diagnostic ignored "-Wattributes"
#endif
#include <vcpkg/pragmas.h>
#if defined(_WIN32)
#define NOMINMAX
@ -38,8 +30,8 @@
#include <cstddef>
#include <cstdint>
#define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING
#include <experimental/filesystem>
#include <cstring>
#include <experimental/filesystem>
#include <fstream>
#include <functional>
#include <iomanip>

View File

@ -1,42 +1,68 @@
#include <vcpkg-test/catch.h>
#include <vcpkg/pragmas.h>
#include <vcpkg/base/files.h>
#include <vcpkg/statusparagraph.h>
#include <memory>
namespace vcpkg::Test {
#define CHECK_EC(ec) \
do \
{ \
if (ec) \
{ \
FAIL(ec.message()); \
} \
} while (0)
std::unique_ptr<vcpkg::StatusParagraph> make_status_pgh(const char* name,
const char* depends = "",
const char* default_features = "",
const char* triplet = "x86-windows");
std::unique_ptr<vcpkg::StatusParagraph> make_status_feature_pgh(const char* name,
const char* feature,
const char* depends = "",
const char* triplet = "x86-windows");
vcpkg::PackageSpec unsafe_pspec(std::string name, vcpkg::Triplet t = vcpkg::Triplet::X86_WINDOWS);
template<class T, class S>
T&& unwrap(vcpkg::ExpectedT<T, S>&& p)
namespace vcpkg::Test
{
REQUIRE(p.has_value());
return std::move(*p.get());
}
template<class T>
T&& unwrap(vcpkg::Optional<T>&& opt)
{
REQUIRE(opt.has_value());
return std::move(*opt.get());
}
extern const bool SYMLINKS_ALLOWED;
extern const fs::path TEMPORARY_DIRECTORY;
void create_symlink(const fs::path& file, const fs::path& target, std::error_code& ec);
void create_directory_symlink(const fs::path& file, const fs::path& target, std::error_code& ec);
std::unique_ptr<vcpkg::StatusParagraph> make_status_pgh(const char* name,
const char* depends = "",
const char* default_features = "",
const char* triplet = "x86-windows");
std::unique_ptr<vcpkg::StatusParagraph> make_status_feature_pgh(const char* name,
const char* feature,
const char* depends = "",
const char* triplet = "x86-windows");
vcpkg::PackageSpec unsafe_pspec(std::string name, vcpkg::Triplet t = vcpkg::Triplet::X86_WINDOWS);
template<class T, class S>
T&& unwrap(vcpkg::ExpectedT<T, S>&& p)
{
REQUIRE(p.has_value());
return std::move(*p.get());
}
template<class T>
T&& unwrap(vcpkg::Optional<T>&& opt)
{
REQUIRE(opt.has_value());
return std::move(*opt.get());
}
struct AllowSymlinks
{
enum Tag : bool
{
No = false,
Yes = true,
} tag;
constexpr AllowSymlinks(Tag tag) noexcept : tag(tag) {}
constexpr explicit AllowSymlinks(bool b) noexcept : tag(b ? Yes : No) {}
constexpr operator bool() const noexcept { return tag == Yes; }
};
AllowSymlinks can_create_symlinks() noexcept;
const fs::path& base_temporary_directory() noexcept;
void create_symlink(const fs::path& file, const fs::path& target, std::error_code& ec);
void create_directory_symlink(const fs::path& file, const fs::path& target, std::error_code& ec);
}

View File

@ -10,24 +10,68 @@ namespace fs
namespace stdfs = std::experimental::filesystem;
using stdfs::copy_options;
using stdfs::file_status;
using stdfs::file_type;
using stdfs::path;
using stdfs::perms;
using stdfs::u8path;
#if defined(_WIN32)
enum class file_type
{
none = 0,
not_found = -1,
regular = 1,
directory = 2,
symlink = 3,
block = 4,
character = 5,
fifo = 6,
socket = 7,
unknown = 8,
// also stands for a junction
directory_symlink = 42
};
struct file_status
{
explicit file_status(file_type type = file_type::none, perms permissions = perms::unknown) noexcept
: m_type(type), m_permissions(permissions)
{
}
file_type type() const noexcept { return m_type; }
void type(file_type type) noexcept { m_type = type; }
perms permissions() const noexcept { return m_permissions; }
void permissions(perms perm) noexcept { m_permissions = perm; }
private:
file_type m_type;
perms m_permissions;
};
#else
using stdfs::file_status;
using stdfs::file_type;
#endif
/*
std::experimental::filesystem's file_status and file_type are broken in
the presence of symlinks -- a symlink is treated as the object it points
to for `symlink_status` and `symlink_type`
*/
using stdfs::status;
// we want to poison ADL with these niebloids
namespace detail
{
struct status_t
{
file_status operator()(const path& p, std::error_code& ec) const noexcept;
file_status operator()(vcpkg::LineInfo li, const path& p) const noexcept;
file_status operator()(const path& p) const;
};
struct symlink_status_t
{
file_status operator()(const path& p, std::error_code& ec) const noexcept;
@ -35,22 +79,56 @@ namespace fs
};
struct is_symlink_t
{
inline bool operator()(file_status s) const { return stdfs::is_symlink(s); }
bool operator()(file_status s) const
{
#if defined(_WIN32)
return s.type() == file_type::directory_symlink || s.type() == file_type::symlink;
#else
return stdfs::is_symlink(s);
#endif
}
};
struct is_regular_file_t
{
inline bool operator()(file_status s) const { return stdfs::is_regular_file(s); }
inline bool operator()(file_status s) const
{
#if defined(_WIN32)
return s.type() == file_type::regular;
#else
return stdfs::is_regular_file(s);
#endif
}
};
struct is_directory_t
{
inline bool operator()(file_status s) const { return stdfs::is_directory(s); }
inline bool operator()(file_status s) const
{
#if defined(_WIN32)
return s.type() == file_type::directory;
#else
return stdfs::is_directory(s);
#endif
}
};
struct exists_t
{
inline bool operator()(file_status s) const
{
#if defined(_WIN32)
return s.type() != file_type::not_found && s.type() != file_type::none;
#else
return stdfs::exists(s);
#endif
}
};
}
constexpr detail::status_t status{};
constexpr detail::symlink_status_t symlink_status{};
constexpr detail::is_symlink_t is_symlink{};
constexpr detail::is_regular_file_t is_regular_file{};
constexpr detail::is_directory_t is_directory{};
constexpr detail::exists_t exists{};
}
/*
@ -59,12 +137,14 @@ namespace fs
Therefore, put `symlink_status` in the global namespace, so that they get
our symlink_status.
We also want to poison the ADL on is_regular_file and is_directory, because
We also want to poison the ADL on the other functions, because
we don't want people calling these functions on paths
*/
using fs::exists;
using fs::is_directory;
using fs::is_regular_file;
using fs::is_symlink;
using fs::status;
using fs::symlink_status;
namespace vcpkg::Files
@ -92,9 +172,13 @@ namespace vcpkg::Files
bool remove(const fs::path& path, LineInfo linfo);
virtual bool remove(const fs::path& path, std::error_code& ec) = 0;
virtual std::uintmax_t remove_all(const fs::path& path, std::error_code& ec, fs::path& failure_point) = 0;
std::uintmax_t remove_all(const fs::path& path, LineInfo li);
virtual bool exists(const fs::path& path) const = 0;
virtual void remove_all(const fs::path& path, std::error_code& ec, fs::path& failure_point) = 0;
void remove_all(const fs::path& path, LineInfo li);
bool exists(const fs::path& path, std::error_code& ec) const;
bool exists(LineInfo li, const fs::path& path) const;
// this should probably not exist, but would require a pass through of
// existing code to fix
bool exists(const fs::path& path) const;
virtual bool is_directory(const fs::path& path) const = 0;
virtual bool is_regular_file(const fs::path& path) const = 0;
virtual bool is_empty(const fs::path& path) const = 0;

View File

@ -185,8 +185,6 @@ namespace vcpkg::Strings
bool contains(StringView haystack, StringView needle);
// base 32 encoding, since base64 encoding requires lowercase letters,
// which are not distinct from uppercase letters on macOS or Windows filesystems.
// follows RFC 4648
// base 32 encoding, following IETC RFC 4648
std::string b32_encode(std::uint64_t x) noexcept;
}

View File

@ -3,19 +3,19 @@
#include <algorithm>
#include <map>
#include <mutex>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include <vector>
#include <type_traits>
namespace vcpkg::Util
{
template <class T>
template<class T>
constexpr std::add_const_t<T>& as_const(T& t) noexcept
{
return t;
}
template <class T>
template<class T>
void as_const(const T&&) = delete;
template<class Container>

View File

@ -1,114 +1,46 @@
#pragma once
#include <vcpkg/base/checks.h>
#include <condition_variable>
#include <memory>
#include <queue>
#include <vector>
namespace vcpkg
{
template<class Action, class ThreadLocalData>
struct WorkQueue;
namespace detail
{
// for SFINAE purposes, keep out of the class
// also this sfinae is so weird because Backwards Compatibility with VS2015
template<class Action,
class ThreadLocalData,
class = decltype(std::declval<Action>()(std::declval<ThreadLocalData&>(),
std::declval<const WorkQueue<Action, ThreadLocalData>&>()))>
void call_moved_action(Action& action,
const WorkQueue<Action, ThreadLocalData>& work_queue,
ThreadLocalData& tld)
{
std::move(action)(tld, work_queue);
}
template<class Action,
class ThreadLocalData,
class = decltype(std::declval<Action>()(std::declval<ThreadLocalData&>())),
class = void>
void call_moved_action(Action& action, const WorkQueue<Action, ThreadLocalData>&, ThreadLocalData& tld)
{
std::move(action)(tld);
}
}
template<class Action, class ThreadLocalData>
template<class Action>
struct WorkQueue
{
template<class F>
WorkQueue(LineInfo li, std::uint16_t num_threads, const F& tld_init) noexcept
{
m_line_info = li;
set_unjoined_workers(num_threads);
m_threads.reserve(num_threads);
for (std::size_t i = 0; i < num_threads; ++i)
{
m_threads.push_back(std::thread(Worker{this, tld_init()}));
}
}
WorkQueue(WorkQueue const&) = delete;
WorkQueue(WorkQueue&&) = delete;
WorkQueue(LineInfo li) : m_line_info(li) {}
WorkQueue(const WorkQueue&) = delete;
~WorkQueue()
{
auto lck = std::unique_lock<std::mutex>(m_mutex);
if (!is_joined(m_state))
auto lck = std::unique_lock<std::mutex>(m_mutex, std::try_to_lock);
/*
if we don't own the lock, there isn't much we can do
it is likely a spurious failure
*/
if (lck && m_running_workers != 0)
{
Checks::exit_with_message(m_line_info, "Failed to call join() on a WorkQueue that was destroyed");
Checks::exit_with_message(
m_line_info, "Internal error -- outstanding workers (%u) at destruct point", m_running_workers);
}
}
// should only be called once; anything else is an error
void run(LineInfo li)
template<class F>
void run_and_join(unsigned num_threads, const F& tld_init) noexcept
{
// this should _not_ be locked before `run()` is called; however, we
// want to terminate if someone screws up, rather than cause UB
auto lck = std::unique_lock<std::mutex>(m_mutex);
if (m_actions.empty()) return;
if (m_state != State::BeforeRun)
std::vector<std::thread> threads;
threads.reserve(num_threads);
for (unsigned i = 0; i < num_threads; ++i)
{
Checks::exit_with_message(li, "Attempted to run() twice");
threads.emplace_back(Worker<decltype(tld_init())>{this, tld_init()});
}
m_state = State::Running;
}
// runs all remaining tasks, and blocks on their finishing
// if this is called in an existing task, _will block forever_
// DO NOT DO THAT
// thread-unsafe
void join(LineInfo li)
{
{
auto lck = std::unique_lock<std::mutex>(m_mutex);
if (is_joined(m_state))
{
Checks::exit_with_message(li, "Attempted to call join() more than once");
}
else if (m_state == State::Terminated)
{
m_state = State::TerminatedJoined;
}
else
{
m_state = State::Joined;
}
}
while (unjoined_workers())
{
if (!running_workers())
{
m_cv.notify_one();
}
}
// wait for all threads to join
for (auto& thrd : m_threads)
for (auto& thrd : threads)
{
thrd.join();
}
@ -117,18 +49,12 @@ namespace vcpkg
// useful in the case of errors
// doesn't stop any existing running tasks
// returns immediately, so that one can call this in a task
void terminate() const
void cancel() const
{
{
auto lck = std::unique_lock<std::mutex>(m_mutex);
if (is_joined(m_state))
{
m_state = State::TerminatedJoined;
}
else
{
m_state = State::Terminated;
}
auto lck = std::lock_guard<std::mutex>(m_mutex);
m_cancelled = true;
m_actions.clear();
}
m_cv.notify_all();
}
@ -136,15 +62,16 @@ namespace vcpkg
void enqueue_action(Action a) const
{
{
auto lck = std::unique_lock<std::mutex>(m_mutex);
m_actions.push_back(std::move(a));
auto lck = std::lock_guard<std::mutex>(m_mutex);
if (m_cancelled) return;
if (m_state == State::BeforeRun) return;
m_actions.push_back(std::move(a));
}
m_cv.notify_one();
}
private:
template<class ThreadLocalData>
struct Worker
{
const WorkQueue* work_queue;
@ -152,85 +79,62 @@ namespace vcpkg
void operator()()
{
// unlocked when waiting, or when in the action
// locked otherwise
auto lck = std::unique_lock<std::mutex>(work_queue->m_mutex);
work_queue->m_cv.wait(lck, [&] { return work_queue->m_state != State::BeforeRun; });
work_queue->increment_running_workers();
for (;;)
{
const auto state = work_queue->m_state;
const auto& w = *work_queue;
work_queue->m_cv.wait(lck, [&w] {
if (w.m_cancelled)
return true;
else if (!w.m_actions.empty())
return true;
else if (w.m_running_workers == 0)
return true;
else
return false;
});
if (is_terminated(state))
if (work_queue->m_cancelled || work_queue->m_actions.empty())
{
/*
if we've been cancelled, or if the work queue is empty
and there are no other workers, we want to return
immediately; we don't check for the latter condition
since if we're at this point, then either the queue
is not empty, or there are no other workers, or both.
We can't have an empty queue, and other workers, or
we would still be in the wait.
*/
break;
}
if (work_queue->m_actions.empty())
{
if (state == State::Running || work_queue->running_workers() > 1)
{
work_queue->decrement_running_workers();
work_queue->m_cv.wait(lck);
work_queue->increment_running_workers();
continue;
}
++work_queue->m_running_workers;
// the queue is joining, and we are the only worker running
// no more work!
break;
}
Action action = std::move(work_queue->m_actions.back());
auto action = std::move(work_queue->m_actions.back());
work_queue->m_actions.pop_back();
lck.unlock();
work_queue->m_cv.notify_one();
detail::call_moved_action(action, *work_queue, tld);
std::move(action)(tld, *work_queue);
lck.lock();
}
work_queue->decrement_running_workers();
work_queue->decrement_unjoined_workers();
const auto after = --work_queue->m_running_workers;
if (work_queue->m_actions.empty() && after == 0)
{
work_queue->m_cv.notify_all();
return;
}
}
}
};
enum class State : std::int16_t
{
// can only exist upon construction
BeforeRun = -1,
Running,
Joined,
Terminated,
TerminatedJoined,
};
static bool is_terminated(State st) { return st == State::Terminated || st == State::TerminatedJoined; }
static bool is_joined(State st) { return st == State::Joined || st == State::TerminatedJoined; }
mutable std::mutex m_mutex{};
// these are all under m_mutex
mutable State m_state = State::BeforeRun;
mutable bool m_cancelled = false;
mutable std::vector<Action> m_actions{};
mutable std::condition_variable m_cv{};
mutable unsigned long m_running_workers = 0;
mutable std::atomic<std::uint32_t> m_workers;
// = unjoined_workers << 16 | running_workers
void set_unjoined_workers(std::uint16_t threads) { m_workers = std::uint32_t(threads) << 16; }
void decrement_unjoined_workers() const { m_workers -= 1 << 16; }
std::uint16_t unjoined_workers() const { return std::uint16_t(m_workers >> 16); }
void increment_running_workers() const { ++m_workers; }
void decrement_running_workers() const { --m_workers; }
std::uint16_t running_workers() const { return std::uint16_t(m_workers); }
std::vector<std::thread> m_threads{};
LineInfo m_line_info;
};
}

View File

@ -1,8 +1,8 @@
#pragma once
#include <vcpkg/packagespec.h>
#include <vcpkg/sourceparagraph.h>
#include <vcpkg/parse.h>
#include <vcpkg/sourceparagraph.h>
#include <unordered_map>

View File

@ -126,8 +126,8 @@ namespace vcpkg::Build
/// Runs the triplet file in a "capture" mode to create a PreBuildInfo
/// </summary>
static PreBuildInfo from_triplet_file(const VcpkgPaths& paths,
const Triplet& triplet,
Optional<const SourceControlFileLocation&> port = nullopt);
const Triplet& triplet,
Optional<const SourceControlFileLocation&> port = nullopt);
std::string triplet_abi_tag;
std::string target_architecture;

View File

@ -145,7 +145,7 @@ namespace vcpkg::Dependencies
struct PathsPortFileProvider : Util::ResourceBase, PortFileProvider
{
explicit PathsPortFileProvider(const vcpkg::VcpkgPaths& paths,
explicit PathsPortFileProvider(const vcpkg::VcpkgPaths& paths,
const std::vector<std::string>* ports_dirs_paths);
Optional<const SourceControlFileLocation&> get_control_file(const std::string& src_name) const override;
std::vector<const SourceControlFileLocation*> load_all_control_files() const override;
@ -186,9 +186,10 @@ namespace vcpkg::Dependencies
std::vector<ExportPlanAction> create_export_plan(const std::vector<PackageSpec>& specs,
const StatusParagraphs& status_db);
std::vector<AnyAction> create_feature_install_plan(const std::unordered_map<std::string, SourceControlFileLocation>& map,
const std::vector<FeatureSpec>& specs,
const StatusParagraphs& status_db);
std::vector<AnyAction> create_feature_install_plan(
const std::unordered_map<std::string, SourceControlFileLocation>& map,
const std::vector<FeatureSpec>& specs,
const StatusParagraphs& status_db);
/// <summary>Figure out which actions are required to install features specifications in `specs`.</summary>
/// <param name="provider">Contains the ports of the current environment.</param>
@ -199,7 +200,7 @@ namespace vcpkg::Dependencies
const StatusParagraphs& status_db,
const CreateInstallPlanOptions& options = {});
void print_plan(const std::vector<AnyAction>& action_plan,
void print_plan(const std::vector<AnyAction>& action_plan,
const bool is_recursive = true,
const fs::path& default_ports_dir = "");
}

View File

@ -4,7 +4,7 @@
namespace vcpkg
{
// Evaluate simple vcpkg logic expressions. An identifier in the expression is considered 'true'
// if it is a substring of the evaluation_context (typically the name of the triplet)
bool evaluate_expression(const std::string& expression, const std::string& evaluation_context);
// Evaluate simple vcpkg logic expressions. An identifier in the expression is considered 'true'
// if it is a substring of the evaluation_context (typically the name of the triplet)
bool evaluate_expression(const std::string& expression, const std::string& evaluation_context);
}

View File

@ -0,0 +1,11 @@
#pragma once
#if defined(_MSC_VER) && _MSC_VER < 1911
// [[nodiscard]] is not recognized before VS 2017 version 15.3
#pragma warning(disable : 5030)
#endif
#if defined(__GNUC__) && __GNUC__ < 7
// [[nodiscard]] is not recognized before GCC version 7
#pragma GCC diagnostic ignored "-Wattributes"
#endif

View File

@ -23,8 +23,7 @@ namespace vcpkg
std::vector<std::string> filter_dependencies(const std::vector<Dependency>& deps, const Triplet& t);
std::vector<FeatureSpec> filter_dependencies_to_specs(const std::vector<Dependency>& deps, const Triplet& t);
std::vector<Features> filter_dependencies_to_features(const std::vector<vcpkg::Dependency>& deps,
const Triplet& t);
std::vector<Features> filter_dependencies_to_features(const std::vector<vcpkg::Dependency>& deps, const Triplet& t);
// zlib[uwp] becomes Dependency{"zlib", "uwp"}
std::vector<Dependency> expand_qualified_dependencies(const std::vector<std::string>& depends);
@ -71,7 +70,8 @@ namespace vcpkg
};
/// <summary>
/// Full metadata of a package: core and other features. As well as the location the SourceControlFile was loaded from.
/// Full metadata of a package: core and other features. As well as the location the SourceControlFile was loaded
/// from.
/// </summary>
struct SourceControlFileLocation
{

View File

@ -55,7 +55,7 @@ namespace vcpkg
fs::path package_dir(const PackageSpec& spec) const;
fs::path build_info_file_path(const PackageSpec& spec) const;
fs::path listfile_path(const BinaryParagraph& pgh) const;
bool is_valid_triplet(const Triplet& t) const;
const std::vector<std::string>& get_available_triplets() const;
const fs::path get_triplet_file_path(const Triplet& triplet) const;

View File

@ -9,29 +9,63 @@
#include <vector>
using vcpkg::Test::SYMLINKS_ALLOWED;
using vcpkg::Test::TEMPORARY_DIRECTORY;
using vcpkg::Test::AllowSymlinks;
using vcpkg::Test::base_temporary_directory;
using vcpkg::Test::can_create_symlinks;
#define CHECK_EC_ON_FILE(file, ec) \
do \
{ \
if (ec) \
{ \
FAIL(file << ": " << ec.message()); \
} \
} while (0)
namespace
{
using uid = std::uniform_int_distribution<std::uint64_t>;
using uid_t = std::uniform_int_distribution<std::uint64_t>;
using urbg_t = std::mt19937_64;
std::mt19937_64 get_urbg(std::uint64_t index)
urbg_t get_urbg(std::uint64_t index)
{
// smallest prime > 2**63 - 1
return std::mt19937_64{index + 9223372036854775837ULL};
return urbg_t{index + 9223372036854775837ULL};
}
std::string get_random_filename(std::mt19937_64& urbg) { return vcpkg::Strings::b32_encode(uid{}(urbg)); }
std::string get_random_filename(urbg_t& urbg) { return vcpkg::Strings::b32_encode(uid_t{}(urbg)); }
void create_directory_tree(std::mt19937_64& urbg,
struct MaxDepth
{
std::uint64_t i;
explicit MaxDepth(std::uint64_t i) : i(i) {}
operator uint64_t() const { return i; }
};
struct Width
{
std::uint64_t i;
explicit Width(std::uint64_t i) : i(i) {}
operator uint64_t() const { return i; }
};
struct CurrentDepth
{
std::uint64_t i;
explicit CurrentDepth(std::uint64_t i) : i(i) {}
operator uint64_t() const { return i; }
CurrentDepth incremented() const { return CurrentDepth{i + 1}; }
};
void create_directory_tree(urbg_t& urbg,
vcpkg::Files::Filesystem& fs,
std::uint64_t depth,
const fs::path& base)
const fs::path& base,
MaxDepth max_depth,
AllowSymlinks allow_symlinks = AllowSymlinks::Yes,
Width width = Width{5},
CurrentDepth current_depth = CurrentDepth{0})
{
std::random_device rd;
constexpr std::uint64_t max_depth = 5;
constexpr std::uint64_t width = 5;
// we want ~70% of our "files" to be directories, and then a third
// each of the remaining ~30% to be regular files, directory symlinks,
@ -42,18 +76,24 @@ namespace
constexpr std::uint64_t regular_symlink_tag = 8;
constexpr std::uint64_t directory_symlink_tag = 9;
allow_symlinks = AllowSymlinks{allow_symlinks && can_create_symlinks()};
// if we're at the max depth, we only want to build non-directories
std::uint64_t file_type;
if (depth < max_depth)
if (current_depth >= max_depth)
{
file_type = uid{directory_min_tag, regular_symlink_tag}(urbg);
file_type = uid_t{regular_file_tag, directory_symlink_tag}(urbg);
}
else if (current_depth < 2)
{
file_type = directory_min_tag;
}
else
{
file_type = uid{regular_file_tag, regular_symlink_tag}(urbg);
file_type = uid_t{directory_min_tag, regular_symlink_tag}(urbg);
}
if (!SYMLINKS_ALLOWED && file_type > regular_file_tag)
if (!allow_symlinks && file_type > regular_file_tag)
{
file_type = regular_file_tag;
}
@ -62,14 +102,20 @@ namespace
if (file_type <= directory_max_tag)
{
fs.create_directory(base, ec);
if (ec) {
INFO("File that failed: " << base);
REQUIRE_FALSE(ec);
if (ec)
{
CHECK_EC_ON_FILE(base, ec);
}
for (int i = 0; i < width; ++i)
{
create_directory_tree(urbg, fs, depth + 1, base / get_random_filename(urbg));
create_directory_tree(urbg,
fs,
base / get_random_filename(urbg),
max_depth,
allow_symlinks,
width,
current_depth.incremented());
}
}
else if (file_type == regular_file_tag)
@ -80,19 +126,34 @@ namespace
else if (file_type == regular_symlink_tag)
{
// regular symlink
fs.write_contents(base, "", ec);
REQUIRE_FALSE(ec);
auto base_link = base;
base_link.replace_filename(base.filename().u8string() + "-link");
vcpkg::Test::create_symlink(base, base_link, ec);
base_link.replace_filename(base.filename().u8string() + "-orig");
fs.write_contents(base_link, "", ec);
CHECK_EC_ON_FILE(base_link, ec);
vcpkg::Test::create_symlink(base_link, base, ec);
}
else // type == directory_symlink_tag
{
// directory symlink
vcpkg::Test::create_directory_symlink(base / "..", base, ec);
auto parent = base;
parent.remove_filename();
vcpkg::Test::create_directory_symlink(parent, base, ec);
}
REQUIRE_FALSE(ec);
CHECK_EC_ON_FILE(base, ec);
REQUIRE(fs::exists(fs.symlink_status(base, ec)));
CHECK_EC_ON_FILE(base, ec);
}
vcpkg::Files::Filesystem& setup(urbg_t& urbg)
{
auto& fs = vcpkg::Files::get_real_filesystem();
std::error_code ec;
fs.create_directory(base_temporary_directory(), ec);
CHECK_EC_ON_FILE(base_temporary_directory(), ec);
return fs;
}
}
@ -100,24 +161,83 @@ TEST_CASE ("remove all", "[files]")
{
auto urbg = get_urbg(0);
fs::path temp_dir = TEMPORARY_DIRECTORY / get_random_filename(urbg);
auto& fs = vcpkg::Files::get_real_filesystem();
std::error_code ec;
fs.create_directory(TEMPORARY_DIRECTORY, ec);
REQUIRE_FALSE(ec);
auto& fs = setup(urbg);
fs::path temp_dir = base_temporary_directory() / get_random_filename(urbg);
INFO("temp dir is: " << temp_dir);
create_directory_tree(urbg, fs, 0, temp_dir);
create_directory_tree(urbg, fs, temp_dir, MaxDepth{5});
std::error_code ec;
fs::path fp;
fs.remove_all(temp_dir, ec, fp);
if (ec) {
FAIL("remove_all failure on file: " << fp);
}
CHECK_EC_ON_FILE(fp, ec);
REQUIRE_FALSE(fs.exists(temp_dir));
REQUIRE_FALSE(fs.exists(temp_dir, ec));
CHECK_EC_ON_FILE(temp_dir, ec);
}
#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
TEST_CASE ("remove all -- benchmarks", "[files][!benchmark]")
{
auto urbg = get_urbg(1);
auto& fs = setup(urbg);
struct
{
urbg_t& urbg;
vcpkg::Files::Filesystem& fs;
void operator()(Catch::Benchmark::Chronometer& meter, MaxDepth max_depth, AllowSymlinks allow_symlinks) const
{
std::vector<fs::path> temp_dirs;
temp_dirs.resize(meter.runs());
std::generate(begin(temp_dirs), end(temp_dirs), [&] {
fs::path temp_dir = base_temporary_directory() / get_random_filename(urbg);
create_directory_tree(urbg, fs, temp_dir, max_depth, allow_symlinks);
return temp_dir;
});
meter.measure([&](int run) {
std::error_code ec;
fs::path fp;
const auto& temp_dir = temp_dirs[run];
fs.remove_all(temp_dir, ec, fp);
CHECK_EC_ON_FILE(fp, ec);
});
for (const auto& dir : temp_dirs)
{
std::error_code ec;
REQUIRE_FALSE(fs.exists(dir, ec));
CHECK_EC_ON_FILE(dir, ec);
}
}
} do_benchmark = {urbg, fs};
BENCHMARK_ADVANCED("small directory, no symlinks")(Catch::Benchmark::Chronometer meter)
{
do_benchmark(meter, MaxDepth{2}, AllowSymlinks::No);
};
BENCHMARK_ADVANCED("large directory, no symlinks")(Catch::Benchmark::Chronometer meter)
{
do_benchmark(meter, MaxDepth{5}, AllowSymlinks::No);
};
if (can_create_symlinks())
{
BENCHMARK_ADVANCED("small directory, symlinks")(Catch::Benchmark::Chronometer meter)
{
do_benchmark(meter, MaxDepth{2}, AllowSymlinks::Yes);
};
BENCHMARK_ADVANCED("large directory, symlinks")(Catch::Benchmark::Chronometer meter)
{
do_benchmark(meter, MaxDepth{5}, AllowSymlinks::Yes);
};
}
}
#endif

View File

@ -74,14 +74,14 @@ namespace vcpkg::Test
return m_ret.value_or_exit(VCPKG_LINE_INFO);
}
static bool system_allows_symlinks()
static AllowSymlinks internal_can_create_symlinks() noexcept
{
#if FILESYSTEM_SYMLINK == FILESYSTEM_SYMLINK_NONE
return false;
return AllowSymlinks::No;
#elif FILESYSTEM_SYMLINK == FILESYSTEM_SYMLINK_UNIX
return true;
return AllowSymlinks::Yes;
#elif !defined(_WIN32) // FILESYSTEM_SYMLINK == FILESYSTEM_SYMLINK_STD
return true;
return AllowSymlinks::Yes;
#else
HKEY key;
bool allow_symlinks = true;
@ -97,11 +97,14 @@ namespace vcpkg::Test
if (status == ERROR_SUCCESS) RegCloseKey(key);
return allow_symlinks;
return allow_symlinks ? AllowSymlinks::Yes : AllowSymlinks::No;
#endif
}
const static AllowSymlinks CAN_CREATE_SYMLINKS = internal_can_create_symlinks();
static fs::path internal_temporary_directory()
AllowSymlinks can_create_symlinks() noexcept { return CAN_CREATE_SYMLINKS; }
static fs::path internal_base_temporary_directory()
{
#if defined(_WIN32)
wchar_t* tmp = static_cast<wchar_t*>(std::calloc(32'767, 2));
@ -121,8 +124,9 @@ namespace vcpkg::Test
#endif
}
const bool SYMLINKS_ALLOWED = system_allows_symlinks();
const fs::path TEMPORARY_DIRECTORY = internal_temporary_directory();
const static fs::path BASE_TEMPORARY_DIRECTORY = internal_base_temporary_directory();
const fs::path& base_temporary_directory() noexcept { return BASE_TEMPORARY_DIRECTORY; }
#if FILESYSTEM_SYMLINK == FILESYSTEM_SYMLINK_NONE
constexpr char no_filesystem_message[] =
@ -132,7 +136,7 @@ namespace vcpkg::Test
void create_symlink(const fs::path& target, const fs::path& file, std::error_code& ec)
{
#if FILESYSTEM_SYMLINK == FILESYSTEM_SYMLINK_STD
if (SYMLINKS_ALLOWED)
if (can_create_symlinks())
{
std::filesystem::path targetp = target.native();
std::filesystem::path filep = file.native();
@ -156,12 +160,12 @@ namespace vcpkg::Test
void create_directory_symlink(const fs::path& target, const fs::path& file, std::error_code& ec)
{
#if FILESYSTEM_SYMLINK == FILESYSTEM_SYMLINK_STD
if (SYMLINKS_ALLOWED)
if (can_create_symlinks())
{
std::filesystem::path targetp = target.native();
std::filesystem::path filep = file.native();
std::filesystem::create_symlink(targetp, filep);
std::filesystem::create_directory_symlink(targetp, filep);
}
else
{

View File

@ -125,12 +125,8 @@ static void inner(const VcpkgCmdArguments& args)
auto default_vs_path = System::get_environment_variable("VCPKG_VISUAL_STUDIO_PATH").value_or("");
const Expected<VcpkgPaths> expected_paths = VcpkgPaths::create(vcpkg_root_dir,
vcpkg_scripts_root_dir,
default_vs_path,
args.overlay_triplets.get());
const Expected<VcpkgPaths> expected_paths =
VcpkgPaths::create(vcpkg_root_dir, vcpkg_scripts_root_dir, default_vs_path, args.overlay_triplets.get());
Checks::check_exit(VCPKG_LINE_INFO,
!expected_paths.error(),
"Error: Invalid vcpkg root directory %s: %s",
@ -143,7 +139,11 @@ static void inner(const VcpkgCmdArguments& args)
#else
const int exit_code = chdir(paths.root.c_str());
#endif
Checks::check_exit(VCPKG_LINE_INFO, exit_code == 0, "Changing the working directory to the vcpkg root directory failed. Did you incorrectly define the VCPKG_ROOT environment variable, or did you mistakenly create a file named .vcpkg-root somewhere?");
Checks::check_exit(
VCPKG_LINE_INFO,
exit_code == 0,
"Changing the working directory to the vcpkg root directory failed. Did you incorrectly define the VCPKG_ROOT "
"environment variable, or did you mistakenly create a file named .vcpkg-root somewhere?");
if (args.command == "install" || args.command == "remove" || args.command == "export" || args.command == "update")
{

View File

@ -23,22 +23,35 @@
namespace fs::detail
{
file_status symlink_status_t::operator()(const path& p, std::error_code& ec) const noexcept
static file_status status_implementation(bool follow_symlinks, const path& p, std::error_code& ec)
{
#if defined(_WIN32)
static_cast<void>(ec);
WIN32_FILE_ATTRIBUTE_DATA file_attributes;
file_type ft = file_type::unknown;
perms permissions = perms::unknown;
if (!GetFileAttributesExW(p.c_str(), GetFileExInfoStandard, &file_attributes))
{
ft = file_type::not_found;
const auto err = GetLastError();
if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND)
{
ft = file_type::not_found;
}
else
{
ec.assign(err, std::system_category());
}
}
else if (file_attributes.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
else if (!follow_symlinks && file_attributes.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
{
// check for reparse point -- if yes, then symlink
ft = file_type::symlink;
// this also gives junctions file_type::directory_symlink
if (file_attributes.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
ft = file_type::directory_symlink;
}
else
{
ft = file_type::symlink;
}
}
else if (file_attributes.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
@ -50,17 +63,60 @@ namespace fs::detail
ft = file_type::regular;
}
if (file_attributes.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
{
constexpr auto all_write = perms::group_write | perms::owner_write | perms::others_write;
permissions = perms::all & ~all_write;
}
else if (ft != file_type::none && ft != file_type::none)
{
permissions = perms::all;
}
return file_status(ft, permissions);
#else
return stdfs::symlink_status(p, ec);
auto result = symlink ? stdfs::symlink_status(p, ec) : stdfs::status(p, ec);
// libstdc++ doesn't correctly not-set ec on nonexistent paths
if (ec.value() == ENOENT)
{
ec.clear();
result = file_status(file_type::not_found, perms::unknown);
}
return result;
#endif
}
file_status status_t::operator()(const path& p, std::error_code& ec) const noexcept
{
return status_implementation(false, p, ec);
}
file_status status_t::operator()(vcpkg::LineInfo li, const path& p) const noexcept
{
std::error_code ec;
auto result = (*this)(p, ec);
if (ec) vcpkg::Checks::exit_with_message(li, "error getting status of path %s: %s", p.string(), ec.message());
return result;
}
file_status status_t::operator()(const path& p) const
{
#if defined(_WIN32)
return (*this)(VCPKG_LINE_INFO, p);
#else
return fs::stdfs::status(p);
#endif
}
file_status symlink_status_t::operator()(const path& p, std::error_code& ec) const noexcept
{
return status_implementation(true, p, ec);
}
file_status symlink_status_t::operator()(vcpkg::LineInfo li, const path& p) const noexcept
{
std::error_code ec;
auto result = symlink_status(p, ec);
auto result = (*this)(p, ec);
if (ec) vcpkg::Checks::exit_with_message(li, "error getting status of path %s: %s", p.string(), ec.message());
return result;
@ -71,34 +127,41 @@ namespace vcpkg::Files
{
static const std::regex FILESYSTEM_INVALID_CHARACTERS_REGEX = std::regex(R"([\/:*?"<>|])");
namespace {
namespace
{
// does _not_ follow symlinks
void set_writeable(const fs::path& path, std::error_code& ec) noexcept {
void set_writeable(const fs::path& path, std::error_code& ec) noexcept
{
#if defined(_WIN32)
auto const file_name = path.c_str();
WIN32_FILE_ATTRIBUTE_DATA attributes;
if (!GetFileAttributesExW(file_name, GetFileExInfoStandard, &attributes)) {
if (!GetFileAttributesExW(file_name, GetFileExInfoStandard, &attributes))
{
ec.assign(GetLastError(), std::system_category());
return;
}
auto dw_attributes = attributes.dwFileAttributes;
dw_attributes &= ~FILE_ATTRIBUTE_READONLY;
if (!SetFileAttributesW(file_name, dw_attributes)) {
if (!SetFileAttributesW(file_name, dw_attributes))
{
ec.assign(GetLastError(), std::system_category());
}
#else
struct stat s;
if (lstat(path.c_str(), &s)) {
if (lstat(path.c_str(), &s))
{
ec.assign(errno, std::system_category());
return;
}
auto mode = s.st_mode;
// if the file is a symlink, perms don't matter
if (!(mode & S_IFLNK)) {
if (!(mode & S_IFLNK))
{
mode |= S_IWUSR;
if (chmod(path.c_str(), mode)) {
if (chmod(path.c_str(), mode))
{
ec.assign(errno, std::system_category());
}
}
@ -138,6 +201,25 @@ namespace vcpkg::Files
return r;
}
bool Filesystem::exists(const fs::path& path, std::error_code& ec) const
{
return fs::exists(this->symlink_status(path, ec));
}
bool Filesystem::exists(LineInfo li, const fs::path& path) const
{
std::error_code ec;
auto result = this->exists(path, ec);
if (ec) Checks::exit_with_message(li, "error checking existence of file %s: %s", path.u8string(), ec.message());
return result;
}
bool Filesystem::exists(const fs::path& path) const
{
std::error_code ec;
// drop this on the floor, for compatibility with existing code
return exists(path, ec);
}
void Filesystem::write_lines(const fs::path& path, const std::vector<std::string>& lines, LineInfo linfo)
{
std::error_code ec;
@ -145,12 +227,12 @@ namespace vcpkg::Files
if (ec) Checks::exit_with_message(linfo, "error writing lines: %s: %s", path.u8string(), ec.message());
}
std::uintmax_t Filesystem::remove_all(const fs::path& path, LineInfo li)
void Filesystem::remove_all(const fs::path& path, LineInfo li)
{
std::error_code ec;
fs::path failure_point;
const auto result = this->remove_all(path, ec, failure_point);
this->remove_all(path, ec, failure_point);
if (ec)
{
@ -160,8 +242,6 @@ namespace vcpkg::Files
failure_point.string(),
ec.message());
}
return result;
}
struct RealFilesystem final : Filesystem
@ -203,8 +283,7 @@ namespace vcpkg::Files
while (std::getline(file_stream, line))
{
// Remove the trailing \r to accomodate Windows line endings.
if ((!line.empty()) && (line.back() == '\r'))
line.pop_back();
if ((!line.empty()) && (line.back() == '\r')) line.pop_back();
output.push_back(line);
}
@ -216,7 +295,7 @@ namespace vcpkg::Files
const std::string& filename) const override
{
fs::path current_dir = starting_dir;
if (exists(current_dir / filename))
if (exists(VCPKG_LINE_INFO, current_dir / filename))
{
return current_dir;
}
@ -241,7 +320,7 @@ namespace vcpkg::Files
current_dir = std::move(parent);
const fs::path candidate = current_dir / filename;
if (exists(candidate))
if (exists(VCPKG_LINE_INFO, candidate))
{
return current_dir;
}
@ -359,92 +438,96 @@ namespace vcpkg::Files
#endif
}
virtual bool remove(const fs::path& path, std::error_code& ec) override { return fs::stdfs::remove(path, ec); }
virtual std::uintmax_t remove_all(const fs::path& path, std::error_code& ec, fs::path& failure_point) override
virtual void remove_all(const fs::path& path, std::error_code& ec, fs::path& failure_point) override
{
/*
does not use the std::filesystem call since it is buggy, and can
have spurious errors before VS 2017 update 6, and on later versions
(as well as on macOS and Linux), this is just as fast and will have
fewer spurious errors due to locks.
does not use the std::experimental::filesystem call since this is
quite a bit faster, and also supports symlinks
*/
/*
`remove` doesn't actually remove anything -- it simply moves the
files into a parent directory (which ends up being at `path`),
and then inserts `actually_remove{current_path}` into the work
queue.
*/
struct remove
{
struct tld
struct ErrorInfo : Util::ResourceBase
{
const fs::path& tmp_directory;
std::uint64_t index;
std::atomic<std::uintmax_t>& files_deleted;
std::mutex& ec_mutex;
std::error_code& ec;
fs::path& failure_point;
std::error_code ec;
fs::path failure_point;
};
struct actually_remove;
using queue = WorkQueue<actually_remove, tld>;
/*
if `current_path` is a directory, first `remove`s all
elements of the directory, then calls remove.
elements of the directory, then removes current_path.
else, just calls remove.
else if `current_path` exists, removes current_path
else does nothing
*/
struct actually_remove
static void do_remove(const fs::path& current_path, ErrorInfo& err)
{
fs::path current_path;
std::error_code ec;
const auto path_status = fs::symlink_status(current_path, ec);
if (check_ec(ec, current_path, err)) return;
if (!fs::exists(path_status)) return;
void operator()(tld& info, const queue& queue) const
const auto path_type = path_status.type();
if ((path_status.permissions() & fs::perms::owner_write) != fs::perms::owner_write)
{
std::error_code ec;
const auto path_type = fs::symlink_status(current_path, ec).type();
if (check_ec(ec, info, queue, current_path)) return;
if (path_type == fs::file_type::directory)
{
for (const auto& entry : fs::stdfs::directory_iterator(current_path))
{
remove{}(entry, info, queue);
}
}
set_writeable(current_path, ec);
if (check_ec(ec, info, queue, current_path)) return;
if (check_ec(ec, current_path, err)) return;
}
if (fs::stdfs::remove(current_path, ec))
if (path_type == fs::file_type::directory)
{
for (const auto& entry : fs::stdfs::directory_iterator(current_path))
{
info.files_deleted.fetch_add(1, std::memory_order_relaxed);
do_remove(entry, err);
if (err.ec) return;
}
else
#if defined(_WIN32)
if (!RemoveDirectoryW(current_path.c_str()))
{
check_ec(ec, info, queue, current_path);
ec.assign(GetLastError(), std::system_category());
}
#else
if (rmdir(current_path.c_str()))
{
ec.assign(errno, std::system_category());
}
#endif
}
#if defined(_WIN32)
else if (path_type == fs::file_type::directory_symlink)
{
if (!RemoveDirectoryW(current_path.c_str()))
{
ec.assign(GetLastError(), std::system_category());
}
}
};
else
{
if (!DeleteFileW(current_path.c_str()))
{
ec.assign(GetLastError(), std::system_category());
}
}
#else
else
{
if (unlink(current_path.c_str()))
{
ec.assign(errno, std::system_category());
}
}
#endif
static bool check_ec(const std::error_code& ec,
tld& info,
const queue& queue,
const fs::path& failure_point)
check_ec(ec, current_path, err);
}
static bool check_ec(const std::error_code& ec, const fs::path& current_path, ErrorInfo& err)
{
if (ec)
{
queue.terminate();
auto lck = std::unique_lock<std::mutex>(info.ec_mutex);
if (!info.ec)
{
info.ec = ec;
info.failure_point = failure_point;
}
err.ec = ec;
err.failure_point = current_path;
return true;
}
@ -453,55 +536,15 @@ namespace vcpkg::Files
return false;
}
}
void operator()(const fs::path& current_path, tld& info, const queue& queue) const
{
std::error_code ec;
const auto tmp_name = Strings::b32_encode(info.index++);
const auto tmp_path = info.tmp_directory / tmp_name;
fs::stdfs::rename(current_path, tmp_path, ec);
if (check_ec(ec, info, queue, current_path)) return;
queue.enqueue_action(actually_remove{std::move(tmp_path)});
}
};
const auto path_type = fs::symlink_status(path, ec).type();
std::atomic<std::uintmax_t> files_deleted{0};
if (path_type == fs::file_type::directory)
{
std::uint64_t index = 0;
std::mutex ec_mutex;
auto const tld_gen = [&] {
index += static_cast<std::uint64_t>(1) << 32;
return remove::tld{path, index, files_deleted, ec_mutex, ec, failure_point};
};
remove::queue queue{VCPKG_LINE_INFO, 4, tld_gen};
// note: we don't actually start the queue running until the
// `join()`. This allows us to rename all the top-level files in
// peace, so that we don't get collisions.
auto main_tld = tld_gen();
for (const auto& entry : fs::stdfs::directory_iterator(path))
{
remove{}(entry, main_tld, queue);
}
queue.join(VCPKG_LINE_INFO);
}
/*
we need to do backoff on the removal of the top level directory,
since we need to place all moved files into that top level
directory, and so we can only delete the directory after all the
so we can only delete the directory after all the
lower levels have been deleted.
*/
remove::ErrorInfo err;
for (int backoff = 0; backoff < 5; ++backoff)
{
if (backoff)
@ -511,16 +554,16 @@ namespace vcpkg::Files
std::this_thread::sleep_for(backoff_time);
}
if (fs::stdfs::remove(path, ec))
remove::do_remove(path, err);
if (!err.ec)
{
files_deleted.fetch_add(1, std::memory_order_relaxed);
break;
}
}
return files_deleted;
ec = std::move(err.ec);
failure_point = std::move(err.failure_point);
}
virtual bool exists(const fs::path& path) const override { return fs::stdfs::exists(path); }
virtual bool is_directory(const fs::path& path) const override { return fs::stdfs::is_directory(path); }
virtual bool is_regular_file(const fs::path& path) const override { return fs::stdfs::is_regular_file(path); }
virtual bool is_empty(const fs::path& path) const override { return fs::stdfs::is_empty(path); }
@ -598,7 +641,7 @@ namespace vcpkg::Files
for (auto&& ext : EXTS)
{
auto p = fs::u8path(base + ext.c_str());
if (Util::find(ret, p) == ret.end() && this->exists(p))
if (Util::find(ret, p) == ret.end() && this->exists(VCPKG_LINE_INFO, p))
{
ret.push_back(p);
Debug::print("Found path: ", p.u8string(), '\n');

View File

@ -63,8 +63,7 @@ namespace vcpkg::Build::Command
std::set<std::string> features_as_set(full_spec.features.begin(), full_spec.features.end());
features_as_set.emplace("core");
const Build::BuildPackageConfig build_config{
scfl, spec.triplet(), build_package_options, features_as_set};
const Build::BuildPackageConfig build_config{scfl, spec.triplet(), build_package_options, features_as_set};
const auto build_timer = Chrono::ElapsedTimer::create_started();
const auto result = Build::build_package(paths, build_config, status_db);
@ -293,19 +292,17 @@ namespace vcpkg::Build
const std::set<std::string>& feature_list,
const Triplet& triplet)
{
return Util::fmap_flatten(
feature_list,
[&](std::string const& feature) -> std::vector<Features> {
if (feature == "core")
{
return filter_dependencies_to_features(scf.core_paragraph->depends, triplet);
}
return Util::fmap_flatten(feature_list, [&](std::string const& feature) -> std::vector<Features> {
if (feature == "core")
{
return filter_dependencies_to_features(scf.core_paragraph->depends, triplet);
}
auto maybe_feature = scf.find_feature(feature);
Checks::check_exit(VCPKG_LINE_INFO, maybe_feature.has_value());
auto maybe_feature = scf.find_feature(feature);
Checks::check_exit(VCPKG_LINE_INFO, maybe_feature.has_value());
return filter_dependencies_to_features(maybe_feature.get()->depends, triplet);
});
return filter_dependencies_to_features(maybe_feature.get()->depends, triplet);
});
}
static std::vector<std::string> get_dependency_names(const SourceControlFile& scf,
@ -313,10 +310,7 @@ namespace vcpkg::Build
const Triplet& triplet)
{
return Util::fmap(get_dependencies(scf, feature_list, triplet),
[&](const Features& feat) {
return feat.name;
}
);
[&](const Features& feat) { return feat.name; });
}
static std::vector<FeatureSpec> compute_required_feature_specs(const BuildPackageConfig& config,
@ -324,8 +318,7 @@ namespace vcpkg::Build
{
const Triplet& triplet = config.triplet;
const std::vector<std::string> dep_strings =
get_dependency_names(config.scf, config.feature_list, triplet);
const std::vector<std::string> dep_strings = get_dependency_names(config.scf, config.feature_list, triplet);
auto dep_fspecs = FeatureSpec::from_strings_and_triplet(dep_strings, triplet);
Util::sort_unique_erase(dep_fspecs);
@ -428,8 +421,7 @@ namespace vcpkg::Build
{
const Toolset& toolset = paths.get_toolset(pre_build_info);
const fs::path& cmake_exe_path = paths.get_tool_exe(Tools::CMAKE);
std::vector<System::CMakeVariable> variables =
get_cmake_vars(paths, config, triplet, toolset);
std::vector<System::CMakeVariable> variables = get_cmake_vars(paths, config, triplet, toolset);
const std::string cmd_launch_cmake = System::make_cmake_cmd(cmake_exe_path, paths.ports_cmake, variables);
@ -521,13 +513,10 @@ namespace vcpkg::Build
const auto timer = Chrono::ElapsedTimer::create_started();
std::string command =
make_build_cmd(paths, pre_build_info, config, triplet);
std::unordered_map<std::string, std::string> env =
make_env_passthrough(pre_build_info);
std::string command = make_build_cmd(paths, pre_build_info, config, triplet);
std::unordered_map<std::string, std::string> env = make_env_passthrough(pre_build_info);
const int return_code =
System::cmd_execute_clean(command, env);
const int return_code = System::cmd_execute_clean(command, env);
const auto buildtimeus = timer.microseconds();
const auto spec_string = spec.to_string();
@ -785,8 +774,7 @@ namespace vcpkg::Build
AbiEntry{status_it->get()->package.spec.name(), status_it->get()->package.abi});
}
const auto pre_build_info =
PreBuildInfo::from_triplet_file(paths, triplet, config.scfl);
const auto pre_build_info = PreBuildInfo::from_triplet_file(paths, triplet, config.scfl);
auto maybe_abi_tag_and_file = compute_abi_tag(paths, config, pre_build_info, dependency_abis);
@ -995,8 +983,7 @@ namespace vcpkg::Build
BuildInfo read_build_info(const Files::Filesystem& fs, const fs::path& filepath)
{
const Expected<Parse::RawParagraph> pghs =
Paragraphs::get_single_paragraph(fs, filepath);
const Expected<Parse::RawParagraph> pghs = Paragraphs::get_single_paragraph(fs, filepath);
Checks::check_exit(VCPKG_LINE_INFO, pghs.get() != nullptr, "Invalid BUILD_INFO file for package");
return inner_create_buildinfo(*pghs.get());
}
@ -1015,14 +1002,11 @@ namespace vcpkg::Build
if (port)
{
args.emplace_back(
"CMAKE_ENV_OVERRIDES_FILE",
port.value_or_exit(VCPKG_LINE_INFO).source_location / "environment-overrides.cmake");
args.emplace_back("CMAKE_ENV_OVERRIDES_FILE",
port.value_or_exit(VCPKG_LINE_INFO).source_location / "environment-overrides.cmake");
}
const auto cmd_launch_cmake = System::make_cmake_cmd(cmake_exe_path,
ports_cmake_script_path,
args);
const auto cmd_launch_cmake = System::make_cmake_cmd(cmake_exe_path, ports_cmake_script_path, args);
const auto ec_data = System::cmd_execute_and_capture_output(cmd_launch_cmake);
Checks::check_exit(VCPKG_LINE_INFO, ec_data.exit_code == 0, ec_data.output);
@ -1054,41 +1038,39 @@ namespace vcpkg::Build
{
switch (maybe_option->second)
{
case VcpkgTripletVar::TARGET_ARCHITECTURE :
pre_build_info.target_architecture = variable_value;
break;
case VcpkgTripletVar::CMAKE_SYSTEM_NAME :
pre_build_info.cmake_system_name = variable_value;
break;
case VcpkgTripletVar::CMAKE_SYSTEM_VERSION :
pre_build_info.cmake_system_version = variable_value;
break;
case VcpkgTripletVar::PLATFORM_TOOLSET :
pre_build_info.platform_toolset =
variable_value.empty() ? nullopt : Optional<std::string>{variable_value};
break;
case VcpkgTripletVar::VISUAL_STUDIO_PATH :
pre_build_info.visual_studio_path =
variable_value.empty() ? nullopt : Optional<fs::path>{variable_value};
break;
case VcpkgTripletVar::CHAINLOAD_TOOLCHAIN_FILE :
pre_build_info.external_toolchain_file =
variable_value.empty() ? nullopt : Optional<std::string>{variable_value};
break;
case VcpkgTripletVar::BUILD_TYPE :
if (variable_value.empty())
pre_build_info.build_type = nullopt;
else if (Strings::case_insensitive_ascii_equals(variable_value, "debug"))
pre_build_info.build_type = ConfigurationType::DEBUG;
else if (Strings::case_insensitive_ascii_equals(variable_value, "release"))
pre_build_info.build_type = ConfigurationType::RELEASE;
else
Checks::exit_with_message(
case VcpkgTripletVar::TARGET_ARCHITECTURE:
pre_build_info.target_architecture = variable_value;
break;
case VcpkgTripletVar::CMAKE_SYSTEM_NAME: pre_build_info.cmake_system_name = variable_value; break;
case VcpkgTripletVar::CMAKE_SYSTEM_VERSION:
pre_build_info.cmake_system_version = variable_value;
break;
case VcpkgTripletVar::PLATFORM_TOOLSET:
pre_build_info.platform_toolset =
variable_value.empty() ? nullopt : Optional<std::string>{variable_value};
break;
case VcpkgTripletVar::VISUAL_STUDIO_PATH:
pre_build_info.visual_studio_path =
variable_value.empty() ? nullopt : Optional<fs::path>{variable_value};
break;
case VcpkgTripletVar::CHAINLOAD_TOOLCHAIN_FILE:
pre_build_info.external_toolchain_file =
variable_value.empty() ? nullopt : Optional<std::string>{variable_value};
break;
case VcpkgTripletVar::BUILD_TYPE:
if (variable_value.empty())
pre_build_info.build_type = nullopt;
else if (Strings::case_insensitive_ascii_equals(variable_value, "debug"))
pre_build_info.build_type = ConfigurationType::DEBUG;
else if (Strings::case_insensitive_ascii_equals(variable_value, "release"))
pre_build_info.build_type = ConfigurationType::RELEASE;
else
Checks::exit_with_message(
VCPKG_LINE_INFO, "Unknown setting for VCPKG_BUILD_TYPE: %s", variable_value);
break;
case VcpkgTripletVar::ENV_PASSTHROUGH :
pre_build_info.passthrough_env_vars = Strings::split(variable_value, ";");
break;
break;
case VcpkgTripletVar::ENV_PASSTHROUGH:
pre_build_info.passthrough_env_vars = Strings::split(variable_value, ";");
break;
}
}
else
@ -1097,8 +1079,7 @@ namespace vcpkg::Build
}
}
pre_build_info.triplet_abi_tag =
get_triplet_abi(paths, pre_build_info, triplet);
pre_build_info.triplet_abi_tag = get_triplet_abi(paths, pre_build_info, triplet);
return pre_build_info;
}

View File

@ -236,12 +236,7 @@ namespace vcpkg::Commands::CI
{
auto triplet = p->spec.triplet();
const Build::BuildPackageConfig build_config{
*scfl,
triplet,
build_options,
p->feature_list
};
const Build::BuildPackageConfig build_config{*scfl, triplet, build_options, p->feature_list};
auto dependency_abis =
Util::fmap(p->computed_dependencies, [&](const PackageSpec& spec) -> Build::AbiEntry {
@ -253,14 +248,7 @@ namespace vcpkg::Commands::CI
return {spec.name(), it->second};
});
const auto& pre_build_info = pre_build_info_cache.get_lazy(
triplet,
[&]() {
return Build::PreBuildInfo::from_triplet_file(
paths,
triplet,
*scfl);
}
);
triplet, [&]() { return Build::PreBuildInfo::from_triplet_file(paths, triplet, *scfl); });
auto maybe_tag_and_file =
Build::compute_abi_tag(paths, build_config, pre_build_info, dependency_abis);
@ -362,7 +350,7 @@ namespace vcpkg::Commands::CI
}
StatusParagraphs status_db = database_load_check(paths);
Dependencies::PathsPortFileProvider provider(paths, args.overlay_ports.get());
const Build::BuildPackageOptions install_plan_options = {

View File

@ -5,12 +5,12 @@
#include <vcpkg/base/util.h>
#include <vcpkg/commands.h>
#include <vcpkg/help.h>
#include <vcpkg/paragraphs.h>
#include <vcpkg/packagespec.h>
#include <vcpkg/paragraphs.h>
#include <vector>
#include <memory>
#include <vcpkg/dependencies.h>
#include <vector>
using vcpkg::Dependencies::PathsPortFileProvider;
@ -171,14 +171,14 @@ namespace vcpkg::Commands::DependInfo
{
if (requested_feature_name == "*")
{
for (auto &&feature_paragraph : (*source_control_file)->feature_paragraphs)
for (auto&& feature_paragraph : (*source_control_file)->feature_paragraphs)
{
collected_features.insert(std::addressof(Util::as_const(*feature_paragraph)));
}
continue;
}
auto maybe_feature = (*source_control_file)->find_feature(requested_feature_name);
if (auto &&feature_paragraph = maybe_feature.get())
if (auto&& feature_paragraph = maybe_feature.get())
{
collected_features.insert(std::addressof(Util::as_const(*feature_paragraph)));
}
@ -197,7 +197,8 @@ namespace vcpkg::Commands::DependInfo
{
for (const auto& dependency : feature_paragraph->depends)
{
build_dependencies_list(packages_to_keep, dependency.depend.name, source_control_files, switches);
build_dependencies_list(
packages_to_keep, dependency.depend.name, source_control_files, switches);
}
}
}
@ -214,9 +215,9 @@ namespace vcpkg::Commands::DependInfo
// TODO: Optimize implementation, current implementation needs to load all ports from disk which is too slow.
PathsPortFileProvider provider(paths, args.overlay_ports.get());
auto source_control_files = Util::fmap(provider.load_all_control_files(), [](auto&& scfl) -> const SourceControlFile * {
return scfl->source_control_file.get();
});
auto source_control_files =
Util::fmap(provider.load_all_control_files(),
[](auto&& scfl) -> const SourceControlFile* { return scfl->source_control_file.get(); });
if (args.command_arguments.size() >= 1)
{

View File

@ -2,12 +2,12 @@
#include <vcpkg/base/system.print.h>
#include <vcpkg/commands.h>
#include <vcpkg/dependencies.h>
#include <vcpkg/globalstate.h>
#include <vcpkg/help.h>
#include <vcpkg/paragraphs.h>
#include <vcpkg/sourceparagraph.h>
#include <vcpkg/vcpkglib.h>
#include <vcpkg/dependencies.h>
using vcpkg::Dependencies::PathsPortFileProvider;
@ -67,9 +67,9 @@ namespace vcpkg::Commands::Search
const bool full_description = Util::Sets::contains(options.switches, OPTION_FULLDESC);
PathsPortFileProvider provider(paths, args.overlay_ports.get());
auto source_paragraphs = Util::fmap(provider.load_all_control_files(), [](auto&& port) -> const SourceControlFile * {
return port->source_control_file.get();
});
auto source_paragraphs =
Util::fmap(provider.load_all_control_files(),
[](auto&& port) -> const SourceControlFile* { return port->source_control_file.get(); });
if (args.command_arguments.empty())
{

View File

@ -108,9 +108,8 @@ namespace vcpkg::Dependencies
static ClusterSource cluster_from_scf(const SourceControlFileLocation& scfl, Triplet t)
{
ClusterSource ret;
ret.build_edges.emplace("core",
filter_dependencies_to_specs(scfl.source_control_file->core_paragraph->depends,
t));
ret.build_edges.emplace("core",
filter_dependencies_to_specs(scfl.source_control_file->core_paragraph->depends, t));
for (const auto& feature : scfl.source_control_file->feature_paragraphs)
ret.build_edges.emplace(feature->name, filter_dependencies_to_specs(feature->depends, t));
@ -123,22 +122,23 @@ namespace vcpkg::Dependencies
const PortFileProvider& m_provider;
};
std::string to_output_string(RequestType request_type,
const CStringView s,
const Build::BuildPackageOptions& options,
std::string to_output_string(RequestType request_type,
const CStringView s,
const Build::BuildPackageOptions& options,
const fs::path& install_port_path,
const fs::path& default_port_path)
{
if (!default_port_path.empty()
&& !Strings::case_insensitive_ascii_starts_with(install_port_path.u8string(),
default_port_path.u8string()))
if (!default_port_path.empty() &&
!Strings::case_insensitive_ascii_starts_with(install_port_path.u8string(), default_port_path.u8string()))
{
const char* const from_head = options.use_head_version == Build::UseHeadVersion::YES ? " (from HEAD)" : "";
switch (request_type)
{
case RequestType::AUTO_SELECTED: return Strings::format(" * %s%s -- %s", s, from_head, install_port_path.u8string());
case RequestType::USER_REQUESTED: return Strings::format(" %s%s -- %s", s, from_head, install_port_path.u8string());
default: Checks::unreachable(VCPKG_LINE_INFO);
case RequestType::AUTO_SELECTED:
return Strings::format(" * %s%s -- %s", s, from_head, install_port_path.u8string());
case RequestType::USER_REQUESTED:
return Strings::format(" %s%s -- %s", s, from_head, install_port_path.u8string());
default: Checks::unreachable(VCPKG_LINE_INFO);
}
}
return to_output_string(request_type, s, options);
@ -152,7 +152,7 @@ namespace vcpkg::Dependencies
switch (request_type)
{
case RequestType::AUTO_SELECTED: return Strings::format(" * %s%s", s, from_head);
case RequestType::AUTO_SELECTED: return Strings::format(" * %s%s", s, from_head);
case RequestType::USER_REQUESTED: return Strings::format(" %s%s", s, from_head);
default: Checks::unreachable(VCPKG_LINE_INFO);
}
@ -162,7 +162,7 @@ namespace vcpkg::Dependencies
{
switch (request_type)
{
case RequestType::AUTO_SELECTED: return Strings::format(" * %s", s);
case RequestType::AUTO_SELECTED: return Strings::format(" * %s", s);
case RequestType::USER_REQUESTED: return Strings::format(" %s", s);
default: Checks::unreachable(VCPKG_LINE_INFO);
}
@ -293,7 +293,8 @@ namespace vcpkg::Dependencies
MapPortFileProvider::MapPortFileProvider(const std::unordered_map<std::string, SourceControlFileLocation>& map)
: ports(map)
{}
{
}
Optional<const SourceControlFileLocation&> MapPortFileProvider::get_control_file(const std::string& spec) const
{
@ -304,11 +305,11 @@ namespace vcpkg::Dependencies
std::vector<const SourceControlFileLocation*> MapPortFileProvider::load_all_control_files() const
{
return Util::fmap(ports, [](auto&& kvpair) -> const SourceControlFileLocation * { return &kvpair.second; });
return Util::fmap(ports, [](auto&& kvpair) -> const SourceControlFileLocation* { return &kvpair.second; });
}
PathsPortFileProvider::PathsPortFileProvider(const vcpkg::VcpkgPaths& paths,
const std::vector<std::string>* ports_dirs_paths)
const std::vector<std::string>* ports_dirs_paths)
: filesystem(paths.get_filesystem())
{
if (ports_dirs_paths)
@ -325,7 +326,7 @@ namespace vcpkg::Dependencies
overlay.string());
Checks::check_exit(VCPKG_LINE_INFO,
fs::stdfs::is_directory(overlay),
fs::is_directory(status(overlay)),
"Error: Path \"%s\" must be a directory",
overlay.string());
@ -354,7 +355,7 @@ namespace vcpkg::Dependencies
{
if (scf->get()->core_paragraph->name == spec)
{
SourceControlFileLocation scfl{ std::move(*scf), ports_dir };
SourceControlFileLocation scfl{std::move(*scf), ports_dir};
auto it = cache.emplace(spec, std::move(scfl));
return it.first->second;
}
@ -362,9 +363,8 @@ namespace vcpkg::Dependencies
else
{
vcpkg::print_error_message(maybe_scf.error());
Checks::exit_with_message(VCPKG_LINE_INFO,
"Error: Failed to load port from %s",
spec, ports_dir.u8string());
Checks::exit_with_message(
VCPKG_LINE_INFO, "Error: Failed to load port from %s", spec, ports_dir.u8string());
}
}
@ -373,7 +373,7 @@ namespace vcpkg::Dependencies
{
if (scf->get()->core_paragraph->name == spec)
{
SourceControlFileLocation scfl{ std::move(*scf), ports_dir / spec };
SourceControlFileLocation scfl{std::move(*scf), ports_dir / spec};
auto it = cache.emplace(spec, std::move(scfl));
return it.first->second;
}
@ -399,7 +399,7 @@ namespace vcpkg::Dependencies
auto port_name = scf->get()->core_paragraph->name;
if (cache.find(port_name) == cache.end())
{
SourceControlFileLocation scfl{ std::move(*scf), ports_dir };
SourceControlFileLocation scfl{std::move(*scf), ports_dir};
auto it = cache.emplace(port_name, std::move(scfl));
ret.emplace_back(&it.first->second);
}
@ -407,9 +407,8 @@ namespace vcpkg::Dependencies
else
{
vcpkg::print_error_message(maybe_scf.error());
Checks::exit_with_message(VCPKG_LINE_INFO,
"Error: Failed to load port from %s",
ports_dir.u8string());
Checks::exit_with_message(
VCPKG_LINE_INFO, "Error: Failed to load port from %s", ports_dir.u8string());
}
continue;
}
@ -421,7 +420,7 @@ namespace vcpkg::Dependencies
auto port_name = scf->core_paragraph->name;
if (cache.find(port_name) == cache.end())
{
SourceControlFileLocation scfl{ std::move(scf), ports_dir / port_name };
SourceControlFileLocation scfl{std::move(scf), ports_dir / port_name};
auto it = cache.emplace(port_name, std::move(scfl));
ret.emplace_back(&it.first->second);
}
@ -768,9 +767,10 @@ namespace vcpkg::Dependencies
/// <param name="map">Map of all source control files in the current environment.</param>
/// <param name="specs">Feature specifications to resolve dependencies for.</param>
/// <param name="status_db">Status of installed packages in the current environment.</param>
std::vector<AnyAction> create_feature_install_plan(const std::unordered_map<std::string, SourceControlFileLocation>& map,
const std::vector<FeatureSpec>& specs,
const StatusParagraphs& status_db)
std::vector<AnyAction> create_feature_install_plan(
const std::unordered_map<std::string, SourceControlFileLocation>& map,
const std::vector<FeatureSpec>& specs,
const StatusParagraphs& status_db)
{
MapPortFileProvider provider(map);
return create_feature_install_plan(provider, specs, status_db);
@ -832,9 +832,8 @@ namespace vcpkg::Dependencies
{
// If it will be transiently uninstalled, we need to issue a full installation command
auto* pscfl = p_cluster->source.value_or_exit(VCPKG_LINE_INFO).scfl;
Checks::check_exit(VCPKG_LINE_INFO,
pscfl != nullptr,
"Error: Expected a SourceControlFileLocation to exist");
Checks::check_exit(
VCPKG_LINE_INFO, pscfl != nullptr, "Error: Expected a SourceControlFileLocation to exist");
auto&& scfl = *pscfl;
auto dep_specs = Util::fmap(m_graph_plan->install_graph.adjacency_list(p_cluster),
@ -914,7 +913,9 @@ namespace vcpkg::Dependencies
PackageGraph::~PackageGraph() = default;
void print_plan(const std::vector<AnyAction>& action_plan, const bool is_recursive, const fs::path& default_ports_dir)
void print_plan(const std::vector<AnyAction>& action_plan,
const bool is_recursive,
const fs::path& default_ports_dir)
{
std::vector<const RemovePlanAction*> remove_plans;
std::vector<const InstallPlanAction*> rebuilt_plans;
@ -971,13 +972,10 @@ namespace vcpkg::Dependencies
static auto actions_to_output_string = [&](const std::vector<const InstallPlanAction*>& v) {
return Strings::join("\n", v, [&](const InstallPlanAction* p) {
if (auto * pscfl = p->source_control_file_location.get())
if (auto* pscfl = p->source_control_file_location.get())
{
return to_output_string(p->request_type,
p->displayname(),
p->build_options,
pscfl->source_location,
default_ports_dir);
return to_output_string(
p->request_type, p->displayname(), p->build_options, pscfl->source_location, default_ports_dir);
}
return to_output_string(p->request_type, p->displayname(), p->build_options);

View File

@ -331,10 +331,8 @@ namespace vcpkg::Install
auto result = [&]() -> Build::ExtendedBuildResult {
const auto& scfl = action.source_control_file_location.value_or_exit(VCPKG_LINE_INFO);
const Build::BuildPackageConfig build_config{scfl,
action.spec.triplet(),
action.build_options,
action.feature_list};
const Build::BuildPackageConfig build_config{
scfl, action.spec.triplet(), action.build_options, action.feature_list};
return Build::build_package(paths, build_config, status_db);
}();

View File

@ -1,21 +1,18 @@
#include "pch.h"
#include <vcpkg/logicexpression.h>
#include <vcpkg/base/checks.h>
#include <vcpkg/base/system.print.h>
#include <vcpkg/logicexpression.h>
#include <string>
#include <vector>
namespace vcpkg
{
struct ParseError
{
ParseError(int column, std::string line, std::string message)
:column(column), line(line), message(message)
{}
ParseError(int column, std::string line, std::string message) : column(column), line(line), message(message) {}
const int column;
const std::string line;
@ -24,9 +21,15 @@ namespace vcpkg
void print_error() const
{
System::print2(System::Color::error,
"Error: ", message, "\n"
" on expression: \"", line, "\"\n",
" ", std::string(column, ' '), "^\n");
"Error: ",
message,
"\n"
" on expression: \"",
line,
"\"\n",
" ",
std::string(column, ' '),
"^\n");
Checks::exit_fail(VCPKG_LINE_INFO);
}
};
@ -36,7 +39,7 @@ namespace vcpkg
// ( logic-expression )
// identifier
// identifier:
// alpha-numeric string of characters
// alpha-numeric string of characters
// logic-expression: <- this is the entry point
// not-expression
// not-expression | logic-expression
@ -69,27 +72,20 @@ namespace vcpkg
}
}
bool get_result() const
{
return final_result;
}
bool get_result() const { return final_result; }
bool has_error() const
{
return err == nullptr;
}
bool has_error() const { return err == nullptr; }
private:
bool final_result;
std::string::const_iterator current_iter;
const std::string& raw_text;
char current_char;
char current_char;
const std::string& evaluation_context;
const std::string& evaluation_context;
std::unique_ptr<ParseError> err;
std::unique_ptr<ParseError> err;
void add_error(std::string message, int column = -1)
{
@ -107,10 +103,7 @@ namespace vcpkg
skip_to_end();
}
int current_column() const
{
return static_cast<int>(current_iter - raw_text.begin());
}
int current_column() const { return static_cast<int>(current_iter - raw_text.begin()); }
void go_to_begin()
{
@ -127,10 +120,7 @@ namespace vcpkg
current_iter = raw_text.end();
current_char = '\0';
}
char current() const
{
return current_char;
}
char current() const { return current_char; }
char next()
{
if (current_char != '\0')
@ -165,7 +155,7 @@ namespace vcpkg
}
// identifier:
// alpha-numeric string of characters
// alpha-numeric string of characters
bool identifier_expression()
{
auto curr = current();
@ -181,7 +171,7 @@ namespace vcpkg
add_error("Invalid logic expression, unexpected character");
return false;
}
bool result = evaluate_identifier(name);
skip_whitespace();
return result;
@ -201,14 +191,15 @@ namespace vcpkg
return primary_expression();
}
template <char oper, char other, bool operation(bool, bool)>
template<char oper, char other, bool operation(bool, bool)>
bool logic_expression_helper(bool seed)
{
do
{
// Support chains of the operator to avoid breaking backwards compatability
while (next() == oper) {};
while (next() == oper)
{
};
seed = operation(not_expression(), seed);
} while (current() == oper);
@ -218,17 +209,11 @@ namespace vcpkg
add_error("Mixing & and | is not allowed, Use () to specify order of operations.");
}
skip_whitespace();
skip_whitespace();
return seed;
}
static bool and_helper(bool left, bool right)
{
return left && right;
}
static bool or_helper(bool left, bool right)
{
return left || right;
}
static bool and_helper(bool left, bool right) { return left && right; }
static bool or_helper(bool left, bool right) { return left || right; }
// logic-expression: <- entry point
// not-expression
@ -240,16 +225,15 @@ namespace vcpkg
switch (current())
{
case '|':
{
return logic_expression_helper< '|', '&', or_helper > (result);
}
case '&':
{
return logic_expression_helper< '&', '|', and_helper > (result);
}
default:
return result;
case '|':
{
return logic_expression_helper<'|', '&', or_helper>(result);
}
case '&':
{
return logic_expression_helper<'&', '|', and_helper>(result);
}
default: return result;
}
}
@ -275,7 +259,6 @@ namespace vcpkg
}
};
bool evaluate_expression(const std::string& expression, const std::string& evaluation_context)
{
ExpressionParser parser(expression, evaluation_context);

View File

@ -166,8 +166,7 @@ namespace vcpkg::Paragraphs
Expected<RawParagraph> parse_single_paragraph(const std::string& str)
{
const std::vector<RawParagraph> p =
Parser(str.c_str(), str.c_str() + str.size()).get_paragraphs();
const std::vector<RawParagraph> p = Parser(str.c_str(), str.c_str() + str.size()).get_paragraphs();
if (p.size() == 1)
{
@ -177,8 +176,7 @@ namespace vcpkg::Paragraphs
return std::error_code(ParagraphParseResult::EXPECTED_ONE_PARAGRAPH);
}
Expected<RawParagraph> get_single_paragraph(const Files::Filesystem& fs,
const fs::path& control_path)
Expected<RawParagraph> get_single_paragraph(const Files::Filesystem& fs, const fs::path& control_path)
{
const Expected<std::string> contents = fs.read_contents(control_path);
if (auto spgh = contents.get())
@ -189,8 +187,7 @@ namespace vcpkg::Paragraphs
return contents.error();
}
Expected<std::vector<RawParagraph>> get_paragraphs(const Files::Filesystem& fs,
const fs::path& control_path)
Expected<std::vector<RawParagraph>> get_paragraphs(const Files::Filesystem& fs, const fs::path& control_path)
{
const Expected<std::string> contents = fs.read_contents(control_path);
if (auto spgh = contents.get())

View File

@ -6,8 +6,7 @@
namespace vcpkg::Parse
{
static Optional<std::string> remove_field(RawParagraph* fields,
const std::string& fieldname)
static Optional<std::string> remove_field(RawParagraph* fields, const std::string& fieldname)
{
auto it = fields->find(fieldname);
if (it == fields->end())

View File

@ -73,7 +73,7 @@ namespace vcpkg::Remove
if (ec)
{
#if defined(_WIN32)
fs::stdfs::permissions(target, fs::stdfs::perms::owner_all | fs::stdfs::perms::group_all, ec);
fs::stdfs::permissions(target, fs::perms::owner_all | fs::perms::group_all, ec);
fs.remove(target, ec);
if (ec)
{
@ -86,7 +86,7 @@ namespace vcpkg::Remove
#endif
}
}
else if (!fs::stdfs::exists(status))
else if (!fs::exists(status))
{
System::printf(System::Color::warning, "Warning: %s: file not found\n", target.u8string());
}

View File

@ -1,9 +1,9 @@
#include "pch.h"
#include <vcpkg/logicexpression.h>
#include <vcpkg/packagespec.h>
#include <vcpkg/sourceparagraph.h>
#include <vcpkg/triplet.h>
#include <vcpkg/logicexpression.h>
#include <vcpkg/base/checks.h>
#include <vcpkg/base/expected.h>
@ -223,26 +223,25 @@ namespace vcpkg
std::vector<std::string> ret;
for (auto&& dep : deps)
{
const auto & qualifier = dep.qualifier;
if (qualifier.empty() || evaluate_expression(qualifier, t.canonical_name()))
{
ret.emplace_back(dep.name());
}
const auto& qualifier = dep.qualifier;
if (qualifier.empty() || evaluate_expression(qualifier, t.canonical_name()))
{
ret.emplace_back(dep.name());
}
}
return ret;
}
std::vector<Features> filter_dependencies_to_features(const std::vector<vcpkg::Dependency>& deps,
const Triplet& t)
std::vector<Features> filter_dependencies_to_features(const std::vector<vcpkg::Dependency>& deps, const Triplet& t)
{
std::vector<Features> ret;
for (auto&& dep : deps)
{
const auto & qualifier = dep.qualifier;
if (qualifier.empty() || evaluate_expression(qualifier, t.canonical_name()))
{
ret.emplace_back(dep.depend);
}
const auto& qualifier = dep.qualifier;
if (qualifier.empty() || evaluate_expression(qualifier, t.canonical_name()))
{
ret.emplace_back(dep.depend);
}
}
return ret;
}

View File

@ -51,7 +51,7 @@ namespace vcpkg
{
const auto& pghs = *p_pghs;
Parse::RawParagraph keys;
Parse::RawParagraph keys;
if (pghs.size() > 0) keys = pghs[0];
for (size_t x = 1; x < pghs.size(); ++x)

View File

@ -148,9 +148,8 @@ namespace vcpkg
}
if (Strings::starts_with(arg, "--scripts-root="))
{
parse_cojoined_value(arg.substr(sizeof("--scripts-root=") - 1),
"--scripts-root",
args.scripts_root_dir);
parse_cojoined_value(
arg.substr(sizeof("--scripts-root=") - 1), "--scripts-root", args.scripts_root_dir);
continue;
}
if (arg == "--triplet")
@ -161,16 +160,14 @@ namespace vcpkg
}
if (Strings::starts_with(arg, "--overlay-ports="))
{
parse_cojoined_multivalue(arg.substr(sizeof("--overlay-ports=") - 1),
"--overlay-ports",
args.overlay_ports);
parse_cojoined_multivalue(
arg.substr(sizeof("--overlay-ports=") - 1), "--overlay-ports", args.overlay_ports);
continue;
}
if (Strings::starts_with(arg, "--overlay-triplets="))
{
parse_cojoined_multivalue(arg.substr(sizeof("--overlay-triplets=") - 1),
"--overlay-triplets",
args.overlay_triplets);
parse_cojoined_multivalue(
arg.substr(sizeof("--overlay-triplets=") - 1), "--overlay-triplets", args.overlay_triplets);
continue;
}
if (arg == "--debug")
@ -224,11 +221,11 @@ namespace vcpkg
{
const auto& key = arg.substr(0, eq_pos);
const auto& value = arg.substr(eq_pos + 1);
auto it = args.optional_command_arguments.find(key);
if (args.optional_command_arguments.end() == it)
{
args.optional_command_arguments.emplace(key, std::vector<std::string> { value });
args.optional_command_arguments.emplace(key, std::vector<std::string>{value});
}
else
{
@ -370,8 +367,9 @@ namespace vcpkg
{
if (v.empty())
{
System::printf(
System::Color::error, "Error: The option '%s' must be passed an argument.\n", option.name);
System::printf(System::Color::error,
"Error: The option '%s' must be passed an argument.\n",
option.name);
failed = true;
}
else
@ -425,12 +423,9 @@ namespace vcpkg
System::printf(" %-40s %s\n", (option.name + "=..."), option.short_help_text);
}
System::printf(" %-40s %s\n", "--triplet <t>", "Set the default triplet for unqualified packages");
System::printf(" %-40s %s\n",
"--overlay-ports=<path>",
"Specify directories to be used when searching for ports");
System::printf(" %-40s %s\n",
"--overlay-triplets=<path>",
"Specify directories containing triplets files");
System::printf(
" %-40s %s\n", "--overlay-ports=<path>", "Specify directories to be used when searching for ports");
System::printf(" %-40s %s\n", "--overlay-triplets=<path>", "Specify directories containing triplets files");
System::printf(" %-40s %s\n",
"--vcpkg-root <path>",
"Specify the vcpkg directory to use instead of current directory or tool directory");

View File

@ -42,7 +42,7 @@ namespace vcpkg
if (auto odp = overriddenDownloadsPath.get())
{
auto asPath = fs::u8path(*odp);
if (!fs::stdfs::is_directory(asPath))
if (!fs::is_directory(status(asPath)))
{
Metrics::g_metrics.lock()->track_property("error", "Invalid VCPKG_DOWNLOADS override directory.");
Checks::exit_with_message(
@ -71,12 +71,12 @@ namespace vcpkg
{
if (scripts_dir->empty() || !fs::stdfs::is_directory(*scripts_dir))
{
Metrics::g_metrics.lock()->track_property("error", "Invalid scripts override directory.");
Checks::exit_with_message(
VCPKG_LINE_INFO,
"Invalid scripts override directory: %s; "
"create that directory or unset --scripts-root to use the default scripts location.",
scripts_dir->u8string());
Metrics::g_metrics.lock()->track_property("error", "Invalid scripts override directory.");
Checks::exit_with_message(
VCPKG_LINE_INFO,
"Invalid scripts override directory: %s; "
"create that directory or unset --scripts-root to use the default scripts location.",
scripts_dir->u8string());
}
paths.scripts = *scripts_dir;
@ -147,26 +147,25 @@ namespace vcpkg
}
Util::sort_unique_erase(output);
return output;
});
});
}
const fs::path VcpkgPaths::get_triplet_file_path(const Triplet& triplet) const
{
return m_triplets_cache.get_lazy(triplet, [&]()-> auto {
for (auto&& triplet_dir : triplets_dirs)
{
auto&& path = triplet_dir / (triplet.canonical_name() + ".cmake");
if (this->get_filesystem().exists(path))
return m_triplets_cache.get_lazy(
triplet, [&]() -> auto {
for (auto&& triplet_dir : triplets_dirs)
{
return path;
auto&& path = triplet_dir / (triplet.canonical_name() + ".cmake");
if (this->get_filesystem().exists(path))
{
return path;
}
}
}
Checks::exit_with_message(VCPKG_LINE_INFO,
"Error: Triplet file %s.cmake not found",
triplet.canonical_name());
});
Checks::exit_with_message(
VCPKG_LINE_INFO, "Error: Triplet file %s.cmake not found", triplet.canonical_name());
});
}
const fs::path& VcpkgPaths::get_tool_exe(const std::string& tool) const