Skip to content
This repository has been archived by the owner on Jul 30, 2020. It is now read-only.

Add global .cquery file #702

Merged
merged 21 commits into from
Jun 29, 2018
Merged

Add global .cquery file #702

merged 21 commits into from
Jun 29, 2018

Conversation

nick96
Copy link
Contributor

@nick96 nick96 commented Jun 9, 2018

This PR implements the feature requested in #648.

The key changes are:

  • On *nix looks for a .cquery file in $XDG_CONFIG_HOME/cquery or in $HOME if this fails
  • On Windows looks for a .cquery file in the %APPDATA

I've tested this a bit on my machine but haven't tested out the Windows specific stuff as a I don't have access to a Windows machine so I hope it works 😄

@nick96
Copy link
Contributor Author

nick96 commented Jun 10, 2018

It looks like the tests are failing because of:

https://github.com/cquery-project/cquery/blob/9a9b2b373e4995066e135481fd677b266b8f0893/src/cache_manager.cc#L76

I don't really understand the logic of having assert(false) in FakeCacheManager so am not sure how to go about fixing this

std::wstring_convert<convert_type, wchar_t> converter;
cfg_path = converter.to_bytes(roaming_stream.str());
}
CoTaskMemFree(static_cast<void*>(roaming_path));
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://msdn.microsoft.com/en-us/library/windows/desktop/ms680722(v=vs.85).aspx it sounds like this should only be used if the memory was allocated using CoTaskMemAlloc or CoTaskMemRealloc.

Btw, is there a sample somewhere to do this? I'd recommend referencing any doc pages here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the docs for https://msdn.microsoft.com/en-us/library/bb762188(VS.85).aspx, it says call is responsible for freeing ppszPath (in this case roaming_path) using CoTaskMemFree. I have moved the call inside the block where the we use it because CoTaskMemFree does nothing when the parameter is NULL.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have referenced the docs in a comment above the call as you recommended

src/project.cc Outdated
@@ -472,6 +472,15 @@ std::vector<Project::Entry> LoadFromDirectoryListing(ProjectConfig* config) {
}
});

optional<std::string> maybe_cfg = GetGlobalConfigDirectory();
if (folder_args.empty() && maybe_cfg) {
std::string cfg(*maybe_cfg + ".cquery");
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: std::string config = *maybe_config + ".cquery";

src/project.cc Outdated
if (FileExists(project->project_dir + ".cquery"))
return LoadFromDirectoryListing(project);
else if (GetGlobalConfigDirectory() &&
FileExists(*GetGlobalConfigDirectory() + ".cquery"))
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: {}

src/project.cc Outdated
@@ -472,6 +472,15 @@ std::vector<Project::Entry> LoadFromDirectoryListing(ProjectConfig* config) {
}
});

optional<std::string> maybe_cfg = GetGlobalConfigDirectory();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the purpose of this block only for the LOG_S(INFO)? In that case we can move the LOG_S(...) into LoadFromDirectoryListing which should eliminate the need for this block.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No sure what you're referring to here? It that the right block?

EnsureEndsInSlash(config);
config += "cquery";
if (!FileExists(config)) {
MakeDirectoryRecursive(AbsolutePath(config, true));
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MakeDirectoryRecursive(AbsolutePath(config, false /*validate*/));

If validate is true then cquery may abort since validate means the directory should exist.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, have fixed it up

} else if (home) {
config = home;
} else {
return {};
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: return nullopt;

But instead of having a separate return here (returns in the middle of a func make control-flow hard to read) you can refactor this function so that config is an optional. The final call to EnsureEndsInSlash will need to be if (config) EnsureEndsInSlash(*config);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The refactor definitely makes it cleaner, have done as you recommended

@jacobdufault
Copy link
Owner

The CI failure is unrelated, master is broken at the moment. We don't have any tests that would validate this logic.

@jacobdufault
Copy link
Owner

One thing to make sure is that a local compile_commands.json will override the global .cquery file.

@nick96
Copy link
Contributor Author

nick96 commented Jun 11, 2018

I've fixed it so compile_commands.json is preferred over the global .cquery. Have explained the rational in 9c53e2b. The cleanest way I could think of was adding a default parameter to LoadFromDirectoryListing. Previous calls it the function don't need to be changed but I think it makes it clearer what we want to do (load a global config) and doesn't required having to redo a whole bunch of checks.

Copy link
Owner

@jacobdufault jacobdufault left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should make sure to write up the logic of how compilation commands are selected, since the logic is a bit complex/subtle.

src/project.cc Outdated
[&folder_args, &files, &use_global_config](const std::string& path) {
if (SourceFileLanguage(path) != LanguageId::Unknown) {
files.push_back(path);
} else if (GetBaseName(path) == ".cquery" && !use_global_config) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this is disabling a local .cquery file in the source tree if there is a global config? I think we should honor a local .cquery over the global one even if there is not a .cquery at the top level.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In retrospect I can see how this is not very clear. My rational was that if LoadFromDirectoryListing is called with use_global_config set to true then the caller is intending for the global config to be loaded. I'll change it so that the local .cquery is loaded in all cases

Nick Spain added 20 commits June 20, 2018 21:19
This platform specific function is used to get the user's home
directory. This gives us a base from where to get the global config
file for.

Currently WIP as I'm not sure how to implement this for Windows.
This is a tentative implementation, I don't have access to windows so
haven't tested it but based on MSDN and StackOverflow this seems to be
the way to go.
As discussed in jacobdufault#648, fallback to $HOME if $XDG_CINFIG_HOME is not
found.
Previously the global .cquery config was being loaded before the
project specific compile_commands.json file. The global .cquery config
is intended as a fallback when there are no other configs, so we don't
want this.

To make it explicit that the global .cquery is being loaded, add a new
parameter to LoadFromDirectorylisting (use_global_config) which, if
true, will load the global .cquery. This parameter defaults to false,
so no changes are required to existing calls.
@nick96
Copy link
Contributor Author

nick96 commented Jun 20, 2018

To document the process of selecting compilation commands, I think adding a second paragraph under the compile_commands.json header of the wiki home page with something like what I've written below would be sufficient.

There are three options for specifying the compilation commands that cquery uses for a project.

  1. Use a compile_commands.json as generated by your build tool. This can either be in the project root directory or the build directory stemming from the root (the project root is preferred)
  2. Use a .cquery file in the root
  3. Use a .cquery file the configuration directory of your system (i.e. %RoamingAppData%/cquery/, $XDG_CONFIG_HOME/cquery/ or $HOME)

The selection order of the compilation commands is:
compile_commands.json > local .cquery > global .cquery

@jacobdufault
Copy link
Owner

I can do a squash and merge, but if you'd like to preserve commit history this needs to be rebased. Let me know which you prefer.

@nick96
Copy link
Contributor Author

nick96 commented Jun 26, 2018

I think a squash and merge is probably best. There's nothing in the commit history of particular importance.

@jacobdufault jacobdufault merged commit 76a7ea6 into jacobdufault:master Jun 29, 2018
decimad pushed a commit to decimad/cquery that referenced this pull request Jul 13, 2018
* Create `GetHomedirectory()` function

This platform specific function is used to get the user's home
directory. This gives us a base from where to get the global config
file for.

Currently WIP as I'm not sure how to implement this for Windows.

* Find and parse .cquery in home dir if not in project dir

* GetHomeDirectory() -> GetGlobalConfigDirectory()

* Windows specific implementation of `GetGlobalConfigDirectory()`

This is a tentative implementation, I don't have access to windows so
haven't tested it but based on MSDN and StackOverflow this seems to be
the way to go.

* Update *nix implementation of `GetGlobalConfigDirectory()`

As discussed in jacobdufault#648, fallback to $HOME if $XDG_CINFIG_HOME is not
found.

* Use a global .cquery file if none found in the project dir

* Add rational to usage of XDG_CONFIG_HOME

* Create "cquery" directory in .config if it does not exist

* Fix up  on Windows

* Only call `CoTaskMemFree` if `ShGetKnownFolderpath` passes

* Refactor config to optional to improve control flow

* Add comment referencing MSDN blog on getting config dir on Windows

* Implicitly call string constructor using = operator

* Add braces around if-else block

* Fixup some errors introduced when making config optional

* config -> cfg

* Fixup crash when assigning to config

* Load global .cquery config at end of LoadCompilationEntriesfromdirectory

Previously the global .cquery config was being loaded before the
project specific compile_commands.json file. The global .cquery config
is intended as a fallback when there are no other configs, so we don't
want this.

To make it explicit that the global .cquery is being loaded, add a new
parameter to LoadFromDirectorylisting (use_global_config) which, if
true, will load the global .cquery. This parameter defaults to false,
so no changes are required to existing calls.

* Add reference for usage of CoTaskMemFree after SHGetKnownFolderPath

* Load the local .cquery in all cases that it is present
@liushapku
Copy link

Reading this thread and also the commit message, I get completely lost about the the position of the global configuration file. Which one is it:

~/.cquery
~/.config/cquery/settings
~/.config/cquery
~/.config/.cquery

@nick96
Copy link
Contributor Author

nick96 commented Dec 17, 2018

For the global config file you can either have a .cquery file in your $HOME directory or at $XDG_CONFIG_HOME/cquery

@liushapku
Copy link

I just found a better way to set the global options which is to add a cquery.cfg file in the same folder of cqeury-clang; according to the configuration of clang. The ~/.cquery file is not respected when a compile_commands.json file exists for the project.

For me, there is no system wide gcc installed, neither the header files and libraries for the c++17. So I installed a gcc in my home folder. And I need to use --gcc-toolchain=my_gcc_path to make clang work.
I need to combine the project compile_commands.json with the --gcc-toolchain specification. The ~/.cquery approach does not work here.

@nick96
Copy link
Contributor Author

nick96 commented Jan 12, 2019

Am I right in reading that you want to have the options set by ~/.cquery and compile_commands.json?

I didn't know about having cquery.cfg in the same folder as cquery-clang. It could be a bit annoying having config files in that directory. I like having in a more centralised place, preferable in an XDG directory but the home directory also works.

Having a global config setting the baseline options and the a project local config overriding these as necessary would probably be quite useful, @jacobdufault thoughts?

@liushapku
Copy link

liushapku commented Jan 14, 2019

@nick96, I didn't mean that I expect to add an cquery.cfg in the installation folder by this cqeury-project. I just wanted to share there exists another approach to fill the needs like what I had. It is the end users that need to add that file in their own interest.

In summary, there are at least three ways to set the config for cquery:

  1. use the project level compile_commands.json. If this exists, the ~/.cquery is ignored
  2. use the user level ~/.cquery or $XDG_CONFIG_HOME/cquery. This is the fallback when item 1 does not exist.
  3. use the cquery.cfg in the same folder of the cquery binary. This also works in the user level like item 2. But, more flexible in that the configuration here is combined with item 1 or item 2.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants