1 //===-- BuildSystemFrontend.cpp -------------------------------------------===//
2 //
3 // This source file is part of the Swift.org open source project
4 //
5 // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
6 // Licensed under Apache License v2.0 with Runtime Library Exception
7 //
8 // See http://swift.org/LICENSE.txt for license information
9 // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "llbuild/BuildSystem/BuildSystemFrontend.h"
14 
15 #include "llbuild/Basic/FileSystem.h"
16 #include "llbuild/Basic/LLVM.h"
17 #include "llbuild/Basic/PlatformUtility.h"
18 #include "llbuild/BuildSystem/BuildDescription.h"
19 #include "llbuild/BuildSystem/BuildExecutionQueue.h"
20 #include "llbuild/BuildSystem/BuildFile.h"
21 #include "llbuild/BuildSystem/BuildKey.h"
22 
23 #include "llvm/ADT/DenseMap.h"
24 #include "llvm/ADT/SmallString.h"
25 #include "llvm/Support/Format.h"
26 #include "llvm/Support/Path.h"
27 #include "llvm/Support/SourceMgr.h"
28 #include "llvm/Support/raw_ostream.h"
29 
30 #include <atomic>
31 #include <memory>
32 #include <mutex>
33 #include <thread>
34 
35 using namespace llbuild;
36 using namespace llbuild::basic;
37 using namespace llbuild::buildsystem;
38 
39 #pragma mark - BuildSystemInvocation implementation
40 
getUsage(int optionWidth,raw_ostream & os)41 void BuildSystemInvocation::getUsage(int optionWidth, raw_ostream& os) {
42   const struct Options {
43     llvm::StringRef option, helpText;
44   } options[] = {
45     { "--help", "show this help message and exit" },
46     { "--version", "show the tool version" },
47     { "-C <PATH>, --chdir <PATH>", "change directory to PATH before building" },
48     { "--no-db", "disable use of a build database" },
49     { "--db <PATH>", "enable building against the database at PATH" },
50     { "-f <PATH>", "load the build task file at PATH" },
51     { "--serial", "do not build in parallel" },
52     { "-v, --verbose", "show verbose status information" },
53     { "--trace <PATH>", "trace build engine operation to PATH" },
54   };
55 
56   for (const auto& entry: options) {
57     os << "  " << llvm::format("%-*s", optionWidth, entry.option) << " "
58        << entry.helpText << "\n";
59   }
60 }
61 
parse(llvm::ArrayRef<std::string> args,llvm::SourceMgr & sourceMgr)62 void BuildSystemInvocation::parse(llvm::ArrayRef<std::string> args,
63                                   llvm::SourceMgr& sourceMgr) {
64   auto error = [&](const Twine &message) {
65     sourceMgr.PrintMessage(llvm::SMLoc{}, llvm::SourceMgr::DK_Error, message);
66     hadErrors = true;
67   };
68 
69   while (!args.empty()) {
70     const auto& option = args.front();
71     args = args.slice(1);
72 
73     if (option == "-") {
74       for (const auto& arg: args) {
75         positionalArgs.push_back(arg);
76       }
77       break;
78     }
79 
80     if (!option.empty() && option[0] != '-') {
81       positionalArgs.push_back(option);
82       continue;
83     }
84 
85     if (option == "--help") {
86       showUsage = true;
87       break;
88     } else if (option == "--version") {
89       showVersion = true;
90       break;
91     } else if (option == "--no-db") {
92       dbPath = "";
93     } else if (option == "--db") {
94       if (args.empty()) {
95         error("missing argument to '" + option + "'");
96         break;
97       }
98       dbPath = args[0];
99       args = args.slice(1);
100     } else if (option == "-C" || option == "--chdir") {
101       if (args.empty()) {
102         error("missing argument to '" + option + "'");
103         break;
104       }
105       chdirPath = args[0];
106       args = args.slice(1);
107     } else if (option == "-f") {
108       if (args.empty()) {
109         error("missing argument to '" + option + "'");
110         break;
111       }
112       buildFilePath = args[0];
113       args = args.slice(1);
114     } else if (option == "--serial") {
115       useSerialBuild = true;
116     } else if (option == "-v" || option == "--verbose") {
117       showVerboseStatus = true;
118     } else if (option == "--trace") {
119       if (args.empty()) {
120         error("missing argument to '" + option + "'");
121         break;
122       }
123       traceFilePath = args[0];
124       args = args.slice(1);
125     } else {
126       error("invalid option '" + option + "'");
127       break;
128     }
129   }
130 }
131 
formatDetectedCycle(const std::vector<core::Rule * > & cycle)132 std::string BuildSystemInvocation::formatDetectedCycle(const std::vector<core::Rule*>& cycle) {
133   // Compute a description of the cycle path.
134   SmallString<256> message;
135   llvm::raw_svector_ostream os(message);
136   os << "cycle detected while building: ";
137   bool first = true;
138   for (const auto* rule: cycle) {
139     if (!first)
140       os << " -> ";
141 
142     // Convert to a build key.
143     auto key = BuildKey::fromData(rule->key);
144     switch (key.getKind()) {
145       case BuildKey::Kind::Unknown:
146         os << "((unknown))";
147         break;
148       case BuildKey::Kind::Command:
149         os << "command '" << key.getCommandName() << "'";
150         break;
151       case BuildKey::Kind::CustomTask:
152         os << "custom task '" << key.getCustomTaskName() << "'";
153         break;
154       case BuildKey::Kind::DirectoryContents:
155         os << "directory-contents '" << key.getDirectoryContentsPath() << "'";
156         break;
157       case BuildKey::Kind::DirectoryTreeSignature:
158         os << "directory-tree-signature '"
159         << key.getDirectoryTreeSignaturePath() << "'";
160         break;
161       case BuildKey::Kind::Node:
162         os << "node '" << key.getNodeName() << "'";
163         break;
164       case BuildKey::Kind::Target:
165         os << "target '" << key.getTargetName() << "'";
166         break;
167     }
168     first = false;
169   }
170 
171   return os.str().str();
172 }
173 
174 #pragma mark - BuildSystemFrontendDelegate implementation
175 
176 namespace {
177 
178 struct BuildSystemFrontendDelegateImpl;
179 
180 class BuildSystemFrontendExecutionQueueDelegate
181       : public BuildExecutionQueueDelegate {
182   BuildSystemFrontendDelegateImpl &delegateImpl;
183 
184   bool showVerboseOutput() const;
185 
186   BuildSystem& getSystem() const;
187 
188 public:
BuildSystemFrontendExecutionQueueDelegate(BuildSystemFrontendDelegateImpl & delegateImpl)189   BuildSystemFrontendExecutionQueueDelegate(
190           BuildSystemFrontendDelegateImpl& delegateImpl)
191       : delegateImpl(delegateImpl) { }
192 
commandJobStarted(Command * command)193   virtual void commandJobStarted(Command* command) override {
194     static_cast<BuildSystemFrontendDelegate*>(&getSystem().getDelegate())->
195       commandJobStarted(command);
196   }
197 
commandJobFinished(Command * command)198   virtual void commandJobFinished(Command* command) override {
199     static_cast<BuildSystemFrontendDelegate*>(&getSystem().getDelegate())->
200       commandJobFinished(command);
201   }
202 
commandProcessStarted(Command * command,ProcessHandle handle)203   virtual void commandProcessStarted(Command* command,
204                                      ProcessHandle handle) override {
205     static_cast<BuildSystemFrontendDelegate*>(&getSystem().getDelegate())->
206       commandProcessStarted(
207           command, BuildSystemFrontendDelegate::ProcessHandle { handle.id });
208   }
209 
commandProcessHadError(Command * command,ProcessHandle handle,const Twine & message)210   virtual void commandProcessHadError(Command* command, ProcessHandle handle,
211                                       const Twine& message) override {
212     static_cast<BuildSystemFrontendDelegate*>(&getSystem().getDelegate())->
213       commandProcessHadError(
214           command, BuildSystemFrontendDelegate::ProcessHandle { handle.id },
215           message);
216   }
217 
commandProcessHadOutput(Command * command,ProcessHandle handle,StringRef data)218   virtual void commandProcessHadOutput(Command* command, ProcessHandle handle,
219                                        StringRef data) override {
220     static_cast<BuildSystemFrontendDelegate*>(&getSystem().getDelegate())->
221       commandProcessHadOutput(
222           command, BuildSystemFrontendDelegate::ProcessHandle { handle.id },
223           data);
224   }
225 
commandProcessFinished(Command * command,ProcessHandle handle,CommandResult result,int exitStatus)226   virtual void commandProcessFinished(Command* command, ProcessHandle handle,
227                                       CommandResult result,
228                                       int exitStatus) override {
229     static_cast<BuildSystemFrontendDelegate*>(&getSystem().getDelegate())->
230       commandProcessFinished(
231           command, BuildSystemFrontendDelegate::ProcessHandle { handle.id },
232           result, exitStatus);
233   }
234 };
235 
236 struct BuildSystemFrontendDelegateImpl {
237 
238   /// The status of delegate.
239   enum class Status {
240     Uninitialized = 0,
241     Initialized,
242     InitializationFailure,
243     Cancelled,
244   };
245 
246   llvm::SourceMgr& sourceMgr;
247   const BuildSystemInvocation& invocation;
248 
249   StringRef bufferBeingParsed;
250   std::atomic<unsigned> numErrors{0};
251   std::atomic<unsigned> numFailedCommands{0};
252 
253   BuildSystemFrontendExecutionQueueDelegate executionQueueDelegate;
254 
255   BuildSystemFrontend* frontend = nullptr;
256   BuildSystem* system = nullptr;
257 
258   /// The set of active command output buffers, by process handle.
259   llvm::DenseMap<uintptr_t, std::vector<uint8_t>> processOutputBuffers;
260 
261   /// The lock protecting `processOutputBuffers`.
262   std::mutex processOutputBuffersMutex;
263 
BuildSystemFrontendDelegateImpl__anon25a8402b0211::BuildSystemFrontendDelegateImpl264   BuildSystemFrontendDelegateImpl(llvm::SourceMgr& sourceMgr,
265                                   const BuildSystemInvocation& invocation)
266       : sourceMgr(sourceMgr), invocation(invocation),
267         executionQueueDelegate(*this) {}
268 
269 private:
270   /// The status of the delegate.
271   std::atomic<Status> status{Status::Uninitialized};
272 
273 public:
274 
275   /// Set the status of delegate to the given value.
276   ///
277   /// It is not possible to update the status once status is set to initialization failure.
setStatus__anon25a8402b0211::BuildSystemFrontendDelegateImpl278   void setStatus(Status newStatus) {
279     // Disallow changing status if there was an initialization failure.
280     if (status == Status::InitializationFailure) {
281       return;
282     }
283     status = newStatus;
284   }
285 
286   /// Returns the current status.
getStatus__anon25a8402b0211::BuildSystemFrontendDelegateImpl287   Status getStatus() {
288     return status;
289   }
290 };
291 
showVerboseOutput() const292 bool BuildSystemFrontendExecutionQueueDelegate::showVerboseOutput() const {
293   return delegateImpl.invocation.showVerboseStatus;
294 }
295 
getSystem() const296 BuildSystem& BuildSystemFrontendExecutionQueueDelegate::getSystem() const {
297   assert(delegateImpl.system);
298   return *delegateImpl.system;
299 }
300 
301 }
302 
303 BuildSystemFrontendDelegate::
BuildSystemFrontendDelegate(llvm::SourceMgr & sourceMgr,const BuildSystemInvocation & invocation,StringRef name,uint32_t version)304 BuildSystemFrontendDelegate(llvm::SourceMgr& sourceMgr,
305                             const BuildSystemInvocation& invocation,
306                             StringRef name,
307                             uint32_t version)
308     : BuildSystemDelegate(name, version),
309       impl(new BuildSystemFrontendDelegateImpl(sourceMgr, invocation))
310 {
311 
312 }
313 
~BuildSystemFrontendDelegate()314 BuildSystemFrontendDelegate::~BuildSystemFrontendDelegate() {
315   delete static_cast<BuildSystemFrontendDelegateImpl*>(this->impl);
316 }
317 
318 void
setFileContentsBeingParsed(StringRef buffer)319 BuildSystemFrontendDelegate::setFileContentsBeingParsed(StringRef buffer) {
320   auto impl = static_cast<BuildSystemFrontendDelegateImpl*>(this->impl);
321 
322   impl->bufferBeingParsed = buffer;
323 }
324 
getFrontend()325 BuildSystemFrontend& BuildSystemFrontendDelegate::getFrontend() {
326   auto impl = static_cast<BuildSystemFrontendDelegateImpl*>(this->impl);
327 
328   return *impl->frontend;
329 }
330 
getSourceMgr()331 llvm::SourceMgr& BuildSystemFrontendDelegate::getSourceMgr() {
332   auto impl = static_cast<BuildSystemFrontendDelegateImpl*>(this->impl);
333 
334   return impl->sourceMgr;
335 }
336 
getNumErrors()337 unsigned BuildSystemFrontendDelegate::getNumErrors() {
338   auto impl = static_cast<BuildSystemFrontendDelegateImpl*>(this->impl);
339 
340   return impl->numErrors;
341 }
342 
getNumFailedCommands()343 unsigned BuildSystemFrontendDelegate::getNumFailedCommands() {
344   auto impl = static_cast<BuildSystemFrontendDelegateImpl*>(this->impl);
345 
346   return impl->numFailedCommands;
347 }
348 
349 void
error(const Twine & message)350 BuildSystemFrontendDelegate::error(const Twine& message) {
351   error("", {}, message.str());
352 }
353 
354 void
error(StringRef filename,const Token & at,const Twine & message)355 BuildSystemFrontendDelegate::error(StringRef filename,
356                                    const Token& at,
357                                    const Twine& message) {
358   auto impl = static_cast<BuildSystemFrontendDelegateImpl*>(this->impl);
359 
360   ++impl->numErrors;
361 
362   // If we have a file and token, resolve the location and range to one
363   // accessible by the source manager.
364   //
365   // FIXME: We shouldn't need to do this, but should switch llbuild to using
366   // SourceMgr natively.
367   llvm::SMLoc loc{};
368   llvm::SMRange range{};
369   if (!filename.empty() && at.start) {
370     // FIXME: We ignore errors here, for now, this will be resolved when we move
371     // to SourceMgr completely.
372     auto buffer = getFileSystem().getFileContents(filename);
373     if (buffer) {
374       unsigned offset = at.start - impl->bufferBeingParsed.data();
375       if (offset + at.length < buffer->getBufferSize()) {
376         range.Start = loc = llvm::SMLoc::getFromPointer(
377             buffer->getBufferStart() + offset);
378         range.End = llvm::SMLoc::getFromPointer(
379             buffer->getBufferStart() + (offset + at.length));
380         getSourceMgr().AddNewSourceBuffer(std::move(buffer), llvm::SMLoc{});
381       }
382     }
383   }
384 
385   if (range.Start.isValid()) {
386     getSourceMgr().PrintMessage(loc, llvm::SourceMgr::DK_Error, message, range);
387   } else {
388     getSourceMgr().PrintMessage(loc, llvm::SourceMgr::DK_Error, message);
389   }
390   fflush(stderr);
391 }
392 
393 std::unique_ptr<BuildExecutionQueue>
createExecutionQueue()394 BuildSystemFrontendDelegate::createExecutionQueue() {
395   auto impl = static_cast<BuildSystemFrontendDelegateImpl*>(this->impl);
396 
397   if (impl->invocation.useSerialBuild) {
398     return std::unique_ptr<BuildExecutionQueue>(
399         createLaneBasedExecutionQueue(impl->executionQueueDelegate, 1,
400                                       impl->invocation.environment));
401   }
402 
403   // Get the number of CPUs to use.
404   unsigned numCPUs = std::thread::hardware_concurrency();
405   unsigned numLanes;
406   if (numCPUs == 0) {
407     error("<unknown>", {}, "unable to detect number of CPUs");
408     numLanes = 1;
409   } else {
410     numLanes = numCPUs;
411   }
412 
413   return std::unique_ptr<BuildExecutionQueue>(
414       createLaneBasedExecutionQueue(impl->executionQueueDelegate, numLanes,
415                                     impl->invocation.environment));
416 }
417 
cancel()418 void BuildSystemFrontendDelegate::cancel() {
419   auto delegateImpl = static_cast<BuildSystemFrontendDelegateImpl*>(impl);
420   assert(delegateImpl->getStatus() != BuildSystemFrontendDelegateImpl::Status::Uninitialized);
421 
422   // Update the status to cancelled.
423   delegateImpl->setStatus(BuildSystemFrontendDelegateImpl::Status::Cancelled);
424 
425   auto system = delegateImpl->system;
426   if (system) {
427     system->cancel();
428   }
429 }
430 
resetForBuild()431 void BuildSystemFrontendDelegate::resetForBuild() {
432   auto impl = static_cast<BuildSystemFrontendDelegateImpl*>(this->impl);
433 
434   impl->numFailedCommands = 0;
435   impl->numErrors = 0;
436 
437   // Update status back to initialized on reset.
438   if (impl->getStatus() == BuildSystemFrontendDelegateImpl::Status::Cancelled) {
439       impl->setStatus(BuildSystemFrontendDelegateImpl::Status::Initialized);
440   }
441 
442   // Reset the build system.
443   auto system = impl->system;
444   if (system) {
445     system->resetForBuild();
446   }
447 }
448 
hadCommandFailure()449 void BuildSystemFrontendDelegate::hadCommandFailure() {
450   auto impl = static_cast<BuildSystemFrontendDelegateImpl*>(this->impl);
451 
452   // Increment the failed command count.
453   ++impl->numFailedCommands;
454 }
455 
commandStatusChanged(Command *,CommandStatusKind)456 void BuildSystemFrontendDelegate::commandStatusChanged(Command*,
457                                                        CommandStatusKind) {
458 }
459 
commandPreparing(Command *)460 void BuildSystemFrontendDelegate::commandPreparing(Command*) {
461 }
462 
shouldCommandStart(Command *)463 bool BuildSystemFrontendDelegate::shouldCommandStart(Command*) {
464   return true;
465 }
466 
commandStarted(Command * command)467 void BuildSystemFrontendDelegate::commandStarted(Command* command) {
468   // Don't report status if opted out by the command.
469   if (!command->shouldShowStatus()) {
470     return;
471   }
472 
473   // Log the command.
474   //
475   // FIXME: Design the logging and status output APIs.
476   SmallString<64> description;
477   if (getFrontend().getInvocation().showVerboseStatus) {
478     command->getVerboseDescription(description);
479   } else {
480     command->getShortDescription(description);
481 
482     // If the short description is empty, always show the verbose one.
483     if (description.empty()) {
484       command->getVerboseDescription(description);
485     }
486   }
487   fprintf(stdout, "%s\n", description.c_str());
488   fflush(stdout);
489 }
490 
commandHadError(Command * command,StringRef data)491 void BuildSystemFrontendDelegate::commandHadError(Command* command, StringRef data) {
492   fwrite(data.data(), data.size(), 1, stderr);
493   fflush(stderr);
494 }
495 
commandHadNote(Command * command,StringRef data)496 void BuildSystemFrontendDelegate::commandHadNote(Command* command, StringRef data) {
497   fwrite(data.data(), data.size(), 1, stdout);
498   fflush(stdout);
499 }
500 
commandHadWarning(Command * command,StringRef data)501 void BuildSystemFrontendDelegate::commandHadWarning(Command* command, StringRef data) {
502   fwrite(data.data(), data.size(), 1, stdout);
503   fflush(stdout);
504 }
505 
commandFinished(Command *,CommandResult)506 void BuildSystemFrontendDelegate::commandFinished(Command*, CommandResult) {
507 }
508 
commandJobStarted(Command *)509 void BuildSystemFrontendDelegate::commandJobStarted(Command*) {
510 }
511 
commandJobFinished(Command *)512 void BuildSystemFrontendDelegate::commandJobFinished(Command*) {
513 }
514 
commandProcessStarted(Command *,ProcessHandle)515 void BuildSystemFrontendDelegate::commandProcessStarted(Command*,
516                                                         ProcessHandle) {
517 }
518 
519 void BuildSystemFrontendDelegate::
commandProcessHadError(Command * command,ProcessHandle handle,const Twine & message)520 commandProcessHadError(Command* command, ProcessHandle handle,
521                        const Twine& message) {
522   SmallString<256> buffer;
523   auto str = message.toStringRef(buffer);
524 
525   // FIXME: Design the logging and status output APIs.
526   fwrite(str.data(), str.size(), 1, stderr);
527   fputc('\n', stderr);
528   fflush(stderr);
529 }
530 
531 void BuildSystemFrontendDelegate::
commandProcessHadOutput(Command * command,ProcessHandle handle,StringRef data)532 commandProcessHadOutput(Command* command, ProcessHandle handle,
533                         StringRef data) {
534   auto impl = static_cast<BuildSystemFrontendDelegateImpl*>(this->impl);
535   std::unique_lock<std::mutex> lock(impl->processOutputBuffersMutex);
536 
537   // Append to the output buffer.
538   auto& buffer = impl->processOutputBuffers[handle.id];
539   buffer.insert(buffer.end(), data.begin(), data.end());
540 }
541 
542 void BuildSystemFrontendDelegate::
commandProcessFinished(Command *,ProcessHandle handle,CommandResult result,int exitStatus)543 commandProcessFinished(Command*, ProcessHandle handle,
544                        CommandResult result,
545                        int exitStatus) {
546   auto impl = static_cast<BuildSystemFrontendDelegateImpl*>(this->impl);
547   std::unique_lock<std::mutex> lock(impl->processOutputBuffersMutex);
548 
549   // If there was an output buffer, flush it.
550   auto it = impl->processOutputBuffers.find(handle.id);
551   if (it == impl->processOutputBuffers.end())
552     return;
553 
554   fwrite(it->second.data(), it->second.size(), 1, stdout);
555   fflush(stdout);
556 
557   impl->processOutputBuffers.erase(it);
558 }
559 
560 #pragma mark - BuildSystemFrontend implementation
561 
562 BuildSystemFrontend::
BuildSystemFrontend(BuildSystemFrontendDelegate & delegate,const BuildSystemInvocation & invocation)563 BuildSystemFrontend(BuildSystemFrontendDelegate& delegate,
564                     const BuildSystemInvocation& invocation)
565     : delegate(delegate), invocation(invocation)
566 {
567   auto delegateImpl =
568     static_cast<BuildSystemFrontendDelegateImpl*>(delegate.impl);
569 
570   delegateImpl->frontend = this;
571 }
572 
initialize()573 bool BuildSystemFrontend::initialize() {
574   if (!invocation.chdirPath.empty()) {
575     if (!sys::chdir(invocation.chdirPath.c_str())) {
576       getDelegate().error(Twine("unable to honor --chdir: ") + strerror(errno));
577       return false;
578     }
579   }
580 
581   // Create the build system.
582   buildSystem.emplace(delegate);
583 
584   // Load the build file.
585   if (!buildSystem->loadDescription(invocation.buildFilePath))
586     return false;
587 
588   // Register the system back pointer.
589   //
590   // FIXME: Eliminate this.
591   auto delegateImpl =
592     static_cast<BuildSystemFrontendDelegateImpl*>(delegate.impl);
593   delegateImpl->system = buildSystem.getPointer();
594 
595   // Enable tracing, if requested.
596   if (!invocation.traceFilePath.empty()) {
597     std::string error;
598     if (!buildSystem->enableTracing(invocation.traceFilePath, &error)) {
599       getDelegate().error(Twine("unable to enable tracing: ") + error);
600       return false;
601     }
602   }
603 
604   // Attach the database.
605   if (!invocation.dbPath.empty()) {
606     // If the database path is relative, always make it relative to the input
607     // file.
608     SmallString<256> tmp;
609     StringRef dbPath = invocation.dbPath;
610     if (llvm::sys::path::is_relative(invocation.dbPath) &&
611         dbPath.find("://") == StringRef::npos && !dbPath.startswith(":")) {
612       llvm::sys::path::append(
613           tmp, llvm::sys::path::parent_path(invocation.buildFilePath),
614           invocation.dbPath);
615       dbPath = tmp.str();
616     }
617 
618     std::string error;
619     if (!buildSystem->attachDB(dbPath, &error)) {
620       getDelegate().error(Twine("unable to attach DB: ") + error);
621       return false;
622     }
623   }
624 
625   return true;
626 }
627 
build(StringRef targetToBuild)628 bool BuildSystemFrontend::build(StringRef targetToBuild) {
629 
630   auto delegateImpl =
631     static_cast<BuildSystemFrontendDelegateImpl*>(delegate.impl);
632 
633   // We expect build to be called in these states only.
634   assert(delegateImpl->getStatus() == BuildSystemFrontendDelegateImpl::Status::Uninitialized
635     || delegateImpl->getStatus() == BuildSystemFrontendDelegateImpl::Status::Initialized);
636 
637   // Set the delegate status to initialized.
638   delegateImpl->setStatus(BuildSystemFrontendDelegateImpl::Status::Initialized);
639 
640   // Initialize the build system, if necessary
641   if (!buildSystem.hasValue()) {
642     if (!initialize()) {
643       // Set status to initialization failure. It is not possible to recover from this state.
644       delegateImpl->setStatus(BuildSystemFrontendDelegateImpl::Status::InitializationFailure);
645       return false;
646     }
647   }
648 
649   // If delegate was told to cancel while we were initializing, abort now.
650   if (delegateImpl->getStatus() == BuildSystemFrontendDelegateImpl::Status::Cancelled) {
651       return false;
652   }
653 
654   // Build the target; if something unspecified failed about the build, return
655   // an error.
656   if (!buildSystem->build(targetToBuild))
657     return false;
658 
659   // The build was successful if there were no failed commands or unspecified
660   // errors.
661   //
662   // It is the job of the client to report a summary, if desired.
663   return delegate.getNumFailedCommands() == 0 && delegate.getNumErrors() == 0;
664 }
665