Skip to content

Commit 52b528f

Browse files
fix: Adjust command-line based on true compiler location (#375)
1 parent 5ffd41e commit 52b528f

File tree

2 files changed

+81
-38
lines changed

2 files changed

+81
-38
lines changed

indexer/CompilationDatabase.cc

Lines changed: 67 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,11 @@ struct CompletedProcess {
4141
}
4242
};
4343

44-
struct ResourceDirResult {
44+
struct ToolchainPathsResult {
4545
std::string resourceDir;
4646
std::vector<std::string> cliInvocation;
4747
CompilerKind compilerKind;
48+
std::string compilerDriverPath; // non-null for Clang
4849
};
4950
} // namespace
5051

@@ -78,11 +79,19 @@ static CompletedProcess runProcess(std::vector<std::string> &args,
7879
}
7980

8081
/// Returns an empty path if we failed to determine the resource dir
81-
ResourceDirResult static determineResourceDir(
82+
ToolchainPathsResult static determineToolchainPaths(
8283
const scip_clang::AbsolutePath &compilerPath) {
83-
ResourceDirResult out{"",
84-
{compilerPath.asStringRef(), "-print-resource-dir"},
85-
CompilerKind::Clang};
84+
ToolchainPathsResult out{"",
85+
{compilerPath.asStringRef(), "-print-resource-dir"},
86+
CompilerKind::Clang,
87+
""};
88+
89+
auto noteStdlib = []() {
90+
spdlog::warn("may be unable to locate standard library headers");
91+
spdlog::info("compilation errors are suppressed by default, but can be "
92+
"turned on using --show-compiler-diagnostics");
93+
};
94+
8695
auto printResourceDirResult =
8796
::runProcess(out.cliInvocation, "attempting to find resource dir");
8897
if (printResourceDirResult.isSuccess()) {
@@ -93,17 +102,33 @@ ResourceDirResult static determineResourceDir(
93102
}
94103
out.resourceDir = std::string(
95104
absl::StripAsciiWhitespace(printResourceDirResult.stdoutLines.front()));
105+
out.cliInvocation = {compilerPath.asStringRef(), "-###"};
106+
auto hashHashHashResult = ::runProcess(
107+
out.cliInvocation, "attempting to find installed directory");
108+
if (hashHashHashResult.isSuccess()) {
109+
for (auto &line : hashHashHashResult.stderrLines) {
110+
auto clangDriverDir = absl::StripPrefix(line, "InstalledDir: ");
111+
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");
116+
break;
117+
}
118+
}
119+
}
120+
if (out.compilerDriverPath.empty()) {
121+
spdlog::warn(
122+
"failed to determine compiler path using -### for compiler at '{}'",
123+
compilerPath.asStringRef());
124+
noteStdlib();
125+
}
96126
return out;
97127
}
98128
out.compilerKind = CompilerKind::Gcc;
99129
out.cliInvocation = {compilerPath.asStringRef(), "-print-search-dirs"};
100130
auto printSearchDirsResult =
101131
::runProcess(out.cliInvocation, "attempting to find search dirs");
102-
auto noteStdlib = []() {
103-
spdlog::warn("may be unable to locate standard library headers");
104-
spdlog::info("compilation errors are suppressed by default, but can be "
105-
"turned on using --show-compiler-diagnostics");
106-
};
107132
if (!printSearchDirsResult.isSuccess()) {
108133
spdlog::warn(
109134
"both -print-resource-dir and -print-search-dirs failed for {}",
@@ -610,73 +635,82 @@ void ResumableParser::parseMore(
610635

611636
void ResumableParser::tryInferResourceDir(
612637
const std::string &directoryPath, std::vector<std::string> &commandLine) {
613-
auto &compilerPath = commandLine.front();
614-
auto it = this->extraArgsMap.find(compilerPath);
615-
if (it != this->extraArgsMap.end()) {
616-
for (auto &extraArg : it->second) {
638+
auto &compilerOrWrapperPath = commandLine.front();
639+
auto adjustCommandLine = [](auto &commandLine, auto it) {
640+
if (!it->second.compilerDriverPath.empty()) {
641+
commandLine[0] = it->second.compilerDriverPath;
642+
}
643+
for (auto &extraArg : it->second.extraArgs) {
617644
commandLine.push_back(extraArg);
618645
}
646+
};
647+
auto it = this->toolchainConfigMap.find(compilerOrWrapperPath);
648+
if (it != this->toolchainConfigMap.end()) {
649+
adjustCommandLine(commandLine, it);
619650
return;
620651
}
621652
AbsolutePath compilerInvocationPath;
622-
auto fail = [&]() { this->extraArgsMap.insert({compilerPath, {}}); };
653+
auto fail = [&]() {
654+
this->toolchainConfigMap.insert(
655+
{compilerOrWrapperPath, ToolchainConfig{"", {}}});
656+
};
623657

624-
if (compilerPath.find(std::filesystem::path::preferred_separator)
658+
if (compilerOrWrapperPath.find(std::filesystem::path::preferred_separator)
625659
== std::string::npos) {
626-
auto absPath = boost::process::search_path(compilerPath).native();
660+
auto absPath = boost::process::search_path(compilerOrWrapperPath).native();
627661
if (absPath.empty()) {
628662
this->emitResourceDirError(fmt::format(
629663
"scip-clang needs to be invoke '{0}' (found via the compilation"
630664
" database) to determine the resource directory, but couldn't find"
631665
" '{0}' on PATH. Hint: Use a modified PATH to invoke scip-clang,"
632666
" or change the compilation database to use absolute paths"
633667
" for the compiler.",
634-
compilerPath));
668+
compilerOrWrapperPath));
635669
return fail();
636670
}
637671
compilerInvocationPath =
638672
AbsolutePath(std::string(absPath.data(), absPath.size()));
639-
} else if (llvm::sys::path::is_relative(compilerPath)) {
673+
} else if (llvm::sys::path::is_relative(compilerOrWrapperPath)) {
640674
if (llvm::sys::path::is_absolute(directoryPath)) {
641675
compilerInvocationPath = AbsolutePath(fmt::format(
642676
"{}{}{}", directoryPath, std::filesystem::path::preferred_separator,
643-
compilerPath));
677+
compilerOrWrapperPath));
644678
} else {
645679
spdlog::warn(
646680
R"("directory": "{}" key in compilation database is not an absolute path)"
647681
"; unable to determine resource directory for compiler: {}",
648-
directoryPath, compilerPath);
682+
directoryPath, compilerOrWrapperPath);
649683
}
650684
} else {
651-
ENFORCE(llvm::sys::path::is_absolute(compilerPath));
652-
compilerInvocationPath = AbsolutePath(std::string(compilerPath));
685+
ENFORCE(llvm::sys::path::is_absolute(compilerOrWrapperPath));
686+
compilerInvocationPath = AbsolutePath(std::string(compilerOrWrapperPath));
653687
}
654688
if (compilerInvocationPath.asStringRef().empty()) {
655689
return fail();
656690
}
657-
auto resourceDirResult = ::determineResourceDir(compilerInvocationPath);
658-
if (resourceDirResult.resourceDir.empty()) {
691+
auto toolchainPathsResult = ::determineToolchainPaths(compilerInvocationPath);
692+
if (toolchainPathsResult.resourceDir.empty()) {
659693
return fail();
660694
}
661-
auto &resourceDir = resourceDirResult.resourceDir;
695+
auto &resourceDir = toolchainPathsResult.resourceDir;
662696
std::vector<std::string> extraArgs{"-resource-dir", resourceDir};
663-
if (resourceDirResult.compilerKind == CompilerKind::Gcc) {
697+
if (toolchainPathsResult.compilerKind == CompilerKind::Gcc) {
664698
// gcc-7 adds headers like limits.h and syslimits.h in include-fixed
665699
extraArgs.push_back(fmt::format("-I{}/include-fixed", resourceDir));
666700
}
667701
spdlog::debug("got resource dir '{}'", resourceDir);
668702
if (!std::filesystem::exists(resourceDir)) {
669703
this->emitResourceDirError(fmt::format(
670704
"'{}' returned '{}' but the directory does not exist",
671-
fmt::join(resourceDirResult.cliInvocation, " "), resourceDir));
705+
fmt::join(toolchainPathsResult.cliInvocation, " "), resourceDir));
672706
return fail();
673707
}
674-
auto [newIt, inserted] =
675-
this->extraArgsMap.emplace(compilerPath, std::move(extraArgs));
708+
auto [newIt, inserted] = this->toolchainConfigMap.emplace(
709+
compilerOrWrapperPath,
710+
ToolchainConfig{toolchainPathsResult.compilerDriverPath,
711+
std::move(extraArgs)});
676712
ENFORCE(inserted);
677-
for (auto &arg : newIt->second) {
678-
commandLine.push_back(arg);
679-
}
713+
adjustCommandLine(commandLine, newIt);
680714
}
681715

682716
void ResumableParser::emitResourceDirError(std::string &&error) {

indexer/CompilationDatabase.h

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,13 @@ class CommandObjectHandler
8080
bool reachedLimit() const;
8181
};
8282

83+
struct ToolchainConfig {
84+
std::string compilerDriverPath;
85+
/// The vector may be empty if we failed to determine
86+
/// the correct arguments.
87+
std::vector<std::string> extraArgs;
88+
};
89+
8390
class ResumableParser {
8491
std::string jsonStreamBuffer;
8592
std::optional<rapidjson::FileReadStream> compDbStream;
@@ -89,12 +96,14 @@ class ResumableParser {
8996
bool inferResourceDir;
9097
absl::flat_hash_set<std::string> emittedErrors;
9198

92-
/// Mapping from compiler -> extra command-line arguments needed
93-
/// to set up include directories correctly.
99+
/// Mapping from compiler/wrapper path to extra information needed
100+
/// to tweak the compilation database entry before invoking the driver.
94101
///
95-
/// The vector may be empty if we failed to determine the correct
96-
/// arguments.
97-
absl::flat_hash_map<std::string, std::vector<std::string>> extraArgsMap;
102+
/// For example, Bazel uses a compiler wrapper, but scip-clang needs
103+
/// to use the full path to the compiler driver when running semantic
104+
/// analysis, so that include directories are picked up correctly
105+
/// relative to the driver's location.
106+
absl::flat_hash_map<std::string, ToolchainConfig> toolchainConfigMap;
98107

99108
public:
100109
ResumableParser() = default;

0 commit comments

Comments
 (0)