blob: 68e04e22fe04ed6b11538cc906b0ce716d89da48 [file] [log] [blame]
//===-- BuildSystem.cpp ---------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 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/Basic/FileInfo.h"
#include "llbuild/Basic/Hashing.h"
#include "llbuild/Basic/LLVM.h"
#include "llbuild/Core/BuildDB.h"
#include "llbuild/Core/BuildEngine.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/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/raw_ostream.h"
#include <memory>
using namespace llbuild;
using namespace llbuild::basic;
using namespace llbuild::core;
using namespace llbuild::buildsystem;
BuildExecutionQueue::~BuildExecutionQueue() {}
bool BuildExecutionQueue::executeShellCommand(QueueJobContext* context,
StringRef command) {
SmallString<1024> commandStorage(command);
std::vector<StringRef> commandLine(
{ "/bin/sh", "-c", commandStorage.c_str() });
return executeProcess(context, commandLine);
}
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;
public:
BuildSystemFileDelegate(BuildSystemImpl& system)
: BuildFileDelegate(), system(system) {}
BuildSystemDelegate& getSystemDelegate();
/// @name Delegate Implementation
/// @{
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 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;
BuildFile& getBuildFile();
virtual Rule lookupRule(const KeyType& keyData) override;
virtual void cycleDetected(const std::vector<Rule*>& items) override;
public:
BuildSystemEngineDelegate(BuildSystemImpl& system) : system(system) {}
BuildSystemImpl& getBuildSystem() {
return system;
}
};
class BuildSystemImpl : public BuildSystemCommandInterface {
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 file the system is building.
BuildFile buildFile;
/// The delegate used for building the file contents.
BuildSystemEngineDelegate engineDelegate;
/// The build engine.
BuildEngine buildEngine;
/// The execution queue.
std::unique_ptr<BuildExecutionQueue> executionQueue;
/// @name BuildSystemCommandInterface Implementation
/// @{
virtual BuildEngine& getBuildEngine() override {
return buildEngine;
}
virtual BuildExecutionQueue& getExecutionQueue() override {
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,
StringRef mainFilename)
: buildSystem(buildSystem), delegate(delegate),
mainFilename(mainFilename),
fileDelegate(*this), buildFile(mainFilename, fileDelegate),
engineDelegate(*this), buildEngine(engineDelegate),
executionQueue(delegate.createExecutionQueue()) {}
BuildSystem& getBuildSystem() {
return buildSystem;
}
BuildSystemDelegate& getDelegate() override {
return delegate;
}
StringRef getMainFilename() {
return mainFilename;
}
BuildSystemCommandInterface& getCommandInterface() {
return *this;
}
BuildFile& getBuildFile() {
return buildFile;
}
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);
/// @name Client API
/// @{
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, delegate.getVersion(),
error_out));
if (!db)
return false;
buildEngine.attachDB(std::move(db));
return true;
}
bool enableTracing(StringRef filename, std::string* error_out) {
return buildEngine.enableTracing(filename, error_out);
}
bool build(StringRef target);
/// @}
};
#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 (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(Target& node, const BuildValue& value) {
// Always treat target tasks as invalid.
return false;
}
};
/// This is the task to "build" a node which represents pure raw input to the
/// system.
class InputNodeTask : 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 {
// Handle virtual nodes.
if (node.isVirtual()) {
engine.taskIsComplete(
this, BuildValue::makeVirtualInput().toData());
return;
}
// Get the information on the file.
//
// FIXME: This needs to delegate, since we want to have a notion of
// different node types.
auto info = node.getFileInfo();
if (info.isMissing()) {
engine.taskIsComplete(this, BuildValue::makeMissingInput().toData());
return;
}
engine.taskIsComplete(
this, BuildValue::makeExistingInput(info).toData());
}
public:
InputNodeTask(BuildNode& node) : node(node) {}
static bool isResultValid(const BuildNode& node, const BuildValue& value) {
// Virtual input nodes are always valid unless the value type is wrong.
if (node.isVirtual())
return value.isVirtualInput();
// If the previous value wasn't for an existing input, always recompute.
if (!value.isExistingInput())
return false;
// Otherwise, the result is valid if the path exists 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.
auto info = node.getFileInfo();
if (info.isMissing())
return false;
return value.getOutputInfo() == info;
}
};
/// 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) {
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(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 is the task to actually execute a command.
class CommandTask : public Task {
Command& command;
virtual void start(BuildEngine& engine) override {
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 {
command.inputsAvailable(getBuildSystem(engine).getCommandInterface(), this);
}
public:
CommandTask(Command& command) : command(command) {}
static bool isResultValid(Command& command, const BuildValue& value) {
// Delegate to the command for further checking.
return command.isResultValid(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;
};
BuildFile& BuildSystemEngineDelegate::getBuildFile() {
return system.getBuildFile();
}
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 = getBuildFile().getCommands().find(key.getCommandName());
if (it == getBuildFile().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=*/ [](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](const Rule& rule, const ValueType& value) -> bool {
return CommandTask::isResultValid(
*command, BuildValue::fromData(value));
}
};
}
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: getBuildFile().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](const Rule& rule,
const ValueType& value) -> bool {
return CommandTask::isResultValid(
*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=*/ [](const Rule& rule, const ValueType& value) -> bool {
// The cached result for a missing command is never valid.
return false;
}
};
}
case BuildKey::Kind::Node: {
// Find the node.
auto it = getBuildFile().getNodes().find(key.getNodeName());
BuildNode* node;
if (it != getBuildFile().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()) {
return Rule{
keyData,
/*Action=*/ [node](BuildEngine& engine) -> Task* {
return engine.registerTask(new InputNodeTask(*node));
},
/*IsValid=*/ [node](const Rule& rule, const ValueType& value) -> bool {
return InputNodeTask::isResultValid(
*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](const Rule& rule, const ValueType& value) -> bool {
return ProducedNodeTask::isResultValid(
*node, BuildValue::fromData(value));
}
};
}
case BuildKey::Kind::Target: {
// Find the target.
auto it = getBuildFile().getTargets().find(key.getTargetName());
if (it == getBuildFile().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](const Rule& rule, const ValueType& value) -> bool {
return TargetTask::isResultValid(*target, BuildValue::fromData(value));
}
};
}
}
assert(0 && "invalid key type");
abort();
}
void BuildSystemEngineDelegate::cycleDetected(const std::vector<Rule*>& cycle) {
// Compute a description of the cycle path.
SmallString<256> message;
llvm::raw_svector_ostream os(message);
os << "cycle detected while building: ";
bool first = true;
for (const auto* rule: cycle) {
if (!first)
os << " -> ";
// Convert to a build key.
auto key = BuildKey::fromData(rule->key);
switch (key.getKind()) {
case BuildKey::Kind::Unknown:
os << "((unknown))";
break;
case BuildKey::Kind::Command:
os << "command '" << key.getCommandName() << "'";
break;
case BuildKey::Kind::CustomTask:
os << "custom task '" << key.getCustomTaskName() << "'";
break;
case BuildKey::Kind::Node:
os << "node '" << key.getNodeName() << "'";
break;
case BuildKey::Kind::Target:
os << "target '" << key.getTargetName() << "'";
break;
}
first = false;
}
system.error(system.getMainFilename(), os.str());
}
#pragma mark - BuildSystemImpl implementation
std::unique_ptr<BuildNode>
BuildSystemImpl::lookupNode(StringRef name, bool isImplicit) {
bool isVirtual = !name.empty() && name[0] == '<' && name.back() == '>';
return llvm::make_unique<BuildNode>(name, isVirtual);
}
bool BuildSystemImpl::build(StringRef target) {
// Load the build file.
//
// FIXME: Eventually, we may want to support something fancier where we load
// the build file in the background so we can immediately start building
// things as they show up.
//
// FIXME: We need to load this only once.
if (!getBuildFile().load()) {
error(getMainFilename(), "unable to load build file");
return false;
}
// Build the target.
getBuildEngine().build(BuildKey::makeTarget(target).toData());
return true;
}
#pragma mark - PhonyTool implementation
class PhonyCommand : public ExternalCommand {
public:
using ExternalCommand::ExternalCommand;
virtual bool executeExternalCommand(BuildSystemCommandInterface& bsci,
Task* task,
QueueJobContext* context) override {
// Nothing needs to be done for phony commands.
return true;
}
};
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 std::unique_ptr<Command> createCommand(StringRef name) override {
return llvm::make_unique<PhonyCommand>(name);
}
};
#pragma mark - ShellTool implementation
class ShellCommand : public ExternalCommand {
std::vector<std::string> args;
virtual uint64_t getSignature() override {
uint64_t result = ExternalCommand::getSignature();
for (const auto& arg: args) {
result ^= basic::hashString(arg);
}
return result;
}
public:
using ExternalCommand::ExternalCommand;
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("/bin/sh");
args.push_back("-c");
args.push_back(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") {
// Diagnose missing arguments.
if (values.empty()) {
ctx.error("invalid arguments for command '" + getName() + "'");
return false;
}
args = std::vector<std::string>(values.begin(), values.end());
} else {
return ExternalCommand::configureAttribute(ctx, name, values);
}
return true;
}
virtual bool executeExternalCommand(BuildSystemCommandInterface& bsci,
Task* task,
QueueJobContext* context) override {
// Log the command.
//
// FIXME: Design the logging and status output APIs.
if (bsci.getDelegate().showVerboseStatus() || getDescription().empty()) {
SmallString<256> command;
llvm::raw_svector_ostream commandOS(command);
bool first = true;
for (const auto& arg: args) {
if (!first) commandOS << " ";
first = false;
// FIXME: This isn't correct, we need utilities for doing shell quoting.
if (arg.find(' ') != StringRef::npos) {
commandOS << '"' << arg << '"';
} else {
commandOS << arg;
}
}
commandOS.flush();
fprintf(stdout, "%s\n", command.c_str());
} else {
fprintf(stdout, "%s\n", getDescription().str().c_str());
}
fflush(stdout);
// Execute the command.
return bsci.getExecutionQueue().executeProcess(
context, std::vector<StringRef>(args.begin(), args.end()));
}
};
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 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::string args;
/// The path to the dependency output file, if used.
std::string depsPath;
virtual uint64_t getSignature() override {
uint64_t result = ExternalCommand::getSignature();
result ^= basic::hashString(args);
return result;
}
bool processDiscoveredDependencies(BuildSystemCommandInterface& bsci,
Task* task,
QueueJobContext* context) {
// Read the dependencies file.
auto res = llvm::MemoryBuffer::getFile(depsPath);
if (auto ec = res.getError()) {
getBuildSystem(bsci.getBuildEngine()).error(
depsPath, "unable to open dependencies file (" + ec.message() + ")");
return false;
}
std::unique_ptr<llvm::MemoryBuffer> input(res->release());
// 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) override {
bsci.taskDiscoveredDependency(
task, BuildKey::makeNode(StringRef(dependency, length)));
}
virtual void actOnRuleStart(const char* name, uint64_t length) 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 bool configureAttribute(const ConfigureContext& ctx, StringRef name,
StringRef value) override {
if (name == "args") {
args = 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 {
return ExternalCommand::configureAttribute(ctx, name, values);
}
virtual bool executeExternalCommand(BuildSystemCommandInterface& bsci,
Task* task,
QueueJobContext* context) override {
// Log the command.
//
// FIXME: Design the logging and status output APIs.
if (getDescription().empty()) {
fprintf(stdout, "%s\n", args.c_str());
} else {
fprintf(stdout, "%s\n", getDescription().str().c_str());
}
fflush(stdout);
// Execute the command.
if (!bsci.getExecutionQueue().executeShellCommand(context, args)) {
// If the command failed, there is no need to gather dependencies.
return false;
}
// 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 false;
}
}
return true;
}
};
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 std::unique_ptr<Command> createCommand(StringRef name) override {
return llvm::make_unique<ClangShellCommand>(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);
}
return nullptr;
}
void BuildSystemFileDelegate::loadedTarget(StringRef name,
const Target& 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,
StringRef mainFilename)
: impl(new BuildSystemImpl(*this, delegate, mainFilename))
{
}
BuildSystem::~BuildSystem() {
delete static_cast<BuildSystemImpl*>(impl);
}
BuildSystemDelegate& BuildSystem::getDelegate() {
return static_cast<BuildSystemImpl*>(impl)->getDelegate();
}
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);
}
bool BuildSystem::build(StringRef name) {
return static_cast<BuildSystemImpl*>(impl)->build(name);
}