1 //===-- BuildSystemCommand.cpp --------------------------------------------===//
2 //
3 // This source file is part of the Swift.org open source project
4 //
5 // Copyright (c) 2014 - 2015 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/Commands/Commands.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/BuildFile.h"
20 #include "llbuild/BuildSystem/BuildSystem.h"
21 #include "llbuild/BuildSystem/BuildSystemFrontend.h"
22 #include "llbuild/BuildSystem/BuildValue.h"
23 
24 #include "llvm/ADT/SmallString.h"
25 #include "llvm/ADT/STLExtras.h"
26 #include "llvm/ADT/StringMap.h"
27 #include "llvm/Support/SourceMgr.h"
28 #include "llvm/Support/raw_ostream.h"
29 
30 #include "CommandUtil.h"
31 
32 #include <cerrno>
33 #include <thread>
34 
35 #include <signal.h>
36 #include <unistd.h>
37 
38 using namespace llbuild;
39 using namespace llbuild::commands;
40 using namespace llbuild::core;
41 using namespace llbuild::buildsystem;
42 
43 namespace {
44 
45 /*  Parse Command */
46 
47 class ParseBuildFileDelegate : public BuildFileDelegate {
48   bool showOutput;
49   StringRef bufferBeingParsed;
50   std::unique_ptr<basic::FileSystem> fileSystem;
51   llvm::StringMap<bool> internedStrings;
52 
53 public:
ParseBuildFileDelegate(bool showOutput)54   ParseBuildFileDelegate(bool showOutput)
55       : showOutput(showOutput),
56         fileSystem(basic::createLocalFileSystem()) {}
~ParseBuildFileDelegate()57   ~ParseBuildFileDelegate() {}
58 
shouldShowOutput()59   virtual bool shouldShowOutput() { return showOutput; }
60 
getInternedString(StringRef value)61   virtual StringRef getInternedString(StringRef value) override {
62     auto entry = internedStrings.insert(std::make_pair(value, true));
63     return entry.first->getKey();
64   }
65 
getFileSystem()66   virtual basic::FileSystem& getFileSystem() override { return *fileSystem; }
67 
68   virtual void setFileContentsBeingParsed(StringRef buffer) override;
69 
70   virtual void error(StringRef filename,
71                      const BuildFileToken& at,
72                      const Twine& message) override;
73 
74   virtual bool configureClient(const ConfigureContext&, StringRef name,
75                                uint32_t version,
76                                const property_list_type& properties) override;
77 
78   virtual std::unique_ptr<Tool> lookupTool(StringRef name) override;
79 
80   virtual void loadedTarget(StringRef name,
81                             const Target& target) override;
82 
83   virtual void loadedDefaultTarget(StringRef target) override;
84 
85   virtual std::unique_ptr<Node> lookupNode(StringRef name,
86                                            bool isImplicit) override;
87 
88   virtual void loadedCommand(StringRef name,
89                              const Command& command) override;
90 };
91 
92 class ParseDummyNode : public Node {
93   ParseBuildFileDelegate& delegate;
94 
95 public:
ParseDummyNode(ParseBuildFileDelegate & delegate,StringRef name)96   ParseDummyNode(ParseBuildFileDelegate& delegate, StringRef name)
97       : Node(name), delegate(delegate) {}
98 
configureAttribute(const ConfigureContext &,StringRef name,StringRef value)99   virtual bool configureAttribute(const ConfigureContext&, StringRef name,
100                                   StringRef value) override {
101     if (delegate.shouldShowOutput()) {
102       printf("  -- '%s': '%s'\n", name.str().c_str(), value.str().c_str());
103     }
104     return true;
105   }
configureAttribute(const ConfigureContext &,StringRef name,ArrayRef<StringRef> values)106   virtual bool configureAttribute(const ConfigureContext&, StringRef name,
107                                   ArrayRef<StringRef> values) override {
108     if (delegate.shouldShowOutput()) {
109       printf("  -- '%s': [", name.str().c_str());
110       bool first = true;
111       for (const auto& value: values) {
112         printf("%s'%s'", first ? "" : ", ", value.str().c_str());
113         first = false;
114       }
115       printf("]\n");
116     }
117     return true;
118   }
configureAttribute(const ConfigureContext &,StringRef name,ArrayRef<std::pair<StringRef,StringRef>> values)119   virtual bool configureAttribute(
120       const ConfigureContext&, StringRef name,
121       ArrayRef<std::pair<StringRef, StringRef>> values) override {
122     if (delegate.shouldShowOutput()) {
123       printf("  -- '%s': {\n", name.str().c_str());
124       for (const auto& value: values) {
125         printf("  --   '%s': '%s'\n", value.first.str().c_str(),
126                value.second.str().c_str());
127       }
128       printf("  -- }\n");
129     }
130     return true;
131   }
132 };
133 
134 class ParseDummyCommand : public Command {
135   ParseBuildFileDelegate& delegate;
136 
137 public:
ParseDummyCommand(ParseBuildFileDelegate & delegate,StringRef name)138   ParseDummyCommand(ParseBuildFileDelegate& delegate, StringRef name)
139       : Command(name), delegate(delegate) {}
140 
getShortDescription(SmallVectorImpl<char> & result)141   virtual void getShortDescription(SmallVectorImpl<char> &result) override {
142     llvm::raw_svector_ostream(result) << "<dummy command>";
143   }
144 
getVerboseDescription(SmallVectorImpl<char> & result)145   virtual void getVerboseDescription(SmallVectorImpl<char> &result) override {
146     llvm::raw_svector_ostream(result) << "<dummy command>";
147   }
148 
configureDescription(const ConfigureContext &,StringRef description)149   virtual void configureDescription(const ConfigureContext&,
150                                     StringRef description) override {
151     if (delegate.shouldShowOutput()) {
152       printf("  -- 'description': '%s'", description.str().c_str());
153     }
154   }
155 
configureInputs(const ConfigureContext &,const std::vector<Node * > & inputs)156   virtual void configureInputs(const ConfigureContext&,
157                                const std::vector<Node*>& inputs) override {
158     if (delegate.shouldShowOutput()) {
159       bool first = true;
160       printf("  -- 'inputs': [");
161       for (const auto& node: inputs) {
162         printf("%s'%s'", first ? "" : ", ", node->getName().str().c_str());
163         first = false;
164       }
165       printf("]\n");
166     }
167   }
168 
configureOutputs(const ConfigureContext &,const std::vector<Node * > & outputs)169   virtual void configureOutputs(const ConfigureContext&,
170                                 const std::vector<Node*>& outputs) override {
171     if (delegate.shouldShowOutput()) {
172       bool first = true;
173       printf("  -- 'outputs': [");
174       for (const auto& node: outputs) {
175         printf("%s'%s'", first ? "" : ", ", node->getName().str().c_str());
176         first = false;
177       }
178       printf("]\n");
179     }
180   }
181 
configureAttribute(const ConfigureContext &,StringRef name,StringRef value)182   virtual bool configureAttribute(const ConfigureContext&, StringRef name,
183                                   StringRef value) override {
184     if (delegate.shouldShowOutput()) {
185       printf("  -- '%s': '%s'\n", name.str().c_str(), value.str().c_str());
186     }
187     return true;
188   }
configureAttribute(const ConfigureContext &,StringRef name,ArrayRef<StringRef> values)189   virtual bool configureAttribute(const ConfigureContext&, StringRef name,
190                                   ArrayRef<StringRef> values) override {
191     if (delegate.shouldShowOutput()) {
192       printf("  -- '%s': [", name.str().c_str());
193       bool first = true;
194       for (const auto& value: values) {
195         printf("%s'%s'", first ? "" : ", ", value.str().c_str());
196         first = false;
197       }
198       printf("]\n");
199     }
200     return true;
201   }
configureAttribute(const ConfigureContext &,StringRef name,ArrayRef<std::pair<StringRef,StringRef>> values)202   virtual bool configureAttribute(
203       const ConfigureContext&, StringRef name,
204       ArrayRef<std::pair<StringRef, StringRef>> values) override {
205     if (delegate.shouldShowOutput()) {
206       printf("  -- '%s': {\n", name.str().c_str());
207       for (const auto& value: values) {
208         printf("  --   '%s': '%s'\n", value.first.str().c_str(),
209                value.second.str().c_str());
210       }
211       printf("  -- }\n");
212     }
213     return true;
214   }
215 
getResultForOutput(Node * node,const BuildValue & value)216   virtual BuildValue getResultForOutput(Node* node,
217                                         const BuildValue& value) override {
218     return BuildValue::makeMissingInput();
219   }
isResultValid(BuildSystem &,const BuildValue &)220   virtual bool isResultValid(BuildSystem&, const BuildValue&) override {
221     return false;
222   }
start(BuildSystemCommandInterface &,Task *)223   virtual void start(BuildSystemCommandInterface&, Task*) override {}
providePriorValue(BuildSystemCommandInterface &,Task *,const BuildValue &)224   virtual void providePriorValue(BuildSystemCommandInterface&, Task*,
225                                  const BuildValue&) override {}
provideValue(BuildSystemCommandInterface &,Task *,uintptr_t inputID,const BuildValue &)226   virtual void provideValue(BuildSystemCommandInterface&, Task*,
227                                  uintptr_t inputID,
228                                  const BuildValue&) override {}
execute(BuildSystemCommandInterface &,Task *,QueueJobContext *)229   virtual BuildValue execute(BuildSystemCommandInterface&, Task*,
230                              QueueJobContext*) override {
231     return BuildValue::makeFailedCommand();
232   }
233 };
234 
235 class ParseDummyTool : public Tool {
236   ParseBuildFileDelegate& delegate;
237 
238 public:
ParseDummyTool(ParseBuildFileDelegate & delegate,StringRef name)239   ParseDummyTool(ParseBuildFileDelegate& delegate, StringRef name)
240       : Tool(name), delegate(delegate) {}
241 
configureAttribute(const ConfigureContext &,StringRef name,StringRef value)242   virtual bool configureAttribute(const ConfigureContext&, StringRef name,
243                                   StringRef value) override {
244     if (delegate.shouldShowOutput()) {
245       printf("  -- '%s': '%s'\n", name.str().c_str(), value.str().c_str());
246     }
247     return true;
248   }
configureAttribute(const ConfigureContext &,StringRef name,ArrayRef<StringRef> values)249   virtual bool configureAttribute(const ConfigureContext&, StringRef name,
250                                   ArrayRef<StringRef> values) override {
251     if (delegate.shouldShowOutput()) {
252       printf("  -- '%s': [", name.str().c_str());
253       bool first = true;
254       for (const auto& value: values) {
255         printf("%s'%s'", first ? "" : ", ", value.str().c_str());
256         first = false;
257       }
258       printf("]\n");
259     }
260     return true;
261   }
configureAttribute(const ConfigureContext &,StringRef name,ArrayRef<std::pair<StringRef,StringRef>> values)262   virtual bool configureAttribute(
263       const ConfigureContext&, StringRef name,
264       ArrayRef<std::pair<StringRef, StringRef>> values) override {
265     if (delegate.shouldShowOutput()) {
266       printf("  -- '%s': {\n", name.str().c_str());
267       for (const auto& value: values) {
268         printf("  --   '%s': '%s'", value.first.str().c_str(),
269                value.second.str().c_str());
270       }
271       printf("  -- }\n");
272     }
273     return true;
274   }
275 
createCommand(StringRef name)276   virtual std::unique_ptr<Command> createCommand(
277       StringRef name) override {
278     if (delegate.shouldShowOutput()) {
279       printf("command('%s')\n", name.str().c_str());
280       printf("  -- 'tool': '%s')\n", getName().str().c_str());
281     }
282 
283     return llvm::make_unique<ParseDummyCommand>(delegate, name);
284   }
285 };
286 
setFileContentsBeingParsed(StringRef buffer)287 void ParseBuildFileDelegate::setFileContentsBeingParsed(StringRef buffer) {
288   bufferBeingParsed = buffer;
289 }
290 
error(StringRef filename,const BuildFileToken & at,const Twine & message)291 void ParseBuildFileDelegate::error(StringRef filename,
292                                    const BuildFileToken& at,
293                                    const Twine& message) {
294   if (at.start) {
295     util::emitError(filename, message.str(), at.start, at.length,
296                     bufferBeingParsed);
297   } else {
298     fprintf(stderr, "%s: error: %s\n", filename.str().c_str(),
299             message.str().c_str());
300   }
301 }
302 
303 bool
configureClient(const ConfigureContext &,StringRef name,uint32_t version,const property_list_type & properties)304 ParseBuildFileDelegate::configureClient(const ConfigureContext&,
305                                         StringRef name,
306                                         uint32_t version,
307                                         const property_list_type& properties) {
308   if (showOutput) {
309     // Dump the client information.
310     printf("client ('%s', version: %u)\n", name.str().c_str(), version);
311     for (const auto& property: properties) {
312       printf("  -- '%s': '%s'\n", property.first.c_str(),
313              property.second.c_str());
314     }
315   }
316 
317   return true;
318 }
319 
320 std::unique_ptr<Tool>
lookupTool(StringRef name)321 ParseBuildFileDelegate::lookupTool(StringRef name) {
322   if (showOutput) {
323     printf("tool('%s')\n", name.str().c_str());
324   }
325 
326   return llvm::make_unique<ParseDummyTool>(*this, name);
327 }
328 
loadedTarget(StringRef name,const Target & target)329 void ParseBuildFileDelegate::loadedTarget(StringRef name,
330                                           const Target& target) {
331   if (showOutput) {
332     printf("target('%s')\n", target.getName().str().c_str());
333 
334     // Print the nodes in the target.
335     bool first = true;
336     printf(" -- nodes: [");
337     for (const auto& node: target.getNodes()) {
338       printf("%s'%s'", first ? "" : ", ", node->getName().str().c_str());
339       first = false;
340     }
341     printf("]\n");
342   }
343 }
344 
loadedDefaultTarget(StringRef target)345 void ParseBuildFileDelegate::loadedDefaultTarget(StringRef target) {
346   if (showOutput) {
347     printf("default_target('%s')\n", target.str().c_str());
348   }
349 }
350 
351 std::unique_ptr<Node>
lookupNode(StringRef name,bool isImplicit)352 ParseBuildFileDelegate::lookupNode(StringRef name,
353                                    bool isImplicit) {
354   if (!isImplicit) {
355     if (showOutput) {
356       printf("node('%s')\n", name.str().c_str());
357     }
358   }
359 
360   return llvm::make_unique<ParseDummyNode>(*this, name);
361 }
362 
loadedCommand(StringRef name,const Command & command)363 void ParseBuildFileDelegate::loadedCommand(StringRef name,
364                                         const Command& command) {
365   if (showOutput) {
366     printf("  -- -- loaded command('%s')\n", command.getName().str().c_str());
367   }
368 }
369 
parseUsage(int exitCode)370 static void parseUsage(int exitCode) {
371   int optionWidth = 20;
372   fprintf(stderr, "Usage: %s buildsystem parse [options] <path>\n",
373           getProgramName());
374   fprintf(stderr, "\nOptions:\n");
375   fprintf(stderr, "  %-*s %s\n", optionWidth, "--help",
376           "show this help message and exit");
377   fprintf(stderr, "  %-*s %s\n", optionWidth, "--no-output",
378           "don't display parser output");
379   ::exit(exitCode);
380 }
381 
executeParseCommand(std::vector<std::string> args)382 static int executeParseCommand(std::vector<std::string> args) {
383   bool showOutput = true;
384 
385   while (!args.empty() && args[0][0] == '-') {
386     const std::string option = args[0];
387     args.erase(args.begin());
388 
389     if (option == "--")
390       break;
391 
392     if (option == "--help") {
393       parseUsage(0);
394     } else if (option == "--no-output") {
395       showOutput = false;
396     } else {
397       fprintf(stderr, "error: %s: invalid option: '%s'\n\n",
398               getProgramName(), option.c_str());
399       parseUsage(1);
400     }
401   }
402 
403   if (args.size() != 1) {
404     fprintf(stderr, "error: %s: invalid number of arguments\n",
405             getProgramName());
406     parseUsage(1);
407   }
408 
409   std::string filename = args[0].c_str();
410 
411   // Load the BuildFile.
412   fprintf(stderr, "note: parsing '%s'\n", filename.c_str());
413   ParseBuildFileDelegate delegate(showOutput);
414   BuildFile buildFile(filename, delegate);
415   buildFile.load();
416 
417   return 0;
418 }
419 
420 
421 /* Build Command */
422 
423 class BasicBuildSystemFrontendDelegate : public BuildSystemFrontendDelegate {
424   std::unique_ptr<basic::FileSystem> fileSystem;
425 
426   /// The previous SIGINT handler.
427   struct sigaction previousSigintHandler;
428 
429   /// Low-level flag for when a SIGINT has been received.
430   static std::atomic<bool> wasInterrupted;
431 
432   /// Pipe used to allow detection of signals.
433   static int signalWatchingPipe[2];
434 
sigintHandler(int)435   static void sigintHandler(int) {
436     // Set the atomic interrupt flag.
437     BasicBuildSystemFrontendDelegate::wasInterrupted = true;
438 
439     // Write to wake up the signal monitoring thread.
440     char byte{};
441     basic::sys::write(signalWatchingPipe[1], &byte, 1);
442   }
443 
444   /// Check if an interrupt has occurred.
checkForInterrupt()445   void checkForInterrupt() {
446     // Save and clear the interrupt flag, atomically.
447     bool wasInterrupted = BasicBuildSystemFrontendDelegate::wasInterrupted.exchange(false);
448 
449     // Process the interrupt flag, if present.
450     if (wasInterrupted) {
451       // Otherwise, cancel the build.
452       printf("cancelling build.\n");
453       cancel();
454     }
455   }
456 
457   /// Thread function to wait for indications that signals have arrived and to
458   /// process them.
signalWaitThread()459   void signalWaitThread() {
460     // Wait for signal arrival indications.
461     while (true) {
462       char byte;
463       int res = basic::sys::read(signalWatchingPipe[0], &byte, 1);
464 
465       // If nothing was read, the pipe has been closed and we should shut down.
466       if (res == 0)
467         break;
468 
469       // Otherwise, check if we were awoke because of an interrupt.
470       checkForInterrupt();
471     }
472 
473     // Shut down the pipe.
474     basic::sys::close(signalWatchingPipe[0]);
475     signalWatchingPipe[0] = -1;
476   }
477 
478 public:
BasicBuildSystemFrontendDelegate(llvm::SourceMgr & sourceMgr,const BuildSystemInvocation & invocation)479   BasicBuildSystemFrontendDelegate(llvm::SourceMgr& sourceMgr,
480                                    const BuildSystemInvocation& invocation)
481       : BuildSystemFrontendDelegate(sourceMgr, invocation,
482                                     "basic", /*version=*/0),
483         fileSystem(basic::createLocalFileSystem()) {
484     // Register an interrupt handler.
485     struct sigaction action{};
486     action.sa_handler = &BasicBuildSystemFrontendDelegate::sigintHandler;
487     sigaction(SIGINT, &action, &previousSigintHandler);
488 
489     // Create a pipe and thread to watch for signals.
490     assert(BasicBuildSystemFrontendDelegate::signalWatchingPipe[0] == -1 &&
491            BasicBuildSystemFrontendDelegate::signalWatchingPipe[1] == -1);
492     if (basic::sys::pipe(BasicBuildSystemFrontendDelegate::signalWatchingPipe) < 0) {
493       perror("pipe");
494     }
495     new std::thread(&BasicBuildSystemFrontendDelegate::signalWaitThread, this);
496   }
497 
~BasicBuildSystemFrontendDelegate()498   ~BasicBuildSystemFrontendDelegate() {
499     // Restore any previous SIGINT handler.
500     sigaction(SIGINT, &previousSigintHandler, NULL);
501 
502     // Close the signal watching pipe.
503     basic::sys::close(BasicBuildSystemFrontendDelegate::signalWatchingPipe[1]);
504     signalWatchingPipe[1] = -1;
505   }
506 
getFileSystem()507   virtual basic::FileSystem& getFileSystem() override { return *fileSystem; }
508 
hadCommandFailure()509   virtual void hadCommandFailure() override {
510     // Call the base implementation.
511     BuildSystemFrontendDelegate::hadCommandFailure();
512 
513     // Cancel the build, by default.
514     cancel();
515   }
516 
lookupTool(StringRef name)517   virtual std::unique_ptr<Tool> lookupTool(StringRef name) override {
518     // We do not support any non-built-in tools.
519     return nullptr;
520   }
521 
cycleDetected(const std::vector<Rule * > & cycle)522   virtual void cycleDetected(const std::vector<Rule*>& cycle) override {
523     auto message = BuildSystemInvocation::formatDetectedCycle(cycle);
524     error(message);
525   }
526 };
527 
528 std::atomic<bool> BasicBuildSystemFrontendDelegate::wasInterrupted{false};
529 int BasicBuildSystemFrontendDelegate::signalWatchingPipe[2]{-1, -1};
530 
buildUsage(int exitCode)531 static void buildUsage(int exitCode) {
532   int optionWidth = 25;
533   fprintf(stderr, "Usage: %s buildsystem build [options] [<target>]\n",
534           getProgramName());
535   fprintf(stderr, "\nOptions:\n");
536   BuildSystemInvocation::getUsage(optionWidth, llvm::errs());
537   ::exit(exitCode);
538 }
539 
executeBuildCommand(std::vector<std::string> args)540 static int executeBuildCommand(std::vector<std::string> args) {
541   // The source manager to use for diagnostics.
542   llvm::SourceMgr sourceMgr;
543 
544   // Create the invocation.
545   BuildSystemInvocation invocation{};
546 
547   // Initialize defaults.
548   invocation.dbPath = "build.db";
549   invocation.buildFilePath = "build.llbuild";
550   invocation.parse(args, sourceMgr);
551 
552   // Handle invocation actions.
553   if (invocation.showUsage) {
554     buildUsage(0);
555   } else if (invocation.hadErrors) {
556     buildUsage(1);
557   }
558 
559   if (invocation.positionalArgs.size() > 1) {
560     fprintf(stderr, "error: %s: invalid number of arguments\n",
561             getProgramName());
562     buildUsage(1);
563   }
564 
565   // Select the target to build.
566   std::string targetToBuild =
567     invocation.positionalArgs.empty() ? "" : invocation.positionalArgs[0];
568 
569   // Create the frontend object.
570   BasicBuildSystemFrontendDelegate delegate(sourceMgr, invocation);
571   BuildSystemFrontend frontend(delegate, invocation);
572   if (!frontend.build(targetToBuild)) {
573     // If there were failed commands, report the count and return an error.
574     if (delegate.getNumFailedCommands()) {
575       delegate.error("build had " + Twine(delegate.getNumFailedCommands()) +
576                      " command failures");
577     }
578 
579     return 1;
580   }
581 
582   return 0;
583 }
584 
585 }
586 
587 #pragma mark - Build System Top-Level Command
588 
usage(int exitCode)589 static void usage(int exitCode) {
590   fprintf(stderr, "Usage: %s buildsystem [--help] <command> [<args>]\n",
591           getProgramName());
592   fprintf(stderr, "\n");
593   fprintf(stderr, "Available commands:\n");
594   fprintf(stderr, "  parse         -- Parse a build file\n");
595   fprintf(stderr, "  build         -- Build using a build file\n");
596   fprintf(stderr, "\n");
597   exit(exitCode);
598 }
599 
executeBuildSystemCommand(const std::vector<std::string> & args)600 int commands::executeBuildSystemCommand(const std::vector<std::string> &args) {
601   // Expect the first argument to be the name of another subtool to delegate to.
602   if (args.empty() || args[0] == "--help")
603     usage(0);
604 
605   if (args[0] == "parse") {
606     return executeParseCommand({args.begin()+1, args.end()});
607   } else if (args[0] == "build") {
608     return executeBuildCommand({args.begin()+1, args.end()});
609   } else {
610     fprintf(stderr, "error: %s: unknown command '%s'\n", getProgramName(),
611             args[0].c_str());
612     usage(1);
613     return 1;
614   }
615 }
616