Implement initialization option compilationDatabaseCommand on Windows

This commit is contained in:
Fangrui Song 2019-01-21 12:15:28 +08:00
parent 0dbf6c89f1
commit b2fcec4b97
4 changed files with 42 additions and 69 deletions

View File

@ -28,8 +28,5 @@ void FreeUnusedMemory();
// Stop self and wait for SIGCONT. // Stop self and wait for SIGCONT.
void TraceMe(); void TraceMe();
std::string GetExternalCommandOutput(const std::vector<std::string> &command,
std::string_view input);
void SpawnThread(void *(*fn)(void *), void *arg); void SpawnThread(void *(*fn)(void *), void *arg);
} // namespace ccls } // namespace ccls

View File

@ -73,49 +73,6 @@ void TraceMe() {
raise(traceme[0] == 's' ? SIGSTOP : SIGTSTP); raise(traceme[0] == 's' ? SIGSTOP : SIGTSTP);
} }
std::string GetExternalCommandOutput(const std::vector<std::string> &command,
std::string_view input) {
int pin[2], pout[2];
if (pipe(pin) < 0) {
perror("pipe(stdin)");
return "";
}
if (pipe(pout) < 0) {
perror("pipe(stdout)");
close(pin[0]);
close(pin[1]);
return "";
}
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]);
// O_NONBLOCK is disabled, write(2) blocks until all bytes are written.
(void)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);
close(pin[0]);
waitpid(child, NULL, 0);
return ret;
}
void SpawnThread(void *(*fn)(void *), void *arg) { void SpawnThread(void *(*fn)(void *), void *arg) {
pthread_t thd; pthread_t thd;
pthread_attr_t attr; pthread_attr_t attr;

View File

@ -57,11 +57,6 @@ void FreeUnusedMemory() {}
// TODO Wait for debugger to attach // TODO Wait for debugger to attach
void TraceMe() {} void TraceMe() {}
std::string GetExternalCommandOutput(const std::vector<std::string> &command,
std::string_view input) {
return "";
}
void SpawnThread(void *(*fn)(void *), void *arg) { void SpawnThread(void *(*fn)(void *), void *arg) {
std::thread(fn, arg).detach(); std::thread(fn, arg).detach();
} }

View File

@ -31,13 +31,17 @@ limitations under the License.
#include <llvm/ADT/STLExtras.h> #include <llvm/ADT/STLExtras.h>
#include <llvm/ADT/StringSet.h> #include <llvm/ADT/StringSet.h>
#include <llvm/Support/LineIterator.h> #include <llvm/Support/LineIterator.h>
#include <llvm/Support/Program.h>
#include <rapidjson/writer.h> #include <rapidjson/writer.h>
#if defined(__unix__) || defined(__APPLE__) #ifdef _WIN32
#include <unistd.h> # include <Windows.h>
#else
# include <unistd.h>
#endif #endif
#include <array>
#include <limits.h> #include <limits.h>
#include <unordered_set> #include <unordered_set>
#include <vector> #include <vector>
@ -308,7 +312,8 @@ int ComputeGuessScore(std::string_view a, std::string_view b) {
} // namespace } // namespace
void Project::LoadDirectory(const std::string &root, Project::Folder &folder) { void Project::LoadDirectory(const std::string &root, Project::Folder &folder) {
SmallString<256> Path, CDBDir; SmallString<256> CDBDir, Path, StdinPath;
std::string err_msg;
folder.entries.clear(); folder.entries.clear();
if (g_config->compilationDatabaseCommand.empty()) { if (g_config->compilationDatabaseCommand.empty()) {
CDBDir = root; CDBDir = root;
@ -319,33 +324,49 @@ void Project::LoadDirectory(const std::string &root, Project::Folder &folder) {
// If `compilationDatabaseCommand` is specified, execute it to get the // If `compilationDatabaseCommand` is specified, execute it to get the
// compdb. // compdb.
#ifdef _WIN32 #ifdef _WIN32
// TODO char tmpdir[L_tmpnam];
tmpnam_s(tmpdir, L_tmpnam);
CDBDir = tmpdir;
if (sys::fs::create_directory(tmpdir, false))
return;
#else #else
char tmpdir[] = "/tmp/ccls-compdb-XXXXXX"; char tmpdir[] = "/tmp/ccls-compdb-XXXXXX";
if (!mkdtemp(tmpdir)) if (!mkdtemp(tmpdir))
return; return;
CDBDir = tmpdir; CDBDir = tmpdir;
sys::path::append(Path, CDBDir, "compile_commands.json");
rapidjson::StringBuffer input;
rapidjson::Writer<rapidjson::StringBuffer> writer(input);
JsonWriter json_writer(&writer);
Reflect(json_writer, *g_config);
std::string contents = GetExternalCommandOutput(
std::vector<std::string>{g_config->compilationDatabaseCommand, root},
input.GetString());
FILE *fout = fopen(Path.c_str(), "wb");
fwrite(contents.c_str(), contents.size(), 1, fout);
fclose(fout);
#endif #endif
sys::path::append(Path, CDBDir, "compile_commands.json");
sys::path::append(StdinPath, CDBDir, "stdin");
{
rapidjson::StringBuffer sb;
rapidjson::Writer<rapidjson::StringBuffer> writer(sb);
JsonWriter json_writer(&writer);
Reflect(json_writer, *g_config);
std::string input = sb.GetString();
FILE *fout = fopen(StdinPath.c_str(), "wb");
fwrite(input.c_str(), input.size(), 1, fout);
fclose(fout);
}
std::array<Optional<StringRef>, 3> Redir{StringRef(StdinPath),
StringRef(Path), StringRef()};
std::vector<StringRef> args{g_config->compilationDatabaseCommand, root};
if (sys::ExecuteAndWait(args[0], args, llvm::None, Redir, 0, 0, &err_msg) <
0) {
LOG_S(ERROR) << "failed to execute " << args[0].str() << " "
<< args[1].str() << ": " << err_msg;
return;
}
} }
std::string err_msg;
std::unique_ptr<tooling::CompilationDatabase> CDB = std::unique_ptr<tooling::CompilationDatabase> CDB =
tooling::CompilationDatabase::loadFromDirectory(CDBDir, err_msg); tooling::CompilationDatabase::loadFromDirectory(CDBDir, err_msg);
if (!g_config->compilationDatabaseCommand.empty()) { if (!g_config->compilationDatabaseCommand.empty()) {
#ifdef _WIN32 #ifdef _WIN32
// TODO DeleteFileA(StdinPath.c_str());
DeleteFileA(Path.c_str());
RemoveDirectoryA(CDBDir.c_str());
#else #else
unlink(StdinPath.c_str());
unlink(Path.c_str()); unlink(Path.c_str());
rmdir(CDBDir.c_str()); rmdir(CDBDir.c_str());
#endif #endif
@ -354,7 +375,10 @@ void Project::LoadDirectory(const std::string &root, Project::Folder &folder) {
ProjectProcessor proc(folder); ProjectProcessor proc(folder);
StringSet<> Seen; StringSet<> Seen;
std::vector<Project::Entry> result; std::vector<Project::Entry> result;
if (CDB) { if (!CDB) {
if (g_config->compilationDatabaseCommand.size() || sys::fs::exists(Path))
LOG_S(ERROR) << "failed to load " << Path.c_str();
} else {
LOG_S(INFO) << "loaded " << Path.c_str(); LOG_S(INFO) << "loaded " << Path.c_str();
for (tooling::CompileCommand &Cmd : CDB->getAllCompileCommands()) { for (tooling::CompileCommand &Cmd : CDB->getAllCompileCommands()) {
static bool once; static bool once;