Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Install files in parallel #1256

Open
wants to merge 40 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
6cd56a7
Install files in parallel
Thomas1664 Oct 29, 2023
7dad374
optimize + fixes
Thomas1664 Oct 29, 2023
05bc1f0
async create listfile dir
Thomas1664 Oct 29, 2023
ba36960
format
Thomas1664 Oct 29, 2023
4dab20c
Remove unneeded optional
Thomas1664 Oct 30, 2023
dc14e81
fix format of error message
Thomas1664 Oct 30, 2023
c8cd17b
noexcept + constexpr for fs one-liners
Thomas1664 Oct 30, 2023
80af37d
inline + noexcept for one-liners in InstallDir
Thomas1664 Oct 30, 2023
0e09c92
decouple installation from list file creation
Thomas1664 Oct 30, 2023
4fe1e0f
fixes
Thomas1664 Oct 30, 2023
188d451
initialize member
Thomas1664 Oct 30, 2023
65b8c3e
format
Thomas1664 Oct 30, 2023
f7bd6d5
investigate unit test failure
Thomas1664 Oct 30, 2023
23aae9b
Merge branch 'parallel-file-install' of https://github.com/Thomas1664…
Thomas1664 Oct 30, 2023
2cd0e1b
revert
Thomas1664 Oct 30, 2023
0b44c21
format
Thomas1664 Oct 30, 2023
284ac35
format
Thomas1664 Oct 30, 2023
4e787dd
revert
Thomas1664 Oct 30, 2023
c0cba4b
Merge branch 'microsoft:main' into parallel-file-install
Thomas1664 Nov 1, 2023
69fbb7d
invert if
Thomas1664 Nov 1, 2023
e6c094a
unify duplicated if
Thomas1664 Nov 1, 2023
4684372
Merge branch 'microsoft:main' into parallel-file-install
Thomas1664 Nov 4, 2023
99106d3
Fix merge
Thomas1664 Nov 4, 2023
85a9ae9
Merge branch 'microsoft:main' into parallel-file-install
Thomas1664 Nov 9, 2023
d0c1c3a
Merge branch 'microsoft:main' into parallel-file-install
Thomas1664 Nov 12, 2023
599ecf4
Fix comments
Thomas1664 Nov 17, 2023
d04f108
Merge branch 'parallel-file-install' of https://github.com/Thomas1664…
Thomas1664 Nov 17, 2023
376829c
Fix most CR comments
Thomas1664 Nov 28, 2023
68ba04e
format
Thomas1664 Nov 28, 2023
4179d0e
Merge branch 'microsoft:main' into parallel-file-install
Thomas1664 Dec 16, 2023
b211761
Merge branch 'microsoft:main' into parallel-file-install
Thomas1664 Mar 5, 2024
9b676c4
include future
Thomas1664 Mar 5, 2024
670d164
use partition instead of custom sort
Thomas1664 Mar 5, 2024
cac8672
remove compairson operator
Thomas1664 Mar 5, 2024
f109858
format
Thomas1664 Mar 5, 2024
f0048c6
Merge remote-tracking branch 'origin/main' into parallel-file-install
Thomas1664 Mar 18, 2024
247c957
Merge branch 'microsoft:main' into parallel-file-install
Thomas1664 Apr 28, 2024
0927c90
Merge remote-tracking branch 'upstream/main' into parallel-file-install
Thomas1664 Aug 25, 2024
8a44030
Merge branch 'microsoft:main' into parallel-file-install
Thomas1664 Dec 8, 2024
42c406e
Only parallelize symlink_status
Thomas1664 Dec 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion include/vcpkg/commands.install.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ namespace vcpkg

void install_files_and_write_listfile(const Filesystem& fs,
const Path& source_dir,
const std::vector<Path>& files,
std::vector<Path>&& files,
const InstallDir& destination_dir);

InstallResult install_package(const VcpkgPaths& paths,
Expand Down
2 changes: 1 addition & 1 deletion src/vcpkg/commands.export.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -527,7 +527,7 @@ namespace
files.push_back(paths.installed().root() / suffix);
}

install_files_and_write_listfile(fs, paths.installed().triplet_dir(action.spec.triplet()), files, dirs);
install_files_and_write_listfile(fs, paths.installed().triplet_dir(action.spec.triplet()), std::move(files), dirs);
}
}

Expand Down
132 changes: 87 additions & 45 deletions src/vcpkg/commands.install.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <vcpkg/base/files.h>
#include <vcpkg/base/hash.h>
#include <vcpkg/base/messages.h>
#include <vcpkg/base/parallel-algorithms.h>
#include <vcpkg/base/system.debug.h>
#include <vcpkg/base/system.h>
#include <vcpkg/base/util.h>
Expand All @@ -29,6 +30,9 @@
#include <vcpkg/xunitwriter.h>

#include <iterator>
#ifdef _WIN32
#include <execution>
#endif

namespace vcpkg
{
Expand All @@ -55,15 +59,13 @@ namespace vcpkg
Strings::concat("Source directory ", source_dir, "does not exist"));
auto files = fs.get_files_recursive(source_dir, VCPKG_LINE_INFO);
Util::erase_remove_if(files, [](Path& path) { return path.filename() == ".DS_Store"; });
install_files_and_write_listfile(fs, source_dir, files, destination_dir);
install_files_and_write_listfile(fs, source_dir, std::move(files), destination_dir);
}
void install_files_and_write_listfile(const Filesystem& fs,
const Path& source_dir,
const std::vector<Path>& files,
std::vector<Path>&& files,
const InstallDir& destination_dir)
{
std::vector<std::string> output;

const size_t prefix_length = source_dir.native().size();
const Path& destination = destination_dir.destination();
std::string destination_subdirectory = destination.filename().to_string();
Expand All @@ -73,60 +75,99 @@ namespace vcpkg
const auto listfile_parent = listfile.parent_path();
fs.create_directories(listfile_parent, VCPKG_LINE_INFO);

output.push_back(destination_subdirectory + "/");
for (auto&& file : files)
std::vector<Optional<FileType>> symlink_statuses(files.size());

parallel_transform(
files.begin(), files.size(), symlink_statuses.begin(), [&fs](auto&& file) -> Optional<FileType> {
std::error_code ec;
auto status = fs.symlink_status(file, ec);
if (ec)
{
msg::println_warning(format_filesystem_call_error(ec, "symlink_status", {file}));
return nullopt;
}
return status;
});

std::vector<std::string> dir_output;
dir_output.push_back(destination_subdirectory + "/");

std::vector<std::pair<Path, Optional<FileType>>> files_and_status;
files_and_status.reserve(files.size());

for (size_t i = 0; i < files.size(); ++i)
{
std::error_code ec;
const auto status = fs.symlink_status(file, ec);
if (ec)
if (!symlink_statuses[i].has_value())
{
continue;
}
auto status = *symlink_statuses[i].get();

if (is_directory(status))
{
msg::println_warning(format_filesystem_call_error(ec, "symlink_status", {file}));
const auto suffix = files[i].generic_u8string().substr(prefix_length + 1);
const auto target = destination / suffix;
auto this_output = Strings::concat(destination_subdirectory, "/", suffix);

std::error_code ec;
fs.create_directory(target, ec);
if (ec)
{
msg::println_error(msgInstallFailed, msg::path = target, msg::error_msg = ec.message());
}

// Trailing backslash for directories
this_output.push_back('/');
dir_output.push_back(std::move(this_output));
continue;
}

const auto filename = file.filename();
if (vcpkg::is_regular_file(status) &&
const auto filename = files[i].filename();

if (is_regular_file(status) &&
(filename == "CONTROL" || filename == "vcpkg.json" || filename == "BUILD_INFO"))
{
// Do not copy the control file or manifest file
continue;
}

const auto suffix = file.generic_u8string().substr(prefix_length + 1);
const auto target = destination / suffix;

bool use_hard_link = true;
auto this_output = Strings::concat(destination_subdirectory, "/", suffix);
switch (status)
if (!is_regular_file(status) && !is_symlink(status))
{
case FileType::directory:
{
fs.create_directory(target, ec);
if (ec)
{
msg::println_error(msgInstallFailed, msg::path = target, msg::error_msg = ec.message());
}
msg::println_error(msgInvalidFileType, msg::path = files[i]);
continue;
}
files_and_status.emplace_back(std::move(files[i]), std::move(symlink_statuses[i]));
}

// Trailing backslash for directories
this_output.push_back('/');
output.push_back(std::move(this_output));
break;
}
case FileType::regular:
std::vector<std::string> output(files_and_status.size());

parallel_transform(
files_and_status.begin(), files_and_status.size(), output.begin(), [&](auto&& file_and_status) {
auto&& [file, maybe_status] = file_and_status;

const auto status = *maybe_status.get();
std::error_code ec;

const auto suffix = file.generic_u8string().substr(prefix_length + 1);
const auto target = destination / suffix;

bool use_hard_link = true;
auto this_output = Strings::concat(destination_subdirectory, "/", suffix);
if (is_regular_file(status))
{
if (fs.exists(target, IgnoreErrors{}))
{
msg::println_warning(msgOverwritingFile, msg::path = target);
fs.remove_all(target, IgnoreErrors{});
fs.remove(target, IgnoreErrors{});
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no difference for regular files between remove_all and remove (provided that vcpkg's implementation is identical to the STL): https://en.cppreference.com/w/cpp/filesystem/remove

}
if (use_hard_link)
{
fs.create_hard_link(file, target, ec);
if (ec)
{
Debug::println("Install from packages to installed: Fallback to copy "
"instead creating hard links because of: ",
ec.message());
"instead creating hard links because of: ",
ec.message());
use_hard_link = false;
}
}
Expand All @@ -139,12 +180,8 @@ namespace vcpkg
{
msg::println_error(msgInstallFailed, msg::path = target, msg::error_msg = ec.message());
}

output.push_back(std::move(this_output));
break;
}
case FileType::symlink:
case FileType::junction:
else if (is_symlink(status))
{
if (fs.exists(target, IgnoreErrors{}))
{
Expand All @@ -156,15 +193,20 @@ namespace vcpkg
{
msg::println_error(msgInstallFailed, msg::path = target, msg::error_msg = ec.message());
}

output.push_back(std::move(this_output));
break;
}
default: msg::println_error(msgInvalidFileType, msg::path = file); break;
}
}
else
{
Checks::unreachable(VCPKG_LINE_INFO);
}
return this_output;
});

std::move(dir_output.begin(), dir_output.end(), std::back_inserter(output));
#ifdef _WIN32
std::sort(std::execution::par_unseq, output.begin(), output.end());
#else
std::sort(output.begin(), output.end());
#endif
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm open for suggestions on how to implement parallel sorting for all platforms

fs.write_lines(listfile, output, VCPKG_LINE_INFO);
}

Expand Down