├── .github ├── readme │ ├── build-project.png │ ├── search-packages.png │ ├── select-conan-executable.png │ └── tool-window-extension.png └── workflows │ ├── main.yml │ └── release.yml ├── .gitignore ├── BuildEventsHandler.cs ├── ConanFileManager.cs ├── ConanOptionsPage.cs ├── ConanProfilesManager.cs ├── ConanToolWindow.cs ├── ConanToolWindowCommand.cs ├── ConanToolWindowControl.xaml ├── ConanToolWindowControl.xaml.cs ├── ConanToolWindowPackage.cs ├── ConanToolWindowPackage.vsct ├── GlobalSettings.cs ├── Guids.cs ├── Key.snk ├── LICENSE ├── ProjectConfigurationManager.cs ├── Properties └── AssemblyInfo.cs ├── README.md ├── Resources ├── ConanToolWindowCommand.png ├── ConanToolWindowPackage.ico ├── LICENSE.txt ├── gear.png ├── icons.xaml ├── refresh.png └── targets-data.json ├── VSPackage.resx ├── conan-vs-extension.csproj ├── conan-vs-extension.sln ├── source.extension.vsixmanifest └── vsixManifest.json /.github/readme/build-project.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/conan-io/conan-vs-extension/02aae0e62c583ab00319145d8dd251a451ca652f/.github/readme/build-project.png -------------------------------------------------------------------------------- /.github/readme/search-packages.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/conan-io/conan-vs-extension/02aae0e62c583ab00319145d8dd251a451ca652f/.github/readme/search-packages.png -------------------------------------------------------------------------------- /.github/readme/select-conan-executable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/conan-io/conan-vs-extension/02aae0e62c583ab00319145d8dd251a451ca652f/.github/readme/select-conan-executable.png -------------------------------------------------------------------------------- /.github/readme/tool-window-extension.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/conan-io/conan-vs-extension/02aae0e62c583ab00319145d8dd251a451ca652f/.github/readme/tool-window-extension.png -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Build conan-vs-extension 2 | 3 | on: 4 | push: 5 | branches: [ develop2 ] 6 | pull_request: 7 | branches: [ develop2 ] 8 | 9 | jobs: 10 | build: 11 | runs-on: windows-latest 12 | steps: 13 | - name: Checkout the repository 14 | uses: actions/checkout@v4 15 | with: 16 | fetch-depth: 1 17 | 18 | - name: Cache NuGet packages 19 | uses: actions/cache@v3 20 | with: 21 | path: ~\nuget\packages 22 | key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.config') }} 23 | restore-keys: | 24 | ${{ runner.os }}-nuget- 25 | 26 | - name: Setup NuGet 27 | uses: nuget/setup-nuget@v1 28 | 29 | - name: Setup msbuild 30 | uses: microsoft/setup-msbuild@v2 31 | 32 | - name: Restore NuGet 33 | run: nuget restore 34 | 35 | - name: Build conan-vs-extension 36 | run: msbuild /p:configuration=Release /p:Platform="Any CPU" /p:DeployExtension=false /p:ZipPackageCompressionLevel=normal 37 | 38 | - name: Upload VSIX as artifact 39 | uses: actions/upload-artifact@v4 40 | with: 41 | name: conan-vs-extension.vsix 42 | path: bin\Release\conan-vs-extension.vsix 43 | if-no-files-found: error 44 | 45 | releaseDraft: 46 | name: Release draft 47 | if: github.event_name != 'pull_request' && github.ref_name == 'develop2' 48 | needs: [ build ] 49 | runs-on: ubuntu-latest 50 | permissions: 51 | contents: write 52 | steps: 53 | - name: Checkout the repository 54 | uses: actions/checkout@v4 55 | with: 56 | fetch-depth: 1 57 | 58 | - name: Download extension artifact from latest workflow build 59 | uses: actions/download-artifact@v4 60 | with: 61 | name: conan-vs-extension.vsix 62 | token: ${{ secrets.GITHUB_TOKEN }} 63 | run-id: ${{ needs.build.run_id }} 64 | 65 | - name: Remove old release drafts 66 | env: 67 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 68 | run: | 69 | gh api repos/{owner}/{repo}/releases \ 70 | --jq '.[] | select(.draft == true) | .id' \ 71 | | xargs -I '{}' gh api -X DELETE repos/{owner}/{repo}/releases/{} 72 | 73 | - name: Get release version 74 | id: version 75 | run: | 76 | VERSION=$(sed -n 's/.*> $GITHUB_OUTPUT 78 | 79 | - name: Create new release draft 80 | uses: actions/create-release@v1 81 | id: create_release 82 | env: 83 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 84 | with: 85 | draft: true 86 | prerelease: false 87 | release_name: v${{ steps.version.outputs.version }} 88 | tag_name: v${{ steps.version.outputs.version }} 89 | 90 | - name: Upload extension artifact to new release draft 91 | uses: actions/upload-release-asset@v1 92 | env: 93 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 94 | with: 95 | upload_url: ${{ steps.create_release.outputs.upload_url }} 96 | asset_path: conan-vs-extension.vsix 97 | asset_name: conan-vs-extension.vsix 98 | asset_content_type: application/vsix 99 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # GitHub Actions Workflow created for handling the release process based on the draft release prepared with the Build workflow. 2 | 3 | name: Release 4 | on: 5 | release: 6 | types: [prereleased, released] 7 | 8 | jobs: 9 | release: 10 | runs-on: windows-latest 11 | name: Publish the extension to Visual Studio Marketplace 12 | steps: 13 | - name: Checkout the repository 14 | uses: actions/checkout@v4 15 | with: 16 | fetch-depth: 1 17 | - name: Donwload extension from latest release assets 18 | uses: robinraju/release-downloader@v1.9 19 | with: 20 | latest: true 21 | fileName: conan-vs-extension.vsix 22 | out-file-path: output 23 | extract: false 24 | token: ${{ secrets.GITHUB_TOKEN }} 25 | - name: Visual Studio Marketplace Publisher 26 | uses: CalvinAllen/action-vs-marketplace-publish@v1 27 | with: 28 | marketplace-pat: ${{ secrets.vs_marketplace_pat }} # Personal Access Token to upload to the VS Marketplace 29 | publish-manifest-path: vsixManifest.json 30 | vsix-path: output/conan-vs-extension.vsix 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vs/ 2 | /packages/ 3 | /Backup/ 4 | /MigrationBackup/ 5 | UpgradeLog.htm 6 | obj/ 7 | bin/ 8 | Conan.VisualStudio.Examples/**/x64/ 9 | 10 | *.user 11 | .conan/ 12 | *.tlog 13 | *.log 14 | *.pdb 15 | *.idb 16 | *.obj 17 | *.ilk 18 | *.iobj 19 | *.ipdb 20 | *.exe 21 | *.db 22 | .DS_Store 23 | -------------------------------------------------------------------------------- /BuildEventsHandler.cs: -------------------------------------------------------------------------------- 1 | using EnvDTE; 2 | using Microsoft.VisualStudio.Shell; 3 | using Microsoft.VisualStudio.VCProjectEngine; 4 | 5 | namespace conan_vs_extension 6 | { 7 | public class BuildEventsHandler 8 | { 9 | private readonly DTE _dte; 10 | private readonly BuildEvents _buildEvents; 11 | private ConanProfilesManager _profiles_manager; 12 | 13 | public BuildEventsHandler(DTE dte) 14 | { 15 | ThreadHelper.ThrowIfNotOnUIThread(); 16 | _dte = dte; 17 | _buildEvents = _dte.Events.BuildEvents; 18 | _profiles_manager = new ConanProfilesManager(); 19 | 20 | // Subscribe to compilation events 21 | _buildEvents.OnBuildDone += OnBuildDone; 22 | _buildEvents.OnBuildProjConfigBegin += OnBuildProjConfigBegin; 23 | _buildEvents.OnBuildProjConfigDone += OnBuildProjConfigDone; 24 | } 25 | 26 | private bool IsConanEnabledForProject(string projectName) 27 | { 28 | ThreadHelper.ThrowIfNotOnUIThread(); 29 | var project = ProjectConfigurationManager.GetProjectByName(_dte, projectName); 30 | return project != null && ProjectConfigurationManager.conandataFileExists(project); 31 | } 32 | 33 | private void OnBuildProjConfigBegin(string Project, string ProjectConfig, string Platform, string SolutionConfig) 34 | { 35 | ThreadHelper.ThrowIfNotOnUIThread(); 36 | 37 | if (!IsConanEnabledForProject(Project)) 38 | { 39 | return; 40 | } 41 | 42 | Project invokedProject = ProjectConfigurationManager.GetProjectByName(_dte, Project); 43 | _profiles_manager.GenerateProfilesForProject(invokedProject); 44 | 45 | // We always overrwrite the script for the prebuild event 46 | // the ps1 is always overwritten but the event is only added if no conan_install.ps1 is found 47 | // in the prebuild events 48 | _ = ProjectConfigurationManager.SaveConanPrebuildEventsAllConfigAsync(invokedProject); 49 | } 50 | 51 | private void OnBuildProjConfigDone(string Project, string ProjectConfig, string Platform, string SolutionConfig, bool Success) 52 | { 53 | ThreadHelper.ThrowIfNotOnUIThread(); 54 | 55 | if (!IsConanEnabledForProject(Project)) 56 | { 57 | return; 58 | } 59 | 60 | Project invokedProject = ProjectConfigurationManager.GetProjectByName(_dte, Project); 61 | VCConfiguration config = ProjectConfigurationManager.GetVCConfig(invokedProject, ProjectConfig, Platform); 62 | // FIXME: the problem with this is that the first time you build 63 | // the build is started before this step finishes 64 | // maybe we can inject the dependency to the project via a prebuild event? 65 | // with a script? 66 | _ = ProjectConfigurationManager.InjectConanDepsAsync(invokedProject, config); 67 | } 68 | 69 | private void OnBuildDone(vsBuildScope Scope, vsBuildAction Action) 70 | { 71 | var message = "OnBuildDone"; 72 | System.Diagnostics.Debug.WriteLine(message); 73 | } 74 | 75 | public void Dispose() 76 | { 77 | ThreadHelper.ThrowIfNotOnUIThread(); 78 | _buildEvents.OnBuildDone -= OnBuildDone; 79 | _buildEvents.OnBuildProjConfigBegin -= OnBuildProjConfigBegin; 80 | _buildEvents.OnBuildProjConfigDone -= OnBuildProjConfigDone; } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /ConanFileManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using YamlDotNet.Serialization; 6 | using YamlDotNet.Serialization.NamingConventions; 7 | 8 | namespace conan_vs_extension 9 | { 10 | public static class ConanFileManager 11 | { 12 | private static readonly string[] _modifyCommentGuard = new[] 13 | { 14 | "# This file is managed by the Conan Visual Studio Extension, contents will be overwritten.", 15 | "# To keep your changes, remove these comment lines, but the plugin won't be able to modify your requirements" 16 | }; 17 | 18 | public static bool IsFileCommentGuarded(string path) 19 | { 20 | if (!File.Exists(path)) return false; 21 | 22 | var guardCommentLines = new List(_modifyCommentGuard.Length); 23 | 24 | using (var reader = new StreamReader(path)) 25 | { 26 | for (int i = 0; i < _modifyCommentGuard.Length; i++) 27 | { 28 | if (reader.EndOfStream) return false; 29 | guardCommentLines.Add(reader.ReadLine()); 30 | } 31 | } 32 | 33 | return guardCommentLines.SequenceEqual(_modifyCommentGuard); 34 | } 35 | 36 | public static string[] GetConandataRequirements(string projectDirectory) 37 | { 38 | string path = Path.Combine(projectDirectory, "conandata.yml"); 39 | if (File.Exists(path)) 40 | { 41 | string[] conandataContents = File.ReadAllLines(path); 42 | 43 | var deserializer = new DeserializerBuilder() 44 | .WithNamingConvention(UnderscoredNamingConvention.Instance) 45 | .IgnoreUnmatchedProperties() 46 | .Build(); 47 | 48 | var result = deserializer.Deserialize(string.Join(Environment.NewLine, conandataContents)); 49 | 50 | if (result.requirements != null) 51 | { 52 | return result.requirements; 53 | } 54 | } 55 | return new string[] { }; 56 | } 57 | 58 | public static bool ReCreateConanfile(string projectDirectory) 59 | { 60 | string conanfilePath = Path.Combine(projectDirectory, "conanfile.py"); 61 | if (IsFileCommentGuarded(conanfilePath) || !File.Exists(conanfilePath)) 62 | { 63 | string conanfileContents = string.Join(Environment.NewLine, 64 | _modifyCommentGuard.Concat(new string[] 65 | { 66 | "", 67 | "from conan import ConanFile", 68 | "from conan.tools.microsoft import vs_layout, MSBuildDeps", 69 | "class ConanApplication(ConanFile):", 70 | " package_type = \"application\"", 71 | " settings = \"os\", \"compiler\", \"build_type\", \"arch\"", 72 | "", 73 | " def layout(self):", 74 | " vs_layout(self)", 75 | "", 76 | " def generate(self):", 77 | " deps = MSBuildDeps(self)", 78 | " deps.generate()", 79 | "", 80 | " def requirements(self):", 81 | " requirements = self.conan_data.get('requirements', [])", 82 | " for requirement in requirements:", 83 | " self.requires(requirement)" 84 | }) 85 | ); 86 | 87 | File.WriteAllText(conanfilePath, conanfileContents); 88 | return true; 89 | } 90 | return false; 91 | } 92 | 93 | public static bool ReCreateConanData(string projectDirectory) 94 | { 95 | string conandataPath = Path.Combine(projectDirectory, "conandata.yml"); 96 | if (IsFileCommentGuarded(conandataPath) || !File.Exists(conandataPath)) 97 | { 98 | string conandataContents = string.Join(Environment.NewLine, 99 | _modifyCommentGuard.Concat(new string[] { "requirements:" }) 100 | ); 101 | 102 | File.WriteAllText(conandataPath, conandataContents); 103 | return true; 104 | } 105 | return false; 106 | } 107 | 108 | public static void WriteNewRequirement(string projectDirectory, string newRequirement) 109 | { 110 | string path = Path.Combine(projectDirectory, "conandata.yml"); 111 | if (IsFileCommentGuarded(path)) 112 | { 113 | string[] requirements = GetConandataRequirements(projectDirectory); 114 | if (!requirements.Contains(newRequirement)) 115 | { 116 | var newRequirements = requirements.Append(newRequirement).ToArray(); 117 | var serializer = new SerializerBuilder() 118 | .WithNamingConvention(UnderscoredNamingConvention.Instance) 119 | .Build(); 120 | var yaml = serializer.Serialize(new Requirements(newRequirements)); 121 | 122 | // Combine guard comments and YAML contents. 123 | string fileContents = string.Join(Environment.NewLine, _modifyCommentGuard) + Environment.NewLine + yaml; 124 | 125 | // Write the combined contents to the file. 126 | File.WriteAllText(path, fileContents); 127 | } 128 | } 129 | } 130 | 131 | public static void RemoveRequirement(string projectDirectory, string oldRequirement) 132 | { 133 | string path = Path.Combine(projectDirectory, "conandata.yml"); 134 | if (IsFileCommentGuarded(path)) 135 | { 136 | string[] requirements = GetConandataRequirements(projectDirectory); 137 | if (requirements.Contains(oldRequirement)) 138 | { 139 | var newRequirements = requirements.Where(req => req != oldRequirement).ToArray(); 140 | var serializer = new SerializerBuilder() 141 | .WithNamingConvention(UnderscoredNamingConvention.Instance) 142 | .Build(); 143 | var yaml = serializer.Serialize(new Requirements(newRequirements)); 144 | 145 | // Combine guard comments and YAML contents. 146 | string fileContents = string.Join(Environment.NewLine, _modifyCommentGuard) + Environment.NewLine + yaml; 147 | 148 | // Write the combined contents to the file. 149 | File.WriteAllText(path, fileContents); 150 | } 151 | } 152 | } 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /ConanOptionsPage.cs: -------------------------------------------------------------------------------- 1 | using conan_vs_extension; 2 | using Microsoft.VisualStudio.Shell; 3 | using System; 4 | using System.ComponentModel; 5 | using System.Runtime.InteropServices; 6 | using System.Windows.Forms; 7 | 8 | [Guid(GuidList.strConanOptionsPage)] 9 | public class ConanOptionsPage : DialogPage 10 | { 11 | private string _conanExecutablePath; 12 | private bool _useSystemConan; 13 | 14 | [DisplayName("Executable Path")] 15 | [Description("Path to the Conan executable.")] 16 | [Editor(typeof(ExecutablePathEditor), typeof(System.Drawing.Design.UITypeEditor))] 17 | public string ConanExecutablePath 18 | { 19 | get => _conanExecutablePath; 20 | set 21 | { 22 | _conanExecutablePath = value?.Trim(); 23 | GlobalSettings.ConanExecutablePath = value; 24 | 25 | if (value != "conan") 26 | { 27 | _useSystemConan = false; 28 | } 29 | } 30 | } 31 | 32 | [DisplayName("Use System Conan")] 33 | [Description("Specify whether to use the Conan executable installed in the system.")] 34 | public bool UseSystemConan 35 | { 36 | get => _useSystemConan; 37 | set 38 | { 39 | _useSystemConan = value; 40 | if (_useSystemConan) 41 | { 42 | _conanExecutablePath = "conan"; 43 | GlobalSettings.ConanExecutablePath = "conan"; 44 | } 45 | 46 | if (!_useSystemConan && _conanExecutablePath == "conan") 47 | { 48 | _conanExecutablePath = ""; 49 | GlobalSettings.ConanExecutablePath = ""; 50 | } 51 | } 52 | } 53 | 54 | public class ExecutablePathEditor : System.Drawing.Design.UITypeEditor 55 | { 56 | public override System.Drawing.Design.UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context) 57 | { 58 | return System.Drawing.Design.UITypeEditorEditStyle.Modal; 59 | } 60 | 61 | public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value) 62 | { 63 | using (OpenFileDialog openFileDialog = new OpenFileDialog()) 64 | { 65 | openFileDialog.Filter = "Executable Files (*.exe)|*.exe|All Files (*.*)|*.*"; 66 | openFileDialog.RestoreDirectory = true; 67 | 68 | if (openFileDialog.ShowDialog() == DialogResult.OK) 69 | { 70 | return openFileDialog.FileName; 71 | } 72 | } 73 | return value; 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /ConanProfilesManager.cs: -------------------------------------------------------------------------------- 1 | using EnvDTE; 2 | using Microsoft.VisualStudio.Shell; 3 | using Microsoft.VisualStudio.Shell.Interop; 4 | using Microsoft.VisualStudio.VCProjectEngine; 5 | using System; 6 | using System.Collections; 7 | using System.Collections.Generic; 8 | using System.IO; 9 | using System.Windows; 10 | 11 | namespace conan_vs_extension 12 | { 13 | public class ConanProfilesManager 14 | { 15 | 16 | public ConanProfilesManager() 17 | { 18 | } 19 | 20 | public static string getProfileName(VCConfiguration vcConfig) 21 | { 22 | return vcConfig.Name.Replace("|", "_"); 23 | } 24 | 25 | private string getConanArch(string platform) 26 | { 27 | var archMap = new Dictionary(); 28 | archMap["x64"] = "x86_64"; 29 | archMap["Win32"] = "x86"; 30 | archMap["ARM64"] = "armv8"; 31 | return archMap[platform]; 32 | } 33 | 34 | private string getConanCompilerVersion(string platformToolset, string vsVersion) 35 | { 36 | if (Version.TryParse(vsVersion, out Version parsedVersion)) 37 | { 38 | // https://github.com/conan-io/conan/issues/16239 39 | if (parsedVersion.Major == 17 && parsedVersion.Minor >= 10) 40 | { 41 | return "194"; 42 | } 43 | } 44 | 45 | var msvcVersionMap = new Dictionary(); 46 | msvcVersionMap["v143"] = "193"; 47 | msvcVersionMap["v142"] = "192"; 48 | msvcVersionMap["v141"] = "191"; 49 | return msvcVersionMap[platformToolset]; 50 | } 51 | 52 | private string GetRuntimeLibraryType(runtimeLibraryOption runtimeLibraryValue) 53 | { 54 | switch (runtimeLibraryValue) 55 | { 56 | case runtimeLibraryOption.rtMultiThreaded: 57 | case runtimeLibraryOption.rtMultiThreadedDebug: 58 | return "static"; 59 | case runtimeLibraryOption.rtMultiThreadedDLL: 60 | case runtimeLibraryOption.rtMultiThreadedDebugDLL: 61 | return "dynamic"; 62 | default: 63 | return "dynamic"; 64 | } 65 | } 66 | 67 | 68 | private string getConanCppstd(string languageStandard) 69 | { 70 | // https://learn.microsoft.com/en-us/cpp/build/reference/std-specify-language-standard-version?view=msvc-170 71 | 72 | if (languageStandard.ToLower().Contains("default")) 73 | { 74 | return "14"; 75 | } 76 | 77 | List cppStdValues = new List() { "14", "17", "20", "23" }; 78 | 79 | foreach (string cppStdValue in cppStdValues) 80 | { 81 | if (languageStandard.Contains(cppStdValue)) 82 | { 83 | return cppStdValue; 84 | } 85 | } 86 | return "null"; 87 | } 88 | 89 | public void GenerateProfilesForProject(Project project) 90 | { 91 | ThreadHelper.ThrowIfNotOnUIThread(); 92 | try 93 | { 94 | if (project != null && project.Object is VCProject vcProject) 95 | { 96 | string projectDirectory = System.IO.Path.GetDirectoryName(project.FullName); 97 | string path = Path.Combine(projectDirectory, "conandata.yml"); 98 | 99 | // only generate the profiles if we had a conandata that means 100 | // that at some point the user wanted to use conan 101 | 102 | bool fileExists = File.Exists(path); 103 | 104 | if (fileExists) 105 | { 106 | string conanProjectDirectory = System.IO.Path.Combine(projectDirectory, ".conan"); 107 | if (!Directory.Exists(conanProjectDirectory)) 108 | { 109 | Directory.CreateDirectory(conanProjectDirectory); 110 | } 111 | 112 | foreach (VCConfiguration vcConfig in (IEnumerable)vcProject.Configurations) 113 | { 114 | string profileName = getProfileName(vcConfig); 115 | string profilePath = System.IO.Path.Combine(conanProjectDirectory, profileName); 116 | 117 | string toolset = vcConfig.Evaluate("$(PlatformToolset)").ToString(); 118 | string vsVersion = vcConfig.Evaluate("$(MSBuildVersion)").ToString(); 119 | string compilerVersion = getConanCompilerVersion(toolset, vsVersion); 120 | string arch = getConanArch(vcConfig.Evaluate("$(PlatformName)").ToString()); 121 | IVCRulePropertyStorage generalRule = vcConfig.Rules.Item("ConfigurationGeneral") as IVCRulePropertyStorage; 122 | string languageStandard = generalRule == null ? null : generalRule.GetEvaluatedPropertyValue("LanguageStandard"); 123 | string cppStd = getConanCppstd(languageStandard); 124 | 125 | var tools = (IVCCollection) vcConfig.Tools; 126 | var vcCTool = (VCCLCompilerTool) tools.Item("VCCLCompilerTool"); 127 | 128 | string runtime = GetRuntimeLibraryType(vcCTool.RuntimeLibrary); 129 | 130 | string buildType = vcConfig.ConfigurationName; 131 | string profileContent = 132 | $@" 133 | [settings] 134 | arch={arch} 135 | build_type={buildType} 136 | compiler=msvc 137 | compiler.cppstd={cppStd} 138 | compiler.runtime={runtime} 139 | " + 140 | $@" 141 | compiler.runtime_type={buildType} 142 | compiler.version={compilerVersion} 143 | os=Windows 144 | "; 145 | 146 | bool shouldWriteFile = true; 147 | 148 | if (File.Exists(profilePath)) 149 | { 150 | string existingProfileContent = File.ReadAllText(profilePath); 151 | if (existingProfileContent == profileContent) 152 | { 153 | shouldWriteFile = false; 154 | } 155 | } 156 | 157 | if (shouldWriteFile) 158 | { 159 | File.WriteAllText(profilePath, profileContent); 160 | // We create this .runconan file to indicate that there were changes in the profile and that 161 | // Conan should run, this file is removed by the script that launches conan to reset the state 162 | string runConanFilePath = System.IO.Path.Combine(conanProjectDirectory, ".runconan"); 163 | File.WriteAllText(runConanFilePath, ""); 164 | } 165 | } 166 | } 167 | } 168 | //MessageBox.Show($"Generated profiles for actual project.", "Conan profiles generated", MessageBoxButton.OK, MessageBoxImage.Information); 169 | } 170 | catch (Exception ex) 171 | { 172 | MessageBox.Show($"There was a problem generating the file: {ex.Message}", "Error - Conan C/C++ Package Manager", MessageBoxButton.OK, MessageBoxImage.Error); 173 | } 174 | } 175 | 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /ConanToolWindow.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.Shell; 2 | using System; 3 | using System.Runtime.InteropServices; 4 | 5 | namespace conan_vs_extension 6 | { 7 | /// 8 | /// This class implements the tool window exposed by this package and hosts a user control. 9 | /// 10 | /// 11 | /// In Visual Studio tool windows are composed of a frame (implemented by the shell) and a pane, 12 | /// usually implemented by the package implementer. 13 | /// 14 | /// This class derives from the ToolWindowPane class provided from the MPF in order to use its 15 | /// implementation of the IVsUIElementPane interface. 16 | /// 17 | /// 18 | [Guid("188e5905-b7fe-4f68-a738-46e4a698caac")] 19 | public class ConanToolWindow : ToolWindowPane 20 | { 21 | /// 22 | /// Initializes a new instance of the class. 23 | /// 24 | public ConanToolWindow() : base(null) 25 | { 26 | this.Caption = "Conan C/C++ Package Manager"; 27 | 28 | // This is the user control hosted by the tool window; Note that, even if this class implements IDisposable, 29 | // we are not calling Dispose on this object. This is because ToolWindowPane calls Dispose on 30 | // the object returned by the Content property. 31 | this.Content = new ConanToolWindowControl(); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /ConanToolWindowCommand.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.Shell; 2 | using Microsoft.VisualStudio.Shell.Interop; 3 | using System; 4 | using System.ComponentModel.Design; 5 | using Task = System.Threading.Tasks.Task; 6 | 7 | namespace conan_vs_extension 8 | { 9 | /// 10 | /// Command handler 11 | /// 12 | internal sealed class ConanToolWindowCommand 13 | { 14 | /// 15 | /// Command ID. 16 | /// 17 | public const int CommandId = 0x0100; 18 | 19 | /// 20 | /// Command menu group (command set GUID). 21 | /// 22 | public static readonly Guid CommandSet = new Guid("3e9359cf-7e7a-4e99-8c6d-254264e59580"); 23 | 24 | /// 25 | /// VS Package that provides this command, not null. 26 | /// 27 | private readonly AsyncPackage package; 28 | 29 | /// 30 | /// Initializes a new instance of the class. 31 | /// Adds our command handlers for menu (commands must exist in the command table file) 32 | /// 33 | /// Owner package, not null. 34 | /// Command service to add command to, not null. 35 | private ConanToolWindowCommand(AsyncPackage package, OleMenuCommandService commandService) 36 | { 37 | this.package = package ?? throw new ArgumentNullException(nameof(package)); 38 | commandService = commandService ?? throw new ArgumentNullException(nameof(commandService)); 39 | 40 | var menuCommandID = new CommandID(CommandSet, CommandId); 41 | var menuItem = new MenuCommand(this.Execute, menuCommandID); 42 | commandService.AddCommand(menuItem); 43 | } 44 | 45 | /// 46 | /// Gets the instance of the command. 47 | /// 48 | public static ConanToolWindowCommand Instance 49 | { 50 | get; 51 | private set; 52 | } 53 | 54 | /// 55 | /// Gets the service provider from the owner package. 56 | /// 57 | private Microsoft.VisualStudio.Shell.IAsyncServiceProvider ServiceProvider 58 | { 59 | get 60 | { 61 | return this.package; 62 | } 63 | } 64 | 65 | /// 66 | /// Initializes the singleton instance of the command. 67 | /// 68 | /// Owner package, not null. 69 | public static async Task InitializeAsync(AsyncPackage package) 70 | { 71 | // Switch to the main thread - the call to AddCommand in ConanToolWindowCommand's constructor requires 72 | // the UI thread. 73 | await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(package.DisposalToken); 74 | 75 | OleMenuCommandService commandService = await package.GetServiceAsync(typeof(IMenuCommandService)) as OleMenuCommandService; 76 | Instance = new ConanToolWindowCommand(package, commandService); 77 | } 78 | 79 | /// 80 | /// Shows the tool window when the menu item is clicked. 81 | /// 82 | /// The event sender. 83 | /// The event args. 84 | private void Execute(object sender, EventArgs e) 85 | { 86 | ThreadHelper.ThrowIfNotOnUIThread(); 87 | 88 | // Get the instance number 0 of this tool window. This window is single instance so this instance 89 | // is actually the only one. 90 | // The last flag is set to true so that if the tool window does not exists it will be created. 91 | ToolWindowPane window = this.package.FindToolWindow(typeof(ConanToolWindow), 0, true); 92 | if ((null == window) || (null == window.Frame)) 93 | { 94 | throw new NotSupportedException("Cannot create tool window"); 95 | } 96 | 97 | IVsWindowFrame windowFrame = (IVsWindowFrame)window.Frame; 98 | Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure(windowFrame.Show()); 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /ConanToolWindowControl.xaml: -------------------------------------------------------------------------------- 1 |  12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 40 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | Unsupported Project Type 50 | 51 | You're using an unsupported project type, or no project has yet been loaded. 52 | The Conan VS Extension only supports projects based on msbuild. 53 | 54 | 55 | 56 | 57 |