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