From b2fcec4b97dcdbaf924e45c2c57e6f53b674f8e4 Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Mon, 21 Jan 2019 12:15:28 +0800 Subject: [PATCH] Implement initialization option compilationDatabaseCommand on Windows --- src/platform.hh | 3 --- src/platform_posix.cc | 43 ------------------------------- src/platform_win.cc | 5 ---- src/project.cc | 60 ++++++++++++++++++++++++++++++------------- 4 files changed, 42 insertions(+), 69 deletions(-) diff --git a/src/platform.hh b/src/platform.hh index 12446263..79aca558 100644 --- a/src/platform.hh +++ b/src/platform.hh @@ -28,8 +28,5 @@ void FreeUnusedMemory(); // Stop self and wait for SIGCONT. void TraceMe(); -std::string GetExternalCommandOutput(const std::vector &command, - std::string_view input); - void SpawnThread(void *(*fn)(void *), void *arg); } // namespace ccls diff --git a/src/platform_posix.cc b/src/platform_posix.cc index 35d8d271..c172446b 100644 --- a/src/platform_posix.cc +++ b/src/platform_posix.cc @@ -73,49 +73,6 @@ void TraceMe() { raise(traceme[0] == 's' ? SIGSTOP : SIGTSTP); } -std::string GetExternalCommandOutput(const std::vector &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(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) { pthread_t thd; pthread_attr_t attr; diff --git a/src/platform_win.cc b/src/platform_win.cc index a2ca5185..f933b257 100644 --- a/src/platform_win.cc +++ b/src/platform_win.cc @@ -57,11 +57,6 @@ void FreeUnusedMemory() {} // TODO Wait for debugger to attach void TraceMe() {} -std::string GetExternalCommandOutput(const std::vector &command, - std::string_view input) { - return ""; -} - void SpawnThread(void *(*fn)(void *), void *arg) { std::thread(fn, arg).detach(); } diff --git a/src/project.cc b/src/project.cc index 491af313..6833ad7b 100644 --- a/src/project.cc +++ b/src/project.cc @@ -31,13 +31,17 @@ limitations under the License. #include #include #include +#include #include -#if defined(__unix__) || defined(__APPLE__) -#include +#ifdef _WIN32 +# include +#else +# include #endif +#include #include #include #include @@ -308,7 +312,8 @@ int ComputeGuessScore(std::string_view a, std::string_view b) { } // namespace 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(); if (g_config->compilationDatabaseCommand.empty()) { 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 // compdb. #ifdef _WIN32 - // TODO + char tmpdir[L_tmpnam]; + tmpnam_s(tmpdir, L_tmpnam); + CDBDir = tmpdir; + if (sys::fs::create_directory(tmpdir, false)) + return; #else char tmpdir[] = "/tmp/ccls-compdb-XXXXXX"; if (!mkdtemp(tmpdir)) return; CDBDir = tmpdir; - sys::path::append(Path, CDBDir, "compile_commands.json"); - rapidjson::StringBuffer input; - rapidjson::Writer writer(input); - JsonWriter json_writer(&writer); - Reflect(json_writer, *g_config); - std::string contents = GetExternalCommandOutput( - std::vector{g_config->compilationDatabaseCommand, root}, - input.GetString()); - FILE *fout = fopen(Path.c_str(), "wb"); - fwrite(contents.c_str(), contents.size(), 1, fout); - fclose(fout); #endif + sys::path::append(Path, CDBDir, "compile_commands.json"); + sys::path::append(StdinPath, CDBDir, "stdin"); + { + rapidjson::StringBuffer sb; + rapidjson::Writer 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, 3> Redir{StringRef(StdinPath), + StringRef(Path), StringRef()}; + std::vector 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 CDB = tooling::CompilationDatabase::loadFromDirectory(CDBDir, err_msg); if (!g_config->compilationDatabaseCommand.empty()) { #ifdef _WIN32 - // TODO + DeleteFileA(StdinPath.c_str()); + DeleteFileA(Path.c_str()); + RemoveDirectoryA(CDBDir.c_str()); #else + unlink(StdinPath.c_str()); unlink(Path.c_str()); rmdir(CDBDir.c_str()); #endif @@ -354,7 +375,10 @@ void Project::LoadDirectory(const std::string &root, Project::Folder &folder) { ProjectProcessor proc(folder); StringSet<> Seen; std::vector 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(); for (tooling::CompileCommand &Cmd : CDB->getAllCompileCommands()) { static bool once;