blob: 3af7939974fd4da29a7b7344218569c642ff2586 [file] [log] [blame]
//===-- BuildSystem.cpp ---------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#include "llbuild/BuildSystem/BuildSystem.h"
#include "llbuild/BuildSystem/BuildSystemCommandInterface.h"
#include "llbuild/BuildSystem/BuildSystemFrontend.h"
#include "llbuild/BuildSystem/CommandResult.h"
#include "llbuild/Basic/FileInfo.h"
#include "llbuild/Basic/FileSystem.h"
#include "llbuild/Basic/Hashing.h"
#include "llbuild/Basic/LLVM.h"
#include "llbuild/Basic/PlatformUtility.h"
#include "llbuild/Basic/ShellUtility.h"
#include "llbuild/Core/BuildDB.h"
#include "llbuild/Core/BuildEngine.h"
#include "llbuild/Core/DependencyInfoParser.h"
#include "llbuild/Core/MakefileDepsParser.h"
#include "llbuild/BuildSystem/BuildExecutionQueue.h"
#include "llbuild/BuildSystem/BuildFile.h"
#include "llbuild/BuildSystem/BuildKey.h"
#include "llbuild/BuildSystem/BuildNode.h"
#include "llbuild/BuildSystem/BuildValue.h"
#include "llbuild/BuildSystem/ExternalCommand.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
#include <memory>
#include <mutex>
#include <set>
#include <unistd.h>
using namespace llbuild;
using namespace llbuild::basic;
using namespace llbuild::core;
using namespace llbuild::buildsystem;
BuildSystemDelegate::~BuildSystemDelegate() {}
BuildSystemCommandInterface::~BuildSystemCommandInterface() {}
#pragma mark - BuildSystem implementation
namespace {
class BuildSystemImpl;
/// The delegate used to load the build file for use by a build system.
class BuildSystemFileDelegate : public BuildFileDelegate {
BuildSystemImpl& system;
/// FIXME: It would be nice to have a StringSet.
llvm::StringMap<bool> internedStrings;
public:
BuildSystemFileDelegate(BuildSystemImpl& system)
: BuildFileDelegate(), system(system) {}
BuildSystemDelegate& getSystemDelegate();
/// @name Delegate Implementation
/// @{
virtual StringRef getInternedString(StringRef value) override {
auto entry = internedStrings.insert(std::make_pair(value, true));
return entry.first->getKey();
}
virtual FileSystem& getFileSystem() override {
return getSystemDelegate().getFileSystem();
}
virtual void setFileContentsBeingParsed(StringRef buffer) override;
virtual void error(StringRef filename,
const BuildFileToken& at,
const Twine& message) override;
virtual bool configureClient(const ConfigureContext&, StringRef name,
uint32_t version,
const property_list_type& properties) override;
virtual std::unique_ptr<Tool> lookupTool(StringRef name) override;
virtual void loadedTarget(StringRef name,
const Target& target) override;
virtual void loadedDefaultTarget(StringRef target) override;
virtual void loadedCommand(StringRef name,
const Command& target) override;
virtual std::unique_ptr<Node> lookupNode(StringRef name,
bool isImplicit=false) override;
/// @}
};
/// The delegate used to build a loaded build file.
class BuildSystemEngineDelegate : public BuildEngineDelegate {
BuildSystemImpl& system;
// FIXME: This is an inefficent map, the string is duplicated.
std::unordered_map<std::string, std::unique_ptr<BuildNode>> dynamicNodes;
/// The custom tasks which are owned by the build system.
std::vector<std::unique_ptr<Command>> customTasks;
const BuildDescription& getBuildDescription() const;
virtual Rule lookupRule(const KeyType& keyData) override;
virtual void cycleDetected(const std::vector<Rule*>& items) override;
virtual void error(const Twine& message) override;
public:
BuildSystemEngineDelegate(BuildSystemImpl& system) : system(system) {}
BuildSystemImpl& getBuildSystem() {
return system;
}
};
class BuildSystemImpl : public BuildSystemCommandInterface {
/// The internal schema version.
///
/// Version History:
/// * 7: Added StaleFileRemoval to BuildValue
/// * 6: Added DirectoryContents to BuildKey
/// * 5: Switch BuildValue to be BinaryCoding based
/// * 4: Pre-history
static const uint32_t internalSchemaVersion = 7;
BuildSystem& buildSystem;
/// The delegate the BuildSystem was configured with.
BuildSystemDelegate& delegate;
/// The name of the main input file.
std::string mainFilename;
/// The delegate used for the loading the build file.
BuildSystemFileDelegate fileDelegate;
/// The build description, once loaded.
std::unique_ptr<BuildDescription> buildDescription;
/// The delegate used for building the file contents.
BuildSystemEngineDelegate engineDelegate;
/// The build engine.
BuildEngine buildEngine;
/// Mutex for access to execution queue.
std::mutex executionQueueMutex;
/// The execution queue reference; this is only valid while a build is
/// actually in progress.
std::unique_ptr<BuildExecutionQueue> executionQueue;
/// Flag indicating if the build has been aborted.
bool buildWasAborted = false;
/// Flag indicating if the build has been cancelled.
std::atomic<bool> isCancelled_{ false };
/// @name BuildSystemCommandInterface Implementation
/// @{
virtual BuildEngine& getBuildEngine() override {
return buildEngine;
}
virtual BuildExecutionQueue& getExecutionQueue() override {
assert(executionQueue.get());
return *executionQueue;
}
virtual void taskNeedsInput(core::Task* task, const BuildKey& key,
uintptr_t inputID) override {
return buildEngine.taskNeedsInput(task, key.toData(), inputID);
}
virtual void taskMustFollow(core::Task* task, const BuildKey& key) override {
return buildEngine.taskMustFollow(task, key.toData());
}
virtual void taskDiscoveredDependency(core::Task* task,
const BuildKey& key) override {
return buildEngine.taskDiscoveredDependency(task, key.toData());
}
virtual void taskIsComplete(core::Task* task, const BuildValue& value,
bool forceChange) override {
return buildEngine.taskIsComplete(task, value.toData(), forceChange);
}
virtual void addJob(QueueJob&& job) override {
executionQueue->addJob(std::move(job));
}
/// @}
public:
BuildSystemImpl(class BuildSystem& buildSystem,
BuildSystemDelegate& delegate)
: buildSystem(buildSystem), delegate(delegate),
fileDelegate(*this), engineDelegate(*this), buildEngine(engineDelegate),
executionQueue() {}
BuildSystem& getBuildSystem() {
return buildSystem;
}
BuildSystemDelegate& getDelegate() override {
return delegate;
}
// FIXME: We should eliminate this, it isn't well formed when loading
// descriptions not from a file. We currently only use that for unit testing,
// though.
StringRef getMainFilename() {
return mainFilename;
}
BuildSystemCommandInterface& getCommandInterface() {
return *this;
}
const BuildDescription& getBuildDescription() const {
assert(buildDescription);
return *buildDescription;
}
void error(StringRef filename, const Twine& message) {
getDelegate().error(filename, {}, message);
}
void error(StringRef filename, const BuildSystemDelegate::Token& at,
const Twine& message) {
getDelegate().error(filename, at, message);
}
std::unique_ptr<BuildNode> lookupNode(StringRef name,
bool isImplicit);
uint32_t getMergedSchemaVersion() {
// FIXME: Find a cleaner strategy for merging the internal schema version
// with that from the client.
auto clientVersion = delegate.getVersion();
assert(clientVersion <= (1 << 16) && "unsupported client verison");
return internalSchemaVersion + (clientVersion << 16);
}
/// @name Client API
/// @{
bool loadDescription(StringRef filename) {
this->mainFilename = filename;
auto description = BuildFile(filename, fileDelegate).load();
if (!description) {
error(getMainFilename(), "unable to load build file");
return false;
}
buildDescription = std::move(description);
return true;
}
void loadDescription(std::unique_ptr<BuildDescription> description) {
buildDescription = std::move(description);
}
bool attachDB(StringRef filename, std::string* error_out) {
// FIXME: How do we pass the client schema version here, if we haven't
// loaded the file yet.
std::unique_ptr<core::BuildDB> db(
core::createSQLiteBuildDB(filename, getMergedSchemaVersion(),
error_out));
if (!db)
return false;
return buildEngine.attachDB(std::move(db), error_out);
}
bool enableTracing(StringRef filename, std::string* error_out) {
return buildEngine.enableTracing(filename, error_out);
}
/// Build the given key, and return the result and an indication of success.
llvm::Optional<BuildValue> build(BuildKey key);
bool build(StringRef target);
void setBuildWasAborted(bool value) {
buildWasAborted = value;
}
void resetForBuild() {
std::lock_guard<std::mutex> guard(executionQueueMutex);
isCancelled_ = false;
}
/// Cancel the running build.
void cancel() {
std::lock_guard<std::mutex> guard(executionQueueMutex);
isCancelled_ = true;
// Cancel jobs if we actually have a queue.
if (executionQueue.get() != nullptr)
getExecutionQueue().cancelAllJobs();
}
/// Check if the build has been cancelled.
bool isCancelled() {
return isCancelled_;
}
/// @}
};
#pragma mark - BuildSystem engine integration
#pragma mark - Task implementations
static BuildSystemImpl& getBuildSystem(BuildEngine& engine) {
return static_cast<BuildSystemEngineDelegate*>(
engine.getDelegate())->getBuildSystem();
}
/// This is the task used to "build" a target, it translates between the request
/// for building a target key and the requests for all of its nodes.
class TargetTask : public Task {
Target& target;
// Build specific data.
//
// FIXME: We should probably factor this out somewhere else, so we can enforce
// it is never used when initialized incorrectly.
/// If true, the command had a missing input (this implies ShouldSkip is
/// true).
bool hasMissingInput = false;
virtual void start(BuildEngine& engine) override {
// Request all of the necessary system tasks.
unsigned id = 0;
for (auto it = target.getNodes().begin(),
ie = target.getNodes().end(); it != ie; ++it, ++id) {
engine.taskNeedsInput(this, BuildKey::makeNode(*it).toData(), id);
}
}
virtual void providePriorValue(BuildEngine&,
const ValueType& value) override {
// Do nothing.
}
virtual void provideValue(BuildEngine& engine, uintptr_t inputID,
const ValueType& valueData) override {
// Do nothing.
auto value = BuildValue::fromData(valueData);
if (value.isMissingInput()) {
hasMissingInput = true;
// FIXME: Design the logging and status output APIs.
auto& system = getBuildSystem(engine);
system.error(system.getMainFilename(),
(Twine("missing input '") +
target.getNodes()[inputID]->getName() +
"' and no rule to build it"));
}
}
virtual void inputsAvailable(BuildEngine& engine) override {
// If the build should cancel, do nothing.
if (getBuildSystem(engine).isCancelled()) {
engine.taskIsComplete(this, BuildValue::makeSkippedCommand().toData());
return;
}
if (hasMissingInput) {
// FIXME: Design the logging and status output APIs.
auto& system = getBuildSystem(engine);
system.error(system.getMainFilename(),
(Twine("cannot build target '") + target.getName() +
"' due to missing input"));
// Report the command failure.
system.getDelegate().hadCommandFailure();
}
// Complete the task immediately.
engine.taskIsComplete(this, BuildValue::makeTarget().toData());
}
public:
TargetTask(Target& target) : target(target) {}
static bool isResultValid(BuildEngine& engine, Target& node,
const BuildValue& value) {
// Always treat target tasks as invalid.
return false;
}
};
/// This is the task to "build" a file node which represents pure raw input to
/// the system.
class FileInputNodeTask : public Task {
BuildNode& node;
virtual void start(BuildEngine& engine) override {
assert(node.getProducers().empty());
}
virtual void providePriorValue(BuildEngine&,
const ValueType& value) override {
}
virtual void provideValue(BuildEngine&, uintptr_t inputID,
const ValueType& value) override {
}
virtual void inputsAvailable(BuildEngine& engine) override {
// FIXME: We should do this work in the background.
// Get the information on the file.
//
// FIXME: This needs to delegate, since we want to have a notion of
// different node types.
assert(!node.isVirtual());
auto info = node.getFileInfo(
getBuildSystem(engine).getDelegate().getFileSystem());
if (info.isMissing()) {
engine.taskIsComplete(this, BuildValue::makeMissingInput().toData());
return;
}
engine.taskIsComplete(
this, BuildValue::makeExistingInput(info).toData());
}
public:
FileInputNodeTask(BuildNode& node) : node(node) {
assert(!node.isVirtual());
}
static bool isResultValid(BuildEngine& engine, const BuildNode& node,
const BuildValue& value) {
// The result is valid if the existence matches the value type and the file
// information remains the same.
//
// FIXME: This is inefficient, we will end up doing the stat twice, once
// when we check the value for up to dateness, and once when we "build" the
// output.
//
// We can solve this by caching ourselves but I wonder if it is something
// the engine should support more naturally. In practice, this is unlikely
// to be very performance critical in practice because this is only
// redundant in the case where we have never built the node before (or need
// to rebuild it), and thus the additional stat is only one small part of
// the work we need to perform.
auto info = node.getFileInfo(
getBuildSystem(engine).getDelegate().getFileSystem());
if (info.isMissing()) {
return value.isMissingInput();
} else {
return value.isExistingInput() && value.getOutputInfo() == info;
}
}
};
/// This is the task to "build" a directory node.
///
/// This node effectively just adapts a directory tree signature to a node. The
/// reason why we need it (versus simply making the directory tree signature
/// *be* this, is that we want the directory signature to be able to interface
/// with other build nodes produced by commands).
class DirectoryInputNodeTask : public Task {
BuildNode& node;
core::ValueType directorySignature;
virtual void start(BuildEngine& engine) override {
// Remove any trailing slash from the node name.
StringRef path = node.getName();
if (path.endswith("/") && path != "/") {
path = path.substr(0, path.size() - 1);
}
engine.taskNeedsInput(
this, BuildKey::makeDirectoryTreeSignature(path).toData(),
/*inputID=*/0);
}
virtual void providePriorValue(BuildEngine&,
const ValueType& value) override {
}
virtual void provideValue(BuildEngine&, uintptr_t inputID,
const ValueType& value) override {
directorySignature = value;
}
virtual void inputsAvailable(BuildEngine& engine) override {
// Simply propagate the value.
engine.taskIsComplete(this, ValueType(directorySignature));
}
public:
DirectoryInputNodeTask(BuildNode& node) : node(node) {
assert(!node.isVirtual());
}
};
/// This is the task to build a virtual node which isn't connected to any
/// output.
class VirtualInputNodeTask : public Task {
virtual void start(BuildEngine& engine) override {
}
virtual void providePriorValue(BuildEngine&,
const ValueType& value) override {
}
virtual void provideValue(BuildEngine&, uintptr_t inputID,
const ValueType& value) override {
}
virtual void inputsAvailable(BuildEngine& engine) override {
engine.taskIsComplete(
this, BuildValue::makeVirtualInput().toData());
}
public:
VirtualInputNodeTask() {}
static bool isResultValid(BuildEngine& engine, const BuildNode& node,
const BuildValue& value) {
// Virtual input nodes are always valid unless the value type is wrong.
return value.isVirtualInput();
}
};
/// This is the task to "build" a node which is the product of some command.
///
/// It is responsible for selecting the appropriate producer command to run to
/// produce the node, and for synchronizing any external state the node depends
/// on.
class ProducedNodeTask : public Task {
Node& node;
BuildValue nodeResult;
Command* producingCommand = nullptr;
// Build specific data.
//
// FIXME: We should probably factor this out somewhere else, so we can enforce
// it is never used when initialized incorrectly.
// Whether this is a node we are unable to produce.
bool isInvalid = false;
virtual void start(BuildEngine& engine) override {
// Request the producer command.
if (node.getProducers().size() == 1) {
producingCommand = node.getProducers()[0];
engine.taskNeedsInput(this, BuildKey::makeCommand(
producingCommand->getName()).toData(),
/*InputID=*/0);
return;
}
// We currently do not support building nodes which have multiple producers.
auto producerA = node.getProducers()[0];
auto producerB = node.getProducers()[1];
getBuildSystem(engine).error(
"", "unable to build node: '" + node.getName() + "' (node is produced "
"by multiple commands; e.g., '" + producerA->getName() + "' and '" +
producerB->getName() + "')");
isInvalid = true;
}
virtual void providePriorValue(BuildEngine&,
const ValueType& value) override {
}
virtual void provideValue(BuildEngine&, uintptr_t inputID,
const ValueType& valueData) override {
auto value = BuildValue::fromData(valueData);
// Extract the node result from the command.
assert(producingCommand);
nodeResult = producingCommand->getResultForOutput(&node, value);
}
virtual void inputsAvailable(BuildEngine& engine) override {
if (isInvalid) {
getBuildSystem(engine).getDelegate().hadCommandFailure();
engine.taskIsComplete(this, BuildValue::makeFailedInput().toData());
return;
}
assert(!nodeResult.isInvalid());
// Complete the task immediately.
engine.taskIsComplete(this, nodeResult.toData());
}
public:
ProducedNodeTask(Node& node)
: node(node), nodeResult(BuildValue::makeInvalid()) {}
static bool isResultValid(BuildEngine&, Node& node,
const BuildValue& value) {
// If the result was failure, we always need to rebuild (it may produce an
// error).
if (value.isFailedInput())
return false;
// The produced node result itself doesn't need any synchronization.
return true;
}
};
/// This task is responsible for computing the lists of files in directories.
class DirectoryContentsTask : public Task {
std::string path;
/// The value for the input directory.
ValueType directoryValue;
virtual void start(BuildEngine& engine) override {
// Request the base directory node -- this task doesn't actually use the
// value, but this connects the task to its producer if present.
engine.taskNeedsInput(
this, BuildKey::makeNode(path).toData(), /*inputID=*/0);
}
virtual void providePriorValue(BuildEngine&,
const ValueType& value) override {
}
virtual void provideValue(BuildEngine&, uintptr_t inputID,
const ValueType& value) override {
if (inputID == 0) {
directoryValue = value;
return;
}
}
virtual void inputsAvailable(BuildEngine& engine) override {
// FIXME: We should do this work in the background.
// Get the stat information on the directory.
//
// FIXME: We should probably be using the directory value here, but that
// requires reworking some of the value encoding for the directory.
auto info = getBuildSystem(engine).getDelegate().getFileSystem().getFileInfo(
path);
if (info.isMissing()) {
engine.taskIsComplete(
this, BuildValue::makeMissingInput().toData());
return;
}
// Get the list of files in the directory.
std::error_code ec;
std::vector<std::string> filenames;
for (auto it = llvm::sys::fs::directory_iterator(path, ec),
end = llvm::sys::fs::directory_iterator(); it != end;
it = it.increment(ec)) {
filenames.push_back(llvm::sys::path::filename(it->path()));
}
// Order the filenames.
std::sort(filenames.begin(), filenames.end(),
[](const std::string& a, const std::string& b) {
return a < b;
});
// Create the result.
engine.taskIsComplete(
this, BuildValue::makeDirectoryContents(info, filenames).toData());
}
public:
DirectoryContentsTask(StringRef path) : path(path) {}
static bool isResultValid(BuildEngine& engine, StringRef path,
const BuildValue& value) {
// The result is valid if the existence matches the existing value type, and
// the file information remains the same.
auto info = getBuildSystem(engine).getDelegate().getFileSystem().getFileInfo(
path);
if (info.isMissing()) {
return value.isMissingInput();
} else {
return value.isDirectoryContents() && value.getOutputInfo() == info;
}
}
};
/// This is the task to "build" a directory node which will encapsulate (via a
/// signature) all of the contents of the directory, recursively.
class DirectoryTreeSignatureTask : public Task {
// The basic algorithm we need to follow:
//
// 1. Get the directory contents.
// 2. Get the subpath directory info.
// 3. For each node input, if it is a directory, get the input node for it.
//
// FIXME: This algorithm currently does a redundant stat for each directory,
// because we stat it once to find out it is a directory, then again when we
// gather its contents (to use for validating the directory contents).
//
// FIXME: We need to fix the directory list to not get contents for symbolic
// links.
/// This structure encapsulates the information we need on each child.
struct SubpathInfo {
/// The filename;
std::string filename;
/// The result of requesting the node at this subpath, once available.
ValueType value;
/// The directory signature, if needed.
llvm::Optional<ValueType> directorySignatureValue;
};
/// The path we are taking the signature of.
std::string path;
/// The value for the directory itself.
ValueType directoryValue;
/// The accumulated list of child input info.
///
/// Once we have the input directory information, we resize this to match the
/// number of children to avoid dynamically resizing it.
std::vector<SubpathInfo> childResults;
virtual void start(BuildEngine& engine) override {
// Ask for the base directory directory contents.
engine.taskNeedsInput(
this, BuildKey::makeDirectoryContents(path).toData(),
/*inputID=*/0);
}
virtual void providePriorValue(BuildEngine&,
const ValueType& value) override {
}
virtual void provideValue(BuildEngine& engine, uintptr_t inputID,
const ValueType& valueData) override {
// The first input is the directory contents.
if (inputID == 0) {
// Record the value for the directory.
directoryValue = valueData;
// Request the inputs for each subpath.
auto value = BuildValue::fromData(valueData);
if (value.isMissingInput())
return;
assert(value.isDirectoryContents());
auto filenames = value.getDirectoryContents();
for (size_t i = 0; i != filenames.size(); ++i) {
SmallString<256> childPath{ path };
llvm::sys::path::append(childPath, filenames[i]);
childResults.emplace_back(SubpathInfo{ filenames[i], {}, None });
engine.taskNeedsInput(this, BuildKey::makeNode(childPath).toData(),
/*inputID=*/1 + i);
}
return;
}
// If the input is a child, add it to the collection and dispatch a
// directory request if needed.
if (inputID >= 1 && inputID < 1 + childResults.size()) {
auto index = inputID - 1;
auto& childResult = childResults[index];
childResult.value = valueData;
// If this node is a directory, request its signature recursively.
auto value = BuildValue::fromData(valueData);
if (value.isExistingInput()) {
if (value.getOutputInfo().isDirectory()) {
SmallString<256> childPath{ path };
llvm::sys::path::append(childPath, childResult.filename);
engine.taskNeedsInput(
this, BuildKey::makeDirectoryTreeSignature(childPath).toData(),
/*inputID=*/1 + childResults.size() + index);
}
}
return;
}
// Otherwise, the input should be a directory signature.
auto index = inputID - 1 - childResults.size();
assert(index < childResults.size());
childResults[index].directorySignatureValue = valueData;
}
virtual void inputsAvailable(BuildEngine& engine) override {
// Compute the signature across all of the inputs.
using llvm::hash_combine;
llvm::hash_code code = hash_value(path);
// Add the signature for the actual input path.
code = hash_combine(
code, hash_combine_range(directoryValue.begin(), directoryValue.end()));
// For now, we represent this task as the aggregation of all the inputs.
for (const auto& info: childResults) {
// We merge the children by simply combining their encoded representation.
code = hash_combine(
code, hash_combine_range(info.value.begin(), info.value.end()));
if (info.directorySignatureValue.hasValue()) {
auto& data = info.directorySignatureValue.getValue();
code = hash_combine(
code, hash_combine_range(data.begin(), data.end()));
} else {
// Combine a random number to represent nil.
code = hash_combine(code, 0XC183979C3E98722E);
}
}
// Compute the signature.
engine.taskIsComplete(this, BuildValue::makeDirectoryTreeSignature(
uint64_t(code)).toData());
}
public:
DirectoryTreeSignatureTask(StringRef path) : path(path) {}
};
/// This is the task to actually execute a command.
class CommandTask : public Task {
Command& command;
virtual void start(BuildEngine& engine) override {
// Notify the client the command is preparing to run.
getBuildSystem(engine).getDelegate().commandPreparing(&command);
command.start(getBuildSystem(engine).getCommandInterface(), this);
}
virtual void providePriorValue(BuildEngine& engine,
const ValueType& valueData) override {
BuildValue value = BuildValue::fromData(valueData);
command.providePriorValue(
getBuildSystem(engine).getCommandInterface(), this, value);
}
virtual void provideValue(BuildEngine& engine, uintptr_t inputID,
const ValueType& valueData) override {
command.provideValue(
getBuildSystem(engine).getCommandInterface(), this, inputID,
BuildValue::fromData(valueData));
}
virtual void inputsAvailable(BuildEngine& engine) override {
auto& bsci = getBuildSystem(engine).getCommandInterface();
auto fn = [this, &bsci=bsci](QueueJobContext* context) {
// If the build should cancel, do nothing.
if (getBuildSystem(bsci.getBuildEngine()).isCancelled()) {
bsci.taskIsComplete(this, BuildValue::makeCancelledCommand());
return;
}
// Check if the command should be skipped.
if (!bsci.getDelegate().shouldCommandStart(&command)) {
// We need to call commandFinished here because commandPreparing and
// shouldCommandStart guarantee that they're followed by
// commandFinished.
bsci.getDelegate().commandFinished(&command, CommandResult::Skipped);
bsci.taskIsComplete(this, BuildValue::makeSkippedCommand());
return;
}
// Execute the command, with notifications to the delegate.
auto result = command.execute(bsci, this, context);
// Inform the engine of the result.
if (result.isFailedCommand()) {
bsci.getDelegate().hadCommandFailure();
}
bsci.taskIsComplete(this, std::move(result));
};
bsci.addJob({ &command, std::move(fn) });
}
public:
CommandTask(Command& command) : command(command) {}
static bool isResultValid(BuildEngine& engine, Command& command,
const BuildValue& value) {
// Delegate to the command for further checking.
return command.isResultValid(
getBuildSystem(engine).getBuildSystem(), value);
}
};
#pragma mark - BuildSystemEngineDelegate implementation
/// This is a synthesized task used to represent a missing command.
///
/// This command is used in cases where a command has been removed from the
/// manifest, but can still be found during an incremental rebuild. This command
/// is used to inject an invalid value thus forcing downstream clients to
/// rebuild.
class MissingCommandTask : public Task {
private:
virtual void start(BuildEngine& engine) override { }
virtual void providePriorValue(BuildEngine& engine,
const ValueType& valueData) override { }
virtual void provideValue(BuildEngine& engine, uintptr_t inputID,
const ValueType& valueData) override { }
virtual void inputsAvailable(BuildEngine& engine) override {
// A missing command always builds to an invalid value, and forces
// downstream clients to be rebuilt (at which point they will presumably see
// the command is no longer used).
return engine.taskIsComplete(this, BuildValue::makeInvalid().toData(),
/*forceChange=*/true);
}
public:
using Task::Task;
};
const BuildDescription& BuildSystemEngineDelegate::getBuildDescription() const {
return system.getBuildDescription();
}
static BuildSystemDelegate::CommandStatusKind
convertStatusKind(core::Rule::StatusKind kind) {
switch (kind) {
case core::Rule::StatusKind::IsScanning:
return BuildSystemDelegate::CommandStatusKind::IsScanning;
case core::Rule::StatusKind::IsUpToDate:
return BuildSystemDelegate::CommandStatusKind::IsUpToDate;
case core::Rule::StatusKind::IsComplete:
return BuildSystemDelegate::CommandStatusKind::IsComplete;
}
assert(0 && "unknown status kind");
return BuildSystemDelegate::CommandStatusKind::IsScanning;
}
Rule BuildSystemEngineDelegate::lookupRule(const KeyType& keyData) {
// Decode the key.
auto key = BuildKey::fromData(keyData);
switch (key.getKind()) {
case BuildKey::Kind::Unknown:
break;
case BuildKey::Kind::Command: {
// Find the comand.
auto it = getBuildDescription().getCommands().find(key.getCommandName());
if (it == getBuildDescription().getCommands().end()) {
// If there is no such command, produce an error task.
return Rule{
keyData,
/*Action=*/ [](BuildEngine& engine) -> Task* {
return engine.registerTask(new MissingCommandTask());
},
/*IsValid=*/ [](BuildEngine&, const Rule& rule,
const ValueType& value) -> bool {
// The cached result for a missing command is never valid.
return false;
}
};
}
// Create the rule for the command.
Command* command = it->second.get();
return Rule{
keyData,
/*Action=*/ [command](BuildEngine& engine) -> Task* {
return engine.registerTask(new CommandTask(*command));
},
/*IsValid=*/ [command](BuildEngine& engine, const Rule& rule,
const ValueType& value) -> bool {
return CommandTask::isResultValid(
engine, *command, BuildValue::fromData(value));
},
/*UpdateStatus=*/ [command](BuildEngine& engine,
core::Rule::StatusKind status) {
return ::getBuildSystem(engine).getDelegate().commandStatusChanged(
command, convertStatusKind(status));
}
};
}
case BuildKey::Kind::CustomTask: {
// Search for a tool which knows how to create the given custom task.
//
// FIXME: We should most likely have some kind of registration process so we
// can do an efficient query here, but exactly how this should look isn't
// clear yet.
for (const auto& it: getBuildDescription().getTools()) {
auto result = it.second->createCustomCommand(key);
if (!result) continue;
// Save the custom command.
customTasks.emplace_back(std::move(result));
Command *command = customTasks.back().get();
return Rule{
keyData,
/*Action=*/ [command](BuildEngine& engine) -> Task* {
return engine.registerTask(new CommandTask(*command));
},
/*IsValid=*/ [command](BuildEngine& engine, const Rule& rule,
const ValueType& value) -> bool {
return CommandTask::isResultValid(
engine, *command, BuildValue::fromData(value));
}
};
}
// We were unable to create an appropriate custom command, produce an error
// task.
return Rule{
keyData,
/*Action=*/ [](BuildEngine& engine) -> Task* {
return engine.registerTask(new MissingCommandTask());
},
/*IsValid=*/ [](BuildEngine&, const Rule& rule,
const ValueType& value) -> bool {
// The cached result for a missing command is never valid.
return false;
}
};
}
case BuildKey::Kind::DirectoryContents: {
std::string path = key.getDirectoryContentsPath();
return Rule{
keyData,
/*Action=*/ [path](BuildEngine& engine) -> Task* {
return engine.registerTask(new DirectoryContentsTask(path));
},
/*IsValid=*/ [path](BuildEngine& engine, const Rule& rule,
const ValueType& value) -> bool {
return DirectoryContentsTask::isResultValid(
engine, path, BuildValue::fromData(value));
}
};
}
case BuildKey::Kind::DirectoryTreeSignature: {
std::string path = key.getDirectoryTreeSignaturePath();
return Rule{
keyData,
/*Action=*/ [path](BuildEngine& engine) -> Task* {
return engine.registerTask(new DirectoryTreeSignatureTask(path));
},
// Directory signatures don't require any validation outside of their
// concrete dependencies.
/*IsValid=*/ nullptr
};
}
case BuildKey::Kind::Node: {
// Find the node.
auto it = getBuildDescription().getNodes().find(key.getNodeName());
BuildNode* node;
if (it != getBuildDescription().getNodes().end()) {
node = static_cast<BuildNode*>(it->second.get());
} else {
auto it = dynamicNodes.find(key.getNodeName());
if (it != dynamicNodes.end()) {
node = it->second.get();
} else {
// Create nodes on the fly for any unknown ones.
auto nodeOwner = system.lookupNode(
key.getNodeName(), /*isImplicit=*/true);
node = nodeOwner.get();
dynamicNodes[key.getNodeName()] = std::move(nodeOwner);
}
}
// Create the rule used to construct this node.
//
// We could bypass this level and directly return the rule to run the
// command, which would reduce the number of tasks in the system. For now we
// do the uniform thing, but do differentiate between input and command
// nodes.
// Create an input node if there are no producers.
if (node->getProducers().empty()) {
if (node->isVirtual()) {
return Rule{
keyData,
/*Action=*/ [node](BuildEngine& engine) -> Task* {
return engine.registerTask(new VirtualInputNodeTask());
},
/*IsValid=*/ [node](BuildEngine& engine, const Rule& rule,
const ValueType& value) -> bool {
return VirtualInputNodeTask::isResultValid(
engine, *node, BuildValue::fromData(value));
}
};
}
if (node->isDirectory()) {
return Rule{
keyData,
/*Action=*/ [node](BuildEngine& engine) -> Task* {
return engine.registerTask(new DirectoryInputNodeTask(*node));
},
// Directory nodes don't require any validation outside of their
// concrete dependencies.
/*IsValid=*/ nullptr
};
}
return Rule{
keyData,
/*Action=*/ [node](BuildEngine& engine) -> Task* {
return engine.registerTask(new FileInputNodeTask(*node));
},
/*IsValid=*/ [node](BuildEngine& engine, const Rule& rule,
const ValueType& value) -> bool {
return FileInputNodeTask::isResultValid(
engine, *node, BuildValue::fromData(value));
}
};
}
// Otherwise, create a task for a produced node.
return Rule{
keyData,
/*Action=*/ [node](BuildEngine& engine) -> Task* {
return engine.registerTask(new ProducedNodeTask(*node));
},
/*IsValid=*/ [node](BuildEngine& engine, const Rule& rule,
const ValueType& value) -> bool {
return ProducedNodeTask::isResultValid(
engine, *node, BuildValue::fromData(value));
}
};
}
case BuildKey::Kind::Target: {
// Find the target.
auto it = getBuildDescription().getTargets().find(key.getTargetName());
if (it == getBuildDescription().getTargets().end()) {
// FIXME: Invalid target name, produce an error.
assert(0 && "FIXME: invalid target");
abort();
}
// Create the rule to construct this target.
Target* target = it->second.get();
return Rule{
keyData,
/*Action=*/ [target](BuildEngine& engine) -> Task* {
return engine.registerTask(new TargetTask(*target));
},
/*IsValid=*/ [target](BuildEngine& engine, const Rule& rule,
const ValueType& value) -> bool {
return TargetTask::isResultValid(
engine, *target, BuildValue::fromData(value));
}
};
}
}
assert(0 && "invalid key type");
abort();
}
void BuildSystemEngineDelegate::cycleDetected(const std::vector<Rule*>& cycle) {
// Track that the build has been aborted.
getBuildSystem().setBuildWasAborted(true);
static_cast<BuildSystemFrontendDelegate*>(&getBuildSystem().getDelegate())->cycleDetected(cycle);
}
void BuildSystemEngineDelegate::error(const Twine& message) {
system.error(system.getMainFilename(), message);
}
#pragma mark - BuildSystemImpl implementation
std::unique_ptr<BuildNode>
BuildSystemImpl::lookupNode(StringRef name, bool isImplicit) {
bool isDirectory = name.endswith("/");
bool isVirtual = !name.empty() && name[0] == '<' && name.back() == '>';
return llvm::make_unique<BuildNode>(name, isDirectory, isVirtual,
/*isCommandTimestamp=*/false,
/*isMutable=*/false);
}
llvm::Optional<BuildValue> BuildSystemImpl::build(BuildKey key) {
// Aquire lock and create execution queue.
{
std::lock_guard<std::mutex> guard(executionQueueMutex);
// If we were cancelled, return.
if (isCancelled()) {
return None;
}
executionQueue = delegate.createExecutionQueue();
}
// Build the target.
buildWasAborted = false;
auto result = getBuildEngine().build(key.toData());
// Release the execution queue, impicitly waiting for it to complete. The
// asynchronous nature of the engine callbacks means it is possible for the
// queue to have notified the engine of the last task completion, but still
// have other work to perform (e.g., informing the client of command
// completion).
executionQueue.reset();
if (buildWasAborted)
return None;
return BuildValue::fromData(result);
}
bool BuildSystemImpl::build(StringRef target) {
// The build description must have been loaded.
if (!buildDescription) {
error(getMainFilename(), "no build description loaded");
return false;
}
// If target name is not passed then we try to load the default target name
// from manifest file
if (target.empty()) {
target = getBuildDescription().getDefaultTarget();
}
return build(BuildKey::makeTarget(target)).hasValue();
}
#pragma mark - PhonyTool implementation
class PhonyCommand : public ExternalCommand {
public:
using ExternalCommand::ExternalCommand;
virtual bool shouldShowStatus() override { return false; }
virtual void getShortDescription(SmallVectorImpl<char> &result) override {
llvm::raw_svector_ostream(result) << getName();
}
virtual void getVerboseDescription(SmallVectorImpl<char> &result) override {
llvm::raw_svector_ostream(result) << getName();
}
virtual CommandResult executeExternalCommand(BuildSystemCommandInterface& bsci,
Task* task,
QueueJobContext* context) override {
// Nothing needs to be done for phony commands.
return CommandResult::Succeeded;
}
};
class PhonyTool : public Tool {
public:
using Tool::Tool;
virtual bool configureAttribute(const ConfigureContext& ctx, StringRef name,
StringRef value) override {
// No supported configuration attributes.
ctx.error("unexpected attribute: '" + name + "'");
return false;
}
virtual bool configureAttribute(const ConfigureContext& ctx, StringRef name,
ArrayRef<StringRef> values) override {
// No supported configuration attributes.
ctx.error("unexpected attribute: '" + name + "'");
return false;
}
virtual bool configureAttribute(
const ConfigureContext& ctx, StringRef name,
ArrayRef<std::pair<StringRef, StringRef>> values) override {
// No supported attributes.
ctx.error("unexpected attribute: '" + name + "'");
return false;
}
virtual std::unique_ptr<Command> createCommand(StringRef name) override {
return llvm::make_unique<PhonyCommand>(name);
}
};
#pragma mark - ShellTool implementation
class ShellCommand : public ExternalCommand {
/// The dependencies style to expect (in the `depsPath`).
enum class DepsStyle {
/// No discovered dependencies are in use.
Unused = 0,
/// "Makefile" style dependencies in the form typically generated by C
/// compilers, wherein the dependencies of the first target are treated as
/// dependencies of the command.
Makefile,
/// Darwin's DependencyInfo format.
DependencyInfo,
};
/// The command line arguments.
std::vector<StringRef> args;
/// The environment to use. If empty, the environment will be inherited.
SmallVector<std::pair<StringRef, StringRef>, 1> env;
/// The path to the dependency output file, if used.
SmallVector<std::string, 1> depsPaths{};
/// The style of dependencies used.
DepsStyle depsStyle = DepsStyle::Unused;
/// Whether to inherit the base environment.
bool inheritEnv = true;
/// The cached signature, once computed -- 0 is used as a sentinel value.
std::atomic<uint64_t> cachedSignature{ 0 };
virtual uint64_t getSignature() override {
uint64_t signature = cachedSignature;
if (signature != 0)
return signature;
// FIXME: Use a more appropriate hashing infrastructure.
using llvm::hash_combine;
llvm::hash_code code = ExternalCommand::getSignature();
for (const auto& arg: args) {
code = hash_combine(code, arg);
}
for (const auto& entry: env) {
code = hash_combine(code, entry.first);
code = hash_combine(code, entry.second);
}
for (const auto& path: depsPaths) {
code = hash_combine(code, path);
}
code = hash_combine(code, int(depsStyle));
code = hash_combine(code, int(inheritEnv));
signature = size_t(code);
if (signature == 0) {
signature = 1;
}
cachedSignature = signature;
return signature;
}
bool processDiscoveredDependencies(BuildSystemCommandInterface& bsci,
Task* task,
QueueJobContext* context) {
// It is an error if the dependencies style is not specified.
//
// FIXME: Diagnose this sooner.
if (depsStyle == DepsStyle::Unused) {
getBuildSystem(bsci.getBuildEngine()).error(
"", "missing required 'deps-style' specifier");
return false;
}
for (const auto& depsPath: depsPaths) {
// Read the dependencies file.
auto input = bsci.getDelegate().getFileSystem().getFileContents(depsPath);
if (!input) {
getBuildSystem(bsci.getBuildEngine()).error(
depsPath, "unable to open dependencies file (" + depsPath + ")");
return false;
}
switch (depsStyle) {
case DepsStyle::Unused:
assert(0 && "unreachable");
break;
case DepsStyle::Makefile:
if (!processMakefileDiscoveredDependencies(
bsci, task, context, depsPath, input.get()))
return false;
continue;
case DepsStyle::DependencyInfo:
if (!processDependencyInfoDiscoveredDependencies(
bsci, task, context, depsPath, input.get()))
return false;
continue;
}
llvm::report_fatal_error("unknown dependencies style");
}
return true;
}
bool processMakefileDiscoveredDependencies(BuildSystemCommandInterface& bsci,
Task* task,
QueueJobContext* context,
StringRef depsPath,
llvm::MemoryBuffer* input) {
// Parse the output.
//
// We just ignore the rule, and add any dependency that we encounter in the
// file.
struct DepsActions : public core::MakefileDepsParser::ParseActions {
BuildSystemCommandInterface& bsci;
Task* task;
ShellCommand* command;
StringRef depsPath;
unsigned numErrors{0};
DepsActions(BuildSystemCommandInterface& bsci, Task* task,
ShellCommand* command, StringRef depsPath)
: bsci(bsci), task(task), command(command), depsPath(depsPath) {}
virtual void error(const char* message, uint64_t position) override {
getBuildSystem(bsci.getBuildEngine()).error(
depsPath, "error reading dependency file: " + std::string(message));
++numErrors;
}
virtual void actOnRuleDependency(const char* dependency,
uint64_t length,
const StringRef unescapedWord) override {
bsci.taskDiscoveredDependency(task, BuildKey::makeNode(unescapedWord));
}
virtual void actOnRuleStart(const char* name, uint64_t length,
const StringRef unescapedWord) override {}
virtual void actOnRuleEnd() override {}
};
DepsActions actions(bsci, task, this, depsPath);
core::MakefileDepsParser(input->getBufferStart(), input->getBufferSize(),
actions).parse();
return actions.numErrors == 0;
}
bool
processDependencyInfoDiscoveredDependencies(BuildSystemCommandInterface& bsci,
Task* task,
QueueJobContext* context,
StringRef depsPath,
llvm::MemoryBuffer* input) {
// Parse the output.
//
// We just ignore the rule, and add any dependency that we encounter in the
// file.
struct DepsActions : public core::DependencyInfoParser::ParseActions {
BuildSystemCommandInterface& bsci;
Task* task;
ShellCommand* command;
StringRef depsPath;
unsigned numErrors{0};
DepsActions(BuildSystemCommandInterface& bsci, Task* task,
ShellCommand* command, StringRef depsPath)
: bsci(bsci), task(task), command(command), depsPath(depsPath) {}
virtual void error(const char* message, uint64_t position) override {
getBuildSystem(bsci.getBuildEngine()).error(
depsPath, "error reading dependency file: " + std::string(message));
++numErrors;
}
// Ignore everything but actual inputs.
virtual void actOnVersion(StringRef) override { }
virtual void actOnMissing(StringRef) override { }
virtual void actOnOutput(StringRef) override { }
virtual void actOnInput(StringRef name) override {
bsci.taskDiscoveredDependency(task, BuildKey::makeNode(name));
}
};
DepsActions actions(bsci, task, this, depsPath);
core::DependencyInfoParser(input->getBuffer(), actions).parse();
return actions.numErrors == 0;
}
public:
using ExternalCommand::ExternalCommand;
virtual void getShortDescription(SmallVectorImpl<char> &result) override {
llvm::raw_svector_ostream(result) << getDescription();
}
virtual void getVerboseDescription(SmallVectorImpl<char> &result) override {
llvm::raw_svector_ostream os(result);
bool first = true;
for (const auto& arg: args) {
if (!first) os << " ";
first = false;
basic::appendShellEscapedString(os, arg);
}
}
virtual bool configureAttribute(const ConfigureContext& ctx, StringRef name,
StringRef value) override {
if (name == "args") {
// When provided as a scalar string, we default to executing using the
// shell.
args.clear();
args.push_back(ctx.getDelegate().getInternedString("/bin/sh"));
args.push_back(ctx.getDelegate().getInternedString("-c"));
args.push_back(ctx.getDelegate().getInternedString(value));
} else if (name == "deps") {
depsPaths.clear();
depsPaths.emplace_back(value);
} else if (name == "deps-style") {
if (value == "makefile") {
depsStyle = DepsStyle::Makefile;
} else if (value == "dependency-info") {
depsStyle = DepsStyle::DependencyInfo;
} else {
ctx.error("unknown 'deps-style': '" + value + "'");
return false;
}
return true;
} else if (name == "inherit-env") {
if (value != "true" && value != "false") {
ctx.error("invalid value: '" + value + "' for attribute '" +
name + "'");
return false;
}
inheritEnv = value == "true";
} else {
return ExternalCommand::configureAttribute(ctx, name, value);
}
return true;
}
virtual bool configureAttribute(const ConfigureContext& ctx, StringRef name,
ArrayRef<StringRef> values) override {
if (name == "args") {
// Diagnose missing arguments.
if (values.empty()) {
ctx.error("invalid arguments for command '" + getName() + "'");
return false;
}
args.clear();
args.reserve(values.size());
for (auto arg: values) {
args.emplace_back(ctx.getDelegate().getInternedString(arg));
}
} else if (name == "deps") {
depsPaths.clear();
depsPaths.insert(depsPaths.begin(), values.begin(), values.end());
} else {
return ExternalCommand::configureAttribute(ctx, name, values);
}
return true;
}
virtual bool configureAttribute(
const ConfigureContext& ctx, StringRef name,
ArrayRef<std::pair<StringRef, StringRef>> values) override {
if (name == "env") {
env.clear();
env.reserve(values.size());
for (const auto& entry: values) {
env.emplace_back(
std::make_pair(
ctx.getDelegate().getInternedString(entry.first),
ctx.getDelegate().getInternedString(entry.second)));
}
} else {
return ExternalCommand::configureAttribute(ctx, name, values);
}
return true;
}
virtual CommandResult executeExternalCommand(BuildSystemCommandInterface& bsci,
Task* task,
QueueJobContext* context) override {
// Execute the command.
auto result = bsci.getExecutionQueue().executeProcess(
context, args,
env, /*inheritEnvironment=*/inheritEnv);
if (result != CommandResult::Succeeded) {
// If the command failed, there is no need to gather dependencies.
return result;
}
// Collect the discovered dependencies, if used.
if (!depsPaths.empty()) {
if (!processDiscoveredDependencies(bsci, task, context)) {
// If we were unable to process the dependencies output, report a
// failure.
return CommandResult::Failed;
}
}
return result;
}
};
class ShellTool : public Tool {
public:
using Tool::Tool;
virtual bool configureAttribute(const ConfigureContext& ctx, StringRef name,
StringRef value) override {
// No supported attributes.
ctx.error("unexpected attribute: '" + name + "'");
return false;
}
virtual bool configureAttribute(const ConfigureContext& ctx, StringRef name,
ArrayRef<StringRef> values) override {
// No supported attributes.
ctx.error("unexpected attribute: '" + name + "'");
return false;
}
virtual bool configureAttribute(
const ConfigureContext& ctx, StringRef name,
ArrayRef<std::pair<StringRef, StringRef>> values) override {
// No supported attributes.
ctx.error("unexpected attribute: '" + name + "'");
return false;
}
virtual std::unique_ptr<Command> createCommand(StringRef name) override {
return llvm::make_unique<ShellCommand>(name);
}
};
#pragma mark - ClangTool implementation
class ClangShellCommand : public ExternalCommand {
/// The compiler command to invoke.
std::vector<StringRef> args;
/// The path to the dependency output file, if used.
std::string depsPath;
virtual uint64_t getSignature() override {
using llvm::hash_combine;
llvm::hash_code code = ExternalCommand::getSignature();
for (const auto& arg: args) {
code = hash_combine(code, arg);
}
return size_t(code);
}
bool processDiscoveredDependencies(BuildSystemCommandInterface& bsci,
Task* task,
QueueJobContext* context) {
// Read the dependencies file.
auto input = bsci.getDelegate().getFileSystem().getFileContents(depsPath);
if (!input) {
getBuildSystem(bsci.getBuildEngine()).error(
depsPath, "unable to open dependencies file (" + depsPath + ")");
return false;
}
// Parse the output.
//
// We just ignore the rule, and add any dependency that we encounter in the
// file.
struct DepsActions : public core::MakefileDepsParser::ParseActions {
BuildSystemCommandInterface& bsci;
Task* task;
ClangShellCommand* command;
unsigned numErrors{0};
DepsActions(BuildSystemCommandInterface& bsci, Task* task,
ClangShellCommand* command)
: bsci(bsci), task(task), command(command) {}
virtual void error(const char* message, uint64_t position) override {
getBuildSystem(bsci.getBuildEngine()).error(
command->depsPath,
"error reading dependency file: " + std::string(message));
++numErrors;
}
virtual void actOnRuleDependency(const char* dependency,
uint64_t length,
const StringRef unescapedWord) override {
bsci.taskDiscoveredDependency(task, BuildKey::makeNode(unescapedWord));
}
virtual void actOnRuleStart(const char* name, uint64_t length,
const StringRef unescapedWord) override {}
virtual void actOnRuleEnd() override {}
};
DepsActions actions(bsci, task, this);
core::MakefileDepsParser(input->getBufferStart(), input->getBufferSize(),
actions).parse();
return actions.numErrors == 0;
}
public:
using ExternalCommand::ExternalCommand;
virtual void getShortDescription(SmallVectorImpl<char> &result) override {
llvm::raw_svector_ostream(result) << getDescription();
}
virtual void getVerboseDescription(SmallVectorImpl<char> &result) override {
llvm::raw_svector_ostream os(result);
bool first = true;
for (const auto& arg: args) {
if (!first) os << " ";
first = false;
basic::appendShellEscapedString(os, arg);
}
}
virtual bool configureAttribute(const ConfigureContext& ctx, StringRef name,
StringRef value) override {
if (name == "args") {
// When provided as a scalar string, we default to executing using the
// shell.
args.clear();
args.push_back(ctx.getDelegate().getInternedString("/bin/sh"));
args.push_back(ctx.getDelegate().getInternedString("-c"));
args.push_back(ctx.getDelegate().getInternedString(value));
} else if (name == "deps") {
depsPath = value;
} else {
return ExternalCommand::configureAttribute(ctx, name, value);
}
return true;
}
virtual bool configureAttribute(const ConfigureContext& ctx, StringRef name,
ArrayRef<StringRef> values) override {
if (name == "args") {
args.clear();
args.reserve(values.size());
for (auto arg: values) {
args.emplace_back(ctx.getDelegate().getInternedString(arg));
}
} else {
return ExternalCommand::configureAttribute(ctx, name, values);
}
return true;
}
virtual bool configureAttribute(
const ConfigureContext& ctx, StringRef name,
ArrayRef<std::pair<StringRef, StringRef>> values) override {
return ExternalCommand::configureAttribute(ctx, name, values);
}
virtual CommandResult executeExternalCommand(BuildSystemCommandInterface& bsci,
Task* task,
QueueJobContext* context) override {
// Execute the command.
auto result = bsci.getExecutionQueue().executeProcess(context, args);
if (result != CommandResult::Succeeded) {
// If the command failed, there is no need to gather dependencies.
return result;
}
// Otherwise, collect the discovered dependencies, if used.
if (!depsPath.empty()) {
if (!processDiscoveredDependencies(bsci, task, context)) {
// If we were unable to process the dependencies output, report a
// failure.
return CommandResult::Failed;
}
}
return result;
}
};
class ClangTool : public Tool {
public:
using Tool::Tool;
virtual bool configureAttribute(const ConfigureContext& ctx, StringRef name,
StringRef value) override {
// No supported attributes.
ctx.error("unexpected attribute: '" + name + "'");
return false;
}
virtual bool configureAttribute(const ConfigureContext& ctx, StringRef name,
ArrayRef<StringRef> values) override {
// No supported attributes.
ctx.error("unexpected attribute: '" + name + "'");
return false;
}
virtual bool configureAttribute(
const ConfigureContext& ctx, StringRef name,
ArrayRef<std::pair<StringRef, StringRef>> values) override {
// No supported attributes.
ctx.error("unexpected attribute: '" + name + "'");
return false;
}
virtual std::unique_ptr<Command> createCommand(StringRef name) override {
return llvm::make_unique<ClangShellCommand>(name);
}
};
#pragma mark - MkdirTool implementation
class MkdirCommand : public ExternalCommand {
virtual void getShortDescription(SmallVectorImpl<char> &result) override {
llvm::raw_svector_ostream(result) << getDescription();
}
virtual void getVerboseDescription(SmallVectorImpl<char> &result) override {
llvm::raw_svector_ostream os(result);
os << "mkdir -p ";
// FIXME: This isn't correct, we need utilities for doing shell quoting.
if (StringRef(getOutputs()[0]->getName()).find(' ') != StringRef::npos) {
os << '"' << getOutputs()[0]->getName() << '"';
} else {
os << getOutputs()[0]->getName();
}
}
virtual bool isResultValid(BuildSystem& system,
const BuildValue& value) override {
// If the prior value wasn't for a successful command, recompute.
if (!value.isSuccessfulCommand())
return false;
// Otherwise, the result is valid if the directory still exists.
auto info = getOutputs()[0]->getFileInfo(
system.getDelegate().getFileSystem());
if (info.isMissing())
return false;
// If the item is not a directory, it needs to be recreated.
if (!info.isDirectory())
return false;
// FIXME: We should strictly enforce the integrity of this validity routine
// by ensuring that the build result for this command does not fully encode
// the file info, but rather just encodes its success. As is, we are leaking
// out the details of the file info (like the timestamp), but not rerunning
// when they change. This is by design for this command, but it would still
// be nice to be strict about it.
return true;
}
virtual CommandResult executeExternalCommand(BuildSystemCommandInterface& bsci,
Task* task,
QueueJobContext* context) override {
auto output = getOutputs()[0];
if (!bsci.getDelegate().getFileSystem().createDirectories(
output->getName())) {
getBuildSystem(bsci.getBuildEngine()).error(
"", "unable to create directory '" + output->getName() + "'");
return CommandResult::Failed;
}
return CommandResult::Succeeded;
}
public:
using ExternalCommand::ExternalCommand;
};
class MkdirTool : public Tool {
public:
using Tool::Tool;
virtual bool configureAttribute(const ConfigureContext& ctx, StringRef name,
StringRef value) override {
// No supported attributes.
ctx.error("unexpected attribute: '" + name + "'");
return false;
}
virtual bool configureAttribute(const ConfigureContext& ctx, StringRef name,
ArrayRef<StringRef> values) override {
// No supported attributes.
ctx.error("unexpected attribute: '" + name + "'");
return false;
}
virtual bool configureAttribute(
const ConfigureContext& ctx, StringRef name,
ArrayRef<std::pair<StringRef, StringRef>> values) override {
// No supported attributes.
ctx.error("unexpected attribute: '" + name + "'");
return false;
}
virtual std::unique_ptr<Command> createCommand(StringRef name) override {
return llvm::make_unique<MkdirCommand>(name);
}
};
#pragma mark - SymlinkTool implementation
class SymlinkCommand : public Command {
/// The declared output node.
BuildNode* output = nullptr;
/// The path of the actual symbolic link to create, if different from the
/// output node.
std::string linkOutputPath;
/// The command description.
std::string description;
/// Declared command inputs, used only for ordering purposes.
std::vector<BuildNode*> inputs;
/// The contents to write at the output path.
std::string contents;
/// Get the destination path.
StringRef getActualOutputPath() const {
return linkOutputPath.empty() ? output->getName() :
StringRef(linkOutputPath);
}
virtual uint64_t getSignature() {
using llvm::hash_combine;
llvm::hash_code code = hash_value(output->getName());
code = hash_combine(code, contents);
for (const auto* input: inputs) {
code = hash_combine(code, input->getName());
}
return size_t(code);
}
virtual void configureDescription(const ConfigureContext&,
StringRef value) override {
description = value;
}
virtual void getShortDescription(SmallVectorImpl<char> &result) override {
llvm::raw_svector_ostream(result) << description;
}
virtual void getVerboseDescription(SmallVectorImpl<char> &result) override {
llvm::raw_svector_ostream os(result);
os << "ln -sfh ";
StringRef outputPath = getActualOutputPath();
if (!output || !outputPath.empty()) {
// FIXME: This isn't correct, we need utilities for doing shell quoting.
if (outputPath.find(' ') != StringRef::npos) {
os << '"' << outputPath << '"';
} else {
os << outputPath;
}
} else {
os << "<<<missing output>>>";
}
os << ' ';
// FIXME: This isn't correct, we need utilities for doing shell quoting.
if (StringRef(contents).find(' ') != StringRef::npos) {
os << '"' << contents << '"';
} else {
os << contents;
}
}
virtual void configureInputs(const ConfigureContext& ctx,
const std::vector<Node*>& value) override {
inputs.reserve(value.size());
for (auto* node: value) {
inputs.emplace_back(static_cast<BuildNode*>(node));
}
}
virtual void configureOutputs(const ConfigureContext& ctx,
const std::vector<Node*>& value) override {
if (value.size() == 1) {
output = static_cast<BuildNode*>(value[0]);
} else if (value.empty()) {
ctx.error("missing declared output");
} else {
ctx.error("unexpected explicit output: '" + value[1]->getName() + "'");
}
}
virtual bool configureAttribute(const ConfigureContext& ctx, StringRef name,
StringRef value) override {
if (name == "contents") {
contents = value;
return true;
} else if (name == "link-output-path") {
linkOutputPath = value;
return true;
} else {
ctx.error("unexpected attribute: '" + name + "'");
return false;
}
}
virtual bool configureAttribute(const ConfigureContext& ctx, StringRef name,
ArrayRef<StringRef> values) override {
// No supported attributes.
ctx.error("unexpected attribute: '" + name + "'");
return false;
}
virtual bool configureAttribute(
const ConfigureContext& ctx, StringRef name,
ArrayRef<std::pair<StringRef, StringRef>> values) override {
// No supported attributes.
ctx.error("unexpected attribute: '" + name + "'");
return false;
}
virtual BuildValue getResultForOutput(Node* node,
const BuildValue& value) override {
// If the value was a failed command, propagate the failure.
if (value.isFailedCommand() || value.isPropagatedFailureCommand() ||
value.isCancelledCommand())
return BuildValue::makeFailedInput();
if (value.isSkippedCommand())
return BuildValue::makeSkippedCommand();
// Otherwise, we should have a successful command -- return the actual
// result for the output.
assert(value.isSuccessfulCommand());
return BuildValue::makeExistingInput(value.getOutputInfo());
}
virtual bool isResultValid(BuildSystem& system,
const BuildValue& value) override {
// It is an error if this command isn't configured properly.
StringRef outputPath = getActualOutputPath();
if (!output || outputPath.empty())
return false;
// If the prior value wasn't for a successful command, recompute.
if (!value.isSuccessfulCommand())
return false;
// If the command's signature has changed since it was built, rebuild.
if (value.getCommandSignature() != getSignature())
return false;
// If the prior command doesn't look like one for a link, recompute.
if (value.getNumOutputs() != 1)
return false;
// Otherwise, assume the result is valid if its link status matches the
// previous one.
auto info = system.getDelegate().getFileSystem().getLinkInfo(outputPath);
if (info.isMissing())
return false;
return info == value.getOutputInfo();
}
virtual void start(BuildSystemCommandInterface& bsci,
core::Task* task) override {
// The command itself takes no inputs, so just treat any declared inputs as
// "must follow" directives.
//
// FIXME: We should make this explicit once we have actual support for must
// follow inputs.
for (auto it = inputs.begin(), ie = inputs.end(); it != ie; ++it) {
bsci.taskMustFollow(task, BuildKey::makeNode(*it));
}
}
virtual void providePriorValue(BuildSystemCommandInterface&, core::Task*,
const BuildValue& value) override {
// Ignored.
}
virtual void provideValue(BuildSystemCommandInterface&, core::Task*,
uintptr_t inputID,
const BuildValue& value) override {
assert(0 && "unexpected API call");
}
virtual BuildValue execute(BuildSystemCommandInterface& bsci,
core::Task* task,
QueueJobContext* context) override {
// It is an error if this command isn't configured properly.
StringRef outputPath = getActualOutputPath();
if (!output || outputPath.empty()) {
return BuildValue::makeFailedCommand();
}
// Create the directory containing the symlink, if necessary.
//
// FIXME: Shared behavior with ExternalCommand.
{
auto parent = llvm::sys::path::parent_path(outputPath);
if (!parent.empty()) {
(void) bsci.getDelegate().getFileSystem().createDirectories(parent);
}
}
// Create the symbolic link (note that despite the poorly chosen LLVM
// name, this is a symlink).
//
// FIXME: Need to use the filesystem interfaces.
bsci.getDelegate().commandStarted(this);
auto success = true;
if (llvm::sys::fs::create_link(contents, outputPath)) {
// On failure, we attempt to unlink the file and retry.
basic::sys::unlink(outputPath.str().c_str());
if (llvm::sys::fs::create_link(contents, outputPath)) {
getBuildSystem(bsci.getBuildEngine()).error(
"", "unable to create symlink at '" + outputPath + "'");
success = false;
}
}
bsci.getDelegate().commandFinished(this, success ? CommandResult::Succeeded : CommandResult::Failed);
// Process the result.
if (!success) {
return BuildValue::makeFailedCommand();
}
// Capture the *link* information of the output.
FileInfo outputInfo = bsci.getDelegate().getFileSystem().getLinkInfo(
outputPath);
// Complete with a successful result.
return BuildValue::makeSuccessfulCommand(outputInfo, getSignature());
}
public:
using Command::Command;
};
class SymlinkTool : public Tool {
public:
using Tool::Tool;
virtual bool configureAttribute(const ConfigureContext& ctx, StringRef name,
StringRef value) override {
// No supported attributes.
ctx.error("unexpected attribute: '" + name + "'");
return false;
}
virtual bool configureAttribute(const ConfigureContext& ctx, StringRef name,
ArrayRef<StringRef> values) override {
// No supported attributes.
ctx.error("unexpected attribute: '" + name + "'");
return false;
}
virtual bool configureAttribute(
const ConfigureContext& ctx, StringRef name,
ArrayRef<std::pair<StringRef, StringRef>> values) override {
// No supported attributes.
ctx.error("unexpected attribute: '" + name + "'");
return false;
}
virtual std::unique_ptr<Command> createCommand(StringRef name) override {
return llvm::make_unique<SymlinkCommand>(name);
}
};
#pragma mark - ArchiveTool implementation
class ArchiveShellCommand : public ExternalCommand {
std::string archiveName;
std::vector<std::string> archiveInputs;
virtual CommandResult executeExternalCommand(BuildSystemCommandInterface& bsci,
Task* task,
QueueJobContext* context) override {
// First delete the current archive
// TODO instead insert, update and remove files from the archive
if (llvm::sys::fs::remove(archiveName, /*IgnoreNonExisting*/ true)) {
return CommandResult::Failed;
}
// Create archive
auto args = getArgs();
return bsci.getExecutionQueue().executeProcess(
context, std::vector<StringRef>(args.begin(), args.end()));
}
virtual void getShortDescription(SmallVectorImpl<char> &result) override {
if (getDescription().empty()) {
llvm::raw_svector_ostream(result) << "Archiving " + archiveName;
} else {
llvm::raw_svector_ostream(result) << getDescription();
}
}
virtual void getVerboseDescription(SmallVectorImpl<char> &result) override {
llvm::raw_svector_ostream stream(result);
bool first = true;
for (const auto& arg: getArgs()) {
stream << arg;
if (!first) {
stream << " ";
first = false;
}
}
}
virtual void configureInputs(const ConfigureContext& ctx,
const std::vector<Node*>& value) override {
ExternalCommand::configureInputs(ctx, value);
for (const auto& input: getInputs()) {
if (!input->isVirtual()) {
archiveInputs.push_back(input->getName());
}
}
if (archiveInputs.empty()) {
ctx.error("missing expected input");
}
}
virtual void configureOutputs(const ConfigureContext& ctx,
const std::vector<Node*>& value) override {
ExternalCommand::configureOutputs(ctx, value);
for (const auto& output: getOutputs()) {
if (!output->isVirtual()) {
if (archiveName.empty()) {
archiveName = output->getName();
} else {
ctx.error("unexpected explicit output: " + output->getName());
}
}
}
if (archiveName.empty()) {
ctx.error("missing expected output");
}
}
std::vector<std::string> getArgs() {
std::vector<std::string> args;
args.push_back("ar");
args.push_back("cr");
args.push_back(archiveName);
args.insert(args.end(), archiveInputs.begin(), archiveInputs.end());
return args;
}
public:
using ExternalCommand::ExternalCommand;
};
class ArchiveTool : public Tool {
public:
using Tool::Tool;
virtual bool configureAttribute(const ConfigureContext& ctx, StringRef name,
StringRef value) override {
// No supported attributes.
ctx.error("unexpected attribute: '" + name + "'");
return false;
}
virtual bool configureAttribute(const ConfigureContext& ctx, StringRef name,
ArrayRef<StringRef> values) override {
// No supported attributes.
ctx.error("unexpected attribute: '" + name + "'");
return false;
}
virtual bool configureAttribute(
const ConfigureContext& ctx, StringRef name,
ArrayRef<std::pair<StringRef, StringRef>> values) override {
// No supported attributes.
ctx.error("unexpected attribute: '" + name + "'");
return false;
}
virtual std::unique_ptr<Command> createCommand(StringRef name) override {
return llvm::make_unique<ArchiveShellCommand>(name);
}
};
#pragma mark - StaleFileRemovalTool implementation
class StaleFileRemovalCommand : public Command {
std::string description;
std::vector<std::string> expectedOutputs;
std::vector<std::string> filesToDelete;
std::vector<std::string> roots;
bool computedFilesToDelete = false;
BuildValue priorValue;
bool hasPriorResult = false;
char path_separator = llvm::sys::path::get_separator()[0];
virtual void configureDescription(const ConfigureContext&, StringRef value) override {
description = value;
}
virtual void getShortDescription(SmallVectorImpl<char> &result) override {
llvm::raw_svector_ostream(result) << (description.empty() ? "Stale file removal" : description);
}
virtual void getVerboseDescription(SmallVectorImpl<char> &result) override {
computeFilesToDelete();
getShortDescription(result);
llvm::raw_svector_ostream(result) << ", stale files: [";
for (auto fileToDelete : filesToDelete) {
llvm::raw_svector_ostream(result) << fileToDelete;
if (fileToDelete != *(--filesToDelete.end())) {
llvm::raw_svector_ostream(result) << ", ";
}
}
llvm::raw_svector_ostream(result) << "], roots: [";
for (auto root : roots) {
llvm::raw_svector_ostream(result) << root;
if (root != *(--roots.end())) {
llvm::raw_svector_ostream(result) << ", ";
}
}
llvm::raw_svector_ostream(result) << "]";
}
virtual void configureInputs(const ConfigureContext& ctx,
const std::vector<Node*>& value) override {}
virtual void configureOutputs(const ConfigureContext& ctx,
const std::vector<Node*>& value) override {}
virtual bool configureAttribute(const ConfigureContext& ctx, StringRef name,
StringRef value) override {
// No supported attributes.
ctx.error("unexpected attribute: '" + name + "'");
return false;
}
virtual bool configureAttribute(const ConfigureContext& ctx, StringRef name,
ArrayRef<StringRef> values) override {
if (name == "expectedOutputs") {
expectedOutputs.reserve(values.size());
for (auto value : values) {
expectedOutputs.emplace_back(value.str());
}
return true;
} else if (name == "roots") {
roots.reserve(values.size());
for (auto value : values) {
roots.emplace_back(value.str());
}
return true;
}
ctx.error("unexpected attribute: '" + name + "'");
return false;
}
virtual bool configureAttribute(const ConfigureContext& ctx, StringRef name,
ArrayRef<std::pair<StringRef, StringRef>> values) override {
// No supported attributes.
ctx.error("unexpected attribute: '" + name + "'");
return false;
}
virtual BuildValue getResultForOutput(Node* node,
const BuildValue& value) override {
// If the value was a failed command, propagate the failure.
if (value.isFailedCommand() || value.isPropagatedFailureCommand() ||
value.isCancelledCommand())
return BuildValue::makeFailedInput();
if (value.isSkippedCommand())
return BuildValue::makeSkippedCommand();
// Otherwise, this was successful, return the value as-is.
return BuildValue::fromData(value.toData());;
}
virtual bool isResultValid(BuildSystem& system,
const BuildValue& value) override {
// Always re-run stale file removal.
return false;
}
virtual void start(BuildSystemCommandInterface& bsci,
core::Task* task) override {}
virtual void providePriorValue(BuildSystemCommandInterface&, core::Task*,
const BuildValue& value) override {
hasPriorResult = true;
priorValue = BuildValue::fromData(value.toData());
}
virtual void provideValue(BuildSystemCommandInterface&, core::Task*,
uintptr_t inputID,
const BuildValue& value) override {
assert(0 && "unexpected API call");
}
void computeFilesToDelete() {
if (computedFilesToDelete) {
return;
}
std::vector<StringRef> priorValueList = priorValue.getStaleFileList();
std::set<std::string> priorNodes(priorValueList.begin(), priorValueList.end());
std::set<std::string> expectedNodes(expectedOutputs.begin(), expectedOutputs.end());
std::set_difference(priorNodes.begin(), priorNodes.end(),
expectedNodes.begin(), expectedNodes.end(),
std::back_inserter(filesToDelete));
computedFilesToDelete = true;
}
virtual BuildValue execute(BuildSystemCommandInterface& bsci,
core::Task* task,
QueueJobContext* context) override {
// Nothing to do if we do not have a prior result.
if (!hasPriorResult || !priorValue.isStaleFileRemoval()) {
bsci.getDelegate().commandStarted(this);
bsci.getDelegate().commandFinished(this, CommandResult::Succeeded);
return BuildValue::makeStaleFileRemoval(expectedOutputs);
}
computeFilesToDelete();
bsci.getDelegate().commandStarted(this);
for (auto fileToDelete : filesToDelete) {
// If no root paths are specified, any path is valid.
bool isLocatedUnderRootPath = roots.size() == 0 ? true : false;
// If root paths are defined, stale file paths should be absolute.
if (roots.size() > 0 && fileToDelete[0] != path_separator) {
bsci.getDelegate().commandHadWarning(this, "Stale file '" + fileToDelete + "' has a relative path. This is invalid in combination with the root path attribute.\n");
continue;
}
// Check if the file is located under one of the allowed root paths.
for (auto root : roots) {
if (pathIsPrefixedByPath(fileToDelete, root)) {
isLocatedUnderRootPath = true;
}
}
if (!isLocatedUnderRootPath) {
bsci.getDelegate().commandHadWarning(this, "Stale file '" + fileToDelete + "' is located outside of the allowed root paths.\n");
continue;
}
if (getBuildSystem(bsci.getBuildEngine()).getDelegate().getFileSystem().remove(fileToDelete)) {
bsci.getDelegate().commandHadNote(this, "Removed stale file '" + fileToDelete + "'\n");
} else {
bsci.getDelegate().commandHadWarning(this, "cannot remove stale file '" + fileToDelete + "': " + strerror(errno) + "\n");
}
}
bsci.getDelegate().commandFinished(this, CommandResult::Succeeded);
// Complete with a successful result.
return BuildValue::makeStaleFileRemoval(expectedOutputs);
}
public:
StaleFileRemovalCommand(const StringRef name)
: Command(name), priorValue(BuildValue::makeInvalid()) {}
};
class StaleFileRemovalTool : public Tool {
public:
using Tool::Tool;
virtual bool configureAttribute(const ConfigureContext& ctx, StringRef name,
StringRef value) override {
// No supported attributes.
ctx.error("unexpected attribute: '" + name + "'");
return false;
}
virtual bool configureAttribute(const ConfigureContext& ctx, StringRef name,
ArrayRef<StringRef> values) override {
// No supported attributes.
ctx.error("unexpected attribute: '" + name + "'");
return false;
}
virtual bool configureAttribute(
const ConfigureContext& ctx, StringRef name,
ArrayRef<std::pair<StringRef, StringRef>> values) override {
// No supported attributes.
ctx.error("unexpected attribute: '" + name + "'");
return false;
}
virtual std::unique_ptr<Command> createCommand(StringRef name) override {
return llvm::make_unique<StaleFileRemovalCommand>(name);
}
};
#pragma mark - BuildSystemFileDelegate
BuildSystemDelegate& BuildSystemFileDelegate::getSystemDelegate() {
return system.getDelegate();
}
void BuildSystemFileDelegate::setFileContentsBeingParsed(StringRef buffer) {
getSystemDelegate().setFileContentsBeingParsed(buffer);
}
void BuildSystemFileDelegate::error(StringRef filename,
const BuildFileToken& at,
const Twine& message) {
// Delegate to the system delegate.
auto atSystemToken = BuildSystemDelegate::Token{at.start, at.length};
system.error(filename, atSystemToken, message);
}
bool
BuildSystemFileDelegate::configureClient(const ConfigureContext&,
StringRef name,
uint32_t version,
const property_list_type& properties) {
// The client must match the configured name of the build system.
if (name != getSystemDelegate().getName())
return false;
// The client version must match the configured version.
//
// FIXME: We should give the client the opportunity to support a previous
// schema version (auto-upgrade).
if (version != getSystemDelegate().getVersion())
return false;
return true;
}
std::unique_ptr<Tool>
BuildSystemFileDelegate::lookupTool(StringRef name) {
// First, give the client an opportunity to create the tool.
if (auto tool = getSystemDelegate().lookupTool(name)) {
return tool;
}
// Otherwise, look for one of the builtin tool definitions.
if (name == "shell") {
return llvm::make_unique<ShellTool>(name);
} else if (name == "phony") {
return llvm::make_unique<PhonyTool>(name);
} else if (name == "clang") {
return llvm::make_unique<ClangTool>(name);
} else if (name == "mkdir") {
return llvm::make_unique<MkdirTool>(name);
} else if (name == "symlink") {
return llvm::make_unique<SymlinkTool>(name);
} else if (name == "archive") {
return llvm::make_unique<ArchiveTool>(name);
} else if (name == "stale-file-removal") {
return llvm::make_unique<StaleFileRemovalTool>(name);
}
return nullptr;
}
void BuildSystemFileDelegate::loadedTarget(StringRef name,
const Target& target) {
}
void BuildSystemFileDelegate::loadedDefaultTarget(StringRef target) {
}
void BuildSystemFileDelegate::loadedCommand(StringRef name,
const Command& command) {
}
std::unique_ptr<Node>
BuildSystemFileDelegate::lookupNode(StringRef name,
bool isImplicit) {
return system.lookupNode(name, isImplicit);
}
}
#pragma mark - BuildSystem
BuildSystem::BuildSystem(BuildSystemDelegate& delegate)
: impl(new BuildSystemImpl(*this, delegate))
{
}
BuildSystem::~BuildSystem() {
delete static_cast<BuildSystemImpl*>(impl);
}
BuildSystemDelegate& BuildSystem::getDelegate() {
return static_cast<BuildSystemImpl*>(impl)->getDelegate();
}
bool BuildSystem::loadDescription(StringRef mainFilename) {
return static_cast<BuildSystemImpl*>(impl)->loadDescription(mainFilename);
}
void BuildSystem::loadDescription(
std::unique_ptr<BuildDescription> description) {
return static_cast<BuildSystemImpl*>(impl)->loadDescription(
std::move(description));
}
bool BuildSystem::attachDB(StringRef path,
std::string* error_out) {
return static_cast<BuildSystemImpl*>(impl)->attachDB(path, error_out);
}
bool BuildSystem::enableTracing(StringRef path,
std::string* error_out) {
return static_cast<BuildSystemImpl*>(impl)->enableTracing(path, error_out);
}
llvm::Optional<BuildValue> BuildSystem::build(BuildKey key) {
return static_cast<BuildSystemImpl*>(impl)->build(key);
}
bool BuildSystem::build(StringRef name) {
return static_cast<BuildSystemImpl*>(impl)->build(name);
}
void BuildSystem::cancel() {
if (impl) {
static_cast<BuildSystemImpl*>(impl)->cancel();
}
}
void BuildSystem::resetForBuild() {
static_cast<BuildSystemImpl*>(impl)->resetForBuild();
}
// This function checks if the given path is prefixed by another path.
bool llbuild::buildsystem::pathIsPrefixedByPath(std::string path, std::string prefixPath) {
static char path_separator = llvm::sys::path::get_separator()[0];
auto res = std::mismatch(prefixPath.begin(), prefixPath.end(), path.begin());
// Check if `prefixPath` has been exhausted or just a separator remains.
bool isPrefix = res.first == prefixPath.end() || (*(res.first++) == path_separator);
// Check if `path` has been exhausted or just a separator remains.
return isPrefix && (res.second == path.end() || (*(res.second++) == path_separator));
}