diff --git a/src/platform_win.cc b/src/platform_win.cc index ce868c1d..f772bd40 100644 --- a/src/platform_win.cc +++ b/src/platform_win.cc @@ -44,7 +44,6 @@ std::string GetWorkingDirectory() { std::string NormalizePath(const std::string& path) { DWORD retval = 0; TCHAR buffer[MAX_PATH] = TEXT(""); - TCHAR buf[MAX_PATH] = TEXT(""); TCHAR** lpp_part = {NULL}; retval = GetFullPathName(path.c_str(), MAX_PATH, buffer, lpp_part); diff --git a/src/project.cc b/src/project.cc index 4809364e..937ff8cd 100644 --- a/src/project.cc +++ b/src/project.cc @@ -270,38 +270,66 @@ Project::Entry GetCompilationEntryFromCompileCommandEntry( return result; } -std::vector LoadFromDirectoryListing(Config* init_opts, - ProjectConfig* config) { - std::vector result; - +std::vector ReadCompilerArgumentsFromFile( + const std::string& path) { std::vector args; - for (std::string line : - ReadLinesWithEnding(config->project_dir + "/.cquery")) { + for (std::string line : ReadLinesWithEnding(path)) { TrimInPlace(line); if (line.empty() || StartsWith(line, "#")) continue; args.push_back(line); } - LOG_IF_S(INFO, !args.empty()) - << "Using .cquery arguments " << StringJoin(args); + return args; +} + +std::vector LoadFromDirectoryListing(Config* init_opts, + ProjectConfig* config) { + std::vector result; LOG_IF_S(WARNING, !FileExists(config->project_dir + "/.cquery") && config->extra_flags.empty()) << "cquery has no clang arguments. Considering adding either a " "compile_commands.json or .cquery file. See the cquery README for " "more information."; - std::vector files = GetFilesInFolder( - config->project_dir, true /*recursive*/, true /*add_folder_to_path*/); - for (const std::string& file : files) { - if (SourceFileType(file)) { - CompileCommandsEntry e; - e.directory = config->project_dir; - e.file = file; - e.args = args; - e.args.push_back(e.file); - result.push_back( - GetCompilationEntryFromCompileCommandEntry(init_opts, config, e)); + std::unordered_map> folder_args; + std::vector files; + + GetFilesInFolder( + config->project_dir, true /*recursive*/, true /*add_folder_to_path*/, + [&folder_args, &files](const std::string& path) { + if (SourceFileType(path)) { + files.push_back(path); + } else if (GetBaseName(path) == ".cquery") { + LOG_S(INFO) << "Using .cquery arguments from " << path; + folder_args.emplace(GetDirName(path), + ReadCompilerArgumentsFromFile(path)); + } + }); + + const auto& project_dir_args = folder_args[config->project_dir]; + LOG_IF_S(INFO, !project_dir_args.empty()) + << "Using .cquery arguments " << StringJoin(project_dir_args); + + auto GetCompilerArgumentForFile = [&config, + &folder_args](const std::string& path) { + for (std::string cur = GetDirName(path); + NormalizePath(cur) != config->project_dir; cur = GetDirName(cur)) { + auto it = folder_args.find(cur); + if (it != folder_args.end()) { + return it->second; + } } + return folder_args[config->project_dir]; + }; + + for (const std::string& file : files) { + CompileCommandsEntry e; + e.directory = config->project_dir; + e.file = file; + e.args = GetCompilerArgumentForFile(file); + e.args.push_back(e.file); + result.push_back( + GetCompilationEntryFromCompileCommandEntry(init_opts, config, e)); } return result; diff --git a/src/utils.cc b/src/utils.cc index 67f4e878..7501601d 100644 --- a/src/utils.cc +++ b/src/utils.cc @@ -120,6 +120,15 @@ bool FindAnyPartial(const std::string& value, }); } +std::string GetDirName(std::string path) { + if (path.size() && path.back() == '/') + path.pop_back(); + size_t last_slash = path.find_last_of('/'); + if (last_slash == std::string::npos) return "."; + if (last_slash == 0) return "/"; + return path.substr(0, last_slash); +} + std::string GetBaseName(const std::string& path) { size_t last_slash = path.find_last_of('/'); if (last_slash != std::string::npos && (last_slash + 1) < path.size()) @@ -205,14 +214,14 @@ static void GetFilesInFolderHelper( goto bail; } - // Skip all dot files. + // Skip all dot files except .cquery. // // The nested ifs are intentional, branching order is subtle here. // // Note that in the future if we do support dot directories/files, we must // always ignore the '.' and '..' directories otherwise this will loop // infinitely. - if (file.name[0] != '.') { + if (file.name[0] != '.' || strcmp(file.name, ".cquery") == 0) { if (file.is_dir) { if (recursive) { std::string child_dir = q.front().second + file.name + "/"; diff --git a/src/utils.h b/src/utils.h index e02e18c6..0de383cc 100644 --- a/src/utils.h +++ b/src/utils.h @@ -33,7 +33,8 @@ bool EndsWithAny(const std::string& value, const std::vector& endings); bool FindAnyPartial(const std::string& value, const std::vector& values); - +// Returns the dirname of |path|, i.e. "foo/bar.cc" => "foo", "foo" => ".", "/foo" => "/". +std::string GetDirName(std::string path); // Returns the basename of |path|, ie, "foo/bar.cc" => "bar.cc". std::string GetBaseName(const std::string& path); // Returns |path| without the filetype, ie, "foo/bar.cc" => "foo/bar".