Skip to content

Commit 84286ad

Browse files
Reuse swiftmodule for incremental builds (#633)
1 parent 3d6141f commit 84286ad

File tree

5 files changed

+98
-13
lines changed

5 files changed

+98
-13
lines changed

tools/common/file_system.cc

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
#ifdef __APPLE__
2424
#include <copyfile.h>
25+
#include <removefile.h>
2526
#else
2627
#include <fcntl.h>
2728
#include <sys/sendfile.h>
@@ -37,6 +38,20 @@ std::string GetCurrentDirectory() {
3738
return cwd;
3839
}
3940

41+
bool FileExists(const std::string &path) {
42+
return access(path.c_str(), 0) == 0;
43+
}
44+
45+
bool RemoveFile(const std::string &path) {
46+
#ifdef __APPLE__
47+
return removefile(path.c_str(), nullptr, 0);
48+
#elif __unix__
49+
return remove(path.c_str());
50+
#else
51+
#error Only macOS and Unix are supported at this time.
52+
#endif
53+
}
54+
4055
bool CopyFile(const std::string &src, const std::string &dest) {
4156
#ifdef __APPLE__
4257
// The `copyfile` function with `COPYFILE_ALL` mode preserves permissions and

tools/common/file_system.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@
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+
26+
// Removes the file at path. Returns true if successful.
27+
bool RemoveFile(const std::string &path);
28+
2329
// Copies the file at src to dest. Returns true if successful.
2430
bool CopyFile(const std::string &src, const std::string &dest);
2531

tools/worker/output_file_map.cc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ void OutputFileMap::WriteToPath(const std::string &path) {
5757
void OutputFileMap::UpdateForIncremental(const std::string &path) {
5858
nlohmann::json new_output_file_map;
5959
std::map<std::string, std::string> incremental_outputs;
60+
std::map<std::string, std::string> incremental_inputs;
6061

6162
// The empty string key is used to represent outputs that are for the whole
6263
// module, rather than for a particular source file.
@@ -111,6 +112,15 @@ void OutputFileMap::UpdateForIncremental(const std::string &path) {
111112
new_output_file_map[src] = src_map;
112113
}
113114

115+
auto swiftmodule_path = ReplaceExtension(path, ".swiftmodule", /*all_extensions=*/true);
116+
auto copied_swiftmodule_path = MakeIncrementalOutputPath(swiftmodule_path);
117+
incremental_inputs[swiftmodule_path] = copied_swiftmodule_path;
118+
119+
auto swiftdoc_path = ReplaceExtension(path, ".swiftdoc", /*all_extensions=*/true);
120+
auto copied_swiftdoc_path = MakeIncrementalOutputPath(swiftdoc_path);
121+
incremental_inputs[swiftdoc_path] = copied_swiftdoc_path;
122+
114123
json_ = new_output_file_map;
115124
incremental_outputs_ = incremental_outputs;
125+
incremental_inputs_ = incremental_inputs;
116126
}

tools/worker/output_file_map.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,14 @@ class OutputFileMap {
3939
return incremental_outputs_;
4040
}
4141

42+
// A map containing expected output files that will be generated in the
43+
// non-incremental storage area, but need to be copied back at the start of
44+
// the next compile. The key is the original object path; the corresponding
45+
// value is its location in the incremental storage area.
46+
const std::map<std::string, std::string> incremental_inputs() const {
47+
return incremental_inputs_;
48+
}
49+
4250
// Reads the output file map from the JSON file at the given path, and updates
4351
// it to support incremental builds.
4452
void ReadFromPath(const std::string &path);
@@ -53,6 +61,7 @@ class OutputFileMap {
5361

5462
nlohmann::json json_;
5563
std::map<std::string, std::string> incremental_outputs_;
64+
std::map<std::string, std::string> incremental_inputs_;
5665
};
5766

5867
#endif // BUILD_BAZEL_RULES_SWIFT_TOOLS_WORKER_OUTPUT_FILE_MAP_H_

tools/worker/work_processor.cc

Lines changed: 58 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
#include "tools/worker/work_processor.h"
16-
1715
#include <google/protobuf/text_format.h>
1816
#include <sys/stat.h>
1917

@@ -28,6 +26,7 @@
2826
#include "tools/common/temp_file.h"
2927
#include "tools/worker/output_file_map.h"
3028
#include "tools/worker/swift_runner.h"
29+
#include "tools/worker/work_processor.h"
3130

3231
namespace {
3332

@@ -38,6 +37,15 @@ static bool ArgumentEnablesWMO(const std::string &arg) {
3837
arg == "-force-single-frontend-invocation";
3938
}
4039

40+
static void FinalizeWorkRequest(const blaze::worker::WorkRequest &request,
41+
blaze::worker::WorkResponse *response,
42+
int exit_code,
43+
const std::ostringstream &output) {
44+
response->set_exit_code(exit_code);
45+
response->set_output(output.str());
46+
response->set_request_id(request.request_id());
47+
}
48+
4149
}; // end namespace
4250

4351
WorkProcessor::WorkProcessor(const std::vector<std::string> &args) {
@@ -111,6 +119,8 @@ void WorkProcessor::ProcessWorkRequest(
111119
processed_args.push_back("@" + params_file->GetPath());
112120
params_file_stream.close();
113121

122+
std::ostringstream stderr_stream;
123+
114124
if (!is_wmo) {
115125
for (const auto &expected_object_pair :
116126
output_file_map.incremental_outputs()) {
@@ -119,15 +129,30 @@ void WorkProcessor::ProcessWorkRequest(
119129
// incremental storage area.
120130
auto dir_path = Dirname(expected_object_pair.second);
121131
if (!MakeDirs(dir_path, S_IRWXU)) {
122-
std::cerr << "Could not create directory " << dir_path << " (errno "
123-
<< errno << ")\n";
132+
stderr_stream << "swift_worker: Could not create directory " << dir_path
133+
<< " (errno " << errno << ")\n";
134+
FinalizeWorkRequest(request, response, EXIT_FAILURE, stderr_stream);
135+
}
136+
}
137+
138+
// Copy some input files from the incremental storage area to the locations
139+
// where Bazel will generate them.
140+
for (const auto &expected_object_pair :
141+
output_file_map.incremental_inputs()) {
142+
if (FileExists(expected_object_pair.second)) {
143+
if (!CopyFile(expected_object_pair.second,
144+
expected_object_pair.first)) {
145+
stderr_stream << "swift_worker: Could not copy "
146+
<< expected_object_pair.second << " to "
147+
<< expected_object_pair.first << " (errno " << errno
148+
<< ")\n";
149+
FinalizeWorkRequest(request, response, EXIT_FAILURE, stderr_stream);
150+
}
124151
}
125152
}
126153
}
127154

128-
std::ostringstream stderr_stream;
129155
SwiftRunner swift_runner(processed_args, /*force_response_file=*/true);
130-
131156
int exit_code = swift_runner.Run(&stderr_stream, /*stdout_to_stderr=*/true);
132157

133158
if (!is_wmo) {
@@ -136,14 +161,34 @@ void WorkProcessor::ProcessWorkRequest(
136161
for (const auto &expected_object_pair :
137162
output_file_map.incremental_outputs()) {
138163
if (!CopyFile(expected_object_pair.second, expected_object_pair.first)) {
139-
std::cerr << "Could not copy " << expected_object_pair.second << " to "
140-
<< expected_object_pair.first << " (errno " << errno << ")\n";
141-
exit_code = EXIT_FAILURE;
164+
stderr_stream << "swift_worker: Could not copy "
165+
<< expected_object_pair.second << " to "
166+
<< expected_object_pair.first << " (errno " << errno
167+
<< ")\n";
168+
FinalizeWorkRequest(request, response, EXIT_FAILURE, stderr_stream);
142169
}
143170
}
144-
}
145171

146-
response->set_exit_code(exit_code);
147-
response->set_output(stderr_stream.str());
148-
response->set_request_id(request.request_id());
172+
// Copy the replaced input files back to the incremental storage for the
173+
// next run.
174+
for (const auto &expected_object_pair :
175+
output_file_map.incremental_inputs()) {
176+
if (FileExists(expected_object_pair.first)) {
177+
if (FileExists(expected_object_pair.second)) {
178+
// CopyFile fails if the file already exists
179+
RemoveFile(expected_object_pair.second);
180+
}
181+
if (!CopyFile(expected_object_pair.first,
182+
expected_object_pair.second)) {
183+
stderr_stream << "swift_worker: Could not copy "
184+
<< expected_object_pair.first << " to "
185+
<< expected_object_pair.second << " (errno " << errno
186+
<< ")\n";
187+
FinalizeWorkRequest(request, response, EXIT_FAILURE, stderr_stream);
188+
}
189+
}
190+
}
191+
192+
FinalizeWorkRequest(request, response, exit_code, stderr_stream);
193+
}
149194
}

0 commit comments

Comments
 (0)