diff --git a/ClangPowerTools/ClangPowerToolsShared/MVVM/Models/TidySettingsModel.cs b/ClangPowerTools/ClangPowerToolsShared/MVVM/Models/TidySettingsModel.cs index 0d5d9f8ef..e5960c5df 100644 --- a/ClangPowerTools/ClangPowerToolsShared/MVVM/Models/TidySettingsModel.cs +++ b/ClangPowerTools/ClangPowerToolsShared/MVVM/Models/TidySettingsModel.cs @@ -11,7 +11,7 @@ public class TidySettingsModel public string CustomChecks { get; set; } = string.Empty; public string CustomExecutable { get; set; } = string.Empty; - + public string CompilationDatabase { get; set; } = string.Empty; public bool DetectClangTidyFile { get; set; } = true; public bool FormatAfterTidy { get; set; } = false; diff --git a/ClangPowerTools/ClangPowerToolsShared/MVVM/SettingsTooltips.cs b/ClangPowerTools/ClangPowerToolsShared/MVVM/SettingsTooltips.cs index 03837c0e9..c5bbb88c3 100644 --- a/ClangPowerTools/ClangPowerToolsShared/MVVM/SettingsTooltips.cs +++ b/ClangPowerTools/ClangPowerToolsShared/MVVM/SettingsTooltips.cs @@ -30,6 +30,7 @@ public class SettingsTooltips public string PredefinedChecks { get; } = "A list of clang-tidy static analyzer and diagnostics checks from LLVM."; public string CustomChecks { get; } = "Specify clang-tidy checks to run using the standard tidy syntax. You can use wildcards to match multiple checks, combine them, etc (Eg. \"modernize-*, readability-*\")."; public string CustomExecutableTidy { get; } = "Specify a custom path for \"clang-tidy.exe\" file to run instead of the built-in one (v8.0)."; + public string CompilationDatabase{ get; } = "Specify a custom \"compile_commands.json\" file path to use as a source of compilation flags instead of the flags generated by extension."; public string DetectClangTidyFile { get; } = "Automatically detect the \".clang-tidy\" file and set the \"Use checks from\" option to \"TidyFile\" if the file exists. Otherwise, set the \"Use checks from\" option to \"PredefinedChecks\"."; public string FormatAfterTidy { get; } = "Automatically run clang-format after clang-tidy finished."; public string TidyOnSave { get; } = "Automatically run clang-tidy when saving the current source file."; diff --git a/ClangPowerTools/ClangPowerToolsShared/MVVM/ViewModels/TidySettingsViewModel.cs b/ClangPowerTools/ClangPowerToolsShared/MVVM/ViewModels/TidySettingsViewModel.cs index 440eefc2e..010cfcbdf 100644 --- a/ClangPowerTools/ClangPowerToolsShared/MVVM/ViewModels/TidySettingsViewModel.cs +++ b/ClangPowerTools/ClangPowerToolsShared/MVVM/ViewModels/TidySettingsViewModel.cs @@ -21,6 +21,7 @@ public class TidySettingsViewModel : CommonSettingsFunctionality, INotifyPropert private string displayWarning = string.Empty; private ICommand headerFilterAddDataCommand; private ICommand customExecutableBrowseCommand; + private ICommand compilationDatabaseBrowseCommand; private ICommand predefinedChecksSelectCommand; private ICommand customChecksAddDataCommand; private ICommand exportTidyConfigCommand; @@ -118,6 +119,11 @@ public ICommand CustomExecutableBrowseCommand get => customExecutableBrowseCommand ?? (customExecutableBrowseCommand = new RelayCommand(() => UpdateCustomExecutable(), () => CanExecute)); } + public ICommand CompilationDatabaseBrowseCommand + { + get => compilationDatabaseBrowseCommand ?? (compilationDatabaseBrowseCommand = new RelayCommand(() => UpdateCompilationDatabase(), () => CanExecute)); + } + public ICommand PredefinedChecksSelectCommand { get => predefinedChecksSelectCommand ?? (predefinedChecksSelectCommand = new RelayCommand(() => UpdatePredefinedChecks(), () => CanExecute)); @@ -167,6 +173,16 @@ private void UpdateCustomExecutable() PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("TidyModel")); } + private void UpdateCompilationDatabase() + { + string path = OpenFile(string.Empty, ".json", "Compilation database (*.json)|*.json"); + if (string.IsNullOrEmpty(path) == false) + { + tidyModel.CompilationDatabase = path; + } + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("TidyModel")); + } + private void UpdatePredefinedChecks() { OpenChecksWindow(); diff --git a/ClangPowerTools/ClangPowerToolsShared/MVVM/Views/Styles/ImageResources.xaml b/ClangPowerTools/ClangPowerToolsShared/MVVM/Views/Styles/ImageResources.xaml index 31e926055..8837595c2 100644 --- a/ClangPowerTools/ClangPowerToolsShared/MVVM/Views/Styles/ImageResources.xaml +++ b/ClangPowerTools/ClangPowerToolsShared/MVVM/Views/Styles/ImageResources.xaml @@ -8,6 +8,7 @@ + diff --git a/ClangPowerTools/ClangPowerToolsShared/MVVM/Views/TidySettingsView.xaml b/ClangPowerTools/ClangPowerToolsShared/MVVM/Views/TidySettingsView.xaml index dc2ac0a79..664a56a47 100644 --- a/ClangPowerTools/ClangPowerToolsShared/MVVM/Views/TidySettingsView.xaml +++ b/ClangPowerTools/ClangPowerToolsShared/MVVM/Views/TidySettingsView.xaml @@ -5,7 +5,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:ClangPowerTools" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - d:DesignHeight="420" + d:DesignHeight="440" d:DesignWidth="770" mc:Ignorable="d"> @@ -217,8 +217,49 @@ - + + + + + + + + + + + + + + + + - + - + - @@ -330,7 +371,7 @@ - + /// The clang flags - private string GetClangFlags() + private string GetClangFlagsOption() { var compilerSettings = SettingsProvider.CompilerSettingsModel; @@ -182,17 +182,21 @@ private string GetTidyParameters() using FileStream fs = new FileStream(filePath, FileMode.Create); using StreamWriter sw = new StreamWriter(fs); sw.Write(text); - parameters = AppendClangTidyType(filePath); + parameters = AppendClangTidyTypeOption(filePath); } else { - parameters = AppendClangTidyType(parameters); + parameters = AppendClangTidyTypeOption(parameters); } } // Get the header filter option - if (null != tidySettings.HeaderFilter && !string.IsNullOrWhiteSpace(tidySettings.HeaderFilter)) - parameters += $" {GetHeaderFilters()}"; + if (!string.IsNullOrWhiteSpace(tidySettings.HeaderFilter)) + parameters += $" {GetHeaderFiltersOption()}"; + + // Get the compilation database option + if (!string.IsNullOrWhiteSpace(tidySettings.CompilationDatabase)) + parameters += $" {GetCompilationDatabaseOption(tidySettings.CompilationDatabase)}"; parameters += $" {ScriptConstants.kParallel}"; return parameters; @@ -204,7 +208,7 @@ private string GetTidyParameters() /// /// /// The <"aParameters"> value with the clang tidy type with / without the clang tidy config file option attached - private string AppendClangTidyType(string aParameters) + private string AppendClangTidyTypeOption(string aParameters) { return string.Format("{0} '{1}'", (CommandIds.kTidyFixId == mCommandId ? ScriptConstants.kTidyFix : ScriptConstants.kTidy), @@ -216,7 +220,7 @@ private string AppendClangTidyType(string aParameters) /// Get the header filter option from the Clang Tidy Option page /// /// Header filter option - private string GetHeaderFilters() + private string GetHeaderFiltersOption() { var tidySettings = SettingsProvider.TidySettingsModel; @@ -244,6 +248,15 @@ private string GetTidyChecks(TidySettingsModel tidyModel) } } + /// + /// Get the compilation database file option from the Clang Tidy Option page + /// + /// Compilation database file option + private string GetCompilationDatabaseOption(string compilationDatabase) + { + DirectoryInfo dbPath = new DirectoryInfo(compilationDatabase); + return string.Format("{0} '{1}'", ScriptConstants.kCompilationDatabaseDir, dbPath.Parent.FullName); + } #endregion diff --git a/ClangPowerTools/ClangPowerToolsShared/Script/ScriptConstants.cs b/ClangPowerTools/ClangPowerToolsShared/Script/ScriptConstants.cs index bb7b0aad3..32e34d3ef 100644 --- a/ClangPowerTools/ClangPowerToolsShared/Script/ScriptConstants.cs +++ b/ClangPowerTools/ClangPowerToolsShared/Script/ScriptConstants.cs @@ -110,6 +110,7 @@ public class ScriptConstants public const string kHeaderFilter = "-header-filter"; public const string kTidyFile = ".clang-tidy"; + public const string kCompilationDatabaseDir = "-compilation-database-dir"; public const string kClangFormatStyle = "-format-style"; diff --git a/ClangPowerTools/ClangPowerToolsShared/Tooling/v1/clang-build.ps1 b/ClangPowerTools/ClangPowerToolsShared/Tooling/v1/clang-build.ps1 index 646d13fc4..fb61b4414 100644 --- a/ClangPowerTools/ClangPowerToolsShared/Tooling/v1/clang-build.ps1 +++ b/ClangPowerTools/ClangPowerToolsShared/Tooling/v1/clang-build.ps1 @@ -172,6 +172,10 @@ param( [alias("proj")] [Parameter(Mandatory=$false, HelpMessage="Enable Clang-Tidy to run on header files")] [string] $aTidyHeaderFilter + , [alias("compilation-database-dir")] + [Parameter(Mandatory=$false, HelpMessage="Specify a path of a directory where compile_commands.json is located.")] + [string] $aCompilationDatabaseDir + , [alias("format-style")] [Parameter(Mandatory=$false, HelpMessage="Used with 'tidy-fix'; tells CLANG TIDY-FIX to also format the fixed file(s)")] [string] $aAfterTidyFixFormatStyle @@ -240,12 +244,14 @@ Set-Variable -name kClangTidyFixExportFixes -value "--export-fixes=" -option Set-Variable -name kClangCompiler -value "clang++.exe" -option Constant -Set-Variable -name kClangTidyFlags -value @("-quiet" - ,"--") -option Constant +Set-Variable -name kQuiet -value '-quiet' -option Constant +Set-Variable -name kEndOptionMarker -value "--" -option Constant + Set-Variable -name kClangTidyFlagHeaderFilter -value "-header-filter=" -option Constant Set-Variable -name kClangTidyFlagChecks -value "-checks=" -option Constant Set-Variable -name kClangTidyUseFile -value ".clang-tidy" -option Constant Set-Variable -name kClangTidyFormatStyle -value "-format-style=" -option Constant +Set-Variable -name kClangTidyCompilationDatabaseDir -value "-p=" -option Constant Set-Variable -name kClangTidyFlagTempFile -value "" @@ -826,7 +832,8 @@ Function Get-TidyCallArguments( [Parameter(Mandatory=$false)][string[]] $preproc , [Parameter(Mandatory=$false)][string[]] $forceIncludeFiles , [Parameter(Mandatory=$true)][string] $fileToTidy , [Parameter(Mandatory=$false)][string] $pchFilePath - , [Parameter(Mandatory=$false)][switch] $fix) + , [Parameter(Mandatory=$false)][switch] $fix + , [Parameter(Mandatory=$false)][string] $compilationDatabaseDir) { [string[]] $tidyArgs = @() if ($fix) @@ -864,7 +871,21 @@ Function Get-TidyCallArguments( [Parameter(Mandatory=$false)][string[]] $preproc } } - $tidyArgs += $kClangTidyFlags + $tidyArgs += $kQuiet + if (![string]::IsNullOrEmpty($compilationDatabaseDir)) + { + if ($compilationDatabaseDir -eq '_') + { + $compilationDatabaseDir = Get-SourceDirectory + } + # When passed to cmd.exe the quote cannot be preceded by a backslash or it's escaped + $tidyArgs += "$kClangTidyCompilationDatabaseDir`"$($compilationDatabaseDir.TrimEnd("\"))`"" + # When we use compilation database, we don't need to add further args with + # compilation flags + return $tidyArgs + } + + $tidyArgs += $kEndOptionMarker $tidyArgs += Get-ClangIncludeDirectories -includeDirectories $includeDirectories ` -additionalIncludeDirectories $additionalIncludeDirectories @@ -900,7 +921,8 @@ Function Get-ExeCallArguments( [Parameter(Mandatory=$false)][string] $pchF , [Parameter(Mandatory=$false)][string[]] $preprocessorDefinitions , [Parameter(Mandatory=$false)][string[]] $forceIncludeFiles , [Parameter(Mandatory=$true) ][string] $currentFile - , [Parameter(Mandatory=$true) ][WorkloadType] $workloadType) + , [Parameter(Mandatory=$true) ][WorkloadType] $workloadType + , [Parameter(Mandatory=$false)][string] $compilationDatabaseDir) { switch ($workloadType) { @@ -915,13 +937,15 @@ Function Get-ExeCallArguments( [Parameter(Mandatory=$false)][string] $pchF -additionalIncludeDirectories $additionalIncludeDirectories ` -forceIncludeFiles $forceIncludeFiles ` -pchFilePath $pchFilePath ` - -fileToTidy $currentFile } + -fileToTidy $currentFile ` + -compilationDatabaseDir $compilationDatabaseDir} TidyFix { return Get-TidyCallArguments -preprocessorDefinitions $preprocessorDefinitions ` -includeDirectories $includeDirectories ` -additionalIncludeDirectories $additionalIncludeDirectories ` -forceIncludeFiles $forceIncludeFiles ` -pchFilePath $pchFilePath ` -fileToTidy $currentFile ` + -compilationDatabaseDir $compilationDatabaseDir ` -fix} } } @@ -1005,17 +1029,20 @@ Function Run-ClangJobs( [Parameter(Mandatory=$true)] $clangJobs [string] $clangConfigContent = "" if ($job.FilePath -like '*tidy*') { - # We have to separate Clang args from Tidy args - $splitparams = $job.ArgumentList -split " -- " - $clangConfigContent = $splitparams[1] - - # We may have an explicit .clang-tidy check-flag config file to be used - if (![string]::IsNullOrWhiteSpace($job.TidyFlagsTempFile) -and (Test-Path -LiteralPath $job.TidyFlagsTempFile)) + if (!($job.ArgumentList -like "$($job.kClangTidyCompilationDatabaseDir)*")) { - $splitparams[0] += " --config-file=""$($job.TidyFlagsTempFile)"" " - } + # We have to separate Clang args from Tidy args + $splitparams = $job.ArgumentList -split " -- " + $clangConfigContent = $splitparams[1] - $job.ArgumentList = "$($splitparams[0]) -- --config ""$clangConfigFile""" + # We may have an explicit .clang-tidy check-flag config file to be used + if (![string]::IsNullOrWhiteSpace($job.TidyFlagsTempFile) -and (Test-Path -LiteralPath $job.TidyFlagsTempFile)) + { + $splitparams[0] += " --config-file=""$($job.TidyFlagsTempFile)"" " + } + + $job.ArgumentList = "$($splitparams[0]) -- --config ""$clangConfigFile""" + } } else { @@ -1034,7 +1061,6 @@ Function Run-ClangJobs( [Parameter(Mandatory=$true)] $clangJobs # When PowerShell encounters errors, the first one is handled differently from consecutive ones # To circumvent this, do not execute the job directly, but execute it via cmd.exe # See also https://stackoverflow.com/a/35980675 - $callOutput = cmd /c "$($job.FilePath) $($job.ArgumentList) 2>&1" | Out-String $callSuccess = $LASTEXITCODE -eq 0 @@ -1550,7 +1576,8 @@ Function Process-Project( [Parameter(Mandatory=$true)] [string] $vcxprojPa -forceIncludeFiles $cppForceIncludes ` -currentFile $cpp ` -includeDirectories $includeDirectories ` - -additionalIncludeDirectories $additionalIncludeDirectories + -additionalIncludeDirectories $additionalIncludeDirectories ` + -compilationDatabaseDir $aCompilationDatabaseDir $newJob = New-Object PsObject -Prop @{ 'FilePath' = $exeToCall ; 'WorkingDirectory' = Get-SourceDirectory @@ -1558,6 +1585,7 @@ Function Process-Project( [Parameter(Mandatory=$true)] [string] $vcxprojPa ; 'File' = $cpp ; 'JobCounter' = 0 <# will be lazy initialized #> ; 'TidyFlagsTempFile' = $kClangTidyFlagTempFile + ; 'kClangTidyCompilationDatabaseDir' = $kClangTidyCompilationDatabaseDir } $clangJobs += $newJob }