diff --git a/src/config.h b/src/config.h index b6e7589d..996a3bf2 100644 --- a/src/config.h +++ b/src/config.h @@ -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, diff --git a/src/indexer.cc b/src/indexer.cc index aaeff0b5..61c78342 100644 --- a/src/indexer.cc +++ b/src/indexer.cc @@ -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. diff --git a/src/platform.h b/src/platform.h index bf55128c..3e0fae2f 100644 --- a/src/platform.h +++ b/src/platform.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -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& command, + std::string_view input); diff --git a/src/platform_posix.cc b/src/platform_posix.cc index ad7d199a..bff6ac44 100644 --- a/src/platform_posix.cc +++ b/src/platform_posix.cc @@ -27,6 +27,7 @@ #include #include #include // required for stat.h +#include #include #include @@ -285,4 +286,37 @@ void TraceMe() { raise(SIGTSTP); } +std::string GetExternalCommandOutput(const std::vector& 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(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 diff --git a/src/platform_win.cc b/src/platform_win.cc index f772bd40..608cf96e 100644 --- a/src/platform_win.cc +++ b/src/platform_win.cc @@ -152,4 +152,9 @@ bool RunObjectiveCIndexTests() { // TODO Wait for debugger to attach void TraceMe() {} +std::string GetExternalCommandOutput(const std::vector& command, + std::string_view input) { + return ""; +} + #endif diff --git a/src/project.cc b/src/project.cc index 4b742def..b5ddd882 100644 --- a/src/project.cc +++ b/src/project.cc @@ -11,6 +11,11 @@ #include #include +#if defined(__unix__) || defined(__APPLE__) +#include +#endif + +#include #include #include #include @@ -348,17 +353,48 @@ std::vector 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 writer(input); + JsonWriter json_writer(&writer); + Reflect(json_writer, *init_opts); + std::string contents = GetExternalCommandOutput( + std::vector{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 LoadCompilationEntriesFromDirectory( clang_time.ResetAndPrint("compile_commands.json clang time"); our_time.ResetAndPrint("compile_commands.json our time"); - return result; }