Skip to content

Commit

Permalink
fix: Ignore resource dir for non-Clang compilers (#178)
Browse files Browse the repository at this point in the history
  • Loading branch information
varungandhi-src authored Apr 14, 2023
1 parent d075d44 commit ced7e7d
Show file tree
Hide file tree
Showing 7 changed files with 153 additions and 44 deletions.
2 changes: 1 addition & 1 deletion indexer/CliOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ struct CliOptions {
std::string temporaryOutputDir;
std::string indexOutputPath;
std::string statsFilePath;
bool showClangDiagnostics;
bool showCompilerDiagonstics;

std::chrono::seconds receiveTimeout;
uint32_t numWorkers;
Expand Down
169 changes: 136 additions & 33 deletions indexer/CompilationDatabase.cc
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include <cstdint>
#include <filesystem>
#include <memory>
#include <optional>
#include <string>
#include <vector>

Expand All @@ -9,6 +10,8 @@

#include "absl/algorithm/container.h"
#include "absl/container/flat_hash_set.h"
#include "absl/strings/ascii.h"
#include "absl/strings/strip.h"
#include "boost/process/child.hpp"
#include "boost/process/io.hpp"
#include "boost/process/search_path.hpp"
Expand All @@ -21,6 +24,110 @@
#include "indexer/FileSystem.h"
#include "indexer/LlvmCommandLineParsing.h"

namespace {
enum class CompilerKind {
Gcc,
Clang,
};

struct CompletedProcess {
int exitCode;
std::optional<boost::process::process_error> error;
std::vector<std::string> stdoutLines;
std::vector<std::string> stderrLines;

bool isSuccess() const {
return this->exitCode == EXIT_SUCCESS && !this->error.has_value();
}
};

struct ResourceDirResult {
std::string resourceDir;
std::vector<std::string> cliInvocation;
CompilerKind compilerKind;
};
} // namespace

static CompletedProcess runProcess(std::vector<std::string> &args,
const char *logContext) {
CompletedProcess out{.exitCode = EXIT_FAILURE,
.error = std::nullopt,
.stdoutLines = {},
.stderrLines = {}};
boost::process::ipstream stdoutStream, stderrStream;
BOOST_TRY {
spdlog::debug("{}{}invoking '{}'", logContext ? logContext : "",
logContext ? " by " : "", fmt::join(args, " "));
boost::process::child worker(args, boost::process::std_out > stdoutStream,
boost::process::std_err > stderrStream);
worker.wait();
out.exitCode = worker.exit_code();
}
BOOST_CATCH(boost::process::process_error & ex) {
out.error = ex;
}
BOOST_CATCH_END
std::string line;
while (std::getline(stdoutStream, line) && !line.empty()) {
out.stdoutLines.push_back(line);
}
while (std::getline(stderrStream, line) && !line.empty()) {
out.stderrLines.push_back(line);
}
return out;
}

/// Returns an empty path if we failed to determine the resource dir
ResourceDirResult static determineResourceDir(const std::string &compilerPath) {
ResourceDirResult out{
"", {compilerPath, "-print-resource-dir"}, CompilerKind::Clang};
auto printResourceDirResult =
::runProcess(out.cliInvocation, "attempting to find resource dir");
if (printResourceDirResult.isSuccess()) {
if (printResourceDirResult.stdoutLines.empty()) {
spdlog::warn(
"-print-resource-dir succeeded but returned an empty result");
return out;
}
out.resourceDir = std::string(
absl::StripAsciiWhitespace(printResourceDirResult.stdoutLines.front()));
return out;
}
out.compilerKind = CompilerKind::Gcc;
out.cliInvocation = {compilerPath, "-print-search-dirs"};
auto printSearchDirsResult =
::runProcess(out.cliInvocation, "attempting to find search dirs");
auto noteStdlib = []() {
spdlog::warn("may be unable to locate standard library headers");
spdlog::info("compilation errors are suppressed by default, but can be "
"turned on using --show-compiler-diagnostics");
};
if (!printSearchDirsResult.isSuccess()) {
spdlog::warn(
"both -print-resource-dir and -print-search-dirs failed for {}",
compilerPath);
noteStdlib();
return out;
}
absl::c_any_of(
printSearchDirsResult.stdoutLines, [&](const std::string &line) -> bool {
if (line.starts_with("install:")) {
out.resourceDir =
absl::StripAsciiWhitespace(absl::StripPrefix(line, "install:"));
return true;
}
return false;
});
if (out.resourceDir.empty()) {
spdlog::warn(
"missing 'install:' line in -print-search-dirs from GCC(-like?) {}",
compilerPath);
noteStdlib();
return out;
}
return out;
}

namespace scip_clang {
namespace compdb {
namespace {
Expand Down Expand Up @@ -468,57 +575,53 @@ void ResumableParser::parseMore(

void ResumableParser::tryInferResourceDir(
std::vector<std::string> &commandLine) {
auto &clangPath = commandLine.front();
auto it = this->resourceDirMap.find(clangPath);
if (it != this->resourceDirMap.end()) {
commandLine.push_back("-resource-dir");
commandLine.push_back(it->second);
auto &compilerPath = commandLine.front();
auto it = this->extraArgsMap.find(compilerPath);
if (it != this->extraArgsMap.end()) {
for (auto &extraArg : it->second) {
commandLine.push_back(extraArg);
}
return;
}
std::string clangInvocationPath = clangPath;
if (clangPath.find(std::filesystem::path::preferred_separator)
std::string compilerInvocationPath = compilerPath;
if (compilerPath.find(std::filesystem::path::preferred_separator)
== std::string::npos) {
clangInvocationPath = boost::process::search_path(clangPath).native();
if (clangInvocationPath.empty()) {
compilerInvocationPath = boost::process::search_path(compilerPath).native();
if (compilerInvocationPath.empty()) {
this->emitResourceDirError(fmt::format(
"scip-clang needs to be invoke '{0}' (found via the compilation"
" database) to determine the resource directory, but couldn't find"
" '{0}' on PATH. Hint: Use a modified PATH to invoke scip-clang,"
" or change the compilation database to use absolute paths"
" for the compiler.",
clangPath));
compilerPath));
return;
}
}
std::vector<std::string> args = {clangInvocationPath, "-print-resource-dir"};
std::string resourceDir;
BOOST_TRY {
spdlog::debug("attempting to find resource dir by invoking '{}'",
fmt::join(args, " "));
boost::process::ipstream inputStream;
boost::process::child worker(args, boost::process::std_out > inputStream);
worker.wait();
std::getline(inputStream, resourceDir);
auto fail = [&]() { this->extraArgsMap.insert({compilerPath, {}}); };
auto resourceDirResult = ::determineResourceDir(compilerInvocationPath);
if (resourceDirResult.resourceDir.empty()) {
return fail();
}
BOOST_CATCH(boost::process::process_error & ex) {
this->emitResourceDirError(
fmt::format("failed to get resource dir (invocation: '{}'): {}",
fmt::join(args, " "), ex.what()));
return;
auto &resourceDir = resourceDirResult.resourceDir;
std::vector<std::string> extraArgs{"-resource-dir", resourceDir};
if (resourceDirResult.compilerKind == CompilerKind::Gcc) {
// gcc-7 adds headers like limits.h and syslimits.h in include-fixed
extraArgs.push_back(fmt::format("-I{}/include-fixed", resourceDir));
}
BOOST_CATCH_END
spdlog::debug("get resource dir '{}'", resourceDir);
spdlog::debug("got resource dir '{}'", resourceDir);
if (!std::filesystem::exists(resourceDir)) {
this->emitResourceDirError(
fmt::format("'{}' returned '{}' but the directory does not exist",
fmt::join(args, " "), resourceDir));
return;
this->emitResourceDirError(fmt::format(
"'{}' returned '{}' but the directory does not exist",
fmt::join(resourceDirResult.cliInvocation, " "), resourceDir));
return fail();
}
auto [newIt, inserted] =
this->resourceDirMap.emplace(clangPath, std::move(resourceDir));
this->extraArgsMap.emplace(compilerPath, std::move(extraArgs));
ENFORCE(inserted);
commandLine.push_back("-resource-dir");
commandLine.push_back(newIt->second);
for (auto &arg : newIt->second) {
commandLine.push_back(arg);
}
}

void ResumableParser::emitResourceDirError(std::string &&error) {
Expand Down
8 changes: 7 additions & 1 deletion indexer/CompilationDatabase.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,13 @@ class ResumableParser {

bool inferResourceDir;
absl::flat_hash_set<std::string> emittedErrors;
absl::flat_hash_map<std::string, std::string> resourceDirMap;

/// Mapping from compiler -> extra command-line arguments needed
/// to set up include directories correctly.
///
/// The vector may be empty if we failed to determine the correct
/// arguments.
absl::flat_hash_map<std::string, std::vector<std::string>> extraArgsMap;

public:
ResumableParser() = default;
Expand Down
8 changes: 4 additions & 4 deletions indexer/Driver.cc
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ struct DriverOptions {
AbsolutePath compdbPath;
AbsolutePath indexOutputPath;
AbsolutePath statsFilePath;
bool showClangDiagnostics;
bool showCompilerDiagonstics;
size_t numWorkers;
std::chrono::seconds receiveTimeout;
bool deterministic;
Expand All @@ -137,7 +137,7 @@ struct DriverOptions {
: workerExecutablePath(),
projectRootPath(AbsolutePath("/"), RootKind::Project), compdbPath(),
indexOutputPath(), statsFilePath(),
showClangDiagnostics(cliOpts.showClangDiagnostics),
showCompilerDiagonstics(cliOpts.showCompilerDiagonstics),
numWorkers(cliOpts.numWorkers), receiveTimeout(cliOpts.receiveTimeout),
deterministic(cliOpts.deterministic),
preprocessorRecordHistoryFilterRegex(
Expand Down Expand Up @@ -233,8 +233,8 @@ struct DriverOptions {
if (!this->statsFilePath.asStringRef().empty()) {
args.push_back("--measure-statistics");
}
if (this->showClangDiagnostics) {
args.push_back("--show-clang-diagnostics");
if (this->showCompilerDiagonstics) {
args.push_back("--show-compiler-diagnostics");
}
if (!this->preprocessorRecordHistoryFilterRegex.empty()) {
args.push_back(fmt::format("--preprocessor-record-history-filter={}",
Expand Down
4 changes: 2 additions & 2 deletions indexer/Worker.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1135,7 +1135,7 @@ WorkerOptions WorkerOptions::fromCliOptions(const CliOptions &cliOptions) {
compdbPath,
indexOutputPath,
statsFilePath,
cliOptions.showClangDiagnostics,
cliOptions.showCompilerDiagonstics,
cliOptions.logLevel,
cliOptions.deterministic,
cliOptions.measureStatistics,
Expand Down Expand Up @@ -1243,7 +1243,7 @@ void Worker::processTranslationUnit(SemanticAnalysisJobDetails &&job,
std::make_shared<clang::PCHContainerOperations>());

SuppressDiagnosticConsumer suppressDiagnostics;
if (!this->options.showClangDiagnostics) {
if (!this->options.showCompilerDiagonstics) {
invocation.setDiagnosticConsumer(&suppressDiagnostics);
}

Expand Down
2 changes: 1 addition & 1 deletion indexer/Worker.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ struct WorkerOptions {
StdPath indexOutputPath; // only valid if mode == Compdb
StdPath statsFilePath; // only valid if mode == Compdb

bool showClangDiagnostics;
bool showCompilerDiagonstics;

spdlog::level::level_enum logLevel;
bool deterministic;
Expand Down
4 changes: 2 additions & 2 deletions indexer/main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,10 @@ static scip_clang::CliOptions parseArguments(int argc, char *argv[]) {
"If set, this directory will not be deleted after indexing is complete.",
cxxopts::value<std::string>(cliOptions.temporaryOutputDir));
parser.add_options("")(
"show-clang-diagnostics",
"show-compiler-diagnostics",
"Show Clang diagnostics triggered when running semantic analysis."
" Useful for debugging issues related to missing headers.",
cxxopts::value<bool>(cliOptions.showClangDiagnostics));
cxxopts::value<bool>(cliOptions.showCompilerDiagonstics));
parser.add_options("")("version", "Show the version", cxxopts::value<bool>());
parser.add_options("")("h,help", "Show help text", cxxopts::value<bool>());
parser.add_options("Advanced")(
Expand Down

0 comments on commit ced7e7d

Please sign in to comment.