Look for .cquery in any directory above the source file in the hierarchy. (#409)

* Look for .cquery in any directory above the source file in the hierarchy.

Currently cquery only reads compiler arguments (.cquery) from project
root. Under some circumstances (e.g. remote compiling), generating a
compilation database with correct path in it is non-trivial, and
allowing per directory compile arguments usually helps.

* unused var buf
This commit is contained in:
Riatre Foo 2018-02-04 05:16:38 +08:00 committed by Fangrui Song
parent 6933870962
commit 54c587a700
4 changed files with 60 additions and 23 deletions

View File

@ -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);

View File

@ -270,38 +270,66 @@ Project::Entry GetCompilationEntryFromCompileCommandEntry(
return result;
}
std::vector<Project::Entry> LoadFromDirectoryListing(Config* init_opts,
ProjectConfig* config) {
std::vector<Project::Entry> result;
std::vector<std::string> ReadCompilerArgumentsFromFile(
const std::string& path) {
std::vector<std::string> 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<Project::Entry> LoadFromDirectoryListing(Config* init_opts,
ProjectConfig* config) {
std::vector<Project::Entry> 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<std::string> 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<std::string, std::vector<std::string>> folder_args;
std::vector<std::string> 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;

View File

@ -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 + "/";

View File

@ -33,7 +33,8 @@ bool EndsWithAny(const std::string& value,
const std::vector<std::string>& endings);
bool FindAnyPartial(const std::string& value,
const std::vector<std::string>& 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".