From e03402c1c7890f13e52def96d154598b9da9682a Mon Sep 17 00:00:00 2001 From: Georgii Borovinskikh <117642191+georgii-borovinskikh-sonarsource@users.noreply.github.com> Date: Mon, 16 Dec 2024 14:40:47 +0100 Subject: [PATCH] SLVS-1673 Include environment variables in generated compilation db (#5892) [SLVS-1673](https://sonarsource.atlassian.net/browse/SLVS-1673) [SLVS-1673]: https://sonarsource.atlassian.net/browse/SLVS-1673?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ --- .../CompilationDatabaseEntry.cs | 29 ++-- .../EnvironmentVariableProviderTests.cs | 20 +++ src/Core/EnvironmentVariableProvider.cs | 26 +++- .../VCXCompilationDatabaseProviderTests.cs | 132 +++++++++++++++--- .../VCXCompilationDatabaseStorageTests.cs | 47 +++---- ...egration.Vsix_Baseline_WithStrongNames.txt | 5 +- ...ation.Vsix_Baseline_WithoutStrongNames.txt | 5 +- .../IVCXCompilationDatabaseStorage.cs | 38 +++-- .../VCXCompilationDatabaseProvider.cs | 41 +++++- 9 files changed, 260 insertions(+), 83 deletions(-) rename src/CFamily/{CMake => CompilationDatabase}/CompilationDatabaseEntry.cs (62%) diff --git a/src/CFamily/CMake/CompilationDatabaseEntry.cs b/src/CFamily/CompilationDatabase/CompilationDatabaseEntry.cs similarity index 62% rename from src/CFamily/CMake/CompilationDatabaseEntry.cs rename to src/CFamily/CompilationDatabase/CompilationDatabaseEntry.cs index 12273db1a8..1332634f64 100644 --- a/src/CFamily/CMake/CompilationDatabaseEntry.cs +++ b/src/CFamily/CompilationDatabase/CompilationDatabaseEntry.cs @@ -20,23 +20,22 @@ using Newtonsoft.Json; -namespace SonarLint.VisualStudio.CFamily.CMake +namespace SonarLint.VisualStudio.CFamily.CompilationDatabase; + +/// +/// Schema based on https://clang.llvm.org/docs/JSONCompilationDatabase.html +/// +public class CompilationDatabaseEntry { - /// - /// Schema based on https://clang.llvm.org/docs/JSONCompilationDatabase.html - /// - public class CompilationDatabaseEntry - { - [JsonProperty("directory")] - public string Directory { get; set; } + [JsonProperty("directory")] + public string Directory { get; set; } - [JsonProperty("command")] - public string Command { get; set; } + [JsonProperty("command")] + public string Command { get; set; } - [JsonProperty("file")] - public string File { get; set; } + [JsonProperty("file")] + public string File { get; set; } - [JsonProperty("arguments")] - public string Arguments { get; set; } - } + [JsonProperty("environment")] + public IEnumerable Environment { get; set; } } diff --git a/src/Core.UnitTests/EnvironmentVariableProviderTests.cs b/src/Core.UnitTests/EnvironmentVariableProviderTests.cs index d3abc138c3..748ce830e7 100644 --- a/src/Core.UnitTests/EnvironmentVariableProviderTests.cs +++ b/src/Core.UnitTests/EnvironmentVariableProviderTests.cs @@ -28,6 +28,26 @@ namespace SonarLint.VisualStudio.Core.UnitTests [TestClass] public class EnvironmentVariableProviderTests { + [TestMethod] + public void MefCtor_CheckIsExported() => MefTestHelpers.CheckTypeCanBeImported(); + + [TestMethod] + public void MefCtor_CheckIsSingleton() => MefTestHelpers.CheckIsSingletonMefComponent(); + + [TestMethod] + public void GetAll_ReturnsExpectedValues() + { + var testSubject = EnvironmentVariableProvider.Instance; + using var environmentVariableScope = new EnvironmentVariableScope(); + environmentVariableScope.SetVariable("VAR1", "VAL1"); + environmentVariableScope.SetVariable("VAR2", "VAL2"); + + var variables = testSubject.GetAll(); + + variables.Should().HaveCountGreaterThan(2); + variables.Should().Contain([("VAR1", "VAL1"), ("VAR2", "VAL2")]); + } + [TestMethod] [DataRow(null)] [DataRow("")] diff --git a/src/Core/EnvironmentVariableProvider.cs b/src/Core/EnvironmentVariableProvider.cs index 098412c9d3..573d8f62d6 100644 --- a/src/Core/EnvironmentVariableProvider.cs +++ b/src/Core/EnvironmentVariableProvider.cs @@ -19,8 +19,11 @@ */ using System; +using System.Collections; +using System.ComponentModel.Composition; using System.Diagnostics; using System.IO; +using SonarLint.VisualStudio.Core; namespace SonarLint.VisualStudio.Core { @@ -38,12 +41,20 @@ public interface IEnvironmentVariableProvider /// Gets the path to the system special folder that is identified by the specified enumeration. /// string GetFolderPath(Environment.SpecialFolder folder); + + /// + /// Gets the current process environment variables + /// + List<(string name, string value)> GetAll(); } + [Export(typeof(IEnvironmentVariableProvider))] + [PartCreationPolicy(CreationPolicy.Shared)] public class EnvironmentVariableProvider : IEnvironmentVariableProvider { - public static EnvironmentVariableProvider Instance { get; } = new EnvironmentVariableProvider(); + public static EnvironmentVariableProvider Instance { get; } = new(); + [ImportingConstructor] private EnvironmentVariableProvider() { // no-op @@ -60,6 +71,19 @@ public string TryGet(string variableName) } public string GetFolderPath(Environment.SpecialFolder folder) => Environment.GetFolderPath(folder); + + public List<(string name, string value)> GetAll() + { + var variables = new List<(string name, string value)>(); + foreach (DictionaryEntry environmentVariable in Environment.GetEnvironmentVariables()) + { + if (environmentVariable is { Key: string variableName, Value: string variableValue }) + { + variables.Add((variableName, variableValue)); + } + } + return variables; + } } /// diff --git a/src/Integration.Vsix.UnitTests/CFamily/VcxProject/VCXCompilationDatabaseProviderTests.cs b/src/Integration.Vsix.UnitTests/CFamily/VcxProject/VCXCompilationDatabaseProviderTests.cs index 6a8c4100be..13e95e69b2 100644 --- a/src/Integration.Vsix.UnitTests/CFamily/VcxProject/VCXCompilationDatabaseProviderTests.cs +++ b/src/Integration.Vsix.UnitTests/CFamily/VcxProject/VCXCompilationDatabaseProviderTests.cs @@ -20,6 +20,7 @@ using NSubstitute.ReturnsExtensions; using SonarLint.VisualStudio.CFamily; +using SonarLint.VisualStudio.Core; using SonarLint.VisualStudio.Core.CFamily; using SonarLint.VisualStudio.Integration.Vsix.CFamily.VcxProject; @@ -28,60 +29,155 @@ namespace SonarLint.VisualStudio.Integration.UnitTests.CFamily.VcxProject; [TestClass] public class VCXCompilationDatabaseProviderTests { + private const string CDFile = "cdfilevalue"; + private const string CDDirectory = "cddirectoryvalue"; + private const string CDCommand = "cdcommandvalue"; + private const string EnvInclude = "envincludevalue"; private const string SourceFilePath = "some path"; private IVCXCompilationDatabaseStorage storage; private IFileConfigProvider fileConfigProvider; - private VCXCompilationDatabaseProvider testSubject; + private IEnvironmentVariableProvider envVarProvider; + + [TestInitialize] + public void TestInitialize() + { + storage = Substitute.For(); + fileConfigProvider = Substitute.For(); + envVarProvider = Substitute.For(); + envVarProvider.GetAll().Returns([]); + } [TestMethod] - public void MefCtor_CheckIsExported() => + public void MefCtor_CheckIsExported() + { + envVarProvider.GetAll().Returns([]); MefTestHelpers.CheckTypeCanBeImported( MefTestHelpers.CreateExport(), + MefTestHelpers.CreateExport(envVarProvider), MefTestHelpers.CreateExport()); + } [TestMethod] public void MefCtor_CheckIsSingleton() => MefTestHelpers.CheckIsSingletonMefComponent(); - [TestInitialize] - public void TestInitialize() - { - storage = Substitute.For(); - fileConfigProvider = Substitute.For(); - testSubject = new VCXCompilationDatabaseProvider( - storage, - fileConfigProvider); - } - [TestMethod] public void CreateOrNull_NoFileConfig_ReturnsNull() { fileConfigProvider.Get(SourceFilePath, default).ReturnsNull(); + var testSubject = new VCXCompilationDatabaseProvider( + storage, + envVarProvider, + fileConfigProvider); testSubject.CreateOrNull(SourceFilePath).Should().BeNull(); - storage.DidNotReceiveWithAnyArgs().CreateDatabase(default); + storage.DidNotReceiveWithAnyArgs().CreateDatabase(default, default, default, default); } [TestMethod] public void CreateOrNull_FileConfig_CantStore_ReturnsNull() { - var fileConfig = Substitute.For(); + var fileConfig = GetFileConfig(); fileConfigProvider.Get(SourceFilePath, default).Returns(fileConfig); - storage.CreateDatabase(fileConfig).ReturnsNull(); + storage.CreateDatabase(default, default, default, default).ReturnsNullForAnyArgs(); + var testSubject = new VCXCompilationDatabaseProvider( + storage, + envVarProvider, + fileConfigProvider); testSubject.CreateOrNull(SourceFilePath).Should().BeNull(); - storage.Received().CreateDatabase(fileConfig); + storage.Received().CreateDatabase(CDFile, CDDirectory, CDCommand, Arg.Any>()); } [TestMethod] public void CreateOrNull_FileConfig_StoresAndReturnsHandle() { - var fileConfig = Substitute.For(); + var fileConfig = GetFileConfig(); fileConfigProvider.Get(SourceFilePath, default).Returns(fileConfig); var compilationDatabaseHandle = Substitute.For(); - storage.CreateDatabase(fileConfig).Returns(compilationDatabaseHandle); + storage.CreateDatabase(CDFile, CDDirectory, CDCommand, Arg.Any>()).Returns(compilationDatabaseHandle); + var testSubject = new VCXCompilationDatabaseProvider( + storage, + envVarProvider, + fileConfigProvider); testSubject.CreateOrNull(SourceFilePath).Should().Be(compilationDatabaseHandle); } + + [TestMethod] + public void CreateOrNull_NoEnvIncludeInFileConfig_UsesStatic() + { + var fileConfig = GetFileConfig(null); + fileConfigProvider.Get(SourceFilePath, default).Returns(fileConfig); + envVarProvider.GetAll().Returns([("Var1", "Value1"), ("INCLUDE", "static"), ("Var2", "Value2")]); + var testSubject = new VCXCompilationDatabaseProvider( + storage, + envVarProvider, + fileConfigProvider); + + testSubject.CreateOrNull(SourceFilePath); + + storage.Received(1).CreateDatabase(CDFile, CDDirectory, CDCommand, Arg.Is>(x => x.SequenceEqual(new [] { "Var1=Value1", "INCLUDE=static", "Var2=Value2" }))); + } + + [TestMethod] + public void CreateOrNull_FileConfigHasEnvInclude_UsesDynamic() + { + var fileConfig = GetFileConfig(EnvInclude); + fileConfigProvider.Get(SourceFilePath, default).Returns(fileConfig); + envVarProvider.GetAll().Returns([("Var1", "Value1"), ("INCLUDE", "static"), ("Var2", "Value2")]); + var testSubject = new VCXCompilationDatabaseProvider( + storage, + envVarProvider, + fileConfigProvider); + + testSubject.CreateOrNull(SourceFilePath); + + storage.Received(1).CreateDatabase(CDFile, CDDirectory, CDCommand, Arg.Is>(x => x.SequenceEqual(new [] { "Var1=Value1", "Var2=Value2", $"INCLUDE={EnvInclude}"}))); + } + + [TestMethod] + public void CreateOrNull_NoStaticInclude_UsesDynamic() + { + var fileConfig = GetFileConfig(EnvInclude); + fileConfigProvider.Get(SourceFilePath, default).Returns(fileConfig); + envVarProvider.GetAll().Returns([("Var1", "Value1"), ("Var2", "Value2")]); + var testSubject = new VCXCompilationDatabaseProvider( + storage, + envVarProvider, + fileConfigProvider); + + testSubject.CreateOrNull(SourceFilePath); + + storage.Received(1).CreateDatabase(CDFile, CDDirectory, CDCommand, Arg.Is>(x => x.SequenceEqual(new [] { "Var1=Value1", "Var2=Value2", $"INCLUDE={EnvInclude}"}))); + } + + [TestMethod] + public void CreateOrNull_StaticEnvVarsAreCached() + { + var fileConfig = GetFileConfig(); + fileConfigProvider.Get(SourceFilePath, default).Returns(fileConfig); + envVarProvider.GetAll().Returns([("Var1", "Value1"), ("Var2", "Value2")]); + var testSubject = new VCXCompilationDatabaseProvider( + storage, + envVarProvider, + fileConfigProvider); + + testSubject.CreateOrNull(SourceFilePath); + testSubject.CreateOrNull(SourceFilePath); + testSubject.CreateOrNull(SourceFilePath); + + envVarProvider.Received(1).GetAll(); + } + + private IFileConfig GetFileConfig(string envInclude = EnvInclude) + { + var fileConfig = Substitute.For(); + fileConfig.CDFile.Returns(CDFile); + fileConfig.CDDirectory.Returns(CDDirectory); + fileConfig.CDCommand.Returns(CDCommand); + fileConfig.EnvInclude.Returns(envInclude); + return fileConfig; + } } diff --git a/src/Integration.Vsix.UnitTests/CFamily/VcxProject/VCXCompilationDatabaseStorageTests.cs b/src/Integration.Vsix.UnitTests/CFamily/VcxProject/VCXCompilationDatabaseStorageTests.cs index 07a739e6b0..5c9886d678 100644 --- a/src/Integration.Vsix.UnitTests/CFamily/VcxProject/VCXCompilationDatabaseStorageTests.cs +++ b/src/Integration.Vsix.UnitTests/CFamily/VcxProject/VCXCompilationDatabaseStorageTests.cs @@ -21,7 +21,7 @@ using System.IO; using Newtonsoft.Json; using NSubstitute.ExceptionExtensions; -using SonarLint.VisualStudio.CFamily.CMake; +using SonarLint.VisualStudio.CFamily.CompilationDatabase; using SonarLint.VisualStudio.Core; using SonarLint.VisualStudio.Core.Helpers; using SonarLint.VisualStudio.Core.SystemAbstractions; @@ -35,12 +35,22 @@ public class VCXCompilationDatabaseStorageTests private const string CompileCommand = "compile"; private const string SourceDirectory = @"C:\a\b\c"; private const string SourceFileName = "source.cpp"; + private static readonly IEnumerable EnvValue = ["envincludevalue"]; private static readonly string SourceFilePath = Path.Combine(SourceDirectory, SourceFileName); private IFileSystemService fileSystemService; private IThreadHandling threadHandling; private IVCXCompilationDatabaseStorage testSubject; private TestLogger testLogger; + [TestInitialize] + public void TestInitialize() + { + fileSystemService = Substitute.For(); + threadHandling = Substitute.For(); + testLogger = new TestLogger(); + testSubject = new VCXCompilationDatabaseStorage(fileSystemService, threadHandling, testLogger); + } + [TestMethod] public void MefCtor_CheckIsExported() => MefTestHelpers.CheckTypeCanBeImported( @@ -51,21 +61,12 @@ public void MefCtor_CheckIsExported() => [TestMethod] public void MefCtor_CheckIsSingleton() => MefTestHelpers.CheckIsSingletonMefComponent(); - [TestInitialize] - public void TestInitialize() - { - fileSystemService = Substitute.For(); - threadHandling = Substitute.For(); - testLogger = new TestLogger(); - testSubject = new VCXCompilationDatabaseStorage(fileSystemService, threadHandling, testLogger); - } - [TestMethod] public void CreateDatabase_NonCriticalException_ReturnsNull() { fileSystemService.Directory.CreateDirectory(default).ThrowsForAnyArgs(); - var database = testSubject.CreateDatabase(Substitute.For()); + var database = testSubject.CreateDatabase(default, default, default, default); database.Should().BeNull(); testLogger.AssertPartialOutputStrings(nameof(NotImplementedException)); @@ -76,7 +77,7 @@ public void CreateDatabase_CriticalException_Throws() { fileSystemService.Directory.CreateDirectory(default).ThrowsForAnyArgs(); - var act = () => testSubject.CreateDatabase(Substitute.For()); + var act = () => testSubject.CreateDatabase(default, default, default, default); act.Should().Throw(); } @@ -85,9 +86,8 @@ public void CreateDatabase_CriticalException_Throws() public void CreateDatabase_FileWritten_ReturnsPathToDatabaseWithCorrectContent() { var expectedDirectory = Path.Combine(Path.GetTempPath(), "SLVS", "VCXCD", PathHelper.PerVsInstanceFolderName.ToString()); - var fileConfig = SetUpFileConfig(); - var databaseHandle = testSubject.CreateDatabase(fileConfig); + var databaseHandle = testSubject.CreateDatabase(SourceFilePath, SourceDirectory, CompileCommand, EnvValue); var temporaryCompilationDatabaseHandle = databaseHandle.Should().BeOfType().Subject; Directory.GetParent(temporaryCompilationDatabaseHandle.FilePath).FullName.Should().BeEquivalentTo(expectedDirectory); @@ -102,10 +102,9 @@ public void CreateDatabase_FileWritten_ReturnsPathToDatabaseWithCorrectContent() public void CreateDatabase_CreatesDifferentHandlesForSameFile() { var expectedDirectory = Path.Combine(Path.GetTempPath(), "SLVS", "VCXCD", PathHelper.PerVsInstanceFolderName.ToString()); - var fileConfig = SetUpFileConfig(); - var databaseHandle1 = testSubject.CreateDatabase(fileConfig); - var databaseHandle2 = testSubject.CreateDatabase(fileConfig); + var databaseHandle1 = testSubject.CreateDatabase(SourceFilePath, SourceDirectory, CompileCommand, EnvValue); + var databaseHandle2 = testSubject.CreateDatabase(SourceFilePath, SourceDirectory, CompileCommand, EnvValue); Directory.GetParent(databaseHandle1.FilePath).FullName.Should().BeEquivalentTo(expectedDirectory); Directory.GetParent(databaseHandle2.FilePath).FullName.Should().BeEquivalentTo(expectedDirectory); @@ -117,7 +116,7 @@ public void CreateDatabase_Disposed_Throws() { testSubject.Dispose(); - var act = () => testSubject.CreateDatabase(Substitute.For()); + var act = () => testSubject.CreateDatabase(default, default, default, default); act.Should().Throw(); } @@ -157,19 +156,11 @@ public void Dispose_CatchesAndLogsException() testLogger.AssertPartialOutputStringExists(exception.ToString()); } - private static IFileConfig SetUpFileConfig() - { - var fileConfig = Substitute.For(); - fileConfig.CDFile.Returns(SourceFilePath); - fileConfig.CDDirectory.Returns(SourceDirectory); - fileConfig.CDCommand.Returns(CompileCommand); - return fileConfig; - } - private void VerifyDatabaseContents() { var serializedCompilationDatabase = fileSystemService.File.ReceivedCalls().Single().GetArguments()[1] as string; var compilationDatabaseEntries = JsonConvert.DeserializeObject(serializedCompilationDatabase); - compilationDatabaseEntries.Should().BeEquivalentTo(new CompilationDatabaseEntry { Directory = SourceDirectory, File = SourceFilePath, Command = CompileCommand, Arguments = null }); + var compilationDatabaseEntry = compilationDatabaseEntries.Single(); + compilationDatabaseEntry.Should().BeEquivalentTo(new CompilationDatabaseEntry { Directory = SourceDirectory, File = SourceFilePath, Command = CompileCommand, Environment = EnvValue}); } } diff --git a/src/Integration.Vsix/AsmRef_Integration.Vsix_Baseline_WithStrongNames.txt b/src/Integration.Vsix/AsmRef_Integration.Vsix_Baseline_WithStrongNames.txt index ebf7b6c0c2..850e3e1294 100644 --- a/src/Integration.Vsix/AsmRef_Integration.Vsix_Baseline_WithStrongNames.txt +++ b/src/Integration.Vsix/AsmRef_Integration.Vsix_Baseline_WithStrongNames.txt @@ -1,7 +1,7 @@ --- ################################ # Assembly references report -# Report date/time: 2024-12-16T11:53:41.3325180Z +# Report date/time: 2024-12-16T13:22:38.0076364Z ################################ # # Generated by Devtility CheckAsmRefs v0.11.0.223 @@ -56,12 +56,13 @@ Referenced assemblies: - 'SonarLint.VisualStudio.SLCore, Version=8.9.0.0, Culture=neutral, PublicKeyToken=c5b62af9de6d7244' - 'SonarQube.Client, Version=8.9.0.0, Culture=neutral, PublicKeyToken=c5b62af9de6d7244' - 'System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' +- 'System.Collections.Immutable, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' - 'System.ComponentModel.Composition, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' - 'System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' - 'System.IO.Abstractions, Version=9.0.0.0, Culture=neutral, PublicKeyToken=96bf224d23c43e59' - 'System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' - 'System.Xaml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' -# Number of references: 31 +# Number of references: 32 --- Assembly: 'SonarLint.VisualStudio.CFamily, Version=8.9.0.0, Culture=neutral, PublicKeyToken=c5b62af9de6d7244' diff --git a/src/Integration.Vsix/AsmRef_Integration.Vsix_Baseline_WithoutStrongNames.txt b/src/Integration.Vsix/AsmRef_Integration.Vsix_Baseline_WithoutStrongNames.txt index f4d516f9da..4972915101 100644 --- a/src/Integration.Vsix/AsmRef_Integration.Vsix_Baseline_WithoutStrongNames.txt +++ b/src/Integration.Vsix/AsmRef_Integration.Vsix_Baseline_WithoutStrongNames.txt @@ -1,7 +1,7 @@ --- ################################ # Assembly references report -# Report date/time: 2024-12-16T11:53:41.3325180Z +# Report date/time: 2024-12-16T13:22:38.0076364Z ################################ # # Generated by Devtility CheckAsmRefs v0.11.0.223 @@ -56,12 +56,13 @@ Referenced assemblies: - 'SonarLint.VisualStudio.SLCore, Version=8.9.0.0, Culture=neutral, PublicKeyToken=null' - 'SonarQube.Client, Version=8.9.0.0, Culture=neutral, PublicKeyToken=null' - 'System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' +- 'System.Collections.Immutable, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' - 'System.ComponentModel.Composition, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' - 'System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' - 'System.IO.Abstractions, Version=9.0.0.0, Culture=neutral, PublicKeyToken=96bf224d23c43e59' - 'System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' - 'System.Xaml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' -# Number of references: 31 +# Number of references: 32 --- Assembly: 'SonarLint.VisualStudio.CFamily, Version=8.9.0.0, Culture=neutral, PublicKeyToken=null' diff --git a/src/Integration.Vsix/CFamily/VcxProject/IVCXCompilationDatabaseStorage.cs b/src/Integration.Vsix/CFamily/VcxProject/IVCXCompilationDatabaseStorage.cs index df2c92c6c5..8a68220e98 100644 --- a/src/Integration.Vsix/CFamily/VcxProject/IVCXCompilationDatabaseStorage.cs +++ b/src/Integration.Vsix/CFamily/VcxProject/IVCXCompilationDatabaseStorage.cs @@ -21,7 +21,7 @@ using System.ComponentModel.Composition; using System.IO; using Newtonsoft.Json; -using SonarLint.VisualStudio.CFamily.CMake; +using SonarLint.VisualStudio.CFamily.CompilationDatabase; using SonarLint.VisualStudio.Core; using SonarLint.VisualStudio.Core.CFamily; using SonarLint.VisualStudio.Core.Helpers; @@ -31,28 +31,46 @@ namespace SonarLint.VisualStudio.Integration.Vsix.CFamily.VcxProject; internal interface IVCXCompilationDatabaseStorage : IDisposable { - ICompilationDatabaseHandle CreateDatabase(IFileConfig fileConfig); + ICompilationDatabaseHandle CreateDatabase( + string file, + string directory, + string command, + IEnumerable environment); } [Export(typeof(IVCXCompilationDatabaseStorage))] [PartCreationPolicy(CreationPolicy.Shared)] -[method: ImportingConstructor] -internal sealed class VCXCompilationDatabaseStorage(IFileSystemService fileSystemService, IThreadHandling threadHandling, ILogger logger) - : IVCXCompilationDatabaseStorage +internal sealed class VCXCompilationDatabaseStorage : IVCXCompilationDatabaseStorage { - private bool disposed; private readonly string compilationDatabaseDirectoryPath = PathHelper.GetTempDirForTask(true, "VCXCD"); + private readonly IFileSystemService fileSystemService; + private readonly IThreadHandling threadHandling; + private readonly ILogger logger; + private bool disposed; + + [ImportingConstructor] + public VCXCompilationDatabaseStorage(IFileSystemService fileSystemService, IThreadHandling threadHandling, ILogger logger) + { + this.fileSystemService = fileSystemService; + this.threadHandling = threadHandling; + this.logger = logger; + } - public ICompilationDatabaseHandle CreateDatabase(IFileConfig fileConfig) + public ICompilationDatabaseHandle CreateDatabase( + string file, + string directory, + string command, + IEnumerable environment) { ThrowIfDisposed(); threadHandling.ThrowIfOnUIThread(); var compilationDatabaseEntry = new CompilationDatabaseEntry { - Directory = fileConfig.CDDirectory, - Command = fileConfig.CDCommand, - File = fileConfig.CDFile + Directory = directory, + Command = command, + File = file, + Environment = environment }; var compilationDatabase = new[] { compilationDatabaseEntry }; diff --git a/src/Integration.Vsix/CFamily/VcxProject/VCXCompilationDatabaseProvider.cs b/src/Integration.Vsix/CFamily/VcxProject/VCXCompilationDatabaseProvider.cs index c8e5b7c46d..8d287fe8c7 100644 --- a/src/Integration.Vsix/CFamily/VcxProject/VCXCompilationDatabaseProvider.cs +++ b/src/Integration.Vsix/CFamily/VcxProject/VCXCompilationDatabaseProvider.cs @@ -18,22 +18,49 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +using System.Collections.Immutable; using System.ComponentModel.Composition; using SonarLint.VisualStudio.CFamily; +using SonarLint.VisualStudio.Core; using SonarLint.VisualStudio.Core.CFamily; namespace SonarLint.VisualStudio.Integration.Vsix.CFamily.VcxProject; [Export(typeof(IVCXCompilationDatabaseProvider))] [PartCreationPolicy(CreationPolicy.Shared)] -[method: ImportingConstructor] -internal class VCXCompilationDatabaseProvider( - IVCXCompilationDatabaseStorage storage, - IFileConfigProvider fileConfigProvider) - : IVCXCompilationDatabaseProvider +internal class VCXCompilationDatabaseProvider : IVCXCompilationDatabaseProvider { + private const string IncludeEntryName = "INCLUDE"; + private readonly ImmutableList staticEnvironmentVariableEntries; + private readonly IVCXCompilationDatabaseStorage storage; + private readonly IFileConfigProvider fileConfigProvider; + + [method: ImportingConstructor] + public VCXCompilationDatabaseProvider( + IVCXCompilationDatabaseStorage storage, + IEnvironmentVariableProvider environmentVariableProvider, + IFileConfigProvider fileConfigProvider) + { + this.storage = storage; + this.fileConfigProvider = fileConfigProvider; + staticEnvironmentVariableEntries = ImmutableList.CreateRange(environmentVariableProvider.GetAll().Select(x => new EnvironmentEntry(x.name, x.value))); + } + public ICompilationDatabaseHandle CreateOrNull(string filePath) => - fileConfigProvider.Get(filePath, null) is {} fileConfig - ? storage.CreateDatabase(fileConfig) + fileConfigProvider.Get(filePath, null) is { } fileConfig + ? storage.CreateDatabase(fileConfig.CDFile, fileConfig.CDDirectory, fileConfig.CDCommand, GetEnvironmentEntries(fileConfig.EnvInclude).Select(x => x.FormattedEntry)) : null; + + private ImmutableList GetEnvironmentEntries(string fileConfigEnvInclude) => + string.IsNullOrEmpty(fileConfigEnvInclude) + ? staticEnvironmentVariableEntries + : staticEnvironmentVariableEntries + .RemoveAll(x => x.Name == IncludeEntryName) + .Add(new EnvironmentEntry(IncludeEntryName, fileConfigEnvInclude)); + + private readonly struct EnvironmentEntry(string name, string value) + { + public string Name { get; } = name; + public string FormattedEntry { get; } = $"{name}={value}"; + } }