Skip to content

Commit a56c6dd

Browse files
feat: Infer the resource directory (#162)
1 parent bfc4292 commit a56c6dd

File tree

5 files changed

+96
-5
lines changed

5 files changed

+96
-5
lines changed

indexer/CompilationDatabase.cc

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99

1010
#include "absl/algorithm/container.h"
1111
#include "absl/container/flat_hash_set.h"
12+
#include "boost/process/child.hpp"
13+
#include "boost/process/io.hpp"
14+
#include "boost/process/search_path.hpp"
1215
#include "rapidjson/error/en.h"
1316
#include "rapidjson/rapidjson.h"
1417
#include "rapidjson/reader.h"
@@ -412,7 +415,7 @@ CompilationDatabaseFile CompilationDatabaseFile::openAndExitOnErrors(
412415
}
413416

414417
void ResumableParser::initialize(CompilationDatabaseFile compdb,
415-
size_t refillCount) {
418+
size_t refillCount, bool inferResourceDir) {
416419
auto averageJobSize = compdb.sizeInBytes() / compdb.commandCount();
417420
// Some customers have averageJobSize = 150KiB.
418421
// If numWorkers == 300 (very high core count machine),
@@ -427,6 +430,7 @@ void ResumableParser::initialize(CompilationDatabaseFile compdb,
427430
rapidjson::FileReadStream(compdb.file, this->jsonStreamBuffer.data(),
428431
this->jsonStreamBuffer.size());
429432
this->reader.IterativeParseInit();
433+
this->inferResourceDir = inferResourceDir;
430434
}
431435

432436
void ResumableParser::parseMore(
@@ -451,8 +455,78 @@ void ResumableParser::parseMore(
451455
for (auto &cmd : this->handler->commands) {
452456
out.emplace_back(std::move(cmd));
453457
}
458+
if (this->inferResourceDir) {
459+
for (auto &cmd : out) {
460+
if (cmd.CommandLine.empty()) {
461+
continue;
462+
}
463+
this->tryInferResourceDir(cmd.CommandLine);
464+
}
465+
}
454466
this->handler->commands.clear();
455467
}
456468

469+
void ResumableParser::tryInferResourceDir(
470+
std::vector<std::string> &commandLine) {
471+
auto &clangPath = commandLine.front();
472+
auto it = this->resourceDirMap.find(clangPath);
473+
if (it != this->resourceDirMap.end()) {
474+
commandLine.push_back("-resource-dir");
475+
commandLine.push_back(it->second);
476+
return;
477+
}
478+
std::string clangInvocationPath = clangPath;
479+
if (clangPath.find(std::filesystem::path::preferred_separator)
480+
== std::string::npos) {
481+
clangInvocationPath = boost::process::search_path(clangPath).native();
482+
if (clangInvocationPath.empty()) {
483+
this->emitResourceDirError(fmt::format(
484+
"scip-clang needs to be invoke '{0}' (found via the compilation"
485+
" database) to determine the resource directory, but couldn't find"
486+
" '{0}' on PATH. Hint: Use a modified PATH to invoke scip-clang,"
487+
" or change the compilation database to use absolute paths"
488+
" for the compiler.",
489+
clangPath));
490+
return;
491+
}
492+
}
493+
std::vector<std::string> args = {clangInvocationPath, "-print-resource-dir"};
494+
std::string resourceDir;
495+
BOOST_TRY {
496+
spdlog::debug("attempting to find resource dir by invoking '{}'",
497+
fmt::join(args, " "));
498+
boost::process::ipstream inputStream;
499+
boost::process::child worker(args, boost::process::std_out > inputStream);
500+
worker.wait();
501+
std::getline(inputStream, resourceDir);
502+
}
503+
BOOST_CATCH(boost::process::process_error & ex) {
504+
this->emitResourceDirError(
505+
fmt::format("failed to get resource dir (invocation: '{}'): {}",
506+
fmt::join(args, " "), ex.what()));
507+
return;
508+
}
509+
BOOST_CATCH_END
510+
spdlog::debug("get resource dir '{}'", resourceDir);
511+
if (!std::filesystem::exists(resourceDir)) {
512+
this->emitResourceDirError(
513+
fmt::format("'{}' returned '{}' but the directory does not exist",
514+
fmt::join(args, " "), resourceDir));
515+
return;
516+
}
517+
auto [newIt, inserted] =
518+
this->resourceDirMap.emplace(clangPath, std::move(resourceDir));
519+
ENFORCE(inserted);
520+
commandLine.push_back("-resource-dir");
521+
commandLine.push_back(newIt->second);
522+
}
523+
524+
void ResumableParser::emitResourceDirError(std::string &&error) {
525+
auto [it, inserted] = this->emittedErrors.emplace(std::move(error));
526+
if (inserted) {
527+
spdlog::error("{}", *it);
528+
}
529+
}
530+
457531
} // namespace compdb
458532
} // namespace scip_clang

indexer/CompilationDatabase.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include "indexer/Enforce.h" // defines ENFORCE used by rapidjson headers
1010

11+
#include "absl/container/flat_hash_map.h"
1112
#include "absl/container/flat_hash_set.h"
1213
#include "rapidjson/filereadstream.h"
1314
#include "rapidjson/reader.h"
@@ -85,16 +86,28 @@ class ResumableParser {
8586
std::optional<CommandObjectHandler> handler;
8687
rapidjson::Reader reader;
8788

89+
bool inferResourceDir;
90+
absl::flat_hash_set<std::string> emittedErrors;
91+
absl::flat_hash_map<std::string, std::string> resourceDirMap;
92+
8893
public:
8994
ResumableParser() = default;
9095
ResumableParser(const ResumableParser &) = delete;
9196
ResumableParser &operator=(const ResumableParser &) = delete;
9297

93-
void initialize(CompilationDatabaseFile compdb, size_t refillCount);
98+
/// If \param inferResourceDir is set, then the parser will automatically
99+
/// add extra '-resource-dir' '<path>' arguments to the parsed
100+
/// CompileCommands' CommandLine field.
101+
void initialize(CompilationDatabaseFile compdb, size_t refillCount,
102+
bool inferResourceDir);
94103

95104
// Parses at most refillCount elements (passed during initialization)
96105
// from the compilation database passed during initialization.
97106
void parseMore(std::vector<clang::tooling::CompileCommand> &out);
107+
108+
private:
109+
void tryInferResourceDir(std::vector<std::string> &commandLine);
110+
void emitResourceDirError(std::string &&error);
98111
};
99112

100113
} // namespace compdb

indexer/Driver.cc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -818,7 +818,10 @@ class Driver {
818818
std::min(this->compdbCommandCount, this->numWorkers());
819819
spdlog::debug("total {} compilation jobs", this->compdbCommandCount);
820820

821-
this->compdbParser.initialize(compdbFile, this->refillCount());
821+
// FIXME(def: resource-dir-extra): If we're passed in a resource dir
822+
// as an extra argument, we should not pass it here.
823+
this->compdbParser.initialize(compdbFile, this->refillCount(),
824+
!this->options.isTesting);
822825
return FileGuard(compdbFile.file);
823826
}
824827

indexer/Worker.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1115,7 +1115,8 @@ Worker::Worker(WorkerOptions &&options)
11151115
this->options.compdbPath,
11161116
compdb::ValidationOptions{.checkDirectoryPathsAreAbsolute = true});
11171117
compdb::ResumableParser parser{};
1118-
parser.initialize(compdbFile, std::numeric_limits<size_t>::max());
1118+
// See FIXME(ref: resource-dir-extra)
1119+
parser.initialize(compdbFile, std::numeric_limits<size_t>::max(), true);
11191120
parser.parseMore(this->compileCommands);
11201121
std::fclose(compdbFile.file);
11211122
break;

test/test_main.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ TEST_CASE("COMPDB_PARSING") {
139139

140140
for (auto refillCount : testCase.refillCountsToTry) {
141141
compdb::ResumableParser parser{};
142-
parser.initialize(compdbFile, refillCount);
142+
parser.initialize(compdbFile, refillCount, false);
143143
std::vector<std::vector<clang::tooling::CompileCommand>> commandGroups;
144144
std::string buffer;
145145
llvm::raw_string_ostream outStr(buffer);

0 commit comments

Comments
 (0)