Add Config::compilationDatabaseCommand

If specified, this is an external command that provides the JSON compilation database, instead of compile_commands.json
This commit is contained in:
Fangrui Song 2018-02-19 16:19:57 -08:00
parent d33bf50181
commit f9d7361953
6 changed files with 94 additions and 9 deletions

View File

@ -23,6 +23,11 @@ initialization options specified by the client. For example, in shell syntax:
struct Config {
// Root directory of the project. **Not available for configuration**
std::string projectRoot;
// If specified, this option overrides compile_commands.json and this
// external command will be executed with an option |projectRoot|.
// The initialization options will be provided as stdin.
// The stdout of the command should be the JSON compilation database.
std::string compilationDatabaseCommand;
// Directory containing compile_commands.json.
std::string compilationDatabaseDirectory;
// Cache directory for indexed files.
@ -191,6 +196,7 @@ MAKE_REFLECT_STRUCT(Config::Completion, filterAndSort, detailedLabel);
MAKE_REFLECT_STRUCT(Config::Extension, referenceContainer);
MAKE_REFLECT_STRUCT(Config::Index, comments, attributeMakeCallsToCtor);
MAKE_REFLECT_STRUCT(Config,
compilationDatabaseCommand,
compilationDatabaseDirectory,
cacheDirectory,
cacheFormat,

View File

@ -330,8 +330,8 @@ IndexFile* ConsumeFile(IndexParam* param, CXFile file) {
// generating an index for it):
if (param->seen_cx_files.insert(file).second) {
std::string file_name = FileName(file);
// Sometimes the fill name will be empty. Not sure why. Not much we can do
// with it.
// file_name may be empty when it contains .. and is outside of WorkingDir.
// https://reviews.llvm.org/D42893 https://github.com/cquery-project/cquery/issues/413
if (!file_name.empty()) {
// Add to all files we have seen so we can generate proper dependency
// graph.

View File

@ -1,6 +1,7 @@
#pragma once
#include <optional.h>
#include <string_view.h>
#include <memory>
#include <string>
@ -49,4 +50,8 @@ void FreeUnusedMemory();
// If true objective-c index tests will be run.
bool RunObjectiveCIndexTests();
// Stop self and wait for SIGCONT.
void TraceMe();
std::string GetExternalCommandOutput(const std::vector<std::string>& command,
std::string_view input);

View File

@ -27,6 +27,7 @@
#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h> // required for stat.h
#include <sys/wait.h>
#include <errno.h>
#include <fcntl.h>
@ -285,4 +286,37 @@ void TraceMe() {
raise(SIGTSTP);
}
std::string GetExternalCommandOutput(const std::vector<std::string>& command,
std::string_view input) {
int pin[2], pout[2];
pipe(pin);
pipe(pout);
pid_t child = fork();
if (child == 0) {
dup2(pout[0], 0);
dup2(pin[1], 1);
close(pin[0]);
close(pin[1]);
close(pout[0]);
close(pout[1]);
auto argv = new char*[command.size() + 1];
for (size_t i = 0; i < command.size(); i++)
argv[i] = const_cast<char*>(command[i].c_str());
argv[command.size()] = nullptr;
execvp(argv[0], argv);
_Exit(127);
}
close(pin[1]);
close(pout[0]);
write(pout[1], input.data(), input.size());
close(pout[1]);
std::string ret;
char buf[4096];
ssize_t n;
while ((n = read(pin[0], buf, sizeof buf)) > 0)
ret.append(buf, n);
waitpid(child, NULL, 0);
return ret;
}
#endif

View File

@ -152,4 +152,9 @@ bool RunObjectiveCIndexTests() {
// TODO Wait for debugger to attach
void TraceMe() {}
std::string GetExternalCommandOutput(const std::vector<std::string>& command,
std::string_view input) {
return "";
}
#endif

View File

@ -11,6 +11,11 @@
#include <doctest/doctest.h>
#include <loguru.hpp>
#if defined(__unix__) || defined(__APPLE__)
#include <unistd.h>
#endif
#include <fstream>
#include <iostream>
#include <limits>
#include <sstream>
@ -348,17 +353,48 @@ std::vector<Project::Entry> LoadCompilationEntriesFromDirectory(
if (FileExists(config->project_dir + "/.cquery"))
return LoadFromDirectoryListing(init_opts, config);
// Try to load compile_commands.json, but fallback to a project listing.
const auto& compilation_db_dir = opt_compilation_db_dir.empty()
? config->project_dir
: opt_compilation_db_dir;
// If |compilationDatabaseCommand| is specified, execute it to get the compdb.
std::string comp_db_dir;
if (init_opts->compilationDatabaseCommand.empty()) {
// Try to load compile_commands.json, but fallback to a project listing.
comp_db_dir = opt_compilation_db_dir.empty() ? config->project_dir
: opt_compilation_db_dir;
} else {
#ifdef _WIN32
// TODO
#else
char tmpdir[] = "/tmp/cquery-compdb-XXXXXX";
if (!mkdtemp(tmpdir))
return {};
comp_db_dir = tmpdir;
rapidjson::StringBuffer input;
rapidjson::Writer<rapidjson::StringBuffer> writer(input);
JsonWriter json_writer(&writer);
Reflect(json_writer, *init_opts);
std::string contents = GetExternalCommandOutput(
std::vector<std::string>{init_opts->compilationDatabaseCommand,
config->project_dir},
input.GetString());
std::ofstream(comp_db_dir + "/compile_commands.json") << contents;
#endif
}
LOG_S(INFO) << "Trying to load compile_commands.json";
CXCompilationDatabase_Error cx_db_load_error;
CXCompilationDatabase cx_db = clang_CompilationDatabase_fromDirectory(
compilation_db_dir.c_str(), &cx_db_load_error);
comp_db_dir.c_str(), &cx_db_load_error);
if (!init_opts->compilationDatabaseCommand.empty()) {
#ifdef _WIN32
// TODO
#else
unlink((comp_db_dir + "/compile_commands.json").c_str());
rmdir(comp_db_dir.c_str());
#endif
}
if (cx_db_load_error == CXCompilationDatabase_CanNotLoadDatabase) {
LOG_S(INFO) << "Unable to load compile_commands.json located at \""
<< compilation_db_dir << "\"; using directory listing instead.";
<< comp_db_dir << "\"; using directory listing instead.";
return LoadFromDirectoryListing(init_opts, config);
}
@ -416,7 +452,6 @@ std::vector<Project::Entry> LoadCompilationEntriesFromDirectory(
clang_time.ResetAndPrint("compile_commands.json clang time");
our_time.ResetAndPrint("compile_commands.json our time");
return result;
}