blob: 6a113f1dd8c3e868d0a900b63c69d3f25f279a7c [file] [edit]
//===-- Driver.cpp -------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "llvm/Support/Driver.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/Path.h"
#include <cassert>
#include <string>
#include <system_error>
#include <utility>
#include <vector>
using namespace llvm;
namespace {
bool matchesToolName(StringRef RegisteredName, StringRef InvokedName) {
StringRef Stem = sys::path::stem(InvokedName);
StringRef Filename = sys::path::filename(InvokedName);
auto Matches = [RegisteredName](StringRef Candidate) {
size_t Position = Candidate.rfind_insensitive(RegisteredName);
return Position != StringRef::npos &&
(Position + RegisteredName.size() == Candidate.size() ||
!llvm::isAlnum(Candidate[Position + RegisteredName.size()]));
};
return Matches(Stem) || Matches(Filename);
}
bool isMulticallName(StringRef Name) { return matchesToolName("llvm", Name); }
} // namespace
struct ToolSession::Impl {
InitLLVM Initialization;
std::string ExecutablePath;
std::vector<std::pair<std::string, ToolMainFn>> Tools;
Impl(int &Argc, char **&Argv, ArrayRef<CallableTool> RegisteredTools,
bool InstallPipeSignalExitHandler, bool NeedsPOSIXUtilitySignalHandling)
: Initialization(Argc, Argv, InstallPipeSignalExitHandler,
NeedsPOSIXUtilitySignalHandling),
ExecutablePath(Argv[0]) {
Tools.reserve(RegisteredTools.size());
for (const CallableTool &Tool : RegisteredTools)
Tools.emplace_back(Tool.Name.str(), Tool.Main);
}
};
ToolSession::ToolSession(int &Argc, char **&Argv, ArrayRef<CallableTool> Tools,
bool InstallPipeSignalExitHandler,
bool NeedsPOSIXUtilitySignalHandling) {
assert(Argc > 0 && Argv && Argv[0] && "ToolSession requires a valid argv[0]");
PImpl =
std::make_unique<Impl>(Argc, Argv, Tools, InstallPipeSignalExitHandler,
NeedsPOSIXUtilitySignalHandling);
}
ToolSession::~ToolSession() = default;
ErrorOr<CallableTool> ToolSession::findTool(StringRef Name) const {
StringRef Stem = sys::path::stem(Name);
StringRef Filename = sys::path::filename(Name);
for (const auto &[RegisteredName, Main] : PImpl->Tools)
if (Stem.equals_insensitive(RegisteredName) ||
Filename.equals_insensitive(RegisteredName))
return CallableTool{RegisteredName, Main};
for (const auto &[RegisteredName, Main] : PImpl->Tools)
if (matchesToolName(RegisteredName, Name))
return CallableTool{RegisteredName, Main};
return make_error_code(std::errc::no_such_file_or_directory);
}
ToolContext ToolSession::makeContext(StringRef RegisteredName,
const char *PrependArg) {
ErrorOr<CallableTool> ExecutableTool = findTool(PImpl->ExecutablePath);
bool NeedsPrependArg =
!ExecutableTool ||
!ExecutableTool->Name.equals_insensitive(RegisteredName);
ToolContext Context(PImpl->ExecutablePath.c_str(), PrependArg,
NeedsPrependArg);
Context.Session = this;
return Context;
}
ErrorOr<int> ToolSession::callTool(ArrayRef<const char *> Args) {
if (Args.empty())
return make_error_code(std::errc::invalid_argument);
StringRef InvokedName = Args.front();
ErrorOr<CallableTool> Tool = findTool(InvokedName);
if (!Tool) {
if (InvokedName != PImpl->ExecutablePath && !isMulticallName(InvokedName))
return make_error_code(std::errc::no_such_file_or_directory);
Args = Args.drop_front();
if (Args.empty())
return make_error_code(std::errc::invalid_argument);
InvokedName = Args.front();
Tool = findTool(InvokedName);
}
if (!Tool)
return Tool.getError();
std::string PrependArg = sys::path::stem(InvokedName).str();
ToolContext Context = makeContext(Tool->Name, PrependArg.c_str());
SmallVector<char *, 16> MutableArgs;
MutableArgs.reserve(Args.size() + 1);
for (const char *Arg : Args)
MutableArgs.push_back(const_cast<char *>(Arg));
MutableArgs.push_back(nullptr);
return Tool->Main(Args.size(), MutableArgs.data(), Context);
}
ErrorOr<CallableTool> ToolContext::getCallableTool(StringRef Name) const {
if (!Session)
return make_error_code(std::errc::operation_not_permitted);
return Session->findTool(Name);
}
ErrorOr<int> ToolContext::callTool(ArrayRef<const char *> Args) const {
if (!Session)
return make_error_code(std::errc::operation_not_permitted);
return Session->callTool(Args);
}