Skip to content

Commit 64f53c6

Browse files
Deduplicate the recursive file enumeration when installing packages. (#1770)
Co-authored-by: Robert Schumacher <[email protected]>
1 parent 4737a2f commit 64f53c6

File tree

8 files changed

+276
-115
lines changed

8 files changed

+276
-115
lines changed

include/vcpkg/base/files.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,11 @@ namespace vcpkg
155155
std::vector<Path> get_files_recursive(const Path& dir, LineInfo li) const;
156156
ExpectedL<std::vector<Path>> try_get_files_recursive(const Path& dir) const;
157157

158+
virtual std::vector<Path> get_files_recursive_lexically_proximate(const Path& dir,
159+
std::error_code& ec) const = 0;
160+
std::vector<Path> get_files_recursive_lexically_proximate(const Path& dir, LineInfo li) const;
161+
ExpectedL<std::vector<Path>> try_get_files_recursive_lexically_proximate(const Path& dir) const;
162+
158163
virtual std::vector<Path> get_files_non_recursive(const Path& dir, std::error_code& ec) const = 0;
159164
std::vector<Path> get_files_non_recursive(const Path& dir, LineInfo li) const;
160165
ExpectedL<std::vector<Path>> try_get_files_non_recursive(const Path& dir) const;

include/vcpkg/base/path.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,7 @@ namespace vcpkg
5656
private:
5757
std::string m_str;
5858
};
59-
}
59+
60+
// attempt to parse str as a path and return the filename if it exists; otherwise, an empty view
61+
StringView parse_filename(StringView str) noexcept;
62+
}

include/vcpkg/commands.export.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
#include <vcpkg/fwd/vcpkgcmdarguments.h>
55
#include <vcpkg/fwd/vcpkgpaths.h>
66

7+
#include <string>
8+
#include <vector>
9+
710
namespace vcpkg
811
{
912
extern const CommandMetadata CommandExportMetadata;
@@ -14,4 +17,33 @@ namespace vcpkg
1417
Triplet host_triplet);
1518

1619
void export_integration_files(const Path& raw_exported_dir_path, const VcpkgPaths& paths);
20+
21+
// treat the installed/triplet directory as if it were the packages directory by removing the triplet
22+
// e.g.
23+
// x64-windows/
24+
// x64-windows/include/
25+
// x64-windows/include/CONFLICT-A-HEADER-ONLY-CAPS.h
26+
// x64-windows/include/CONFLICT-a-header-ONLY-mixed.h
27+
// x64-windows/include/CONFLICT-a-header-ONLY-mixed2.h
28+
// x64-windows/include/conflict-a-header-only-lowercase.h
29+
// x64-windows/share/
30+
// x64-windows/share/a-conflict/
31+
// x64-windows/share/a-conflict/copyright
32+
// x64-windows/share/a-conflict/vcpkg.spdx.json
33+
// x64-windows/share/a-conflict/vcpkg_abi_info.txt
34+
//
35+
// becomes
36+
//
37+
// include
38+
// include/CONFLICT-A-HEADER-ONLY-CAPS.h
39+
// include/CONFLICT-a-header-ONLY-mixed.h
40+
// include/CONFLICT-a-header-ONLY-mixed2.h
41+
// include/conflict-a-header-only-lowercase.h
42+
// share
43+
// share/a-conflict
44+
// share/a-conflict/copyright
45+
// share/a-conflict/vcpkg.spdx.json
46+
// share/a-conflict/vcpkg_abi_info.txt
47+
std::vector<std::string> convert_list_to_proximate_files(std::vector<std::string>&& lines,
48+
StringView triplet_canonical_name);
1749
}

include/vcpkg/commands.install.h

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -64,23 +64,25 @@ namespace vcpkg
6464
void print_complete_message() const;
6565
};
6666

67-
struct InstallDir
68-
{
69-
static InstallDir from_destination_root(const InstalledPaths& ip, Triplet t, const BinaryParagraph& pgh);
70-
71-
private:
72-
Path m_destination;
73-
Path m_listfile;
74-
75-
public:
76-
const Path& destination() const;
77-
const Path& listfile() const;
78-
};
79-
67+
// First, writes triplet_canonical_name / (including the trailing slash) to listfile. Then:
68+
// For each directory in source_dir / proximate_files
69+
// * create directory destination_installed / triplet_canonical_name / proximate_file
70+
// * write a line in listfile triplet_canonical_name / proximate_file / (note the trailing slash)
71+
// For each regular file in source_dir / proximate_files
72+
// * copy source_dir / proximate_file -> destination_installed / triplet_canonical_name / proximate_file
73+
// * write a line in listfile triplet_canonical_name / proximate_file
74+
// For each symlink or junction in source_dir / proximate_files:
75+
// * if hydrate == SymlinkHydrate::yes, resolve symlinks and follow the rules above, otherwise,
76+
// * copy the symlink or junction source_dir / proximate_file
77+
// -> destination_installed / triplet_canonical_name / proximate_file
78+
// * write a line in listfile triplet_canonical_name / proximate_file
79+
// (note *no* trailing slash, even for directory symlinks)
8080
void install_files_and_write_listfile(const Filesystem& fs,
8181
const Path& source_dir,
82-
const std::vector<Path>& files,
83-
const InstallDir& destination_dir,
82+
const std::vector<std::string>& proximate_files,
83+
const Path& destination_installed,
84+
StringView triplet_canonical_name,
85+
const Path& listfile,
8486
const SymlinkHydrate hydrate);
8587

8688
struct CMakeUsageInfo

src/vcpkg-test/export.cpp

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#include <vcpkg-test/util.h>
2+
3+
#include <vcpkg/base/files.h>
4+
#include <vcpkg/base/util.h>
5+
6+
#include <vcpkg/commands.export.h>
7+
8+
#include <string>
9+
#include <vector>
10+
11+
using namespace vcpkg;
12+
13+
TEST_CASE ("convert_list_to_proximate_files basic example", "[export]")
14+
{
15+
std::vector<std::string> lines = {
16+
"x64-windows/",
17+
"x64-windows/include/", // directory (trailing slash removed)
18+
"x64-windows/include/CONFLICT-A-HEADER-ONLY-CAPS.h",
19+
"x64-windows/include/CONFLICT-a-header-ONLY-mixed.h",
20+
"x64-windows/include/CONFLICT-a-header-ONLY-mixed2.h",
21+
"x64-windows/include/conflict-a-header-only-lowercase.h",
22+
"x64-windows/share/", // directory (trailing slash removed)
23+
"x64-windows/share/a-conflict/", // directory (trailing slash removed)
24+
"x64-windows/share/a-conflict/copyright",
25+
"x64-windows/share/a-conflict/vcpkg.spdx.json",
26+
"x64-windows/share/a-conflict/vcpkg_abi_info.txt",
27+
};
28+
29+
const auto result = convert_list_to_proximate_files(std::move(lines), "x64-windows");
30+
const std::vector<std::string> expected = {
31+
"include",
32+
"include/CONFLICT-A-HEADER-ONLY-CAPS.h",
33+
"include/CONFLICT-a-header-ONLY-mixed.h",
34+
"include/CONFLICT-a-header-ONLY-mixed2.h",
35+
"include/conflict-a-header-only-lowercase.h",
36+
"share",
37+
"share/a-conflict",
38+
"share/a-conflict/copyright",
39+
"share/a-conflict/vcpkg.spdx.json",
40+
"share/a-conflict/vcpkg_abi_info.txt",
41+
};
42+
REQUIRE(result == expected);
43+
}
44+
45+
TEST_CASE ("convert_list_to_proximate_files preserves order and trims trailing slashes", "[export]")
46+
{
47+
std::vector<std::string> lines = {
48+
"x64-windows/share/", // -> share
49+
"x64-windows/share/pkg/", // -> share/pkg
50+
"x64-windows/share/pkg/file", // -> share/pkg/file (unchanged)
51+
"x64-windows/include/", // -> include
52+
"x64-windows/include/file.h", // -> include/file.h
53+
"x64-windows/share/other/file2" // -> share/other/file2
54+
};
55+
56+
auto result = convert_list_to_proximate_files(std::move(lines), "x64-windows");
57+
const std::vector<std::string> expected = {
58+
"share",
59+
"share/pkg",
60+
"share/pkg/file",
61+
"include",
62+
"include/file.h",
63+
"share/other/file2",
64+
};
65+
REQUIRE(result == expected);
66+
}
67+
68+
TEST_CASE ("convert_list_to_proximate_files empty input", "[export]")
69+
{
70+
auto result = convert_list_to_proximate_files(std::vector<std::string>{}, "x64-windows");
71+
REQUIRE(result.empty());
72+
}

src/vcpkg/base/files.cpp

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <vcpkg/base/files.h>
77
#include <vcpkg/base/message_sinks.h>
88
#include <vcpkg/base/messages.h>
9+
#include <vcpkg/base/path.h>
910
#include <vcpkg/base/span.h>
1011
#include <vcpkg/base/system.debug.h>
1112
#include <vcpkg/base/system.h>
@@ -223,15 +224,6 @@ namespace
223224
return last;
224225
}
225226

226-
StringView parse_filename(const StringView str) noexcept
227-
{
228-
// attempt to parse str as a path and return the filename if it exists; otherwise, an empty view
229-
const auto first = str.data();
230-
const auto last = first + str.size();
231-
const auto filename = find_filename(first, last);
232-
return StringView(filename, static_cast<size_t>(last - filename));
233-
}
234-
235227
constexpr const char* find_extension(const char* const filename, const char* const ads) noexcept
236228
{
237229
// find dividing point between stem and extension in a generic format filename consisting of [filename, ads)
@@ -1367,6 +1359,14 @@ namespace vcpkg
13671359

13681360
const char* to_printf_arg(const Path& p) noexcept { return p.m_str.c_str(); }
13691361

1362+
StringView parse_filename(const StringView str) noexcept
1363+
{
1364+
const auto first = str.data();
1365+
const auto last = first + str.size();
1366+
const auto filename = find_filename(first, last);
1367+
return StringView(filename, static_cast<size_t>(last - filename));
1368+
}
1369+
13701370
bool is_symlink(FileType s) { return s == FileType::symlink || s == FileType::junction; }
13711371
bool is_regular_file(FileType s) { return s == FileType::regular; }
13721372
bool is_directory(FileType s) { return s == FileType::directory; }
@@ -1674,6 +1674,23 @@ namespace vcpkg
16741674
return maybe_files;
16751675
}
16761676

1677+
std::vector<Path> ReadOnlyFilesystem::get_files_recursive_lexically_proximate(const Path& dir, LineInfo li) const
1678+
{
1679+
return this->try_get_files_recursive_lexically_proximate(dir).value_or_exit(li);
1680+
}
1681+
1682+
ExpectedL<std::vector<Path>> ReadOnlyFilesystem::try_get_files_recursive_lexically_proximate(const Path& dir) const
1683+
{
1684+
std::error_code ec;
1685+
auto maybe_files = this->get_files_recursive_lexically_proximate(dir, ec);
1686+
if (ec)
1687+
{
1688+
return format_filesystem_call_error(ec, __func__, {dir});
1689+
}
1690+
1691+
return maybe_files;
1692+
}
1693+
16771694
std::vector<Path> ReadOnlyFilesystem::get_files_non_recursive(const Path& dir, LineInfo li) const
16781695
{
16791696
return this->try_get_files_non_recursive(dir).value_or_exit(li);
@@ -2666,6 +2683,21 @@ namespace vcpkg
26662683
return get_directories_impl<stdfs::recursive_directory_iterator>(dir, ec);
26672684
}
26682685

2686+
virtual std::vector<Path> get_files_recursive_lexically_proximate(const Path& dir,
2687+
std::error_code& ec) const override
2688+
{
2689+
auto ret = this->get_files_recursive(dir, ec);
2690+
if (!ec)
2691+
{
2692+
const auto base = to_stdfs_path(dir);
2693+
for (auto& p : ret)
2694+
{
2695+
p = from_stdfs_path(to_stdfs_path(p).lexically_proximate(base));
2696+
}
2697+
}
2698+
return ret;
2699+
}
2700+
26692701
virtual std::vector<Path> get_directories_recursive_lexically_proximate(const Path& dir,
26702702
std::error_code& ec) const override
26712703
{
@@ -2972,6 +3004,15 @@ namespace vcpkg
29723004
return result;
29733005
}
29743006

3007+
virtual std::vector<Path> get_files_recursive_lexically_proximate(const Path& dir,
3008+
std::error_code& ec) const override
3009+
{
3010+
std::vector<Path> result;
3011+
Path out_base;
3012+
get_files_recursive_impl(result, dir, out_base, ec, true, true, true);
3013+
return result;
3014+
}
3015+
29753016
virtual std::vector<Path> get_files_non_recursive(const Path& dir, std::error_code& ec) const override
29763017
{
29773018
std::vector<Path> result;

src/vcpkg/commands.export.cpp

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -419,25 +419,17 @@ namespace
419419
msg::println(msgExportingPackage, msg::package_name = action.spec);
420420

421421
const BinaryParagraph& binary_paragraph = action.core_paragraph().value_or_exit(VCPKG_LINE_INFO);
422-
423-
const InstallDir dirs =
424-
InstallDir::from_destination_root(export_paths, action.spec.triplet(), binary_paragraph);
422+
const auto& triplet_canonical_name = action.spec.triplet().canonical_name();
425423

426424
auto lines =
427425
fs.read_lines(paths.installed().listfile_path(binary_paragraph)).value_or_exit(VCPKG_LINE_INFO);
428-
std::vector<Path> files;
429-
for (auto&& suffix : lines)
430-
{
431-
if (suffix.empty()) continue;
432-
if (suffix.back() == '/') suffix.pop_back();
433-
if (suffix == action.spec.triplet().to_string()) continue;
434-
files.push_back(paths.installed().root() / suffix);
435-
}
436-
426+
auto proximate_files = convert_list_to_proximate_files(std::move(lines), triplet_canonical_name);
437427
install_files_and_write_listfile(fs,
438428
paths.installed().triplet_dir(action.spec.triplet()),
439-
files,
440-
dirs,
429+
proximate_files,
430+
export_paths.root(),
431+
triplet_canonical_name,
432+
export_paths.listfile_path(binary_paragraph),
441433
opts.dereference_symlinks ? SymlinkHydrate::CopyData
442434
: SymlinkHydrate::CopySymlinks);
443435
}
@@ -602,4 +594,25 @@ namespace vcpkg
602594

603595
Checks::exit_success(VCPKG_LINE_INFO);
604596
}
597+
598+
std::vector<std::string> convert_list_to_proximate_files(std::vector<std::string>&& lines,
599+
StringView triplet_canonical_name)
600+
{
601+
auto prefix_length = triplet_canonical_name.size() + 1; // +1 for the trailing '/'
602+
std::vector<std::string> proximate_files;
603+
for (auto&& suffix : lines)
604+
{
605+
if (suffix.size() <= prefix_length)
606+
{
607+
continue;
608+
}
609+
610+
suffix.erase(0, prefix_length);
611+
if (suffix.back() == '/') suffix.pop_back();
612+
if (suffix.empty()) continue;
613+
proximate_files.emplace_back(std::move(suffix));
614+
}
615+
616+
return proximate_files;
617+
}
605618
} // namespace vcpkg

0 commit comments

Comments
 (0)