1//===-- Driver.cpp -------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "llvm/Support/Driver.h"
10
11#include "llvm/ADT/SmallVector.h"
12#include "llvm/ADT/StringExtras.h"
13#include "llvm/Support/InitLLVM.h"
14#include "llvm/Support/Path.h"
15
16#include <cassert>
17#include <string>
18#include <system_error>
19#include <utility>
20#include <vector>
21
22using namespace llvm;
23
24namespace {
25
26bool matchesToolName(StringRef RegisteredName, StringRef InvokedName) {
27 StringRef Stem = sys::path::stem(path: InvokedName);
28 StringRef Filename = sys::path::filename(path: InvokedName);
29 auto Matches = [RegisteredName](StringRef Candidate) {
30 size_t Position = Candidate.rfind_insensitive(Str: RegisteredName);
31 return Position != StringRef::npos &&
32 (Position + RegisteredName.size() == Candidate.size() ||
33 !llvm::isAlnum(C: Candidate[Position + RegisteredName.size()]));
34 };
35 return Matches(Stem) || Matches(Filename);
36}
37
38bool isMulticallName(StringRef Name) { return matchesToolName(RegisteredName: "llvm", InvokedName: Name); }
39
40} // namespace
41
42struct ToolSession::Impl {
43 InitLLVM Initialization;
44 std::string ExecutablePath;
45 std::vector<std::pair<std::string, ToolMainFn>> Tools;
46
47 Impl(int &Argc, char **&Argv, ArrayRef<CallableTool> RegisteredTools,
48 bool InstallPipeSignalExitHandler, bool NeedsPOSIXUtilitySignalHandling)
49 : Initialization(Argc, Argv, InstallPipeSignalExitHandler,
50 NeedsPOSIXUtilitySignalHandling),
51 ExecutablePath(Argv[0]) {
52 Tools.reserve(n: RegisteredTools.size());
53 for (const CallableTool &Tool : RegisteredTools)
54 Tools.emplace_back(args: Tool.Name.str(), args: Tool.Main);
55 }
56};
57
58ToolSession::ToolSession(int &Argc, char **&Argv, ArrayRef<CallableTool> Tools,
59 bool InstallPipeSignalExitHandler,
60 bool NeedsPOSIXUtilitySignalHandling) {
61 assert(Argc > 0 && Argv && Argv[0] && "ToolSession requires a valid argv[0]");
62 PImpl =
63 std::make_unique<Impl>(args&: Argc, args&: Argv, args&: Tools, args&: InstallPipeSignalExitHandler,
64 args&: NeedsPOSIXUtilitySignalHandling);
65}
66
67ToolSession::~ToolSession() = default;
68
69ErrorOr<CallableTool> ToolSession::findTool(StringRef Name) const {
70 StringRef Stem = sys::path::stem(path: Name);
71 StringRef Filename = sys::path::filename(path: Name);
72 for (const auto &[RegisteredName, Main] : PImpl->Tools)
73 if (Stem.equals_insensitive(RHS: RegisteredName) ||
74 Filename.equals_insensitive(RHS: RegisteredName))
75 return CallableTool{.Name: RegisteredName, .Main: Main};
76
77 for (const auto &[RegisteredName, Main] : PImpl->Tools)
78 if (matchesToolName(RegisteredName, InvokedName: Name))
79 return CallableTool{.Name: RegisteredName, .Main: Main};
80 return make_error_code(e: std::errc::no_such_file_or_directory);
81}
82
83ToolContext ToolSession::makeContext(StringRef RegisteredName,
84 const char *PrependArg) {
85 ErrorOr<CallableTool> ExecutableTool = findTool(Name: PImpl->ExecutablePath);
86 bool NeedsPrependArg =
87 !ExecutableTool ||
88 !ExecutableTool->Name.equals_insensitive(RHS: RegisteredName);
89 ToolContext Context(PImpl->ExecutablePath.c_str(), PrependArg,
90 NeedsPrependArg);
91 Context.Session = this;
92 return Context;
93}
94
95ErrorOr<int> ToolSession::callTool(ArrayRef<const char *> Args) {
96 if (Args.empty())
97 return make_error_code(e: std::errc::invalid_argument);
98
99 StringRef InvokedName = Args.front();
100 ErrorOr<CallableTool> Tool = findTool(Name: InvokedName);
101 if (!Tool) {
102 if (InvokedName != PImpl->ExecutablePath && !isMulticallName(Name: InvokedName))
103 return make_error_code(e: std::errc::no_such_file_or_directory);
104 Args = Args.drop_front();
105 if (Args.empty())
106 return make_error_code(e: std::errc::invalid_argument);
107 InvokedName = Args.front();
108 Tool = findTool(Name: InvokedName);
109 }
110
111 if (!Tool)
112 return Tool.getError();
113
114 std::string PrependArg = sys::path::stem(path: InvokedName).str();
115 ToolContext Context = makeContext(RegisteredName: Tool->Name, PrependArg: PrependArg.c_str());
116 SmallVector<char *, 16> MutableArgs;
117 MutableArgs.reserve(N: Args.size() + 1);
118 for (const char *Arg : Args)
119 MutableArgs.push_back(Elt: const_cast<char *>(Arg));
120 MutableArgs.push_back(Elt: nullptr);
121 return Tool->Main(Args.size(), MutableArgs.data(), Context);
122}
123
124ErrorOr<CallableTool> ToolContext::getCallableTool(StringRef Name) const {
125 if (!Session)
126 return make_error_code(e: std::errc::operation_not_permitted);
127 return Session->findTool(Name);
128}
129
130ErrorOr<int> ToolContext::callTool(ArrayRef<const char *> Args) const {
131 if (!Session)
132 return make_error_code(e: std::errc::operation_not_permitted);
133 return Session->callTool(Args);
134}
135