| //===-- 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); | |
| } |