1 //===-- BuildSystem.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/BuildSystem.h"
14 #include "llbuild/BuildSystem/BuildSystemCommandInterface.h"
15 #include "llbuild/BuildSystem/BuildSystemFrontend.h"
16 #include "llbuild/BuildSystem/CommandResult.h"
17
18 #include "llbuild/Basic/FileInfo.h"
19 #include "llbuild/Basic/FileSystem.h"
20 #include "llbuild/Basic/Hashing.h"
21 #include "llbuild/Basic/LLVM.h"
22 #include "llbuild/Basic/PlatformUtility.h"
23 #include "llbuild/Basic/ShellUtility.h"
24 #include "llbuild/Core/BuildDB.h"
25 #include "llbuild/Core/BuildEngine.h"
26 #include "llbuild/Core/DependencyInfoParser.h"
27 #include "llbuild/Core/MakefileDepsParser.h"
28 #include "llbuild/BuildSystem/BuildExecutionQueue.h"
29 #include "llbuild/BuildSystem/BuildFile.h"
30 #include "llbuild/BuildSystem/BuildKey.h"
31 #include "llbuild/BuildSystem/BuildNode.h"
32 #include "llbuild/BuildSystem/BuildValue.h"
33 #include "llbuild/BuildSystem/ExternalCommand.h"
34
35 #include "llvm/ADT/ArrayRef.h"
36 #include "llvm/ADT/Hashing.h"
37 #include "llvm/ADT/STLExtras.h"
38 #include "llvm/ADT/SmallString.h"
39 #include "llvm/ADT/StringMap.h"
40 #include "llvm/ADT/StringRef.h"
41 #include "llvm/Support/ErrorHandling.h"
42 #include "llvm/Support/FileSystem.h"
43 #include "llvm/Support/MemoryBuffer.h"
44 #include "llvm/Support/Path.h"
45 #include "llvm/Support/raw_ostream.h"
46
47 #include <memory>
48 #include <mutex>
49 #include <set>
50
51 #include <unistd.h>
52
53 using namespace llbuild;
54 using namespace llbuild::basic;
55 using namespace llbuild::core;
56 using namespace llbuild::buildsystem;
57
~BuildSystemDelegate()58 BuildSystemDelegate::~BuildSystemDelegate() {}
59
~BuildSystemCommandInterface()60 BuildSystemCommandInterface::~BuildSystemCommandInterface() {}
61
62 #pragma mark - BuildSystem implementation
63
64 namespace {
65
66 class BuildSystemImpl;
67
68 /// The delegate used to load the build file for use by a build system.
69 class BuildSystemFileDelegate : public BuildFileDelegate {
70 BuildSystemImpl& system;
71
72 /// FIXME: It would be nice to have a StringSet.
73 llvm::StringMap<bool> internedStrings;
74
75 public:
BuildSystemFileDelegate(BuildSystemImpl & system)76 BuildSystemFileDelegate(BuildSystemImpl& system)
77 : BuildFileDelegate(), system(system) {}
78
79 BuildSystemDelegate& getSystemDelegate();
80
81 /// @name Delegate Implementation
82 /// @{
83
getInternedString(StringRef value)84 virtual StringRef getInternedString(StringRef value) override {
85 auto entry = internedStrings.insert(std::make_pair(value, true));
86 return entry.first->getKey();
87 }
88
getFileSystem()89 virtual FileSystem& getFileSystem() override {
90 return getSystemDelegate().getFileSystem();
91 }
92
93 virtual void setFileContentsBeingParsed(StringRef buffer) override;
94
95 virtual void error(StringRef filename,
96 const BuildFileToken& at,
97 const Twine& message) override;
98
99 virtual bool configureClient(const ConfigureContext&, StringRef name,
100 uint32_t version,
101 const property_list_type& properties) override;
102
103 virtual std::unique_ptr<Tool> lookupTool(StringRef name) override;
104
105 virtual void loadedTarget(StringRef name,
106 const Target& target) override;
107
108 virtual void loadedDefaultTarget(StringRef target) override;
109
110 virtual void loadedCommand(StringRef name,
111 const Command& target) override;
112
113 virtual std::unique_ptr<Node> lookupNode(StringRef name,
114 bool isImplicit=false) override;
115
116 /// @}
117 };
118
119 /// The delegate used to build a loaded build file.
120 class BuildSystemEngineDelegate : public BuildEngineDelegate {
121 BuildSystemImpl& system;
122
123 // FIXME: This is an inefficent map, the string is duplicated.
124 std::unordered_map<std::string, std::unique_ptr<BuildNode>> dynamicNodes;
125
126 /// The custom tasks which are owned by the build system.
127 std::vector<std::unique_ptr<Command>> customTasks;
128
129 const BuildDescription& getBuildDescription() const;
130
131 virtual Rule lookupRule(const KeyType& keyData) override;
132 virtual void cycleDetected(const std::vector<Rule*>& items) override;
133 virtual void error(const Twine& message) override;
134
135 public:
BuildSystemEngineDelegate(BuildSystemImpl & system)136 BuildSystemEngineDelegate(BuildSystemImpl& system) : system(system) {}
137
getBuildSystem()138 BuildSystemImpl& getBuildSystem() {
139 return system;
140 }
141 };
142
143 class BuildSystemImpl : public BuildSystemCommandInterface {
144 /// The internal schema version.
145 ///
146 /// Version History:
147 /// * 7: Added StaleFileRemoval to BuildValue
148 /// * 6: Added DirectoryContents to BuildKey
149 /// * 5: Switch BuildValue to be BinaryCoding based
150 /// * 4: Pre-history
151 static const uint32_t internalSchemaVersion = 7;
152
153 BuildSystem& buildSystem;
154
155 /// The delegate the BuildSystem was configured with.
156 BuildSystemDelegate& delegate;
157
158 /// The name of the main input file.
159 std::string mainFilename;
160
161 /// The delegate used for the loading the build file.
162 BuildSystemFileDelegate fileDelegate;
163
164 /// The build description, once loaded.
165 std::unique_ptr<BuildDescription> buildDescription;
166
167 /// The delegate used for building the file contents.
168 BuildSystemEngineDelegate engineDelegate;
169
170 /// The build engine.
171 BuildEngine buildEngine;
172
173 /// Mutex for access to execution queue.
174 std::mutex executionQueueMutex;
175
176 /// The execution queue reference; this is only valid while a build is
177 /// actually in progress.
178 std::unique_ptr<BuildExecutionQueue> executionQueue;
179
180 /// Flag indicating if the build has been aborted.
181 bool buildWasAborted = false;
182
183 /// Flag indicating if the build has been cancelled.
184 std::atomic<bool> isCancelled_{ false };
185
186 /// @name BuildSystemCommandInterface Implementation
187 /// @{
188
getBuildEngine()189 virtual BuildEngine& getBuildEngine() override {
190 return buildEngine;
191 }
192
getExecutionQueue()193 virtual BuildExecutionQueue& getExecutionQueue() override {
194 assert(executionQueue.get());
195 return *executionQueue;
196 }
197
taskNeedsInput(core::Task * task,const BuildKey & key,uintptr_t inputID)198 virtual void taskNeedsInput(core::Task* task, const BuildKey& key,
199 uintptr_t inputID) override {
200 return buildEngine.taskNeedsInput(task, key.toData(), inputID);
201 }
202
taskMustFollow(core::Task * task,const BuildKey & key)203 virtual void taskMustFollow(core::Task* task, const BuildKey& key) override {
204 return buildEngine.taskMustFollow(task, key.toData());
205 }
206
taskDiscoveredDependency(core::Task * task,const BuildKey & key)207 virtual void taskDiscoveredDependency(core::Task* task,
208 const BuildKey& key) override {
209 return buildEngine.taskDiscoveredDependency(task, key.toData());
210 }
211
taskIsComplete(core::Task * task,const BuildValue & value,bool forceChange)212 virtual void taskIsComplete(core::Task* task, const BuildValue& value,
213 bool forceChange) override {
214 return buildEngine.taskIsComplete(task, value.toData(), forceChange);
215 }
216
addJob(QueueJob && job)217 virtual void addJob(QueueJob&& job) override {
218 executionQueue->addJob(std::move(job));
219 }
220
221 /// @}
222
223 public:
BuildSystemImpl(class BuildSystem & buildSystem,BuildSystemDelegate & delegate)224 BuildSystemImpl(class BuildSystem& buildSystem,
225 BuildSystemDelegate& delegate)
226 : buildSystem(buildSystem), delegate(delegate),
227 fileDelegate(*this), engineDelegate(*this), buildEngine(engineDelegate),
228 executionQueue() {}
229
getBuildSystem()230 BuildSystem& getBuildSystem() {
231 return buildSystem;
232 }
233
getDelegate()234 BuildSystemDelegate& getDelegate() override {
235 return delegate;
236 }
237
238 // FIXME: We should eliminate this, it isn't well formed when loading
239 // descriptions not from a file. We currently only use that for unit testing,
240 // though.
getMainFilename()241 StringRef getMainFilename() {
242 return mainFilename;
243 }
244
getCommandInterface()245 BuildSystemCommandInterface& getCommandInterface() {
246 return *this;
247 }
248
getBuildDescription() const249 const BuildDescription& getBuildDescription() const {
250 assert(buildDescription);
251 return *buildDescription;
252 }
253
error(StringRef filename,const Twine & message)254 void error(StringRef filename, const Twine& message) {
255 getDelegate().error(filename, {}, message);
256 }
257
error(StringRef filename,const BuildSystemDelegate::Token & at,const Twine & message)258 void error(StringRef filename, const BuildSystemDelegate::Token& at,
259 const Twine& message) {
260 getDelegate().error(filename, at, message);
261 }
262
263 std::unique_ptr<BuildNode> lookupNode(StringRef name,
264 bool isImplicit);
265
getMergedSchemaVersion()266 uint32_t getMergedSchemaVersion() {
267 // FIXME: Find a cleaner strategy for merging the internal schema version
268 // with that from the client.
269 auto clientVersion = delegate.getVersion();
270 assert(clientVersion <= (1 << 16) && "unsupported client verison");
271 return internalSchemaVersion + (clientVersion << 16);
272 }
273
274 /// @name Client API
275 /// @{
276
loadDescription(StringRef filename)277 bool loadDescription(StringRef filename) {
278 this->mainFilename = filename;
279
280 auto description = BuildFile(filename, fileDelegate).load();
281 if (!description) {
282 error(getMainFilename(), "unable to load build file");
283 return false;
284 }
285
286 buildDescription = std::move(description);
287 return true;
288 }
289
loadDescription(std::unique_ptr<BuildDescription> description)290 void loadDescription(std::unique_ptr<BuildDescription> description) {
291 buildDescription = std::move(description);
292 }
293
attachDB(StringRef filename,std::string * error_out)294 bool attachDB(StringRef filename, std::string* error_out) {
295 // FIXME: How do we pass the client schema version here, if we haven't
296 // loaded the file yet.
297 std::unique_ptr<core::BuildDB> db(
298 core::createSQLiteBuildDB(filename, getMergedSchemaVersion(),
299 error_out));
300 if (!db)
301 return false;
302
303 return buildEngine.attachDB(std::move(db), error_out);
304 }
305
enableTracing(StringRef filename,std::string * error_out)306 bool enableTracing(StringRef filename, std::string* error_out) {
307 return buildEngine.enableTracing(filename, error_out);
308 }
309
310 /// Build the given key, and return the result and an indication of success.
311 llvm::Optional<BuildValue> build(BuildKey key);
312
313 bool build(StringRef target);
314
setBuildWasAborted(bool value)315 void setBuildWasAborted(bool value) {
316 buildWasAborted = value;
317 }
318
resetForBuild()319 void resetForBuild() {
320 std::lock_guard<std::mutex> guard(executionQueueMutex);
321 isCancelled_ = false;
322 }
323
324 /// Cancel the running build.
cancel()325 void cancel() {
326 std::lock_guard<std::mutex> guard(executionQueueMutex);
327
328 isCancelled_ = true;
329 // Cancel jobs if we actually have a queue.
330 if (executionQueue.get() != nullptr)
331 getExecutionQueue().cancelAllJobs();
332 }
333
334 /// Check if the build has been cancelled.
isCancelled()335 bool isCancelled() {
336 return isCancelled_;
337 }
338
339 /// @}
340 };
341
342 #pragma mark - BuildSystem engine integration
343
344 #pragma mark - Task implementations
345
getBuildSystem(BuildEngine & engine)346 static BuildSystemImpl& getBuildSystem(BuildEngine& engine) {
347 return static_cast<BuildSystemEngineDelegate*>(
348 engine.getDelegate())->getBuildSystem();
349 }
350
351 /// This is the task used to "build" a target, it translates between the request
352 /// for building a target key and the requests for all of its nodes.
353 class TargetTask : public Task {
354 Target& target;
355
356 // Build specific data.
357 //
358 // FIXME: We should probably factor this out somewhere else, so we can enforce
359 // it is never used when initialized incorrectly.
360
361 /// If true, the command had a missing input (this implies ShouldSkip is
362 /// true).
363 bool hasMissingInput = false;
364
start(BuildEngine & engine)365 virtual void start(BuildEngine& engine) override {
366 // Request all of the necessary system tasks.
367 unsigned id = 0;
368 for (auto it = target.getNodes().begin(),
369 ie = target.getNodes().end(); it != ie; ++it, ++id) {
370 engine.taskNeedsInput(this, BuildKey::makeNode(*it).toData(), id);
371 }
372 }
373
providePriorValue(BuildEngine &,const ValueType & value)374 virtual void providePriorValue(BuildEngine&,
375 const ValueType& value) override {
376 // Do nothing.
377 }
378
provideValue(BuildEngine & engine,uintptr_t inputID,const ValueType & valueData)379 virtual void provideValue(BuildEngine& engine, uintptr_t inputID,
380 const ValueType& valueData) override {
381 // Do nothing.
382 auto value = BuildValue::fromData(valueData);
383
384 if (value.isMissingInput()) {
385 hasMissingInput = true;
386
387 // FIXME: Design the logging and status output APIs.
388 auto& system = getBuildSystem(engine);
389 system.error(system.getMainFilename(),
390 (Twine("missing input '") +
391 target.getNodes()[inputID]->getName() +
392 "' and no rule to build it"));
393 }
394 }
395
inputsAvailable(BuildEngine & engine)396 virtual void inputsAvailable(BuildEngine& engine) override {
397 // If the build should cancel, do nothing.
398 if (getBuildSystem(engine).isCancelled()) {
399 engine.taskIsComplete(this, BuildValue::makeSkippedCommand().toData());
400 return;
401 }
402
403 if (hasMissingInput) {
404 // FIXME: Design the logging and status output APIs.
405 auto& system = getBuildSystem(engine);
406 system.error(system.getMainFilename(),
407 (Twine("cannot build target '") + target.getName() +
408 "' due to missing input"));
409
410 // Report the command failure.
411 system.getDelegate().hadCommandFailure();
412 }
413
414 // Complete the task immediately.
415 engine.taskIsComplete(this, BuildValue::makeTarget().toData());
416 }
417
418 public:
TargetTask(Target & target)419 TargetTask(Target& target) : target(target) {}
420
isResultValid(BuildEngine & engine,Target & node,const BuildValue & value)421 static bool isResultValid(BuildEngine& engine, Target& node,
422 const BuildValue& value) {
423 // Always treat target tasks as invalid.
424 return false;
425 }
426 };
427
428
429 /// This is the task to "build" a file node which represents pure raw input to
430 /// the system.
431 class FileInputNodeTask : public Task {
432 BuildNode& node;
433
start(BuildEngine & engine)434 virtual void start(BuildEngine& engine) override {
435 assert(node.getProducers().empty());
436 }
437
providePriorValue(BuildEngine &,const ValueType & value)438 virtual void providePriorValue(BuildEngine&,
439 const ValueType& value) override {
440 }
441
provideValue(BuildEngine &,uintptr_t inputID,const ValueType & value)442 virtual void provideValue(BuildEngine&, uintptr_t inputID,
443 const ValueType& value) override {
444 }
445
inputsAvailable(BuildEngine & engine)446 virtual void inputsAvailable(BuildEngine& engine) override {
447 // FIXME: We should do this work in the background.
448
449 // Get the information on the file.
450 //
451 // FIXME: This needs to delegate, since we want to have a notion of
452 // different node types.
453 assert(!node.isVirtual());
454 auto info = node.getFileInfo(
455 getBuildSystem(engine).getDelegate().getFileSystem());
456 if (info.isMissing()) {
457 engine.taskIsComplete(this, BuildValue::makeMissingInput().toData());
458 return;
459 }
460
461 engine.taskIsComplete(
462 this, BuildValue::makeExistingInput(info).toData());
463 }
464
465 public:
FileInputNodeTask(BuildNode & node)466 FileInputNodeTask(BuildNode& node) : node(node) {
467 assert(!node.isVirtual());
468 }
469
isResultValid(BuildEngine & engine,const BuildNode & node,const BuildValue & value)470 static bool isResultValid(BuildEngine& engine, const BuildNode& node,
471 const BuildValue& value) {
472 // The result is valid if the existence matches the value type and the file
473 // information remains the same.
474 //
475 // FIXME: This is inefficient, we will end up doing the stat twice, once
476 // when we check the value for up to dateness, and once when we "build" the
477 // output.
478 //
479 // We can solve this by caching ourselves but I wonder if it is something
480 // the engine should support more naturally. In practice, this is unlikely
481 // to be very performance critical in practice because this is only
482 // redundant in the case where we have never built the node before (or need
483 // to rebuild it), and thus the additional stat is only one small part of
484 // the work we need to perform.
485 auto info = node.getFileInfo(
486 getBuildSystem(engine).getDelegate().getFileSystem());
487 if (info.isMissing()) {
488 return value.isMissingInput();
489 } else {
490 return value.isExistingInput() && value.getOutputInfo() == info;
491 }
492 }
493 };
494
495
496 /// This is the task to "build" a directory node.
497 ///
498 /// This node effectively just adapts a directory tree signature to a node. The
499 /// reason why we need it (versus simply making the directory tree signature
500 /// *be* this, is that we want the directory signature to be able to interface
501 /// with other build nodes produced by commands).
502 class DirectoryInputNodeTask : public Task {
503 BuildNode& node;
504
505 core::ValueType directorySignature;
506
start(BuildEngine & engine)507 virtual void start(BuildEngine& engine) override {
508 // Remove any trailing slash from the node name.
509 StringRef path = node.getName();
510 if (path.endswith("/") && path != "/") {
511 path = path.substr(0, path.size() - 1);
512 }
513 engine.taskNeedsInput(
514 this, BuildKey::makeDirectoryTreeSignature(path).toData(),
515 /*inputID=*/0);
516 }
517
providePriorValue(BuildEngine &,const ValueType & value)518 virtual void providePriorValue(BuildEngine&,
519 const ValueType& value) override {
520 }
521
provideValue(BuildEngine &,uintptr_t inputID,const ValueType & value)522 virtual void provideValue(BuildEngine&, uintptr_t inputID,
523 const ValueType& value) override {
524 directorySignature = value;
525 }
526
inputsAvailable(BuildEngine & engine)527 virtual void inputsAvailable(BuildEngine& engine) override {
528 // Simply propagate the value.
529 engine.taskIsComplete(this, ValueType(directorySignature));
530 }
531
532 public:
DirectoryInputNodeTask(BuildNode & node)533 DirectoryInputNodeTask(BuildNode& node) : node(node) {
534 assert(!node.isVirtual());
535 }
536 };
537
538
539 /// This is the task to build a virtual node which isn't connected to any
540 /// output.
541 class VirtualInputNodeTask : public Task {
start(BuildEngine & engine)542 virtual void start(BuildEngine& engine) override {
543 }
544
providePriorValue(BuildEngine &,const ValueType & value)545 virtual void providePriorValue(BuildEngine&,
546 const ValueType& value) override {
547 }
548
provideValue(BuildEngine &,uintptr_t inputID,const ValueType & value)549 virtual void provideValue(BuildEngine&, uintptr_t inputID,
550 const ValueType& value) override {
551 }
552
inputsAvailable(BuildEngine & engine)553 virtual void inputsAvailable(BuildEngine& engine) override {
554 engine.taskIsComplete(
555 this, BuildValue::makeVirtualInput().toData());
556 }
557
558 public:
VirtualInputNodeTask()559 VirtualInputNodeTask() {}
560
isResultValid(BuildEngine & engine,const BuildNode & node,const BuildValue & value)561 static bool isResultValid(BuildEngine& engine, const BuildNode& node,
562 const BuildValue& value) {
563 // Virtual input nodes are always valid unless the value type is wrong.
564 return value.isVirtualInput();
565 }
566 };
567
568
569 /// This is the task to "build" a node which is the product of some command.
570 ///
571 /// It is responsible for selecting the appropriate producer command to run to
572 /// produce the node, and for synchronizing any external state the node depends
573 /// on.
574 class ProducedNodeTask : public Task {
575 Node& node;
576 BuildValue nodeResult;
577 Command* producingCommand = nullptr;
578
579 // Build specific data.
580 //
581 // FIXME: We should probably factor this out somewhere else, so we can enforce
582 // it is never used when initialized incorrectly.
583
584 // Whether this is a node we are unable to produce.
585 bool isInvalid = false;
586
start(BuildEngine & engine)587 virtual void start(BuildEngine& engine) override {
588 // Request the producer command.
589 if (node.getProducers().size() == 1) {
590 producingCommand = node.getProducers()[0];
591 engine.taskNeedsInput(this, BuildKey::makeCommand(
592 producingCommand->getName()).toData(),
593 /*InputID=*/0);
594 return;
595 }
596
597 // We currently do not support building nodes which have multiple producers.
598 auto producerA = node.getProducers()[0];
599 auto producerB = node.getProducers()[1];
600 getBuildSystem(engine).error(
601 "", "unable to build node: '" + node.getName() + "' (node is produced "
602 "by multiple commands; e.g., '" + producerA->getName() + "' and '" +
603 producerB->getName() + "')");
604 isInvalid = true;
605 }
606
providePriorValue(BuildEngine &,const ValueType & value)607 virtual void providePriorValue(BuildEngine&,
608 const ValueType& value) override {
609 }
610
provideValue(BuildEngine &,uintptr_t inputID,const ValueType & valueData)611 virtual void provideValue(BuildEngine&, uintptr_t inputID,
612 const ValueType& valueData) override {
613 auto value = BuildValue::fromData(valueData);
614
615 // Extract the node result from the command.
616 assert(producingCommand);
617 nodeResult = producingCommand->getResultForOutput(&node, value);
618 }
619
inputsAvailable(BuildEngine & engine)620 virtual void inputsAvailable(BuildEngine& engine) override {
621 if (isInvalid) {
622 getBuildSystem(engine).getDelegate().hadCommandFailure();
623 engine.taskIsComplete(this, BuildValue::makeFailedInput().toData());
624 return;
625 }
626
627 assert(!nodeResult.isInvalid());
628
629 // Complete the task immediately.
630 engine.taskIsComplete(this, nodeResult.toData());
631 }
632
633 public:
ProducedNodeTask(Node & node)634 ProducedNodeTask(Node& node)
635 : node(node), nodeResult(BuildValue::makeInvalid()) {}
636
isResultValid(BuildEngine &,Node & node,const BuildValue & value)637 static bool isResultValid(BuildEngine&, Node& node,
638 const BuildValue& value) {
639 // If the result was failure, we always need to rebuild (it may produce an
640 // error).
641 if (value.isFailedInput())
642 return false;
643
644 // The produced node result itself doesn't need any synchronization.
645 return true;
646 }
647 };
648
649
650 /// This task is responsible for computing the lists of files in directories.
651 class DirectoryContentsTask : public Task {
652 std::string path;
653
654 /// The value for the input directory.
655 ValueType directoryValue;
656
start(BuildEngine & engine)657 virtual void start(BuildEngine& engine) override {
658 // Request the base directory node -- this task doesn't actually use the
659 // value, but this connects the task to its producer if present.
660 engine.taskNeedsInput(
661 this, BuildKey::makeNode(path).toData(), /*inputID=*/0);
662 }
663
providePriorValue(BuildEngine &,const ValueType & value)664 virtual void providePriorValue(BuildEngine&,
665 const ValueType& value) override {
666 }
667
provideValue(BuildEngine &,uintptr_t inputID,const ValueType & value)668 virtual void provideValue(BuildEngine&, uintptr_t inputID,
669 const ValueType& value) override {
670 if (inputID == 0) {
671 directoryValue = value;
672 return;
673 }
674 }
675
inputsAvailable(BuildEngine & engine)676 virtual void inputsAvailable(BuildEngine& engine) override {
677 // FIXME: We should do this work in the background.
678
679 // Get the stat information on the directory.
680 //
681 // FIXME: We should probably be using the directory value here, but that
682 // requires reworking some of the value encoding for the directory.
683 auto info = getBuildSystem(engine).getDelegate().getFileSystem().getFileInfo(
684 path);
685 if (info.isMissing()) {
686 engine.taskIsComplete(
687 this, BuildValue::makeMissingInput().toData());
688 return;
689 }
690
691 // Get the list of files in the directory.
692 std::error_code ec;
693 std::vector<std::string> filenames;
694 for (auto it = llvm::sys::fs::directory_iterator(path, ec),
695 end = llvm::sys::fs::directory_iterator(); it != end;
696 it = it.increment(ec)) {
697 filenames.push_back(llvm::sys::path::filename(it->path()));
698 }
699
700 // Order the filenames.
701 std::sort(filenames.begin(), filenames.end(),
702 [](const std::string& a, const std::string& b) {
703 return a < b;
704 });
705
706 // Create the result.
707 engine.taskIsComplete(
708 this, BuildValue::makeDirectoryContents(info, filenames).toData());
709 }
710
711 public:
DirectoryContentsTask(StringRef path)712 DirectoryContentsTask(StringRef path) : path(path) {}
713
isResultValid(BuildEngine & engine,StringRef path,const BuildValue & value)714 static bool isResultValid(BuildEngine& engine, StringRef path,
715 const BuildValue& value) {
716 // The result is valid if the existence matches the existing value type, and
717 // the file information remains the same.
718 auto info = getBuildSystem(engine).getDelegate().getFileSystem().getFileInfo(
719 path);
720 if (info.isMissing()) {
721 return value.isMissingInput();
722 } else {
723 return value.isDirectoryContents() && value.getOutputInfo() == info;
724 }
725 }
726 };
727
728
729
730 /// This is the task to "build" a directory node which will encapsulate (via a
731 /// signature) all of the contents of the directory, recursively.
732 class DirectoryTreeSignatureTask : public Task {
733 // The basic algorithm we need to follow:
734 //
735 // 1. Get the directory contents.
736 // 2. Get the subpath directory info.
737 // 3. For each node input, if it is a directory, get the input node for it.
738 //
739 // FIXME: This algorithm currently does a redundant stat for each directory,
740 // because we stat it once to find out it is a directory, then again when we
741 // gather its contents (to use for validating the directory contents).
742 //
743 // FIXME: We need to fix the directory list to not get contents for symbolic
744 // links.
745
746 /// This structure encapsulates the information we need on each child.
747 struct SubpathInfo {
748 /// The filename;
749 std::string filename;
750
751 /// The result of requesting the node at this subpath, once available.
752 ValueType value;
753
754 /// The directory signature, if needed.
755 llvm::Optional<ValueType> directorySignatureValue;
756 };
757
758 /// The path we are taking the signature of.
759 std::string path;
760
761 /// The value for the directory itself.
762 ValueType directoryValue;
763
764 /// The accumulated list of child input info.
765 ///
766 /// Once we have the input directory information, we resize this to match the
767 /// number of children to avoid dynamically resizing it.
768 std::vector<SubpathInfo> childResults;
769
start(BuildEngine & engine)770 virtual void start(BuildEngine& engine) override {
771 // Ask for the base directory directory contents.
772 engine.taskNeedsInput(
773 this, BuildKey::makeDirectoryContents(path).toData(),
774 /*inputID=*/0);
775 }
776
providePriorValue(BuildEngine &,const ValueType & value)777 virtual void providePriorValue(BuildEngine&,
778 const ValueType& value) override {
779 }
780
provideValue(BuildEngine & engine,uintptr_t inputID,const ValueType & valueData)781 virtual void provideValue(BuildEngine& engine, uintptr_t inputID,
782 const ValueType& valueData) override {
783 // The first input is the directory contents.
784 if (inputID == 0) {
785 // Record the value for the directory.
786 directoryValue = valueData;
787
788 // Request the inputs for each subpath.
789 auto value = BuildValue::fromData(valueData);
790 if (value.isMissingInput())
791 return;
792
793 assert(value.isDirectoryContents());
794 auto filenames = value.getDirectoryContents();
795 for (size_t i = 0; i != filenames.size(); ++i) {
796 SmallString<256> childPath{ path };
797 llvm::sys::path::append(childPath, filenames[i]);
798 childResults.emplace_back(SubpathInfo{ filenames[i], {}, None });
799 engine.taskNeedsInput(this, BuildKey::makeNode(childPath).toData(),
800 /*inputID=*/1 + i);
801 }
802 return;
803 }
804
805 // If the input is a child, add it to the collection and dispatch a
806 // directory request if needed.
807 if (inputID >= 1 && inputID < 1 + childResults.size()) {
808 auto index = inputID - 1;
809 auto& childResult = childResults[index];
810 childResult.value = valueData;
811
812 // If this node is a directory, request its signature recursively.
813 auto value = BuildValue::fromData(valueData);
814 if (value.isExistingInput()) {
815 if (value.getOutputInfo().isDirectory()) {
816 SmallString<256> childPath{ path };
817 llvm::sys::path::append(childPath, childResult.filename);
818
819 engine.taskNeedsInput(
820 this, BuildKey::makeDirectoryTreeSignature(childPath).toData(),
821 /*inputID=*/1 + childResults.size() + index);
822 }
823 }
824 return;
825 }
826
827 // Otherwise, the input should be a directory signature.
828 auto index = inputID - 1 - childResults.size();
829 assert(index < childResults.size());
830 childResults[index].directorySignatureValue = valueData;
831 }
832
inputsAvailable(BuildEngine & engine)833 virtual void inputsAvailable(BuildEngine& engine) override {
834 // Compute the signature across all of the inputs.
835 using llvm::hash_combine;
836 llvm::hash_code code = hash_value(path);
837
838 // Add the signature for the actual input path.
839 code = hash_combine(
840 code, hash_combine_range(directoryValue.begin(), directoryValue.end()));
841
842 // For now, we represent this task as the aggregation of all the inputs.
843 for (const auto& info: childResults) {
844 // We merge the children by simply combining their encoded representation.
845 code = hash_combine(
846 code, hash_combine_range(info.value.begin(), info.value.end()));
847 if (info.directorySignatureValue.hasValue()) {
848 auto& data = info.directorySignatureValue.getValue();
849 code = hash_combine(
850 code, hash_combine_range(data.begin(), data.end()));
851 } else {
852 // Combine a random number to represent nil.
853 code = hash_combine(code, 0XC183979C3E98722E);
854 }
855 }
856
857 // Compute the signature.
858 engine.taskIsComplete(this, BuildValue::makeDirectoryTreeSignature(
859 uint64_t(code)).toData());
860 }
861
862 public:
DirectoryTreeSignatureTask(StringRef path)863 DirectoryTreeSignatureTask(StringRef path) : path(path) {}
864 };
865
866
867 /// This is the task to actually execute a command.
868 class CommandTask : public Task {
869 Command& command;
870
start(BuildEngine & engine)871 virtual void start(BuildEngine& engine) override {
872 // Notify the client the command is preparing to run.
873 getBuildSystem(engine).getDelegate().commandPreparing(&command);
874
875 command.start(getBuildSystem(engine).getCommandInterface(), this);
876 }
877
providePriorValue(BuildEngine & engine,const ValueType & valueData)878 virtual void providePriorValue(BuildEngine& engine,
879 const ValueType& valueData) override {
880 BuildValue value = BuildValue::fromData(valueData);
881 command.providePriorValue(
882 getBuildSystem(engine).getCommandInterface(), this, value);
883 }
884
provideValue(BuildEngine & engine,uintptr_t inputID,const ValueType & valueData)885 virtual void provideValue(BuildEngine& engine, uintptr_t inputID,
886 const ValueType& valueData) override {
887 command.provideValue(
888 getBuildSystem(engine).getCommandInterface(), this, inputID,
889 BuildValue::fromData(valueData));
890 }
891
inputsAvailable(BuildEngine & engine)892 virtual void inputsAvailable(BuildEngine& engine) override {
893 auto& bsci = getBuildSystem(engine).getCommandInterface();
894 auto fn = [this, &bsci=bsci](QueueJobContext* context) {
895 // If the build should cancel, do nothing.
896 if (getBuildSystem(bsci.getBuildEngine()).isCancelled()) {
897 bsci.taskIsComplete(this, BuildValue::makeCancelledCommand());
898 return;
899 }
900
901 // Check if the command should be skipped.
902 if (!bsci.getDelegate().shouldCommandStart(&command)) {
903 // We need to call commandFinished here because commandPreparing and
904 // shouldCommandStart guarantee that they're followed by
905 // commandFinished.
906 bsci.getDelegate().commandFinished(&command, CommandResult::Skipped);
907 bsci.taskIsComplete(this, BuildValue::makeSkippedCommand());
908 return;
909 }
910
911 // Execute the command, with notifications to the delegate.
912 auto result = command.execute(bsci, this, context);
913
914 // Inform the engine of the result.
915 if (result.isFailedCommand()) {
916 bsci.getDelegate().hadCommandFailure();
917 }
918 bsci.taskIsComplete(this, std::move(result));
919 };
920 bsci.addJob({ &command, std::move(fn) });
921 }
922
923 public:
CommandTask(Command & command)924 CommandTask(Command& command) : command(command) {}
925
isResultValid(BuildEngine & engine,Command & command,const BuildValue & value)926 static bool isResultValid(BuildEngine& engine, Command& command,
927 const BuildValue& value) {
928 // Delegate to the command for further checking.
929 return command.isResultValid(
930 getBuildSystem(engine).getBuildSystem(), value);
931 }
932 };
933
934 #pragma mark - BuildSystemEngineDelegate implementation
935
936 /// This is a synthesized task used to represent a missing command.
937 ///
938 /// This command is used in cases where a command has been removed from the
939 /// manifest, but can still be found during an incremental rebuild. This command
940 /// is used to inject an invalid value thus forcing downstream clients to
941 /// rebuild.
942 class MissingCommandTask : public Task {
943 private:
start(BuildEngine & engine)944 virtual void start(BuildEngine& engine) override { }
providePriorValue(BuildEngine & engine,const ValueType & valueData)945 virtual void providePriorValue(BuildEngine& engine,
946 const ValueType& valueData) override { }
947
provideValue(BuildEngine & engine,uintptr_t inputID,const ValueType & valueData)948 virtual void provideValue(BuildEngine& engine, uintptr_t inputID,
949 const ValueType& valueData) override { }
950
inputsAvailable(BuildEngine & engine)951 virtual void inputsAvailable(BuildEngine& engine) override {
952 // A missing command always builds to an invalid value, and forces
953 // downstream clients to be rebuilt (at which point they will presumably see
954 // the command is no longer used).
955 return engine.taskIsComplete(this, BuildValue::makeInvalid().toData(),
956 /*forceChange=*/true);
957 }
958
959 public:
960 using Task::Task;
961 };
962
getBuildDescription() const963 const BuildDescription& BuildSystemEngineDelegate::getBuildDescription() const {
964 return system.getBuildDescription();
965 }
966
967 static BuildSystemDelegate::CommandStatusKind
convertStatusKind(core::Rule::StatusKind kind)968 convertStatusKind(core::Rule::StatusKind kind) {
969 switch (kind) {
970 case core::Rule::StatusKind::IsScanning:
971 return BuildSystemDelegate::CommandStatusKind::IsScanning;
972 case core::Rule::StatusKind::IsUpToDate:
973 return BuildSystemDelegate::CommandStatusKind::IsUpToDate;
974 case core::Rule::StatusKind::IsComplete:
975 return BuildSystemDelegate::CommandStatusKind::IsComplete;
976 }
977 assert(0 && "unknown status kind");
978 return BuildSystemDelegate::CommandStatusKind::IsScanning;
979 }
980
lookupRule(const KeyType & keyData)981 Rule BuildSystemEngineDelegate::lookupRule(const KeyType& keyData) {
982 // Decode the key.
983 auto key = BuildKey::fromData(keyData);
984
985 switch (key.getKind()) {
986 case BuildKey::Kind::Unknown:
987 break;
988
989 case BuildKey::Kind::Command: {
990 // Find the comand.
991 auto it = getBuildDescription().getCommands().find(key.getCommandName());
992 if (it == getBuildDescription().getCommands().end()) {
993 // If there is no such command, produce an error task.
994 return Rule{
995 keyData,
996 /*Action=*/ [](BuildEngine& engine) -> Task* {
997 return engine.registerTask(new MissingCommandTask());
998 },
999 /*IsValid=*/ [](BuildEngine&, const Rule& rule,
1000 const ValueType& value) -> bool {
1001 // The cached result for a missing command is never valid.
1002 return false;
1003 }
1004 };
1005 }
1006
1007 // Create the rule for the command.
1008 Command* command = it->second.get();
1009 return Rule{
1010 keyData,
1011 /*Action=*/ [command](BuildEngine& engine) -> Task* {
1012 return engine.registerTask(new CommandTask(*command));
1013 },
1014 /*IsValid=*/ [command](BuildEngine& engine, const Rule& rule,
1015 const ValueType& value) -> bool {
1016 return CommandTask::isResultValid(
1017 engine, *command, BuildValue::fromData(value));
1018 },
1019 /*UpdateStatus=*/ [command](BuildEngine& engine,
1020 core::Rule::StatusKind status) {
1021 return ::getBuildSystem(engine).getDelegate().commandStatusChanged(
1022 command, convertStatusKind(status));
1023 }
1024 };
1025 }
1026
1027 case BuildKey::Kind::CustomTask: {
1028 // Search for a tool which knows how to create the given custom task.
1029 //
1030 // FIXME: We should most likely have some kind of registration process so we
1031 // can do an efficient query here, but exactly how this should look isn't
1032 // clear yet.
1033 for (const auto& it: getBuildDescription().getTools()) {
1034 auto result = it.second->createCustomCommand(key);
1035 if (!result) continue;
1036
1037 // Save the custom command.
1038 customTasks.emplace_back(std::move(result));
1039 Command *command = customTasks.back().get();
1040
1041 return Rule{
1042 keyData,
1043 /*Action=*/ [command](BuildEngine& engine) -> Task* {
1044 return engine.registerTask(new CommandTask(*command));
1045 },
1046 /*IsValid=*/ [command](BuildEngine& engine, const Rule& rule,
1047 const ValueType& value) -> bool {
1048 return CommandTask::isResultValid(
1049 engine, *command, BuildValue::fromData(value));
1050 }
1051 };
1052 }
1053
1054 // We were unable to create an appropriate custom command, produce an error
1055 // task.
1056 return Rule{
1057 keyData,
1058 /*Action=*/ [](BuildEngine& engine) -> Task* {
1059 return engine.registerTask(new MissingCommandTask());
1060 },
1061 /*IsValid=*/ [](BuildEngine&, const Rule& rule,
1062 const ValueType& value) -> bool {
1063 // The cached result for a missing command is never valid.
1064 return false;
1065 }
1066 };
1067 }
1068
1069 case BuildKey::Kind::DirectoryContents: {
1070 std::string path = key.getDirectoryContentsPath();
1071 return Rule{
1072 keyData,
1073 /*Action=*/ [path](BuildEngine& engine) -> Task* {
1074 return engine.registerTask(new DirectoryContentsTask(path));
1075 },
1076 /*IsValid=*/ [path](BuildEngine& engine, const Rule& rule,
1077 const ValueType& value) -> bool {
1078 return DirectoryContentsTask::isResultValid(
1079 engine, path, BuildValue::fromData(value));
1080 }
1081 };
1082 }
1083
1084 case BuildKey::Kind::DirectoryTreeSignature: {
1085 std::string path = key.getDirectoryTreeSignaturePath();
1086 return Rule{
1087 keyData,
1088 /*Action=*/ [path](BuildEngine& engine) -> Task* {
1089 return engine.registerTask(new DirectoryTreeSignatureTask(path));
1090 },
1091 // Directory signatures don't require any validation outside of their
1092 // concrete dependencies.
1093 /*IsValid=*/ nullptr
1094 };
1095 }
1096
1097 case BuildKey::Kind::Node: {
1098 // Find the node.
1099 auto it = getBuildDescription().getNodes().find(key.getNodeName());
1100 BuildNode* node;
1101 if (it != getBuildDescription().getNodes().end()) {
1102 node = static_cast<BuildNode*>(it->second.get());
1103 } else {
1104 auto it = dynamicNodes.find(key.getNodeName());
1105 if (it != dynamicNodes.end()) {
1106 node = it->second.get();
1107 } else {
1108 // Create nodes on the fly for any unknown ones.
1109 auto nodeOwner = system.lookupNode(
1110 key.getNodeName(), /*isImplicit=*/true);
1111 node = nodeOwner.get();
1112 dynamicNodes[key.getNodeName()] = std::move(nodeOwner);
1113 }
1114 }
1115
1116 // Create the rule used to construct this node.
1117 //
1118 // We could bypass this level and directly return the rule to run the
1119 // command, which would reduce the number of tasks in the system. For now we
1120 // do the uniform thing, but do differentiate between input and command
1121 // nodes.
1122
1123 // Create an input node if there are no producers.
1124 if (node->getProducers().empty()) {
1125 if (node->isVirtual()) {
1126 return Rule{
1127 keyData,
1128 /*Action=*/ [](BuildEngine& engine) -> Task* {
1129 return engine.registerTask(new VirtualInputNodeTask());
1130 },
1131 /*IsValid=*/ [node](BuildEngine& engine, const Rule& rule,
1132 const ValueType& value) -> bool {
1133 return VirtualInputNodeTask::isResultValid(
1134 engine, *node, BuildValue::fromData(value));
1135 }
1136 };
1137 }
1138
1139 if (node->isDirectory()) {
1140 return Rule{
1141 keyData,
1142 /*Action=*/ [node](BuildEngine& engine) -> Task* {
1143 return engine.registerTask(new DirectoryInputNodeTask(*node));
1144 },
1145 // Directory nodes don't require any validation outside of their
1146 // concrete dependencies.
1147 /*IsValid=*/ nullptr
1148 };
1149 }
1150
1151 return Rule{
1152 keyData,
1153 /*Action=*/ [node](BuildEngine& engine) -> Task* {
1154 return engine.registerTask(new FileInputNodeTask(*node));
1155 },
1156 /*IsValid=*/ [node](BuildEngine& engine, const Rule& rule,
1157 const ValueType& value) -> bool {
1158 return FileInputNodeTask::isResultValid(
1159 engine, *node, BuildValue::fromData(value));
1160 }
1161 };
1162 }
1163
1164 // Otherwise, create a task for a produced node.
1165 return Rule{
1166 keyData,
1167 /*Action=*/ [node](BuildEngine& engine) -> Task* {
1168 return engine.registerTask(new ProducedNodeTask(*node));
1169 },
1170 /*IsValid=*/ [node](BuildEngine& engine, const Rule& rule,
1171 const ValueType& value) -> bool {
1172 return ProducedNodeTask::isResultValid(
1173 engine, *node, BuildValue::fromData(value));
1174 }
1175 };
1176 }
1177
1178 case BuildKey::Kind::Target: {
1179 // Find the target.
1180 auto it = getBuildDescription().getTargets().find(key.getTargetName());
1181 if (it == getBuildDescription().getTargets().end()) {
1182 // FIXME: Invalid target name, produce an error.
1183 assert(0 && "FIXME: invalid target");
1184 abort();
1185 }
1186
1187 // Create the rule to construct this target.
1188 Target* target = it->second.get();
1189 return Rule{
1190 keyData,
1191 /*Action=*/ [target](BuildEngine& engine) -> Task* {
1192 return engine.registerTask(new TargetTask(*target));
1193 },
1194 /*IsValid=*/ [target](BuildEngine& engine, const Rule& rule,
1195 const ValueType& value) -> bool {
1196 return TargetTask::isResultValid(
1197 engine, *target, BuildValue::fromData(value));
1198 }
1199 };
1200 }
1201 }
1202
1203 assert(0 && "invalid key type");
1204 abort();
1205 }
1206
cycleDetected(const std::vector<Rule * > & cycle)1207 void BuildSystemEngineDelegate::cycleDetected(const std::vector<Rule*>& cycle) {
1208 // Track that the build has been aborted.
1209 getBuildSystem().setBuildWasAborted(true);
1210 static_cast<BuildSystemFrontendDelegate*>(&getBuildSystem().getDelegate())->cycleDetected(cycle);
1211 }
1212
error(const Twine & message)1213 void BuildSystemEngineDelegate::error(const Twine& message) {
1214 system.error(system.getMainFilename(), message);
1215 }
1216
1217 #pragma mark - BuildSystemImpl implementation
1218
1219 std::unique_ptr<BuildNode>
lookupNode(StringRef name,bool isImplicit)1220 BuildSystemImpl::lookupNode(StringRef name, bool isImplicit) {
1221 bool isDirectory = name.endswith("/");
1222 bool isVirtual = !name.empty() && name[0] == '<' && name.back() == '>';
1223 return llvm::make_unique<BuildNode>(name, isDirectory, isVirtual,
1224 /*isCommandTimestamp=*/false,
1225 /*isMutable=*/false);
1226 }
1227
build(BuildKey key)1228 llvm::Optional<BuildValue> BuildSystemImpl::build(BuildKey key) {
1229
1230 // Aquire lock and create execution queue.
1231 {
1232 std::lock_guard<std::mutex> guard(executionQueueMutex);
1233
1234 // If we were cancelled, return.
1235 if (isCancelled()) {
1236 return None;
1237 }
1238
1239 executionQueue = delegate.createExecutionQueue();
1240 }
1241
1242 // Build the target.
1243 buildWasAborted = false;
1244 auto result = getBuildEngine().build(key.toData());
1245
1246 // Release the execution queue, impicitly waiting for it to complete. The
1247 // asynchronous nature of the engine callbacks means it is possible for the
1248 // queue to have notified the engine of the last task completion, but still
1249 // have other work to perform (e.g., informing the client of command
1250 // completion).
1251 executionQueue.reset();
1252
1253 if (buildWasAborted)
1254 return None;
1255 return BuildValue::fromData(result);
1256 }
1257
build(StringRef target)1258 bool BuildSystemImpl::build(StringRef target) {
1259 // The build description must have been loaded.
1260 if (!buildDescription) {
1261 error(getMainFilename(), "no build description loaded");
1262 return false;
1263 }
1264
1265 // If target name is not passed then we try to load the default target name
1266 // from manifest file
1267 if (target.empty()) {
1268 target = getBuildDescription().getDefaultTarget();
1269 }
1270
1271 return build(BuildKey::makeTarget(target)).hasValue();
1272 }
1273
1274 #pragma mark - PhonyTool implementation
1275
1276 class PhonyCommand : public ExternalCommand {
1277 public:
1278 using ExternalCommand::ExternalCommand;
1279
shouldShowStatus()1280 virtual bool shouldShowStatus() override { return false; }
1281
getShortDescription(SmallVectorImpl<char> & result)1282 virtual void getShortDescription(SmallVectorImpl<char> &result) override {
1283 llvm::raw_svector_ostream(result) << getName();
1284 }
1285
getVerboseDescription(SmallVectorImpl<char> & result)1286 virtual void getVerboseDescription(SmallVectorImpl<char> &result) override {
1287 llvm::raw_svector_ostream(result) << getName();
1288 }
1289
executeExternalCommand(BuildSystemCommandInterface & bsci,Task * task,QueueJobContext * context)1290 virtual CommandResult executeExternalCommand(BuildSystemCommandInterface& bsci,
1291 Task* task,
1292 QueueJobContext* context) override {
1293 // Nothing needs to be done for phony commands.
1294 return CommandResult::Succeeded;
1295 }
1296
getResultForOutput(Node * node,const BuildValue & value)1297 virtual BuildValue getResultForOutput(Node* node, const BuildValue& value) override {
1298 // If the node is virtual, the output is always a virtual input value,
1299 // regardless of the actual build value.
1300 //
1301 // This is a special case for phony commands, to avoid them incorrectly
1302 // propagating failed/cancelled states onwards to downstream commands when
1303 // they are being used only for ordering purposes.
1304 auto buildNode = static_cast<BuildNode*>(node);
1305 if (buildNode->isVirtual() && !buildNode->isCommandTimestamp()) {
1306 return BuildValue::makeVirtualInput();
1307 }
1308
1309 // Otherwise, delegate to the inherited implementation.
1310 return ExternalCommand::getResultForOutput(node, value);
1311 }
1312 };
1313
1314 class PhonyTool : public Tool {
1315 public:
1316 using Tool::Tool;
1317
configureAttribute(const ConfigureContext & ctx,StringRef name,StringRef value)1318 virtual bool configureAttribute(const ConfigureContext& ctx, StringRef name,
1319 StringRef value) override {
1320 // No supported configuration attributes.
1321 ctx.error("unexpected attribute: '" + name + "'");
1322 return false;
1323 }
configureAttribute(const ConfigureContext & ctx,StringRef name,ArrayRef<StringRef> values)1324 virtual bool configureAttribute(const ConfigureContext& ctx, StringRef name,
1325 ArrayRef<StringRef> values) override {
1326 // No supported configuration attributes.
1327 ctx.error("unexpected attribute: '" + name + "'");
1328 return false;
1329 }
configureAttribute(const ConfigureContext & ctx,StringRef name,ArrayRef<std::pair<StringRef,StringRef>> values)1330 virtual bool configureAttribute(
1331 const ConfigureContext& ctx, StringRef name,
1332 ArrayRef<std::pair<StringRef, StringRef>> values) override {
1333 // No supported attributes.
1334 ctx.error("unexpected attribute: '" + name + "'");
1335 return false;
1336 }
1337
createCommand(StringRef name)1338 virtual std::unique_ptr<Command> createCommand(StringRef name) override {
1339 return llvm::make_unique<PhonyCommand>(name);
1340 }
1341 };
1342
1343 #pragma mark - ShellTool implementation
1344
1345 class ShellCommand : public ExternalCommand {
1346 /// The dependencies style to expect (in the `depsPath`).
1347 enum class DepsStyle {
1348 /// No discovered dependencies are in use.
1349 Unused = 0,
1350
1351 /// "Makefile" style dependencies in the form typically generated by C
1352 /// compilers, wherein the dependencies of the first target are treated as
1353 /// dependencies of the command.
1354 Makefile,
1355
1356 /// Darwin's DependencyInfo format.
1357 DependencyInfo,
1358 };
1359
1360 /// The command line arguments.
1361 std::vector<StringRef> args;
1362
1363 /// The environment to use. If empty, the environment will be inherited.
1364 SmallVector<std::pair<StringRef, StringRef>, 1> env;
1365
1366 /// The path to the dependency output file, if used.
1367 SmallVector<std::string, 1> depsPaths{};
1368
1369 /// The style of dependencies used.
1370 DepsStyle depsStyle = DepsStyle::Unused;
1371
1372 /// Whether to inherit the base environment.
1373 bool inheritEnv = true;
1374
1375 /// The cached signature, once computed -- 0 is used as a sentinel value.
1376 std::atomic<uint64_t> cachedSignature{ 0 };
1377
getSignature()1378 virtual uint64_t getSignature() override {
1379 uint64_t signature = cachedSignature;
1380 if (signature != 0)
1381 return signature;
1382
1383 // FIXME: Use a more appropriate hashing infrastructure.
1384 using llvm::hash_combine;
1385 llvm::hash_code code = ExternalCommand::getSignature();
1386 for (const auto& arg: args) {
1387 code = hash_combine(code, arg);
1388 }
1389 for (const auto& entry: env) {
1390 code = hash_combine(code, entry.first);
1391 code = hash_combine(code, entry.second);
1392 }
1393 for (const auto& path: depsPaths) {
1394 code = hash_combine(code, path);
1395 }
1396 code = hash_combine(code, int(depsStyle));
1397 code = hash_combine(code, int(inheritEnv));
1398 signature = size_t(code);
1399 if (signature == 0) {
1400 signature = 1;
1401 }
1402 cachedSignature = signature;
1403 return signature;
1404 }
1405
processDiscoveredDependencies(BuildSystemCommandInterface & bsci,Task * task,QueueJobContext * context)1406 bool processDiscoveredDependencies(BuildSystemCommandInterface& bsci,
1407 Task* task,
1408 QueueJobContext* context) {
1409 // It is an error if the dependencies style is not specified.
1410 //
1411 // FIXME: Diagnose this sooner.
1412 if (depsStyle == DepsStyle::Unused) {
1413 getBuildSystem(bsci.getBuildEngine()).error(
1414 "", "missing required 'deps-style' specifier");
1415 return false;
1416 }
1417
1418 for (const auto& depsPath: depsPaths) {
1419 // Read the dependencies file.
1420 auto input = bsci.getDelegate().getFileSystem().getFileContents(depsPath);
1421 if (!input) {
1422 getBuildSystem(bsci.getBuildEngine()).error(
1423 depsPath, "unable to open dependencies file (" + depsPath + ")");
1424 return false;
1425 }
1426
1427 switch (depsStyle) {
1428 case DepsStyle::Unused:
1429 assert(0 && "unreachable");
1430 break;
1431
1432 case DepsStyle::Makefile:
1433 if (!processMakefileDiscoveredDependencies(
1434 bsci, task, context, depsPath, input.get()))
1435 return false;
1436 continue;
1437
1438 case DepsStyle::DependencyInfo:
1439 if (!processDependencyInfoDiscoveredDependencies(
1440 bsci, task, context, depsPath, input.get()))
1441 return false;
1442 continue;
1443 }
1444
1445 llvm::report_fatal_error("unknown dependencies style");
1446 }
1447
1448 return true;
1449 }
1450
processMakefileDiscoveredDependencies(BuildSystemCommandInterface & bsci,Task * task,QueueJobContext * context,StringRef depsPath,llvm::MemoryBuffer * input)1451 bool processMakefileDiscoveredDependencies(BuildSystemCommandInterface& bsci,
1452 Task* task,
1453 QueueJobContext* context,
1454 StringRef depsPath,
1455 llvm::MemoryBuffer* input) {
1456 // Parse the output.
1457 //
1458 // We just ignore the rule, and add any dependency that we encounter in the
1459 // file.
1460 struct DepsActions : public core::MakefileDepsParser::ParseActions {
1461 BuildSystemCommandInterface& bsci;
1462 Task* task;
1463 ShellCommand* command;
1464 StringRef depsPath;
1465 unsigned numErrors{0};
1466
1467 DepsActions(BuildSystemCommandInterface& bsci, Task* task,
1468 ShellCommand* command, StringRef depsPath)
1469 : bsci(bsci), task(task), command(command), depsPath(depsPath) {}
1470
1471 virtual void error(const char* message, uint64_t position) override {
1472 getBuildSystem(bsci.getBuildEngine()).error(
1473 depsPath, "error reading dependency file: " + std::string(message));
1474 ++numErrors;
1475 }
1476
1477 virtual void actOnRuleDependency(const char* dependency,
1478 uint64_t length,
1479 const StringRef unescapedWord) override {
1480 bsci.taskDiscoveredDependency(task, BuildKey::makeNode(unescapedWord));
1481 }
1482
1483 virtual void actOnRuleStart(const char* name, uint64_t length,
1484 const StringRef unescapedWord) override {}
1485
1486 virtual void actOnRuleEnd() override {}
1487 };
1488
1489 DepsActions actions(bsci, task, this, depsPath);
1490 core::MakefileDepsParser(input->getBufferStart(), input->getBufferSize(),
1491 actions).parse();
1492 return actions.numErrors == 0;
1493 }
1494
1495 bool
processDependencyInfoDiscoveredDependencies(BuildSystemCommandInterface & bsci,Task * task,QueueJobContext * context,StringRef depsPath,llvm::MemoryBuffer * input)1496 processDependencyInfoDiscoveredDependencies(BuildSystemCommandInterface& bsci,
1497 Task* task,
1498 QueueJobContext* context,
1499 StringRef depsPath,
1500 llvm::MemoryBuffer* input) {
1501 // Parse the output.
1502 //
1503 // We just ignore the rule, and add any dependency that we encounter in the
1504 // file.
1505 struct DepsActions : public core::DependencyInfoParser::ParseActions {
1506 BuildSystemCommandInterface& bsci;
1507 Task* task;
1508 ShellCommand* command;
1509 StringRef depsPath;
1510 unsigned numErrors{0};
1511
1512 DepsActions(BuildSystemCommandInterface& bsci, Task* task,
1513 ShellCommand* command, StringRef depsPath)
1514 : bsci(bsci), task(task), command(command), depsPath(depsPath) {}
1515
1516 virtual void error(const char* message, uint64_t position) override {
1517 getBuildSystem(bsci.getBuildEngine()).error(
1518 depsPath, "error reading dependency file: " + std::string(message));
1519 ++numErrors;
1520 }
1521
1522 // Ignore everything but actual inputs.
1523 virtual void actOnVersion(StringRef) override { }
1524 virtual void actOnMissing(StringRef) override { }
1525 virtual void actOnOutput(StringRef) override { }
1526
1527 virtual void actOnInput(StringRef name) override {
1528 bsci.taskDiscoveredDependency(task, BuildKey::makeNode(name));
1529 }
1530 };
1531
1532 DepsActions actions(bsci, task, this, depsPath);
1533 core::DependencyInfoParser(input->getBuffer(), actions).parse();
1534 return actions.numErrors == 0;
1535 }
1536
1537 public:
1538 using ExternalCommand::ExternalCommand;
1539
getShortDescription(SmallVectorImpl<char> & result)1540 virtual void getShortDescription(SmallVectorImpl<char> &result) override {
1541 llvm::raw_svector_ostream(result) << getDescription();
1542 }
1543
getVerboseDescription(SmallVectorImpl<char> & result)1544 virtual void getVerboseDescription(SmallVectorImpl<char> &result) override {
1545 llvm::raw_svector_ostream os(result);
1546 bool first = true;
1547 for (const auto& arg: args) {
1548 if (!first) os << " ";
1549 first = false;
1550 basic::appendShellEscapedString(os, arg);
1551 }
1552 }
1553
configureAttribute(const ConfigureContext & ctx,StringRef name,StringRef value)1554 virtual bool configureAttribute(const ConfigureContext& ctx, StringRef name,
1555 StringRef value) override {
1556 if (name == "args") {
1557 // When provided as a scalar string, we default to executing using the
1558 // shell.
1559 args.clear();
1560 args.push_back(ctx.getDelegate().getInternedString("/bin/sh"));
1561 args.push_back(ctx.getDelegate().getInternedString("-c"));
1562 args.push_back(ctx.getDelegate().getInternedString(value));
1563 } else if (name == "deps") {
1564 depsPaths.clear();
1565 depsPaths.emplace_back(value);
1566 } else if (name == "deps-style") {
1567 if (value == "makefile") {
1568 depsStyle = DepsStyle::Makefile;
1569 } else if (value == "dependency-info") {
1570 depsStyle = DepsStyle::DependencyInfo;
1571 } else {
1572 ctx.error("unknown 'deps-style': '" + value + "'");
1573 return false;
1574 }
1575 return true;
1576 } else if (name == "inherit-env") {
1577 if (value != "true" && value != "false") {
1578 ctx.error("invalid value: '" + value + "' for attribute '" +
1579 name + "'");
1580 return false;
1581 }
1582 inheritEnv = value == "true";
1583 } else {
1584 return ExternalCommand::configureAttribute(ctx, name, value);
1585 }
1586
1587 return true;
1588 }
1589
configureAttribute(const ConfigureContext & ctx,StringRef name,ArrayRef<StringRef> values)1590 virtual bool configureAttribute(const ConfigureContext& ctx, StringRef name,
1591 ArrayRef<StringRef> values) override {
1592 if (name == "args") {
1593 // Diagnose missing arguments.
1594 if (values.empty()) {
1595 ctx.error("invalid arguments for command '" + getName() + "'");
1596 return false;
1597 }
1598
1599 args.clear();
1600 args.reserve(values.size());
1601 for (auto arg: values) {
1602 args.emplace_back(ctx.getDelegate().getInternedString(arg));
1603 }
1604 } else if (name == "deps") {
1605 depsPaths.clear();
1606 depsPaths.insert(depsPaths.begin(), values.begin(), values.end());
1607 } else {
1608 return ExternalCommand::configureAttribute(ctx, name, values);
1609 }
1610
1611 return true;
1612 }
1613
configureAttribute(const ConfigureContext & ctx,StringRef name,ArrayRef<std::pair<StringRef,StringRef>> values)1614 virtual bool configureAttribute(
1615 const ConfigureContext& ctx, StringRef name,
1616 ArrayRef<std::pair<StringRef, StringRef>> values) override {
1617 if (name == "env") {
1618 env.clear();
1619 env.reserve(values.size());
1620 for (const auto& entry: values) {
1621 env.emplace_back(
1622 std::make_pair(
1623 ctx.getDelegate().getInternedString(entry.first),
1624 ctx.getDelegate().getInternedString(entry.second)));
1625 }
1626 } else {
1627 return ExternalCommand::configureAttribute(ctx, name, values);
1628 }
1629
1630 return true;
1631 }
1632
executeExternalCommand(BuildSystemCommandInterface & bsci,Task * task,QueueJobContext * context)1633 virtual CommandResult executeExternalCommand(BuildSystemCommandInterface& bsci,
1634 Task* task,
1635 QueueJobContext* context) override {
1636 // Execute the command.
1637 auto result = bsci.getExecutionQueue().executeProcess(
1638 context, args,
1639 env, /*inheritEnvironment=*/inheritEnv);
1640
1641 if (result != CommandResult::Succeeded) {
1642 // If the command failed, there is no need to gather dependencies.
1643 return result;
1644 }
1645
1646 // Collect the discovered dependencies, if used.
1647 if (!depsPaths.empty()) {
1648 if (!processDiscoveredDependencies(bsci, task, context)) {
1649 // If we were unable to process the dependencies output, report a
1650 // failure.
1651 return CommandResult::Failed;
1652 }
1653 }
1654
1655 return result;
1656 }
1657 };
1658
1659 class ShellTool : public Tool {
1660 public:
1661 using Tool::Tool;
1662
configureAttribute(const ConfigureContext & ctx,StringRef name,StringRef value)1663 virtual bool configureAttribute(const ConfigureContext& ctx, StringRef name,
1664 StringRef value) override {
1665 // No supported attributes.
1666 ctx.error("unexpected attribute: '" + name + "'");
1667 return false;
1668 }
configureAttribute(const ConfigureContext & ctx,StringRef name,ArrayRef<StringRef> values)1669 virtual bool configureAttribute(const ConfigureContext& ctx, StringRef name,
1670 ArrayRef<StringRef> values) override {
1671 // No supported attributes.
1672 ctx.error("unexpected attribute: '" + name + "'");
1673 return false;
1674 }
configureAttribute(const ConfigureContext & ctx,StringRef name,ArrayRef<std::pair<StringRef,StringRef>> values)1675 virtual bool configureAttribute(
1676 const ConfigureContext& ctx, StringRef name,
1677 ArrayRef<std::pair<StringRef, StringRef>> values) override {
1678 // No supported attributes.
1679 ctx.error("unexpected attribute: '" + name + "'");
1680 return false;
1681 }
1682
createCommand(StringRef name)1683 virtual std::unique_ptr<Command> createCommand(StringRef name) override {
1684 return llvm::make_unique<ShellCommand>(name);
1685 }
1686 };
1687
1688 #pragma mark - ClangTool implementation
1689
1690 class ClangShellCommand : public ExternalCommand {
1691 /// The compiler command to invoke.
1692 std::vector<StringRef> args;
1693
1694 /// The path to the dependency output file, if used.
1695 std::string depsPath;
1696
getSignature()1697 virtual uint64_t getSignature() override {
1698 using llvm::hash_combine;
1699 llvm::hash_code code = ExternalCommand::getSignature();
1700 for (const auto& arg: args) {
1701 code = hash_combine(code, arg);
1702 }
1703 return size_t(code);
1704 }
1705
processDiscoveredDependencies(BuildSystemCommandInterface & bsci,Task * task,QueueJobContext * context)1706 bool processDiscoveredDependencies(BuildSystemCommandInterface& bsci,
1707 Task* task,
1708 QueueJobContext* context) {
1709 // Read the dependencies file.
1710 auto input = bsci.getDelegate().getFileSystem().getFileContents(depsPath);
1711 if (!input) {
1712 getBuildSystem(bsci.getBuildEngine()).error(
1713 depsPath, "unable to open dependencies file (" + depsPath + ")");
1714 return false;
1715 }
1716
1717 // Parse the output.
1718 //
1719 // We just ignore the rule, and add any dependency that we encounter in the
1720 // file.
1721 struct DepsActions : public core::MakefileDepsParser::ParseActions {
1722 BuildSystemCommandInterface& bsci;
1723 Task* task;
1724 ClangShellCommand* command;
1725 unsigned numErrors{0};
1726
1727 DepsActions(BuildSystemCommandInterface& bsci, Task* task,
1728 ClangShellCommand* command)
1729 : bsci(bsci), task(task), command(command) {}
1730
1731 virtual void error(const char* message, uint64_t position) override {
1732 getBuildSystem(bsci.getBuildEngine()).error(
1733 command->depsPath,
1734 "error reading dependency file: " + std::string(message));
1735 ++numErrors;
1736 }
1737
1738 virtual void actOnRuleDependency(const char* dependency,
1739 uint64_t length,
1740 const StringRef unescapedWord) override {
1741 bsci.taskDiscoveredDependency(task, BuildKey::makeNode(unescapedWord));
1742 }
1743
1744 virtual void actOnRuleStart(const char* name, uint64_t length,
1745 const StringRef unescapedWord) override {}
1746
1747 virtual void actOnRuleEnd() override {}
1748 };
1749
1750 DepsActions actions(bsci, task, this);
1751 core::MakefileDepsParser(input->getBufferStart(), input->getBufferSize(),
1752 actions).parse();
1753 return actions.numErrors == 0;
1754 }
1755
1756 public:
1757 using ExternalCommand::ExternalCommand;
1758
getShortDescription(SmallVectorImpl<char> & result)1759 virtual void getShortDescription(SmallVectorImpl<char> &result) override {
1760 llvm::raw_svector_ostream(result) << getDescription();
1761 }
1762
getVerboseDescription(SmallVectorImpl<char> & result)1763 virtual void getVerboseDescription(SmallVectorImpl<char> &result) override {
1764 llvm::raw_svector_ostream os(result);
1765 bool first = true;
1766 for (const auto& arg: args) {
1767 if (!first) os << " ";
1768 first = false;
1769 basic::appendShellEscapedString(os, arg);
1770 }
1771 }
1772
configureAttribute(const ConfigureContext & ctx,StringRef name,StringRef value)1773 virtual bool configureAttribute(const ConfigureContext& ctx, StringRef name,
1774 StringRef value) override {
1775 if (name == "args") {
1776 // When provided as a scalar string, we default to executing using the
1777 // shell.
1778 args.clear();
1779 args.push_back(ctx.getDelegate().getInternedString("/bin/sh"));
1780 args.push_back(ctx.getDelegate().getInternedString("-c"));
1781 args.push_back(ctx.getDelegate().getInternedString(value));
1782 } else if (name == "deps") {
1783 depsPath = value;
1784 } else {
1785 return ExternalCommand::configureAttribute(ctx, name, value);
1786 }
1787
1788 return true;
1789 }
configureAttribute(const ConfigureContext & ctx,StringRef name,ArrayRef<StringRef> values)1790 virtual bool configureAttribute(const ConfigureContext& ctx, StringRef name,
1791 ArrayRef<StringRef> values) override {
1792 if (name == "args") {
1793 args.clear();
1794 args.reserve(values.size());
1795 for (auto arg: values) {
1796 args.emplace_back(ctx.getDelegate().getInternedString(arg));
1797 }
1798 } else {
1799 return ExternalCommand::configureAttribute(ctx, name, values);
1800 }
1801
1802 return true;
1803 }
configureAttribute(const ConfigureContext & ctx,StringRef name,ArrayRef<std::pair<StringRef,StringRef>> values)1804 virtual bool configureAttribute(
1805 const ConfigureContext& ctx, StringRef name,
1806 ArrayRef<std::pair<StringRef, StringRef>> values) override {
1807 return ExternalCommand::configureAttribute(ctx, name, values);
1808 }
1809
executeExternalCommand(BuildSystemCommandInterface & bsci,Task * task,QueueJobContext * context)1810 virtual CommandResult executeExternalCommand(BuildSystemCommandInterface& bsci,
1811 Task* task,
1812 QueueJobContext* context) override {
1813 // Execute the command.
1814 auto result = bsci.getExecutionQueue().executeProcess(context, args);
1815
1816 if (result != CommandResult::Succeeded) {
1817 // If the command failed, there is no need to gather dependencies.
1818 return result;
1819 }
1820
1821 // Otherwise, collect the discovered dependencies, if used.
1822 if (!depsPath.empty()) {
1823 if (!processDiscoveredDependencies(bsci, task, context)) {
1824 // If we were unable to process the dependencies output, report a
1825 // failure.
1826 return CommandResult::Failed;
1827 }
1828 }
1829
1830 return result;
1831 }
1832 };
1833
1834 class ClangTool : public Tool {
1835 public:
1836 using Tool::Tool;
1837
configureAttribute(const ConfigureContext & ctx,StringRef name,StringRef value)1838 virtual bool configureAttribute(const ConfigureContext& ctx, StringRef name,
1839 StringRef value) override {
1840 // No supported attributes.
1841 ctx.error("unexpected attribute: '" + name + "'");
1842 return false;
1843 }
configureAttribute(const ConfigureContext & ctx,StringRef name,ArrayRef<StringRef> values)1844 virtual bool configureAttribute(const ConfigureContext& ctx, StringRef name,
1845 ArrayRef<StringRef> values) override {
1846 // No supported attributes.
1847 ctx.error("unexpected attribute: '" + name + "'");
1848 return false;
1849 }
configureAttribute(const ConfigureContext & ctx,StringRef name,ArrayRef<std::pair<StringRef,StringRef>> values)1850 virtual bool configureAttribute(
1851 const ConfigureContext& ctx, StringRef name,
1852 ArrayRef<std::pair<StringRef, StringRef>> values) override {
1853 // No supported attributes.
1854 ctx.error("unexpected attribute: '" + name + "'");
1855 return false;
1856 }
1857
createCommand(StringRef name)1858 virtual std::unique_ptr<Command> createCommand(StringRef name) override {
1859 return llvm::make_unique<ClangShellCommand>(name);
1860 }
1861 };
1862
1863 #pragma mark - MkdirTool implementation
1864
1865 class MkdirCommand : public ExternalCommand {
getShortDescription(SmallVectorImpl<char> & result)1866 virtual void getShortDescription(SmallVectorImpl<char> &result) override {
1867 llvm::raw_svector_ostream(result) << getDescription();
1868 }
1869
getVerboseDescription(SmallVectorImpl<char> & result)1870 virtual void getVerboseDescription(SmallVectorImpl<char> &result) override {
1871 llvm::raw_svector_ostream os(result);
1872 os << "mkdir -p ";
1873 // FIXME: This isn't correct, we need utilities for doing shell quoting.
1874 if (StringRef(getOutputs()[0]->getName()).find(' ') != StringRef::npos) {
1875 os << '"' << getOutputs()[0]->getName() << '"';
1876 } else {
1877 os << getOutputs()[0]->getName();
1878 }
1879 }
1880
isResultValid(BuildSystem & system,const BuildValue & value)1881 virtual bool isResultValid(BuildSystem& system,
1882 const BuildValue& value) override {
1883 // If the prior value wasn't for a successful command, recompute.
1884 if (!value.isSuccessfulCommand())
1885 return false;
1886
1887 // Otherwise, the result is valid if the directory still exists.
1888 auto info = getOutputs()[0]->getFileInfo(
1889 system.getDelegate().getFileSystem());
1890 if (info.isMissing())
1891 return false;
1892
1893 // If the item is not a directory, it needs to be recreated.
1894 if (!info.isDirectory())
1895 return false;
1896
1897 // FIXME: We should strictly enforce the integrity of this validity routine
1898 // by ensuring that the build result for this command does not fully encode
1899 // the file info, but rather just encodes its success. As is, we are leaking
1900 // out the details of the file info (like the timestamp), but not rerunning
1901 // when they change. This is by design for this command, but it would still
1902 // be nice to be strict about it.
1903
1904 return true;
1905 }
1906
executeExternalCommand(BuildSystemCommandInterface & bsci,Task * task,QueueJobContext * context)1907 virtual CommandResult executeExternalCommand(BuildSystemCommandInterface& bsci,
1908 Task* task,
1909 QueueJobContext* context) override {
1910 auto output = getOutputs()[0];
1911 if (!bsci.getDelegate().getFileSystem().createDirectories(
1912 output->getName())) {
1913 getBuildSystem(bsci.getBuildEngine()).error(
1914 "", "unable to create directory '" + output->getName() + "'");
1915 return CommandResult::Failed;
1916 }
1917 return CommandResult::Succeeded;
1918 }
1919
1920 public:
1921 using ExternalCommand::ExternalCommand;
1922 };
1923
1924 class MkdirTool : public Tool {
1925 public:
1926 using Tool::Tool;
1927
configureAttribute(const ConfigureContext & ctx,StringRef name,StringRef value)1928 virtual bool configureAttribute(const ConfigureContext& ctx, StringRef name,
1929 StringRef value) override {
1930 // No supported attributes.
1931 ctx.error("unexpected attribute: '" + name + "'");
1932 return false;
1933 }
configureAttribute(const ConfigureContext & ctx,StringRef name,ArrayRef<StringRef> values)1934 virtual bool configureAttribute(const ConfigureContext& ctx, StringRef name,
1935 ArrayRef<StringRef> values) override {
1936 // No supported attributes.
1937 ctx.error("unexpected attribute: '" + name + "'");
1938 return false;
1939 }
configureAttribute(const ConfigureContext & ctx,StringRef name,ArrayRef<std::pair<StringRef,StringRef>> values)1940 virtual bool configureAttribute(
1941 const ConfigureContext& ctx, StringRef name,
1942 ArrayRef<std::pair<StringRef, StringRef>> values) override {
1943 // No supported attributes.
1944 ctx.error("unexpected attribute: '" + name + "'");
1945 return false;
1946 }
1947
createCommand(StringRef name)1948 virtual std::unique_ptr<Command> createCommand(StringRef name) override {
1949 return llvm::make_unique<MkdirCommand>(name);
1950 }
1951 };
1952
1953 #pragma mark - SymlinkTool implementation
1954
1955 class SymlinkCommand : public Command {
1956 /// The declared output node.
1957 BuildNode* output = nullptr;
1958
1959 /// The path of the actual symbolic link to create, if different from the
1960 /// output node.
1961 std::string linkOutputPath;
1962
1963 /// The command description.
1964 std::string description;
1965
1966 /// Declared command inputs, used only for ordering purposes.
1967 std::vector<BuildNode*> inputs;
1968
1969 /// The contents to write at the output path.
1970 std::string contents;
1971
1972 /// Get the destination path.
getActualOutputPath() const1973 StringRef getActualOutputPath() const {
1974 return linkOutputPath.empty() ? output->getName() :
1975 StringRef(linkOutputPath);
1976 }
1977
getSignature()1978 virtual uint64_t getSignature() {
1979 using llvm::hash_combine;
1980 llvm::hash_code code = hash_value(output->getName());
1981 code = hash_combine(code, contents);
1982 for (const auto* input: inputs) {
1983 code = hash_combine(code, input->getName());
1984 }
1985 return size_t(code);
1986 }
1987
configureDescription(const ConfigureContext &,StringRef value)1988 virtual void configureDescription(const ConfigureContext&,
1989 StringRef value) override {
1990 description = value;
1991 }
1992
getShortDescription(SmallVectorImpl<char> & result)1993 virtual void getShortDescription(SmallVectorImpl<char> &result) override {
1994 llvm::raw_svector_ostream(result) << description;
1995 }
1996
getVerboseDescription(SmallVectorImpl<char> & result)1997 virtual void getVerboseDescription(SmallVectorImpl<char> &result) override {
1998 llvm::raw_svector_ostream os(result);
1999 os << "ln -sfh ";
2000 StringRef outputPath = getActualOutputPath();
2001 if (!output || !outputPath.empty()) {
2002 // FIXME: This isn't correct, we need utilities for doing shell quoting.
2003 if (outputPath.find(' ') != StringRef::npos) {
2004 os << '"' << outputPath << '"';
2005 } else {
2006 os << outputPath;
2007 }
2008 } else {
2009 os << "<<<missing output>>>";
2010 }
2011 os << ' ';
2012 // FIXME: This isn't correct, we need utilities for doing shell quoting.
2013 if (StringRef(contents).find(' ') != StringRef::npos) {
2014 os << '"' << contents << '"';
2015 } else {
2016 os << contents;
2017 }
2018 }
2019
configureInputs(const ConfigureContext & ctx,const std::vector<Node * > & value)2020 virtual void configureInputs(const ConfigureContext& ctx,
2021 const std::vector<Node*>& value) override {
2022 inputs.reserve(value.size());
2023 for (auto* node: value) {
2024 inputs.emplace_back(static_cast<BuildNode*>(node));
2025 }
2026 }
2027
configureOutputs(const ConfigureContext & ctx,const std::vector<Node * > & value)2028 virtual void configureOutputs(const ConfigureContext& ctx,
2029 const std::vector<Node*>& value) override {
2030 if (value.size() == 1) {
2031 output = static_cast<BuildNode*>(value[0]);
2032 } else if (value.empty()) {
2033 ctx.error("missing declared output");
2034 } else {
2035 ctx.error("unexpected explicit output: '" + value[1]->getName() + "'");
2036 }
2037 }
2038
configureAttribute(const ConfigureContext & ctx,StringRef name,StringRef value)2039 virtual bool configureAttribute(const ConfigureContext& ctx, StringRef name,
2040 StringRef value) override {
2041 if (name == "contents") {
2042 contents = value;
2043 return true;
2044 } else if (name == "link-output-path") {
2045 linkOutputPath = value;
2046 return true;
2047 } else {
2048 ctx.error("unexpected attribute: '" + name + "'");
2049 return false;
2050 }
2051 }
configureAttribute(const ConfigureContext & ctx,StringRef name,ArrayRef<StringRef> values)2052 virtual bool configureAttribute(const ConfigureContext& ctx, StringRef name,
2053 ArrayRef<StringRef> values) override {
2054 // No supported attributes.
2055 ctx.error("unexpected attribute: '" + name + "'");
2056 return false;
2057 }
configureAttribute(const ConfigureContext & ctx,StringRef name,ArrayRef<std::pair<StringRef,StringRef>> values)2058 virtual bool configureAttribute(
2059 const ConfigureContext& ctx, StringRef name,
2060 ArrayRef<std::pair<StringRef, StringRef>> values) override {
2061 // No supported attributes.
2062 ctx.error("unexpected attribute: '" + name + "'");
2063 return false;
2064 }
2065
getResultForOutput(Node * node,const BuildValue & value)2066 virtual BuildValue getResultForOutput(Node* node,
2067 const BuildValue& value) override {
2068 // If the value was a failed command, propagate the failure.
2069 if (value.isFailedCommand() || value.isPropagatedFailureCommand() ||
2070 value.isCancelledCommand())
2071 return BuildValue::makeFailedInput();
2072 if (value.isSkippedCommand())
2073 return BuildValue::makeSkippedCommand();
2074
2075 // Otherwise, we should have a successful command -- return the actual
2076 // result for the output.
2077 assert(value.isSuccessfulCommand());
2078
2079 auto info = value.getOutputInfo();
2080 if (info.isMissing())
2081 return BuildValue::makeMissingOutput();
2082 return BuildValue::makeExistingInput(info);
2083 }
2084
isResultValid(BuildSystem & system,const BuildValue & value)2085 virtual bool isResultValid(BuildSystem& system,
2086 const BuildValue& value) override {
2087 // It is an error if this command isn't configured properly.
2088 StringRef outputPath = getActualOutputPath();
2089 if (!output || outputPath.empty())
2090 return false;
2091
2092 // If the prior value wasn't for a successful command, recompute.
2093 if (!value.isSuccessfulCommand())
2094 return false;
2095
2096 // If the command's signature has changed since it was built, rebuild.
2097 if (value.getCommandSignature() != getSignature())
2098 return false;
2099
2100 // If the prior command doesn't look like one for a link, recompute.
2101 if (value.getNumOutputs() != 1)
2102 return false;
2103
2104 // Otherwise, assume the result is valid if its link status matches the
2105 // previous one.
2106 auto info = system.getDelegate().getFileSystem().getLinkInfo(outputPath);
2107 if (info.isMissing())
2108 return false;
2109
2110 return info == value.getOutputInfo();
2111 }
2112
start(BuildSystemCommandInterface & bsci,core::Task * task)2113 virtual void start(BuildSystemCommandInterface& bsci,
2114 core::Task* task) override {
2115 // The command itself takes no inputs, so just treat any declared inputs as
2116 // "must follow" directives.
2117 //
2118 // FIXME: We should make this explicit once we have actual support for must
2119 // follow inputs.
2120 for (auto it = inputs.begin(), ie = inputs.end(); it != ie; ++it) {
2121 bsci.taskMustFollow(task, BuildKey::makeNode(*it));
2122 }
2123 }
2124
providePriorValue(BuildSystemCommandInterface &,core::Task *,const BuildValue & value)2125 virtual void providePriorValue(BuildSystemCommandInterface&, core::Task*,
2126 const BuildValue& value) override {
2127 // Ignored.
2128 }
2129
provideValue(BuildSystemCommandInterface &,core::Task *,uintptr_t inputID,const BuildValue & value)2130 virtual void provideValue(BuildSystemCommandInterface&, core::Task*,
2131 uintptr_t inputID,
2132 const BuildValue& value) override {
2133 assert(0 && "unexpected API call");
2134 }
2135
execute(BuildSystemCommandInterface & bsci,core::Task * task,QueueJobContext * context)2136 virtual BuildValue execute(BuildSystemCommandInterface& bsci,
2137 core::Task* task,
2138 QueueJobContext* context) override {
2139 // It is an error if this command isn't configured properly.
2140 StringRef outputPath = getActualOutputPath();
2141 if (!output || outputPath.empty()) {
2142 return BuildValue::makeFailedCommand();
2143 }
2144
2145 // Create the directory containing the symlink, if necessary.
2146 //
2147 // FIXME: Shared behavior with ExternalCommand.
2148 {
2149 auto parent = llvm::sys::path::parent_path(outputPath);
2150 if (!parent.empty()) {
2151 (void) bsci.getDelegate().getFileSystem().createDirectories(parent);
2152 }
2153 }
2154
2155 // Create the symbolic link (note that despite the poorly chosen LLVM
2156 // name, this is a symlink).
2157 //
2158 // FIXME: Need to use the filesystem interfaces.
2159 bsci.getDelegate().commandStarted(this);
2160 auto success = true;
2161 if (llvm::sys::fs::create_link(contents, outputPath)) {
2162 // On failure, we attempt to unlink the file and retry.
2163 basic::sys::unlink(outputPath.str().c_str());
2164
2165 if (llvm::sys::fs::create_link(contents, outputPath)) {
2166 getBuildSystem(bsci.getBuildEngine()).error(
2167 "", "unable to create symlink at '" + outputPath + "'");
2168 success = false;
2169 }
2170 }
2171 bsci.getDelegate().commandFinished(this, success ? CommandResult::Succeeded : CommandResult::Failed);
2172
2173 // Process the result.
2174 if (!success) {
2175 return BuildValue::makeFailedCommand();
2176 }
2177
2178 // Capture the *link* information of the output.
2179 FileInfo outputInfo = bsci.getDelegate().getFileSystem().getLinkInfo(
2180 outputPath);
2181
2182 // Complete with a successful result.
2183 return BuildValue::makeSuccessfulCommand(outputInfo, getSignature());
2184 }
2185
2186 public:
2187 using Command::Command;
2188 };
2189
2190 class SymlinkTool : public Tool {
2191 public:
2192 using Tool::Tool;
2193
configureAttribute(const ConfigureContext & ctx,StringRef name,StringRef value)2194 virtual bool configureAttribute(const ConfigureContext& ctx, StringRef name,
2195 StringRef value) override {
2196 // No supported attributes.
2197 ctx.error("unexpected attribute: '" + name + "'");
2198 return false;
2199 }
configureAttribute(const ConfigureContext & ctx,StringRef name,ArrayRef<StringRef> values)2200 virtual bool configureAttribute(const ConfigureContext& ctx, StringRef name,
2201 ArrayRef<StringRef> values) override {
2202 // No supported attributes.
2203 ctx.error("unexpected attribute: '" + name + "'");
2204 return false;
2205 }
configureAttribute(const ConfigureContext & ctx,StringRef name,ArrayRef<std::pair<StringRef,StringRef>> values)2206 virtual bool configureAttribute(
2207 const ConfigureContext& ctx, StringRef name,
2208 ArrayRef<std::pair<StringRef, StringRef>> values) override {
2209 // No supported attributes.
2210 ctx.error("unexpected attribute: '" + name + "'");
2211 return false;
2212 }
2213
createCommand(StringRef name)2214 virtual std::unique_ptr<Command> createCommand(StringRef name) override {
2215 return llvm::make_unique<SymlinkCommand>(name);
2216 }
2217 };
2218
2219 #pragma mark - ArchiveTool implementation
2220
2221 class ArchiveShellCommand : public ExternalCommand {
2222
2223 std::string archiveName;
2224 std::vector<std::string> archiveInputs;
2225
executeExternalCommand(BuildSystemCommandInterface & bsci,Task * task,QueueJobContext * context)2226 virtual CommandResult executeExternalCommand(BuildSystemCommandInterface& bsci,
2227 Task* task,
2228 QueueJobContext* context) override {
2229 // First delete the current archive
2230 // TODO instead insert, update and remove files from the archive
2231 if (llvm::sys::fs::remove(archiveName, /*IgnoreNonExisting*/ true)) {
2232 return CommandResult::Failed;
2233 }
2234
2235 // Create archive
2236 auto args = getArgs();
2237 return bsci.getExecutionQueue().executeProcess(
2238 context, std::vector<StringRef>(args.begin(), args.end()));
2239 }
2240
getShortDescription(SmallVectorImpl<char> & result)2241 virtual void getShortDescription(SmallVectorImpl<char> &result) override {
2242 if (getDescription().empty()) {
2243 llvm::raw_svector_ostream(result) << "Archiving " + archiveName;
2244 } else {
2245 llvm::raw_svector_ostream(result) << getDescription();
2246 }
2247 }
2248
getVerboseDescription(SmallVectorImpl<char> & result)2249 virtual void getVerboseDescription(SmallVectorImpl<char> &result) override {
2250 llvm::raw_svector_ostream stream(result);
2251 bool first = true;
2252 for (const auto& arg: getArgs()) {
2253 stream << arg;
2254 if (!first) {
2255 stream << " ";
2256 first = false;
2257 }
2258 }
2259 }
2260
configureInputs(const ConfigureContext & ctx,const std::vector<Node * > & value)2261 virtual void configureInputs(const ConfigureContext& ctx,
2262 const std::vector<Node*>& value) override {
2263 ExternalCommand::configureInputs(ctx, value);
2264
2265 for (const auto& input: getInputs()) {
2266 if (!input->isVirtual()) {
2267 archiveInputs.push_back(input->getName());
2268 }
2269 }
2270 if (archiveInputs.empty()) {
2271 ctx.error("missing expected input");
2272 }
2273 }
2274
configureOutputs(const ConfigureContext & ctx,const std::vector<Node * > & value)2275 virtual void configureOutputs(const ConfigureContext& ctx,
2276 const std::vector<Node*>& value) override {
2277 ExternalCommand::configureOutputs(ctx, value);
2278
2279 for (const auto& output: getOutputs()) {
2280 if (!output->isVirtual()) {
2281 if (archiveName.empty()) {
2282 archiveName = output->getName();
2283 } else {
2284 ctx.error("unexpected explicit output: " + output->getName());
2285 }
2286 }
2287 }
2288 if (archiveName.empty()) {
2289 ctx.error("missing expected output");
2290 }
2291 }
2292
getArgs()2293 std::vector<std::string> getArgs() {
2294 std::vector<std::string> args;
2295 args.push_back("ar");
2296 args.push_back("cr");
2297 args.push_back(archiveName);
2298 args.insert(args.end(), archiveInputs.begin(), archiveInputs.end());
2299 return args;
2300 }
2301
2302 public:
2303 using ExternalCommand::ExternalCommand;
2304 };
2305
2306 class ArchiveTool : public Tool {
2307 public:
2308 using Tool::Tool;
2309
configureAttribute(const ConfigureContext & ctx,StringRef name,StringRef value)2310 virtual bool configureAttribute(const ConfigureContext& ctx, StringRef name,
2311 StringRef value) override {
2312 // No supported attributes.
2313 ctx.error("unexpected attribute: '" + name + "'");
2314 return false;
2315 }
configureAttribute(const ConfigureContext & ctx,StringRef name,ArrayRef<StringRef> values)2316 virtual bool configureAttribute(const ConfigureContext& ctx, StringRef name,
2317 ArrayRef<StringRef> values) override {
2318 // No supported attributes.
2319 ctx.error("unexpected attribute: '" + name + "'");
2320 return false;
2321 }
configureAttribute(const ConfigureContext & ctx,StringRef name,ArrayRef<std::pair<StringRef,StringRef>> values)2322 virtual bool configureAttribute(
2323 const ConfigureContext& ctx, StringRef name,
2324 ArrayRef<std::pair<StringRef, StringRef>> values) override {
2325 // No supported attributes.
2326 ctx.error("unexpected attribute: '" + name + "'");
2327 return false;
2328 }
2329
createCommand(StringRef name)2330 virtual std::unique_ptr<Command> createCommand(StringRef name) override {
2331 return llvm::make_unique<ArchiveShellCommand>(name);
2332 }
2333 };
2334
2335 #pragma mark - StaleFileRemovalTool implementation
2336
2337 class StaleFileRemovalCommand : public Command {
2338 std::string description;
2339
2340 std::vector<std::string> expectedOutputs;
2341 std::vector<std::string> filesToDelete;
2342 std::vector<std::string> roots;
2343 bool computedFilesToDelete = false;
2344
2345 BuildValue priorValue;
2346 bool hasPriorResult = false;
2347
2348 char path_separator = llvm::sys::path::get_separator()[0];
2349
configureDescription(const ConfigureContext &,StringRef value)2350 virtual void configureDescription(const ConfigureContext&, StringRef value) override {
2351 description = value;
2352 }
2353
getShortDescription(SmallVectorImpl<char> & result)2354 virtual void getShortDescription(SmallVectorImpl<char> &result) override {
2355 llvm::raw_svector_ostream(result) << (description.empty() ? "Stale file removal" : description);
2356 }
2357
getVerboseDescription(SmallVectorImpl<char> & result)2358 virtual void getVerboseDescription(SmallVectorImpl<char> &result) override {
2359 computeFilesToDelete();
2360
2361 getShortDescription(result);
2362 llvm::raw_svector_ostream(result) << ", stale files: [";
2363 for (auto fileToDelete : filesToDelete) {
2364 llvm::raw_svector_ostream(result) << fileToDelete;
2365 if (fileToDelete != *(--filesToDelete.end())) {
2366 llvm::raw_svector_ostream(result) << ", ";
2367 }
2368 }
2369 llvm::raw_svector_ostream(result) << "], roots: [";
2370 for (auto root : roots) {
2371 llvm::raw_svector_ostream(result) << root;
2372 if (root != *(--roots.end())) {
2373 llvm::raw_svector_ostream(result) << ", ";
2374 }
2375 }
2376 llvm::raw_svector_ostream(result) << "]";
2377 }
2378
configureInputs(const ConfigureContext & ctx,const std::vector<Node * > & value)2379 virtual void configureInputs(const ConfigureContext& ctx,
2380 const std::vector<Node*>& value) override {}
2381
configureOutputs(const ConfigureContext & ctx,const std::vector<Node * > & value)2382 virtual void configureOutputs(const ConfigureContext& ctx,
2383 const std::vector<Node*>& value) override {}
2384
configureAttribute(const ConfigureContext & ctx,StringRef name,StringRef value)2385 virtual bool configureAttribute(const ConfigureContext& ctx, StringRef name,
2386 StringRef value) override {
2387 // No supported attributes.
2388 ctx.error("unexpected attribute: '" + name + "'");
2389 return false;
2390 }
configureAttribute(const ConfigureContext & ctx,StringRef name,ArrayRef<StringRef> values)2391 virtual bool configureAttribute(const ConfigureContext& ctx, StringRef name,
2392 ArrayRef<StringRef> values) override {
2393 if (name == "expectedOutputs") {
2394 expectedOutputs.reserve(values.size());
2395 for (auto value : values) {
2396 expectedOutputs.emplace_back(value.str());
2397 }
2398 return true;
2399 } else if (name == "roots") {
2400 roots.reserve(values.size());
2401 for (auto value : values) {
2402 roots.emplace_back(value.str());
2403 }
2404 return true;
2405 }
2406
2407 ctx.error("unexpected attribute: '" + name + "'");
2408 return false;
2409 }
configureAttribute(const ConfigureContext & ctx,StringRef name,ArrayRef<std::pair<StringRef,StringRef>> values)2410 virtual bool configureAttribute(const ConfigureContext& ctx, StringRef name,
2411 ArrayRef<std::pair<StringRef, StringRef>> values) override {
2412 // No supported attributes.
2413 ctx.error("unexpected attribute: '" + name + "'");
2414 return false;
2415 }
2416
getResultForOutput(Node * node,const BuildValue & value)2417 virtual BuildValue getResultForOutput(Node* node,
2418 const BuildValue& value) override {
2419 // If the value was a failed command, propagate the failure.
2420 if (value.isFailedCommand() || value.isPropagatedFailureCommand() ||
2421 value.isCancelledCommand())
2422 return BuildValue::makeFailedInput();
2423 if (value.isSkippedCommand())
2424 return BuildValue::makeSkippedCommand();
2425
2426 // Otherwise, this was successful, return the value as-is.
2427 return BuildValue::fromData(value.toData());;
2428 }
2429
isResultValid(BuildSystem & system,const BuildValue & value)2430 virtual bool isResultValid(BuildSystem& system,
2431 const BuildValue& value) override {
2432 // Always re-run stale file removal.
2433 return false;
2434 }
2435
start(BuildSystemCommandInterface & bsci,core::Task * task)2436 virtual void start(BuildSystemCommandInterface& bsci,
2437 core::Task* task) override {}
2438
providePriorValue(BuildSystemCommandInterface &,core::Task *,const BuildValue & value)2439 virtual void providePriorValue(BuildSystemCommandInterface&, core::Task*,
2440 const BuildValue& value) override {
2441 hasPriorResult = true;
2442 priorValue = BuildValue::fromData(value.toData());
2443 }
2444
provideValue(BuildSystemCommandInterface &,core::Task *,uintptr_t inputID,const BuildValue & value)2445 virtual void provideValue(BuildSystemCommandInterface&, core::Task*,
2446 uintptr_t inputID,
2447 const BuildValue& value) override {
2448 assert(0 && "unexpected API call");
2449 }
2450
computeFilesToDelete()2451 void computeFilesToDelete() {
2452 if (computedFilesToDelete) {
2453 return;
2454 }
2455
2456 std::vector<StringRef> priorValueList = priorValue.getStaleFileList();
2457 std::set<std::string> priorNodes(priorValueList.begin(), priorValueList.end());
2458 std::set<std::string> expectedNodes(expectedOutputs.begin(), expectedOutputs.end());
2459
2460 std::set_difference(priorNodes.begin(), priorNodes.end(),
2461 expectedNodes.begin(), expectedNodes.end(),
2462 std::back_inserter(filesToDelete));
2463
2464 computedFilesToDelete = true;
2465 }
2466
execute(BuildSystemCommandInterface & bsci,core::Task * task,QueueJobContext * context)2467 virtual BuildValue execute(BuildSystemCommandInterface& bsci,
2468 core::Task* task,
2469 QueueJobContext* context) override {
2470 // Nothing to do if we do not have a prior result.
2471 if (!hasPriorResult || !priorValue.isStaleFileRemoval()) {
2472 bsci.getDelegate().commandStarted(this);
2473 bsci.getDelegate().commandFinished(this, CommandResult::Succeeded);
2474 return BuildValue::makeStaleFileRemoval(expectedOutputs);
2475 }
2476
2477 computeFilesToDelete();
2478
2479 bsci.getDelegate().commandStarted(this);
2480
2481 for (auto fileToDelete : filesToDelete) {
2482 // If no root paths are specified, any path is valid.
2483 bool isLocatedUnderRootPath = roots.size() == 0 ? true : false;
2484
2485 // If root paths are defined, stale file paths should be absolute.
2486 if (roots.size() > 0 && fileToDelete[0] != path_separator) {
2487 bsci.getDelegate().commandHadWarning(this, "Stale file '" + fileToDelete + "' has a relative path. This is invalid in combination with the root path attribute.\n");
2488 continue;
2489 }
2490
2491 // Check if the file is located under one of the allowed root paths.
2492 for (auto root : roots) {
2493 if (pathIsPrefixedByPath(fileToDelete, root)) {
2494 isLocatedUnderRootPath = true;
2495 }
2496 }
2497
2498 if (!isLocatedUnderRootPath) {
2499 bsci.getDelegate().commandHadWarning(this, "Stale file '" + fileToDelete + "' is located outside of the allowed root paths.\n");
2500 continue;
2501 }
2502
2503 if (getBuildSystem(bsci.getBuildEngine()).getDelegate().getFileSystem().remove(fileToDelete)) {
2504 bsci.getDelegate().commandHadNote(this, "Removed stale file '" + fileToDelete + "'\n");
2505 } else {
2506 bsci.getDelegate().commandHadWarning(this, "cannot remove stale file '" + fileToDelete + "': " + strerror(errno) + "\n");
2507 }
2508 }
2509
2510 bsci.getDelegate().commandFinished(this, CommandResult::Succeeded);
2511
2512 // Complete with a successful result.
2513 return BuildValue::makeStaleFileRemoval(expectedOutputs);
2514 }
2515
2516 public:
StaleFileRemovalCommand(const StringRef name)2517 StaleFileRemovalCommand(const StringRef name)
2518 : Command(name), priorValue(BuildValue::makeInvalid()) {}
2519 };
2520
2521 class StaleFileRemovalTool : public Tool {
2522 public:
2523 using Tool::Tool;
2524
configureAttribute(const ConfigureContext & ctx,StringRef name,StringRef value)2525 virtual bool configureAttribute(const ConfigureContext& ctx, StringRef name,
2526 StringRef value) override {
2527 // No supported attributes.
2528 ctx.error("unexpected attribute: '" + name + "'");
2529 return false;
2530 }
configureAttribute(const ConfigureContext & ctx,StringRef name,ArrayRef<StringRef> values)2531 virtual bool configureAttribute(const ConfigureContext& ctx, StringRef name,
2532 ArrayRef<StringRef> values) override {
2533 // No supported attributes.
2534 ctx.error("unexpected attribute: '" + name + "'");
2535 return false;
2536 }
configureAttribute(const ConfigureContext & ctx,StringRef name,ArrayRef<std::pair<StringRef,StringRef>> values)2537 virtual bool configureAttribute(
2538 const ConfigureContext& ctx, StringRef name,
2539 ArrayRef<std::pair<StringRef, StringRef>> values) override {
2540 // No supported attributes.
2541 ctx.error("unexpected attribute: '" + name + "'");
2542 return false;
2543 }
2544
createCommand(StringRef name)2545 virtual std::unique_ptr<Command> createCommand(StringRef name) override {
2546 return llvm::make_unique<StaleFileRemovalCommand>(name);
2547 }
2548 };
2549
2550 #pragma mark - BuildSystemFileDelegate
2551
getSystemDelegate()2552 BuildSystemDelegate& BuildSystemFileDelegate::getSystemDelegate() {
2553 return system.getDelegate();
2554 }
2555
setFileContentsBeingParsed(StringRef buffer)2556 void BuildSystemFileDelegate::setFileContentsBeingParsed(StringRef buffer) {
2557 getSystemDelegate().setFileContentsBeingParsed(buffer);
2558 }
2559
error(StringRef filename,const BuildFileToken & at,const Twine & message)2560 void BuildSystemFileDelegate::error(StringRef filename,
2561 const BuildFileToken& at,
2562 const Twine& message) {
2563 // Delegate to the system delegate.
2564 auto atSystemToken = BuildSystemDelegate::Token{at.start, at.length};
2565 system.error(filename, atSystemToken, message);
2566 }
2567
2568 bool
configureClient(const ConfigureContext &,StringRef name,uint32_t version,const property_list_type & properties)2569 BuildSystemFileDelegate::configureClient(const ConfigureContext&,
2570 StringRef name,
2571 uint32_t version,
2572 const property_list_type& properties) {
2573 // The client must match the configured name of the build system.
2574 if (name != getSystemDelegate().getName())
2575 return false;
2576
2577 // The client version must match the configured version.
2578 //
2579 // FIXME: We should give the client the opportunity to support a previous
2580 // schema version (auto-upgrade).
2581 if (version != getSystemDelegate().getVersion())
2582 return false;
2583
2584 return true;
2585 }
2586
2587 std::unique_ptr<Tool>
lookupTool(StringRef name)2588 BuildSystemFileDelegate::lookupTool(StringRef name) {
2589 // First, give the client an opportunity to create the tool.
2590 if (auto tool = getSystemDelegate().lookupTool(name)) {
2591 return tool;
2592 }
2593
2594 // Otherwise, look for one of the builtin tool definitions.
2595 if (name == "shell") {
2596 return llvm::make_unique<ShellTool>(name);
2597 } else if (name == "phony") {
2598 return llvm::make_unique<PhonyTool>(name);
2599 } else if (name == "clang") {
2600 return llvm::make_unique<ClangTool>(name);
2601 } else if (name == "mkdir") {
2602 return llvm::make_unique<MkdirTool>(name);
2603 } else if (name == "symlink") {
2604 return llvm::make_unique<SymlinkTool>(name);
2605 } else if (name == "archive") {
2606 return llvm::make_unique<ArchiveTool>(name);
2607 } else if (name == "stale-file-removal") {
2608 return llvm::make_unique<StaleFileRemovalTool>(name);
2609 }
2610
2611 return nullptr;
2612 }
2613
loadedTarget(StringRef name,const Target & target)2614 void BuildSystemFileDelegate::loadedTarget(StringRef name,
2615 const Target& target) {
2616 }
2617
loadedDefaultTarget(StringRef target)2618 void BuildSystemFileDelegate::loadedDefaultTarget(StringRef target) {
2619 }
2620
loadedCommand(StringRef name,const Command & command)2621 void BuildSystemFileDelegate::loadedCommand(StringRef name,
2622 const Command& command) {
2623 }
2624
2625 std::unique_ptr<Node>
lookupNode(StringRef name,bool isImplicit)2626 BuildSystemFileDelegate::lookupNode(StringRef name,
2627 bool isImplicit) {
2628 return system.lookupNode(name, isImplicit);
2629 }
2630
2631 }
2632
2633 #pragma mark - BuildSystem
2634
BuildSystem(BuildSystemDelegate & delegate)2635 BuildSystem::BuildSystem(BuildSystemDelegate& delegate)
2636 : impl(new BuildSystemImpl(*this, delegate))
2637 {
2638 }
2639
~BuildSystem()2640 BuildSystem::~BuildSystem() {
2641 delete static_cast<BuildSystemImpl*>(impl);
2642 }
2643
getDelegate()2644 BuildSystemDelegate& BuildSystem::getDelegate() {
2645 return static_cast<BuildSystemImpl*>(impl)->getDelegate();
2646 }
2647
loadDescription(StringRef mainFilename)2648 bool BuildSystem::loadDescription(StringRef mainFilename) {
2649 return static_cast<BuildSystemImpl*>(impl)->loadDescription(mainFilename);
2650 }
2651
loadDescription(std::unique_ptr<BuildDescription> description)2652 void BuildSystem::loadDescription(
2653 std::unique_ptr<BuildDescription> description) {
2654 return static_cast<BuildSystemImpl*>(impl)->loadDescription(
2655 std::move(description));
2656 }
2657
attachDB(StringRef path,std::string * error_out)2658 bool BuildSystem::attachDB(StringRef path,
2659 std::string* error_out) {
2660 return static_cast<BuildSystemImpl*>(impl)->attachDB(path, error_out);
2661 }
2662
enableTracing(StringRef path,std::string * error_out)2663 bool BuildSystem::enableTracing(StringRef path,
2664 std::string* error_out) {
2665 return static_cast<BuildSystemImpl*>(impl)->enableTracing(path, error_out);
2666 }
2667
build(BuildKey key)2668 llvm::Optional<BuildValue> BuildSystem::build(BuildKey key) {
2669 return static_cast<BuildSystemImpl*>(impl)->build(key);
2670 }
2671
build(StringRef name)2672 bool BuildSystem::build(StringRef name) {
2673 return static_cast<BuildSystemImpl*>(impl)->build(name);
2674 }
2675
cancel()2676 void BuildSystem::cancel() {
2677 if (impl) {
2678 static_cast<BuildSystemImpl*>(impl)->cancel();
2679 }
2680 }
2681
resetForBuild()2682 void BuildSystem::resetForBuild() {
2683 static_cast<BuildSystemImpl*>(impl)->resetForBuild();
2684 }
2685
2686 // This function checks if the given path is prefixed by another path.
pathIsPrefixedByPath(std::string path,std::string prefixPath)2687 bool llbuild::buildsystem::pathIsPrefixedByPath(std::string path, std::string prefixPath) {
2688 static char path_separator = llvm::sys::path::get_separator()[0];
2689 auto res = std::mismatch(prefixPath.begin(), prefixPath.end(), path.begin());
2690 // Check if `prefixPath` has been exhausted or just a separator remains.
2691 bool isPrefix = res.first == prefixPath.end() || (*(res.first++) == path_separator);
2692 // Check if `path` has been exhausted or just a separator remains.
2693 return isPrefix && (res.second == path.end() || (*(res.second++) == path_separator));
2694 }
2695