Skip to content

Commit

Permalink
Look for .cquery in any directory above the source file in the hiera…
Browse files Browse the repository at this point in the history
…rchy. (MaskRay#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
  • Loading branch information
Riatre authored and MaskRay committed Feb 3, 2018
1 parent 6933870 commit 54c587a
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 23 deletions.
1 change: 0 additions & 1 deletion src/platform_win.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
66 changes: 47 additions & 19 deletions src/project.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
13 changes: 11 additions & 2 deletions src/utils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down Expand Up @@ -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 + "/";
Expand Down
3 changes: 2 additions & 1 deletion src/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -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".
Expand Down

0 comments on commit 54c587a

Please sign in to comment.