Skip to content

Commit 30d2a80

Browse files
committed
Support writing to a global swift index store
"-global-index-store-path" doesn't change where the index store is output to on its own, it's only used if at that path a file named "rules_swift_global_index_enabled" exists, which needs to be managed by an external process. Always passing this flag allows caching of the results if they are output to the non-global location. This allows for incremental compilation to use the "rules_swift_global_index_enabled" file and merge cached a fresh results (outside of Bazel).
1 parent 329a810 commit 30d2a80

File tree

9 files changed

+191
-41
lines changed

9 files changed

+191
-41
lines changed

swift/internal/compiling.bzl

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1172,6 +1172,20 @@ def _index_while_building_configurator(prerequisites, args):
11721172
if not _index_store_path_overridden(prerequisites.user_compile_flags):
11731173
args.add("-index-store-path", prerequisites.indexstore_directory.path)
11741174

1175+
# "-global-index-store-path" doesn't change where the index store is
1176+
# output to on its own, it's only used if at that path a file named
1177+
# "rules_swift_global_index_enabled" exists, which needs to be managed
1178+
# by an external process.
1179+
# Always passing this flag allows caching of the results if they are
1180+
# output to the non-global location. This allows for incremental
1181+
# compilation to use the "rules_swift_global_index_enabled" file and
1182+
# merge cached a fresh results (outside of Bazel).
1183+
if prerequisites.bin_dir:
1184+
args.add(
1185+
"-Xwrapped-swift=-global-index-store-path",
1186+
paths.join(prerequisites.bin_dir.path, "_indexstore"),
1187+
)
1188+
11751189
def _conditional_compilation_flag_configurator(prerequisites, args):
11761190
"""Adds (non-Clang) conditional compilation flags to the command line."""
11771191
all_defines = depset(

tools/common/file_system.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ std::string GetCurrentDirectory() {
3737
return cwd;
3838
}
3939

40+
bool FileExists(const std::string &path) {
41+
return access(path.c_str(), 0) == 0;
42+
}
43+
4044
bool CopyFile(const std::string &src, const std::string &dest) {
4145
#ifdef __APPLE__
4246
// The `copyfile` function with `COPYFILE_ALL` mode preserves permissions and

tools/common/file_system.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@
2020
// Gets the path to the current working directory.
2121
std::string GetCurrentDirectory();
2222

23+
// Returns true if something exists at path.
24+
bool FileExists(const std::string &path);
25+
2326
// Copies the file at src to dest. Returns true if successful.
2427
bool CopyFile(const std::string &src, const std::string &dest);
2528

tools/common/string_utils.cc

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,41 @@ bool MakeSubstitutions(std::string *arg,
4040

4141
return changed;
4242
}
43+
44+
std::string Unescape(const std::string &arg) {
45+
std::string result;
46+
auto length = arg.size();
47+
for (size_t i = 0; i < length; ++i) {
48+
auto ch = arg[i];
49+
50+
// If it's a backslash, consume it and append the character that follows.
51+
if (ch == '\\' && i + 1 < length) {
52+
++i;
53+
result.push_back(arg[i]);
54+
continue;
55+
}
56+
57+
// If it's a quote, process everything up to the matching quote, unescaping
58+
// backslashed characters as needed.
59+
if (ch == '"' || ch == '\'') {
60+
auto quote = ch;
61+
++i;
62+
while (i != length && arg[i] != quote) {
63+
if (arg[i] == '\\' && i + 1 < length) {
64+
++i;
65+
}
66+
result.push_back(arg[i]);
67+
++i;
68+
}
69+
if (i == length) {
70+
break;
71+
}
72+
continue;
73+
}
74+
75+
// It's a regular character.
76+
result.push_back(ch);
77+
}
78+
79+
return result;
80+
}

tools/common/string_utils.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,7 @@
2222
bool MakeSubstitutions(std::string *arg,
2323
const std::map<std::string, std::string> &mappings);
2424

25+
// Unescape and unquote an argument read from a line of a response file.
26+
std::string Unescape(const std::string &arg);
27+
2528
#endif // BUILD_BAZEL_RULES_SWIFT_TOOLS_COMMON_STRING_UTILS_H_

tools/worker/BUILD

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ cc_library(
4646
hdrs = ["compile_without_worker.h"],
4747
deps = [
4848
":swift_runner",
49+
"//tools/common:file_system",
50+
"//tools/common:string_utils",
51+
"//tools/common:temp_file",
4952
],
5053
)
5154

tools/worker/compile_without_worker.cc

Lines changed: 100 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,110 @@
1414

1515
#include "tools/worker/compile_without_worker.h"
1616

17-
#include <iostream>
17+
#include <fstream>
1818
#include <string>
1919
#include <vector>
2020

21+
#include "tools/common/file_system.h"
22+
#include "tools/common/string_utils.h"
23+
#include "tools/common/temp_file.h"
2124
#include "tools/worker/swift_runner.h"
2225

26+
namespace {
27+
28+
static void ProcessPossibleResponseFile(
29+
const std::string &arg, std::function<void(const std::string &)> consumer) {
30+
auto path = arg.substr(1);
31+
std::ifstream original_file(path);
32+
// If we couldn't open it, maybe it's not a file; maybe it's just some other
33+
// argument that starts with "@". (Unlikely, but it's safer to check.)
34+
if (!original_file.good()) {
35+
consumer(arg);
36+
return;
37+
}
38+
39+
std::string arg_from_file;
40+
while (std::getline(original_file, arg_from_file)) {
41+
// Arguments in response files might be quoted/escaped, so we need to
42+
// unescape them ourselves.
43+
consumer(Unescape(arg_from_file));
44+
}
45+
}
46+
47+
static std::tuple<bool, std::vector<std::string>>
48+
ArgumentsIncludingParamsContent(const std::vector<std::string> &args) {
49+
std::vector<std::string> full_args;
50+
std::string prev_arg;
51+
std::string index_store_path;
52+
std::string global_index_store_path;
53+
bool process_args = false;
54+
55+
auto consumer = [&](const std::string &arg) {
56+
if (arg == "-index-store-path") {
57+
// Peel off the `-index-store-path` argument, so we can rewrite it if
58+
// necessary later.
59+
} else if (prev_arg == "-index-store-path") {
60+
index_store_path = arg;
61+
} else if (arg == "-Xwrapped-swift=-global-index-store-path") {
62+
// Minimally we want to not pass this argument to the worker, since it
63+
// can't process it
64+
process_args = true;
65+
} else if (prev_arg == "-Xwrapped-swift=-global-index-store-path") {
66+
global_index_store_path = arg;
67+
} else {
68+
full_args.push_back(arg);
69+
}
70+
71+
prev_arg = arg;
72+
};
73+
74+
for (auto arg : args) {
75+
if (arg[0] == '@') {
76+
ProcessPossibleResponseFile(arg, consumer);
77+
} else {
78+
consumer(arg);
79+
}
80+
}
81+
82+
if (!global_index_store_path.empty() && FileExists(global_index_store_path + "/rules_swift_global_index_enabled")) {
83+
full_args.push_back("-index-store-path");
84+
full_args.push_back(global_index_store_path);
85+
} else if (!index_store_path.empty()) {
86+
// Add back index store path
87+
full_args.push_back("-index-store-path");
88+
full_args.push_back(index_store_path);
89+
}
90+
91+
return std::make_tuple(process_args, full_args);
92+
}
93+
94+
} // end namespace
95+
2396
int CompileWithoutWorker(const std::vector<std::string> &args) {
24-
return SwiftRunner(args).Run(&std::cerr, /*stdout_to_stderr=*/false);
97+
std::vector<std::string> actual_args(args.begin() + 1, args.end());
98+
auto arguments = ArgumentsIncludingParamsContent(actual_args);
99+
100+
bool process_args = std::get<0>(arguments);
101+
if (!process_args) {
102+
return SwiftRunner(args).Run(&std::cerr, /*stdout_to_stderr=*/false);
103+
}
104+
105+
// We have to rewrite the arguments. This means that if we just try to pass
106+
// the new arguments verbatim to swiftc, we might end up with a command line
107+
// that's too long. Rather than try to figure out these limits (which is very
108+
// OS-specific and easy to get wrong), we unconditionally write the processed
109+
// arguments out to a params file.
110+
auto params_file = TempFile::Create("swiftc_params.XXXXXX");
111+
std::ofstream params_file_stream(params_file->GetPath());
112+
113+
// Write out all processed arguments
114+
for (auto arg : std::get<1>(arguments)) {
115+
params_file_stream << arg << '\n';
116+
}
117+
118+
std::vector<std::string> new_args(args.begin(), args.begin() + 1);
119+
new_args.push_back("@" + params_file->GetPath());
120+
params_file_stream.close();
121+
122+
return SwiftRunner(new_args).Run(&std::cerr, /*stdout_to_stderr=*/false);
25123
}

tools/worker/swift_runner.cc

Lines changed: 0 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -60,45 +60,6 @@ static std::unique_ptr<TempFile> WriteResponseFile(
6060
return response_file;
6161
}
6262

63-
// Unescape and unquote an argument read from a line of a response file.
64-
static std::string Unescape(const std::string &arg) {
65-
std::string result;
66-
auto length = arg.size();
67-
for (size_t i = 0; i < length; ++i) {
68-
auto ch = arg[i];
69-
70-
// If it's a backslash, consume it and append the character that follows.
71-
if (ch == '\\' && i + 1 < length) {
72-
++i;
73-
result.push_back(arg[i]);
74-
continue;
75-
}
76-
77-
// If it's a quote, process everything up to the matching quote, unescaping
78-
// backslashed characters as needed.
79-
if (ch == '"' || ch == '\'') {
80-
auto quote = ch;
81-
++i;
82-
while (i != length && arg[i] != quote) {
83-
if (arg[i] == '\\' && i + 1 < length) {
84-
++i;
85-
}
86-
result.push_back(arg[i]);
87-
++i;
88-
}
89-
if (i == length) {
90-
break;
91-
}
92-
continue;
93-
}
94-
95-
// It's a regular character.
96-
result.push_back(ch);
97-
}
98-
99-
return result;
100-
}
101-
10263
} // namespace
10364

10465
SwiftRunner::SwiftRunner(const std::vector<std::string> &args,

tools/worker/work_processor.cc

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ void WorkProcessor::ProcessWorkRequest(
6161

6262
OutputFileMap output_file_map;
6363
std::string output_file_map_path;
64+
std::string index_store_path;
65+
std::string global_index_store_path;
6466
bool is_wmo = false;
6567

6668
std::string prev_arg;
@@ -73,6 +75,18 @@ void WorkProcessor::ProcessWorkRequest(
7375
} else if (prev_arg == "-output-file-map") {
7476
output_file_map_path = arg;
7577
arg.clear();
78+
} else if (arg == "-index-store-path") {
79+
// Peel off the `-index-store-path` argument, so we can rewrite it if
80+
// necessary later.
81+
arg.clear();
82+
} else if (prev_arg == "-index-store-path") {
83+
index_store_path = arg;
84+
arg.clear();
85+
} else if (arg == "-Xwrapped-swift=-global-index-store-path") {
86+
arg.clear();
87+
} else if (prev_arg == "-Xwrapped-swift=-global-index-store-path") {
88+
global_index_store_path = arg;
89+
arg.clear();
7690
} else if (ArgumentEnablesWMO(arg)) {
7791
is_wmo = true;
7892
}
@@ -109,6 +123,18 @@ void WorkProcessor::ProcessWorkRequest(
109123
}
110124
}
111125

126+
if (!index_store_path.empty()) {
127+
if (!global_index_store_path.empty() && FileExists(global_index_store_path + "/rules_swift_global_index_enabled")) {
128+
// Use the global index store path instead.
129+
params_file_stream << "-index-store-path\n";
130+
params_file_stream << global_index_store_path << '\n';
131+
} else {
132+
// Just put the original index store path back.
133+
params_file_stream << "-index-store-path\n";
134+
params_file_stream << index_store_path << '\n';
135+
}
136+
}
137+
112138
processed_args.push_back("@" + params_file->GetPath());
113139
params_file_stream.close();
114140

0 commit comments

Comments
 (0)