Skip to content

Commit ecab783

Browse files
refactor: Centralize code for toolchain handling (#432)
This allows supporting more toolchains more easily by adding more derived classes.
1 parent 0631f99 commit ecab783

File tree

5 files changed

+223
-123
lines changed

5 files changed

+223
-123
lines changed

indexer/CompilationDatabase.cc

Lines changed: 163 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,6 @@
2525
#include "indexer/LlvmCommandLineParsing.h"
2626

2727
namespace {
28-
enum class CompilerKind {
29-
Gcc,
30-
Clang,
31-
};
3228

3329
struct CompletedProcess {
3430
int exitCode;
@@ -41,12 +37,87 @@ struct CompletedProcess {
4137
}
4238
};
4339

44-
struct ToolchainPathsResult {
40+
using ToolchainInfo = scip_clang::compdb::ToolchainInfo;
41+
using CompilerKind = scip_clang::compdb::CompilerKind;
42+
43+
struct ClangToolchainInfo : ToolchainInfo {
4544
std::string resourceDir;
46-
std::vector<std::string> cliInvocation;
47-
CompilerKind compilerKind;
48-
std::string compilerDriverPath; // non-null for Clang
45+
std::vector<std::string> findResourceDirInvocation;
46+
std::string compilerDriverPath;
47+
std::vector<std::string> findDriverInvocation;
48+
49+
// All strings and vectors above should be non-empty for
50+
// a valid toolchain.
51+
52+
ClangToolchainInfo(std::string resourceDir,
53+
std::vector<std::string> findResourceDirInvocation,
54+
std::string compilerDriverPath,
55+
std::vector<std::string> findDriverInvocation)
56+
: ToolchainInfo(), resourceDir(resourceDir),
57+
findResourceDirInvocation(findResourceDirInvocation),
58+
compilerDriverPath(compilerDriverPath),
59+
findDriverInvocation(findDriverInvocation){};
60+
61+
virtual CompilerKind kind() const override {
62+
return CompilerKind::Clang;
63+
}
64+
65+
virtual bool isWellFormed() const override {
66+
if (!std::filesystem::exists(this->resourceDir)) {
67+
spdlog::error(
68+
"clang resource directory '{}' does not exist (obtained via {})",
69+
this->resourceDir, fmt::join(this->findResourceDirInvocation, " "));
70+
return false;
71+
}
72+
if (!std::filesystem::exists(this->compilerDriverPath)) {
73+
spdlog::error("compiler driver at '{}' does not exist (obtained via {})",
74+
this->compilerDriverPath,
75+
fmt::join(this->findDriverInvocation, " "));
76+
return false;
77+
}
78+
return true;
79+
}
80+
81+
virtual void
82+
adjustCommandLine(std::vector<std::string> &commandLine) const override {
83+
commandLine[0] = this->compilerDriverPath;
84+
commandLine.push_back("-resource-dir");
85+
commandLine.push_back(this->resourceDir);
86+
}
4987
};
88+
89+
struct GccToolchainInfo : ToolchainInfo {
90+
std::string installDir;
91+
std::vector<std::string> findInstallDirInvocation;
92+
93+
GccToolchainInfo(std::string installDir,
94+
std::vector<std::string> findInstallDirInvocation)
95+
: ToolchainInfo(), installDir(installDir),
96+
findInstallDirInvocation(findInstallDirInvocation) {}
97+
98+
virtual CompilerKind kind() const override {
99+
return CompilerKind::Gcc;
100+
}
101+
102+
virtual bool isWellFormed() const override {
103+
if (!std::filesystem::exists(this->installDir)) {
104+
spdlog::error(
105+
"GCC install directory '{}' does not exist (obtained via {})",
106+
this->installDir, fmt::join(this->findInstallDirInvocation, " "));
107+
return false;
108+
}
109+
return true;
110+
}
111+
112+
virtual void
113+
adjustCommandLine(std::vector<std::string> &commandLine) const override {
114+
commandLine.push_back("-resource-dir");
115+
commandLine.push_back(this->installDir);
116+
// gcc-7 adds headers like limits.h and syslimits.h in include-fixed
117+
commandLine.push_back(fmt::format("-I{}/include-fixed", this->installDir));
118+
}
119+
};
120+
50121
} // namespace
51122

52123
static CompletedProcess runProcess(std::vector<std::string> &args,
@@ -78,81 +149,92 @@ static CompletedProcess runProcess(std::vector<std::string> &args,
78149
return out;
79150
}
80151

81-
/// Returns an empty path if we failed to determine the resource dir
82-
ToolchainPathsResult static determineToolchainPaths(
83-
const scip_clang::AbsolutePath &compilerPath) {
84-
ToolchainPathsResult out{"",
85-
{compilerPath.asStringRef(), "-print-resource-dir"},
86-
CompilerKind::Clang,
87-
""};
152+
/*static*/ std::unique_ptr<ToolchainInfo>
153+
ToolchainInfo::infer(const scip_clang::AbsolutePath &compilerPath) {
88154

89155
auto noteStdlib = []() {
90156
spdlog::warn("may be unable to locate standard library headers");
91157
spdlog::info("compilation errors are suppressed by default, but can be "
92158
"turned on using --show-compiler-diagnostics");
93159
};
94160

95-
auto printResourceDirResult =
96-
::runProcess(out.cliInvocation, "attempting to find resource dir");
161+
std::vector<std::string> findResourceDirInvocation = {
162+
compilerPath.asStringRef(), "-print-resource-dir"};
163+
auto failure = std::unique_ptr<ToolchainInfo>(nullptr);
164+
165+
auto printResourceDirResult = ::runProcess(findResourceDirInvocation,
166+
"attempting to find resource dir");
97167
if (printResourceDirResult.isSuccess()) {
98168
if (printResourceDirResult.stdoutLines.empty()) {
99-
spdlog::warn(
100-
"-print-resource-dir succeeded but returned an empty result");
101-
return out;
169+
spdlog::warn("{} succeeded but returned an empty result",
170+
fmt::join(findResourceDirInvocation, " "));
171+
return failure;
102172
}
103-
out.resourceDir = std::string(
173+
auto resourceDir = std::string(
104174
absl::StripAsciiWhitespace(printResourceDirResult.stdoutLines.front()));
105-
out.cliInvocation = {compilerPath.asStringRef(), "-###"};
175+
spdlog::debug("got resource dir {} from {}", resourceDir,
176+
compilerPath.asStringRef());
177+
178+
std::vector<std::string> findDriverInvocation = {compilerPath.asStringRef(),
179+
"-###"};
106180
auto hashHashHashResult = ::runProcess(
107-
out.cliInvocation, "attempting to find installed directory");
181+
findDriverInvocation, "attempting to find installed directory");
182+
std::string compilerDriverPath = "";
108183
if (hashHashHashResult.isSuccess()) {
109184
for (auto &line : hashHashHashResult.stderrLines) {
110185
auto clangDriverDir = absl::StripPrefix(line, "InstalledDir: ");
111186
if (clangDriverDir.length() != line.length()) {
112-
out.compilerDriverPath = absl::StripAsciiWhitespace(clangDriverDir);
113-
out.compilerDriverPath.push_back(
114-
std::filesystem::path::preferred_separator);
115-
out.compilerDriverPath.append("clang");
187+
compilerDriverPath = scip_clang::joinPath(
188+
absl::StripAsciiWhitespace(clangDriverDir), "clang");
189+
spdlog::debug("found compiler driver at {}", compilerDriverPath);
116190
break;
117191
}
118192
}
119193
}
120-
if (out.compilerDriverPath.empty()) {
194+
if (compilerDriverPath.empty()) {
121195
spdlog::warn(
122196
"failed to determine compiler path using -### for compiler at '{}'",
123197
compilerPath.asStringRef());
124198
noteStdlib();
199+
return failure;
125200
}
126-
return out;
201+
202+
return std::make_unique<ClangToolchainInfo>(
203+
resourceDir, findResourceDirInvocation, compilerDriverPath,
204+
findDriverInvocation);
127205
}
128-
out.compilerKind = CompilerKind::Gcc;
129-
out.cliInvocation = {compilerPath.asStringRef(), "-print-search-dirs"};
206+
207+
std::vector<std::string> findSearchDirsInvocation = {
208+
compilerPath.asStringRef(), "-print-search-dirs"};
130209
auto printSearchDirsResult =
131-
::runProcess(out.cliInvocation, "attempting to find search dirs");
132-
if (!printSearchDirsResult.isSuccess()) {
133-
spdlog::warn(
134-
"both -print-resource-dir and -print-search-dirs failed for {}",
135-
compilerPath.asStringRef());
136-
noteStdlib();
137-
return out;
138-
}
139-
absl::c_any_of(
140-
printSearchDirsResult.stdoutLines, [&](const std::string &line) -> bool {
141-
if (line.starts_with("install:")) {
142-
out.resourceDir =
143-
absl::StripAsciiWhitespace(absl::StripPrefix(line, "install:"));
144-
return true;
145-
}
146-
return false;
147-
});
148-
if (out.resourceDir.empty()) {
149-
spdlog::warn(
150-
"missing 'install:' line in -print-search-dirs from GCC(-like?) {}",
151-
compilerPath.asStringRef());
152-
noteStdlib();
153-
return out;
210+
::runProcess(findSearchDirsInvocation, "attempting to find search dirs");
211+
if (printSearchDirsResult.isSuccess()) {
212+
std::string installDir;
213+
absl::c_any_of(printSearchDirsResult.stdoutLines,
214+
[&](const std::string &line) -> bool {
215+
if (line.starts_with("install:")) {
216+
installDir = absl::StripAsciiWhitespace(
217+
absl::StripPrefix(line, "install:"));
218+
return true;
219+
}
220+
return false;
221+
});
222+
if (installDir.empty()) {
223+
spdlog::warn(
224+
"missing 'install:' line in -print-search-dirs from GCC(-like?) {}",
225+
compilerPath.asStringRef());
226+
noteStdlib();
227+
return failure;
228+
}
229+
spdlog::debug("found gcc install directory at {}", installDir);
230+
return std::make_unique<GccToolchainInfo>(installDir,
231+
findSearchDirsInvocation);
154232
}
155-
return out;
233+
234+
spdlog::warn("compiler at '{}' is not one of clang/clang++/gcc/g++",
235+
compilerPath.asStringRef());
236+
noteStdlib();
237+
return failure;
156238
}
157239

158240
namespace scip_clang {
@@ -589,7 +671,7 @@ compdb::File::openAndExitOnErrors(const StdPath &path,
589671
// static
590672
ParseOptions ParseOptions::create(size_t refillCount, bool forTesting) {
591673
ENFORCE(refillCount > 0);
592-
return ParseOptions{refillCount, /*inferResourceDir*/ !forTesting,
674+
return ParseOptions{refillCount, /*adjustCommandLine*/ !forTesting,
593675
/*skipNonMainFileTuEntries*/ !forTesting,
594676
/*checkFilesExist*/ !forTesting};
595677
}
@@ -684,43 +766,39 @@ void ResumableParser::parseMore(std::vector<compdb::CommandObject> &out) {
684766
this->handler->commands.clear();
685767
}
686768

687-
if (this->options.inferResourceDir) {
769+
if (this->options.adjustCommandLine) {
688770
for (auto &cmd : out) {
689771
if (cmd.arguments.empty()) {
690772
continue;
691773
}
692-
this->tryInferResourceDir(cmd.workingDirectory, cmd.arguments);
774+
this->adjustCommandLine(cmd.workingDirectory, cmd.arguments);
693775
}
694776
}
695777
}
696778

697-
void ResumableParser::tryInferResourceDir(
698-
const std::string &directoryPath, std::vector<std::string> &commandLine) {
779+
void ResumableParser::adjustCommandLine(const std::string &directoryPath,
780+
std::vector<std::string> &commandLine) {
699781
auto &compilerOrWrapperPath = commandLine.front();
700-
auto adjustCommandLine = [](auto &commandLine, auto it) {
701-
if (!it->second.compilerDriverPath.empty()) {
702-
commandLine[0] = it->second.compilerDriverPath;
703-
}
704-
for (auto &extraArg : it->second.extraArgs) {
705-
commandLine.push_back(extraArg);
782+
auto it = this->toolchainInfoMap.find(compilerOrWrapperPath);
783+
if (it != this->toolchainInfoMap.end()) {
784+
auto &toolchain = it->second;
785+
if (toolchain) {
786+
toolchain->adjustCommandLine(commandLine);
706787
}
707-
};
708-
auto it = this->toolchainConfigMap.find(compilerOrWrapperPath);
709-
if (it != this->toolchainConfigMap.end()) {
710-
adjustCommandLine(commandLine, it);
711788
return;
712789
}
713-
AbsolutePath compilerInvocationPath;
790+
714791
auto fail = [&]() {
715-
this->toolchainConfigMap.insert(
716-
{compilerOrWrapperPath, ToolchainConfig{"", {}}});
792+
this->toolchainInfoMap.insert(
793+
{compilerOrWrapperPath, std::unique_ptr<ToolchainInfo>(nullptr)});
717794
};
718795

796+
AbsolutePath compilerInvocationPath;
719797
if (compilerOrWrapperPath.find(std::filesystem::path::preferred_separator)
720798
== std::string::npos) {
721799
auto absPath = boost::process::search_path(compilerOrWrapperPath).native();
722800
if (absPath.empty()) {
723-
this->emitResourceDirError(fmt::format(
801+
this->emitError(fmt::format(
724802
"scip-clang needs to be invoke '{0}' (found via the compilation"
725803
" database) to determine the resource directory, but couldn't find"
726804
" '{0}' on PATH. Hint: Use a modified PATH to invoke scip-clang,"
@@ -733,9 +811,8 @@ void ResumableParser::tryInferResourceDir(
733811
AbsolutePath(std::string(absPath.data(), absPath.size()));
734812
} else if (llvm::sys::path::is_relative(compilerOrWrapperPath)) {
735813
if (llvm::sys::path::is_absolute(directoryPath)) {
736-
compilerInvocationPath = AbsolutePath(fmt::format(
737-
"{}{}{}", directoryPath, std::filesystem::path::preferred_separator,
738-
compilerOrWrapperPath));
814+
compilerInvocationPath = AbsolutePath(
815+
scip_clang::joinPath(directoryPath, compilerOrWrapperPath));
739816
} else {
740817
spdlog::warn(
741818
R"("directory": "{}" key in compilation database is not an absolute path)"
@@ -746,40 +823,27 @@ void ResumableParser::tryInferResourceDir(
746823
ENFORCE(llvm::sys::path::is_absolute(compilerOrWrapperPath));
747824
compilerInvocationPath = AbsolutePath(std::string(compilerOrWrapperPath));
748825
}
826+
749827
if (compilerInvocationPath.asStringRef().empty()) {
750828
return fail();
751829
}
752-
auto toolchainPathsResult = ::determineToolchainPaths(compilerInvocationPath);
753-
if (toolchainPathsResult.resourceDir.empty()) {
754-
return fail();
755-
}
756-
auto &resourceDir = toolchainPathsResult.resourceDir;
757-
std::vector<std::string> extraArgs{"-resource-dir", resourceDir};
758-
if (toolchainPathsResult.compilerKind == CompilerKind::Gcc) {
759-
// gcc-7 adds headers like limits.h and syslimits.h in include-fixed
760-
extraArgs.push_back(fmt::format("-I{}/include-fixed", resourceDir));
761-
}
762-
spdlog::debug("got resource dir '{}'", resourceDir);
763-
if (!std::filesystem::exists(resourceDir)) {
764-
this->emitResourceDirError(fmt::format(
765-
"'{}' returned '{}' but the directory does not exist",
766-
fmt::join(toolchainPathsResult.cliInvocation, " "), resourceDir));
830+
auto optToolchainInfo = ToolchainInfo::infer(compilerInvocationPath);
831+
if (!optToolchainInfo || !optToolchainInfo->isWellFormed()) {
767832
return fail();
768833
}
769-
auto [newIt, inserted] = this->toolchainConfigMap.emplace(
770-
compilerOrWrapperPath,
771-
ToolchainConfig{toolchainPathsResult.compilerDriverPath,
772-
std::move(extraArgs)});
834+
835+
auto [newIt, inserted] = this->toolchainInfoMap.emplace(
836+
compilerOrWrapperPath, std::move(optToolchainInfo));
773837
ENFORCE(inserted);
774-
adjustCommandLine(commandLine, newIt);
838+
newIt->second->adjustCommandLine(commandLine);
775839
}
776840

777-
void ResumableParser::emitResourceDirError(std::string &&error) {
841+
void ResumableParser::emitError(std::string &&error) {
778842
auto [it, inserted] = this->emittedErrors.emplace(std::move(error));
779843
if (inserted) {
780844
spdlog::error("{}", *it);
781845
}
782846
}
783847

784848
} // namespace compdb
785-
} // namespace scip_clang
849+
} // namespace scip_clang

0 commit comments

Comments
 (0)