├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── XMake.VisualStudio.VS2022 ├── XMake.VisualStudio.VS2022.sln └── XMake.VisualStudio │ ├── Icon.png │ ├── Key.snk │ ├── Properties │ └── AssemblyInfo.cs │ ├── VSPackage.Designer.cs │ ├── VSPackage.resx │ ├── XMake.VisualStudio.VS2022.csproj │ ├── XMakeCommand.cs │ ├── XMakePluginPackage.cs │ ├── XMakePluginPackage.vsct │ ├── XMakeService.cs │ ├── XMakeToolWindow.cs │ ├── XMakeToolWindowControl.xaml │ ├── XMakeToolWindowControl.xaml.cs │ ├── XMakeToolWindowResources.xaml │ ├── XMakeToolWindowResources.xaml.cs │ └── source.extension.vsixmanifest ├── XMake.VisualStudio ├── XMake.VisualStudio.sln └── XMake.VisualStudio │ ├── Icon.png │ ├── Key.snk │ ├── Properties │ └── AssemblyInfo.cs │ ├── VSPackage.Designer.cs │ ├── VSPackage.resx │ ├── XMake.VisualStudio.csproj │ ├── XMakeCommand.cs │ ├── XMakePluginPackage.cs │ ├── XMakePluginPackage.vsct │ ├── XMakeService.cs │ ├── XMakeToolWindow.cs │ ├── XMakeToolWindowControl.xaml │ ├── XMakeToolWindowControl.xaml.cs │ ├── XMakeToolWindowResources.xaml │ ├── XMakeToolWindowResources.xaml.cs │ └── source.extension.vsixmanifest └── screenshot ├── options.png └── tools.png /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vs 2 | bin 3 | obj 4 | .bak 5 | .gitignore.bak 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # xmake-visualstudio 2 | xmake plugin for visual studio 3 | 4 | | Project | IDE | Download | 5 | | ---- | ---- | ---- | 6 | | XMake.VisualStudio | VS2019 | https://marketplace.visualstudio.com/items?itemName=musmus9405.xmake9405&ssr=false#overview | 7 | | XMake.VisualStudio.VS2022 | VS2022 | https://marketplace.visualstudio.com/items?itemName=musmus9405.XMakeVS2022 | 8 | 9 | ![Options](screenshot/options.png) 10 | 11 | ![ToolsWindow](screenshot/tools.png) 12 | 13 | # How to open xmake project 14 | > make sure xmake.lua is in your project directory root, right click on project directory and open with Visual Studio. 15 | -------------------------------------------------------------------------------- /XMake.VisualStudio.VS2022/XMake.VisualStudio.VS2022.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.32002.261 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XMake.VisualStudio.VS2022", "XMake.VisualStudio\XMake.VisualStudio.VS2022.csproj", "{78787B01-1917-4C1E-B997-270DB5AB58A0}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Debug|x86 = Debug|x86 12 | Release|Any CPU = Release|Any CPU 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {78787B01-1917-4C1E-B997-270DB5AB58A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {78787B01-1917-4C1E-B997-270DB5AB58A0}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {78787B01-1917-4C1E-B997-270DB5AB58A0}.Debug|x86.ActiveCfg = Debug|x86 19 | {78787B01-1917-4C1E-B997-270DB5AB58A0}.Debug|x86.Build.0 = Debug|x86 20 | {78787B01-1917-4C1E-B997-270DB5AB58A0}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {78787B01-1917-4C1E-B997-270DB5AB58A0}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {78787B01-1917-4C1E-B997-270DB5AB58A0}.Release|x86.ActiveCfg = Release|x86 23 | {78787B01-1917-4C1E-B997-270DB5AB58A0}.Release|x86.Build.0 = Release|x86 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {FA52575F-CBE3-4FE3-9708-90552B1EF9E2} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /XMake.VisualStudio.VS2022/XMake.VisualStudio/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloWorld886/xmake-visualstudio/04c5dc325d8ce53cf332711ef1f50cd0d5299e73/XMake.VisualStudio.VS2022/XMake.VisualStudio/Icon.png -------------------------------------------------------------------------------- /XMake.VisualStudio.VS2022/XMake.VisualStudio/Key.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloWorld886/xmake-visualstudio/04c5dc325d8ce53cf332711ef1f50cd0d5299e73/XMake.VisualStudio.VS2022/XMake.VisualStudio/Key.snk -------------------------------------------------------------------------------- /XMake.VisualStudio.VS2022/XMake.VisualStudio/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("XMake.VisualStudio")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("XMake.VisualStudio")] 12 | [assembly: AssemblyCopyright("")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | // Version information for an assembly consists of the following four values: 22 | // 23 | // Major Version 24 | // Minor Version 25 | // Build Number 26 | // Revision 27 | // 28 | // You can specify all the values or you can default the Build and Revision Numbers 29 | // by using the '*' as shown below: 30 | // [assembly: AssemblyVersion("1.0.*")] 31 | [assembly: AssemblyVersion("1.0.0.0")] 32 | [assembly: AssemblyFileVersion("1.0.0.0")] 33 | -------------------------------------------------------------------------------- /XMake.VisualStudio.VS2022/XMake.VisualStudio/VSPackage.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // 此代码由工具生成。 4 | // 运行时版本:4.0.30319.42000 5 | // 6 | // 对此文件的更改可能会导致不正确的行为,并且如果 7 | // 重新生成代码,这些更改将会丢失。 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace XMake.VisualStudio { 12 | using System; 13 | 14 | 15 | /// 16 | /// 一个强类型的资源类,用于查找本地化的字符串等。 17 | /// 18 | // 此类是由 StronglyTypedResourceBuilder 19 | // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 20 | // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen 21 | // (以 /str 作为命令选项),或重新生成 VS 项目。 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | public class VSPackage { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal VSPackage() { 33 | } 34 | 35 | /// 36 | /// 返回此类使用的缓存的 ResourceManager 实例。 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | public static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("XMake.VisualStudio.VSPackage", typeof(VSPackage).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// 重写当前线程的 CurrentUICulture 属性,对 51 | /// 使用此强类型资源类的所有资源查找执行重写。 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | public static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// 查找类似 XMakeCommand Extension 的本地化字符串。 65 | /// 66 | public static string _110 { 67 | get { 68 | return ResourceManager.GetString("110", resourceCulture); 69 | } 70 | } 71 | 72 | /// 73 | /// 查找类似 XMakeCommand Visual Studio Extension Detailed Info 的本地化字符串。 74 | /// 75 | public static string _112 { 76 | get { 77 | return ResourceManager.GetString("112", resourceCulture); 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /XMake.VisualStudio.VS2022/XMake.VisualStudio/VSPackage.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | XMakeCommand Extension 122 | 123 | 124 | XMakeCommand Visual Studio Extension Detailed Info 125 | 126 | -------------------------------------------------------------------------------- /XMake.VisualStudio.VS2022/XMake.VisualStudio/XMake.VisualStudio.VS2022.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 17.0 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | 7 | 8 | true 9 | 10 | 11 | Key.snk 12 | 13 | 14 | 15 | Debug 16 | AnyCPU 17 | 2.0 18 | {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 19 | {78787B01-1917-4C1E-B997-270DB5AB58A0} 20 | Library 21 | Properties 22 | XMake.VisualStudio 23 | XMake.VisualStudio 24 | v4.7.2 25 | true 26 | true 27 | true 28 | false 29 | false 30 | true 31 | true 32 | Program 33 | $(DevEnvDir)devenv.exe 34 | /rootsuffix Exp 35 | 36 | 37 | true 38 | full 39 | false 40 | bin\Debug\ 41 | DEBUG;TRACE 42 | prompt 43 | 4 44 | 45 | 46 | pdbonly 47 | true 48 | bin\Release\ 49 | TRACE 50 | prompt 51 | 4 52 | 53 | 54 | 55 | 56 | True 57 | True 58 | VSPackage.resx 59 | 60 | 61 | 62 | 63 | 64 | 65 | XMakeToolWindowControl.xaml 66 | 67 | 68 | XMakeToolWindowResources.xaml 69 | 70 | 71 | 72 | 73 | 74 | Designer 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | Menus.ctmenu 97 | 98 | 99 | 100 | 101 | true 102 | VSPackage 103 | PublicResXFileCodeGenerator 104 | VSPackage.Designer.cs 105 | 106 | 107 | 108 | 109 | Always 110 | true 111 | 112 | 113 | 114 | 115 | Designer 116 | MSBuild:Compile 117 | 118 | 119 | Designer 120 | MSBuild:Compile 121 | 122 | 123 | 124 | 125 | 126 | 133 | -------------------------------------------------------------------------------- /XMake.VisualStudio.VS2022/XMake.VisualStudio/XMakeCommand.cs: -------------------------------------------------------------------------------- 1 | using EnvDTE; 2 | using EnvDTE80; 3 | using Microsoft.VisualStudio.Shell; 4 | using Microsoft.VisualStudio.Shell.Interop; 5 | using System; 6 | using System.ComponentModel.Design; 7 | using Task = System.Threading.Tasks.Task; 8 | 9 | namespace XMake.VisualStudio 10 | { 11 | /// 12 | /// Command handler 13 | /// 14 | internal sealed class XMakeCommand 15 | { 16 | /// 17 | /// Command ID. 18 | /// 19 | public const int CommandId = 257; 20 | 21 | /// 22 | /// Command menu group (command set GUID). 23 | /// 24 | public static readonly Guid CommandSet = new Guid("94e59d17-8ff5-49c2-b8cc-5b4b6c9b92f2"); 25 | 26 | /// 27 | /// VS Package that provides this command, not null. 28 | /// 29 | private readonly AsyncPackage package; 30 | 31 | /// 32 | /// Initializes a new instance of the class. 33 | /// Adds our command handlers for menu (commands must exist in the command table file) 34 | /// 35 | /// Owner package, not null. 36 | /// Command service to add command to, not null. 37 | private XMakeCommand(AsyncPackage package, OleMenuCommandService commandService) 38 | { 39 | this.package = package ?? throw new ArgumentNullException(nameof(package)); 40 | commandService = commandService ?? throw new ArgumentNullException(nameof(commandService)); 41 | 42 | var menuCommandID = new CommandID(CommandSet, CommandId); 43 | var menuItem = new MenuCommand(this.Execute, menuCommandID); 44 | commandService.AddCommand(menuItem); 45 | } 46 | 47 | /// 48 | /// Gets the instance of the command. 49 | /// 50 | public static XMakeCommand Instance 51 | { 52 | get; 53 | private set; 54 | } 55 | 56 | /// 57 | /// Gets the service provider from the owner package. 58 | /// 59 | private Microsoft.VisualStudio.Shell.IAsyncServiceProvider ServiceProvider 60 | { 61 | get 62 | { 63 | return this.package; 64 | } 65 | } 66 | 67 | /// 68 | /// Initializes the singleton instance of the command. 69 | /// 70 | /// Owner package, not null. 71 | public static async Task InitializeAsync(AsyncPackage package) 72 | { 73 | // Switch to the main thread - the call to AddCommand in XMakeCommand's constructor requires 74 | // the UI thread. 75 | await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(package.DisposalToken); 76 | 77 | OleMenuCommandService commandService = await package.GetServiceAsync(typeof(IMenuCommandService)) as OleMenuCommandService; 78 | Instance = new XMakeCommand(package, commandService); 79 | } 80 | 81 | 82 | private void Execute(object sender, EventArgs e) 83 | { 84 | package.JoinableTaskFactory.RunAsync(async () => 85 | { 86 | await Microsoft.VisualStudio.Shell.ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); 87 | 88 | ToolWindowPane window = await package.FindToolWindowAsync( 89 | typeof(XMakeToolWindow), 90 | 0, 91 | create: true, 92 | cancellationToken: package.DisposalToken); 93 | ((IVsWindowFrame)window.Frame).Show(); 94 | }); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /XMake.VisualStudio.VS2022/XMake.VisualStudio/XMakePluginPackage.cs: -------------------------------------------------------------------------------- 1 | using EnvDTE; 2 | using EnvDTE80; 3 | using Microsoft; 4 | using Microsoft.VisualStudio.Shell; 5 | using Microsoft.VisualStudio.Shell.Interop; 6 | using System; 7 | using System.Diagnostics.CodeAnalysis; 8 | using System.IO; 9 | using System.Linq; 10 | using System.Runtime.InteropServices; 11 | using System.Threading; 12 | using System.Threading.Tasks; 13 | using Task = System.Threading.Tasks.Task; 14 | using System.ComponentModel; 15 | 16 | namespace XMake.VisualStudio 17 | { 18 | /// 19 | /// This is the class that implements the package exposed by this assembly. 20 | /// 21 | /// 22 | /// 23 | /// The minimum requirement for a class to be considered a valid package for Visual Studio 24 | /// is to implement the IVsPackage interface and register itself with the shell. 25 | /// This package uses the helper classes defined inside the Managed Package Framework (MPF) 26 | /// to do it: it derives from the Package class that provides the implementation of the 27 | /// IVsPackage interface and uses the registration attributes defined in the framework to 28 | /// register itself and its components with the shell. These attributes tell the pkgdef creation 29 | /// utility what data to put into .pkgdef file. 30 | /// 31 | /// 32 | /// To get loaded into VS, the package must be referred by <Asset Type="Microsoft.VisualStudio.VsPackage" ...> in .vsixmanifest file. 33 | /// 34 | /// 35 | [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)] 36 | [ProvideMenuResource("Menus.ctmenu", 1)] 37 | [Guid(XMakePluginPackage.PackageGuidString)] 38 | [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "pkgdef, VS and vsixmanifest are valid VS terms")] 39 | [ProvideToolWindow(typeof(XMake.VisualStudio.XMakeToolWindow))] 40 | [ProvideOptionPage(typeof(XMakeOptionPage), "XMake", "General", 0, 0, true)] 41 | public sealed class XMakePluginPackage : AsyncPackage 42 | { 43 | /// 44 | /// XMakeCommandPackage GUID string. 45 | /// 46 | public const string PackageGuidString = "f015d5ab-25d8-4ed3-8a3c-38fce53a0baf"; 47 | 48 | /// 49 | /// Initializes a new instance of the class. 50 | /// 51 | public XMakePluginPackage() 52 | { 53 | // Inside this method you can place any initialization code that does not require 54 | // any Visual Studio service because at this point the package object is created but 55 | // not sited yet inside Visual Studio environment. The place to do all the other 56 | // initialization is the Initialize method. 57 | } 58 | 59 | #region Package Members 60 | 61 | /// 62 | /// Initialization of the package; this method is called right after the package is sited, so this is the place 63 | /// where you can put all the initialization code that rely on services provided by VisualStudio. 64 | /// 65 | /// A cancellation token to monitor for initialization cancellation, which can occur when VS is shutting down. 66 | /// A provider for progress updates. 67 | /// A task representing the async work of package initialization, or an already completed task if there is none. Do not return null from this method. 68 | protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress progress) 69 | { 70 | AddService(typeof(XMakeService), CreateXMakeAsync); 71 | // When initialized asynchronously, the current thread may be a background thread at this point. 72 | // Do any initialization that requires the UI thread after switching to the UI thread. 73 | await this.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); 74 | await XMakeCommand.InitializeAsync(this); 75 | } 76 | 77 | private async Task CreateXMakeAsync(IAsyncServiceContainer container, CancellationToken cancellationToken, Type serviceType) 78 | { 79 | var svc = new XMakeService(); 80 | await svc.InitializeAsync(this, cancellationToken); 81 | return svc; 82 | } 83 | 84 | protected override async Task OnAfterPackageLoadedAsync(CancellationToken cancellationToken) 85 | { 86 | XMakeService service = await GetServiceAsync(typeof(XMakeService)) as XMakeService; 87 | XMakeOptionPage page = (XMakeOptionPage)GetDialogPage(typeof(XMakeOptionPage)); 88 | if (service != null && page != null) 89 | await service.OnAfterPackageLoadedAsync(cancellationToken, page); 90 | 91 | await base.OnAfterPackageLoadedAsync(cancellationToken); 92 | 93 | } 94 | 95 | public override IVsAsyncToolWindowFactory GetAsyncToolWindowFactory(Guid toolWindowType) 96 | { 97 | return toolWindowType.Equals(Guid.Parse(XMakeToolWindow.WindowGuidString)) ? this : null; 98 | } 99 | 100 | protected override string GetToolWindowTitle(Type toolWindowType, int id) 101 | { 102 | return toolWindowType == typeof(XMakeToolWindow) ? XMakeToolWindow.Title : base.GetToolWindowTitle(toolWindowType, id); 103 | } 104 | 105 | protected override async Task InitializeToolWindowAsync(Type toolWindowType, int id, CancellationToken cancellationToken) 106 | { 107 | XMakeService service = await GetServiceAsync(typeof(XMakeService)) as XMakeService; 108 | return service; 109 | } 110 | 111 | #endregion 112 | } 113 | 114 | [Guid("5183218B-3091-4919-A875-1BB034166D22")] 115 | public class XMakeOptionPage : DialogPage 116 | { 117 | public Action OptionChange; 118 | 119 | private string _customExecutablePath; 120 | 121 | [Category("XMake")] 122 | [DisplayName("Custom Executable Path")] 123 | [Description("Where is xmake.exe")] 124 | public string CustomExecutablePath 125 | { 126 | get { return _customExecutablePath; } 127 | set 128 | { 129 | if (_customExecutablePath == value) 130 | return; 131 | 132 | _customExecutablePath = value; 133 | if (OptionChange != null) 134 | OptionChange("CustomExecutablePath", _customExecutablePath); 135 | } 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /XMake.VisualStudio.VS2022/XMake.VisualStudio/XMakePluginPackage.vsct: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 9 | 10 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 25 | 26 | 33 | 34 | 43 | 44 | 48 | 53 | 54 | 55 | 57 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /XMake.VisualStudio.VS2022/XMake.VisualStudio/XMakeService.cs: -------------------------------------------------------------------------------- 1 | using EnvDTE; 2 | using Microsoft.VisualStudio.Shell; 3 | using Microsoft.VisualStudio.Shell.Interop; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Diagnostics; 7 | using System.IO; 8 | using System.Linq; 9 | using System.Resources; 10 | using System.Runtime.InteropServices; 11 | using System.Text; 12 | using System.Text.RegularExpressions; 13 | using System.Threading; 14 | using System.Threading.Tasks; 15 | using Task = System.Threading.Tasks.Task; 16 | 17 | namespace XMake.VisualStudio 18 | { 19 | internal struct Intellisense 20 | { 21 | public string name; 22 | public List inheritEnvironments; 23 | public List includePath; 24 | public List defines; 25 | public string intelliSenseMode; 26 | } 27 | 28 | internal struct Launch 29 | { 30 | public string type; 31 | public string name; 32 | public string project; 33 | public string cwd; 34 | public string program; 35 | public string MIMode; 36 | public string miDebuggerPath; 37 | public bool externalConsole; 38 | } 39 | 40 | [ComVisible(true)] 41 | public class XMakeService : IVsSolutionEvents7, IVsSolutionEvents 42 | { 43 | private static string _updateIntellisense = @"""import('core.project.config') 44 | import('core.project.project') 45 | import('core.project.rule') 46 | config.load() 47 | for k,t in pairs(project:targets()) do 48 | local rules = t:get('rules') 49 | if rules and type(rules) == 'string' then 50 | local on_config = rules:script('config') 51 | if on_config then 52 | utils.trycall(on_config, nil, t) 53 | end 54 | elseif rules and type(rules) == 'table' then 55 | for k, r in pairs(rules) do 56 | local rule = rule.rule(r) 57 | local on_config = rule:script('config') 58 | if on_config then 59 | utils.trycall(on_config, nil, t) 60 | end 61 | end 62 | end 63 | local name = t:name() or '' 64 | local define = '' 65 | local defines = t:get('defines') or '' 66 | if type(defines) == 'string' then 67 | define = defines 68 | elseif type(defines) == 'table' then 69 | for k, p in pairs(defines) do 70 | define = define == '' and p or (define.. ';' .. p) 71 | end 72 | end 73 | local arch = t:get('arch') or 'x86' 74 | local includes = '' 75 | for k, p in pairs(t:pkgs()) do 76 | local dir = p:get('sysincludedirs') 77 | if type(dir) == 'string' then 78 | includes = includes == '' and dir or (includes .. ';' .. dir) 79 | elseif type(dir) == 'table' then 80 | for m, n in pairs(dir) do 81 | includes = includes == '' and n or (includes .. ';' .. n) 82 | end 83 | end 84 | end 85 | local includedirs = t:get('includedirs') 86 | if type(includedirs) == 'string' then 87 | includes = includes == '' and includedirs or (includes .. ';' .. includedirs) 88 | elseif type(includedirs) == 'table' then 89 | for k, p in pairs(includedirs) do 90 | includes = includes == '' and p or (includes .. ';' .. p) 91 | end 92 | end 93 | local sysincludedirs = t:get('sysincludedirs') 94 | if type(sysincludedirs) == 'string' then 95 | includes = includes == '' and sysincludedirs or (includes .. ';' .. sysincludedirs) 96 | elseif type(sysincludedirs) == 'table' then 97 | for k, p in pairs(sysincludedirs) do 98 | includes = includes == '' and p or (includes .. ';' .. p) 99 | end 100 | end 101 | local toolchains = t:toolchains() 102 | if toolchains then 103 | for k, toolchain in pairs(toolchains) do 104 | local runenvs = toolchain:runenvs() 105 | if runenvs then 106 | local include = runenvs.INCLUDE 107 | if include then 108 | includes = includes == '' and include or (includes .. ';' .. include) 109 | end 110 | end 111 | end 112 | end 113 | print(string.format('name=%s|define=%s|arch=%s|includes=%s', name, define, arch, includes)) 114 | end"""; 115 | 116 | private static string _updateLaunch = @""" 117 | import('core.project.config') 118 | import('core.project.project') 119 | config.load() 120 | for k,t in pairs(project:targets()) do 121 | if t:is_binary() then 122 | print(t:targetfile()) 123 | end 124 | end 125 | """; 126 | 127 | private XMakePluginPackage _package; 128 | private readonly List _allTargets = new List() { "default" }; 129 | private readonly string[] _allModes = new string[] 130 | { 131 | "debug", 132 | "release" 133 | }; 134 | private readonly Tuple[] _allPlatAndArchs = new Tuple[] 135 | { 136 | new Tuple("windows", new string[]{"x86", "x64"}), 137 | new Tuple("mingw", new string[]{"i386", "x86_64", "arm", "arm64"}), 138 | new Tuple("android", new string[]{"armeabi-v7a", "arm64-v8a"}), 139 | new Tuple("wasm", new string[] { }) 140 | }; 141 | 142 | private string[] _allPlats; 143 | private string[] _allArchs; 144 | private string _projDir; 145 | private string _target; 146 | private string _mode; 147 | private string _plat; 148 | private string _arch; 149 | private XMakeOptionPage _page; 150 | private bool _configChanged = false; 151 | private bool _isExecutable = false; 152 | 153 | public string ProjDir 154 | { 155 | get => _projDir; 156 | } 157 | 158 | public string Target 159 | { 160 | get => _target; 161 | set 162 | { 163 | if (!string.IsNullOrEmpty(_target) && _target != value) 164 | _configChanged = true; 165 | _target = value; 166 | } 167 | } 168 | 169 | public string[] AllModes { get => _allModes; } 170 | 171 | public string[] AllPlats 172 | { 173 | get 174 | { 175 | if(_allPlats == null) 176 | { 177 | _allPlats = new string[_allPlatAndArchs.Length]; 178 | for (int i = 0; i < _allPlatAndArchs.Length; i++) 179 | { 180 | _allPlats[i] = _allPlatAndArchs[i].Item1; 181 | } 182 | } 183 | 184 | return _allPlats; 185 | } 186 | } 187 | 188 | public string[] AllArchs 189 | { 190 | get { return _allArchs; } 191 | } 192 | 193 | public IReadOnlyList AllTargets { get => _allTargets; } 194 | 195 | public string Mode 196 | { 197 | get => _mode; 198 | set 199 | { 200 | if (!string.IsNullOrEmpty(_mode) && _mode != value) 201 | _configChanged = true; 202 | _mode = value; 203 | } 204 | } 205 | 206 | public string Plat 207 | { 208 | get => _plat; 209 | set 210 | { 211 | if (!string.IsNullOrEmpty(_plat) && _plat != value) 212 | _configChanged = true; 213 | _plat = value; 214 | 215 | for(int i = 0; i < _allPlats.Length; ++i) 216 | { 217 | if(_allPlats[i] == _plat) 218 | { 219 | _allArchs = _allPlatAndArchs[i].Item2; 220 | if(_allArchs.Length != 0) 221 | { 222 | _arch = _allArchs[0]; 223 | } 224 | else 225 | { 226 | _arch = string.Empty; 227 | } 228 | break; 229 | } 230 | } 231 | } 232 | } 233 | 234 | public string Arch 235 | { 236 | get => _arch; 237 | set 238 | { 239 | if (!string.IsNullOrEmpty(_arch) && _arch != value) 240 | _configChanged = true; 241 | _arch = value; 242 | } 243 | } 244 | 245 | public string CppProperties { 246 | get 247 | { 248 | return Path.Combine(_projDir, "CppProperties.json"); 249 | } 250 | } 251 | 252 | public string LaunchVS 253 | { 254 | get 255 | { 256 | return Path.Combine(_projDir, "Launch.vs.json"); 257 | } 258 | } 259 | 260 | public bool IsValid 261 | { 262 | get 263 | { 264 | if (string.IsNullOrEmpty(_projDir)) 265 | return false; 266 | return _isExecutable; 267 | } 268 | } 269 | 270 | public string ExecutablePath 271 | { 272 | get 273 | { 274 | if (string.IsNullOrEmpty(_page.CustomExecutablePath)) 275 | return "xmake"; 276 | return _page.CustomExecutablePath; 277 | } 278 | } 279 | 280 | public void QuickStart() 281 | { 282 | _package.JoinableTaskFactory.RunAsync(async () => 283 | { 284 | string command = ""; 285 | if (!string.IsNullOrEmpty(_arch)) 286 | { 287 | command = string.Format("f -p {0} -a {1} -m {2} -y", _plat, _arch, _mode); 288 | } 289 | else 290 | { 291 | command = string.Format("f -p {0} -m {1} -y", _plat, _mode); 292 | } 293 | if (await RunCommandAsync(command) != -1) 294 | await RefreshTargetAsync(); 295 | }); 296 | } 297 | 298 | public void Build() 299 | { 300 | _package.JoinableTaskFactory.RunAsync(async () => 301 | { 302 | if (string.IsNullOrEmpty(_target)) 303 | return; 304 | 305 | string command = "build -v -y"; 306 | if (_target != "default") 307 | command += " " + _target; 308 | else 309 | command += " -a"; 310 | 311 | if (_configChanged) 312 | { 313 | await UpdateConfigAsync(); 314 | _configChanged = false; 315 | } 316 | 317 | if(await RunCommandAsync(command) != -1) 318 | await RefreshTargetAsync(); 319 | }); 320 | } 321 | 322 | public void Run() 323 | { 324 | _package.JoinableTaskFactory.RunAsync(async () => 325 | { 326 | if (string.IsNullOrEmpty(_target)) 327 | return; 328 | 329 | string command = "r"; 330 | if (_target != "default") 331 | command += " " + _target; 332 | await RunCommandAsync(command); 333 | }); 334 | } 335 | 336 | public void Clean() 337 | { 338 | _package.JoinableTaskFactory.RunAsync(async () => 339 | { 340 | string command = "c"; 341 | if (_target != "default") 342 | command += " " + _target; 343 | 344 | if (await RunCommandAsync(command) != -1) 345 | await RefreshAllAsync(); 346 | }); 347 | } 348 | 349 | public void CleanConfig() 350 | { 351 | _package.JoinableTaskFactory.RunAsync(async () => 352 | { 353 | if(await RunCommandAsync("f -c -y") != -1) 354 | await RefreshAllAsync(); 355 | }); 356 | } 357 | 358 | public void UpdateIntellisense() 359 | { 360 | _package.JoinableTaskFactory.RunAsync(async () => 361 | { 362 | if(await RunCommandAsync("f -y") != -1) 363 | await RefreshTargetAsync(); 364 | 365 | string result = await RunScriptAsync(_updateIntellisense); 366 | result = result.Trim(); 367 | Dictionary intellisenseDict = new Dictionary(); 368 | Intellisense intellisense = new Intellisense() 369 | { 370 | name = "", 371 | inheritEnvironments = new List(1) { "msvc_x64" }, 372 | includePath = new List(2) {"${env.INCLUDE}", "${workspaceRoot}\\**"}, 373 | defines = new List(), 374 | intelliSenseMode = "windows-msvc-x64" 375 | }; 376 | DirectoryInfo directoryInfo = new DirectoryInfo(_projDir); 377 | intellisense.name = directoryInfo.Name; 378 | intellisenseDict.Add("configurations", new Intellisense[] { intellisense}); 379 | using (StringReader reader = new StringReader(result)) 380 | { 381 | while (reader.Peek() > -1) 382 | { 383 | string line = await reader.ReadLineAsync(); 384 | line = line.Trim(); 385 | if (string.IsNullOrEmpty(line)) 386 | continue; 387 | string[] fields = line.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries); 388 | for (int i = 0; i < fields.Length; i++) 389 | { 390 | string field = fields[i].Trim(); 391 | string[] kv = field.Split(new char[] { '=' }); 392 | if (kv.Length != 2) 393 | continue; 394 | switch (kv[0]) 395 | { 396 | case "includes": 397 | string includes = kv[1].Trim(); 398 | if (!string.IsNullOrEmpty(includes)) 399 | { 400 | string[] includePaths = includes.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries); 401 | intellisense.includePath.AddRange(includePaths); 402 | } 403 | break; 404 | case "define": 405 | string define = kv[1].Trim(); 406 | if (!string.IsNullOrEmpty(define)) 407 | { 408 | string[] defines = define.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries); 409 | intellisense.defines.AddRange(defines); 410 | } 411 | break; 412 | } 413 | } 414 | } 415 | } 416 | 417 | string json = Newtonsoft.Json.JsonConvert.SerializeObject(intellisenseDict); 418 | try 419 | { 420 | File.WriteAllText(CppProperties, json, Encoding.UTF8); 421 | } 422 | catch (Exception ex) 423 | { 424 | await PrintAsync(ex.Message); 425 | } 426 | }); 427 | 428 | } 429 | 430 | public void UpdateLaunch() 431 | { 432 | _package.JoinableTaskFactory.RunAsync(async () => 433 | { 434 | if(await RunCommandAsync("f -y") != -1) 435 | await RefreshTargetAsync(); 436 | 437 | string result = await RunScriptAsync(_updateLaunch); 438 | result = result.Trim(); 439 | Dictionary lanuchDict = new Dictionary(); 440 | lanuchDict.Add("version", "0.2.1"); 441 | lanuchDict.Add("defaults", new Dictionary()); 442 | List launches = new List(0); 443 | lanuchDict.Add("configurations", launches); 444 | using (StringReader reader = new StringReader(result)) 445 | { 446 | while (reader.Peek() > -1) 447 | { 448 | string line = await reader.ReadLineAsync(); 449 | line = line.Trim(); 450 | if (string.IsNullOrEmpty(line)) 451 | continue; 452 | 453 | string targetFile = Path.Combine(_projDir, line); 454 | Launch launch = new Launch() 455 | { 456 | name = Path.GetFileName(targetFile), 457 | type = "cppvsdbg", 458 | project = targetFile, 459 | cwd = Path.GetDirectoryName(targetFile), 460 | program = targetFile, 461 | MIMode = "gdb", 462 | miDebuggerPath = "", 463 | externalConsole = true, 464 | }; 465 | launches.Add(launch); 466 | } 467 | } 468 | 469 | string json = Newtonsoft.Json.JsonConvert.SerializeObject(lanuchDict); 470 | try 471 | { 472 | File.WriteAllText(LaunchVS, json, Encoding.UTF8); 473 | } 474 | catch (Exception ex) 475 | { 476 | await PrintAsync(ex.Message); 477 | } 478 | }); 479 | } 480 | 481 | public Task UpdateConfigAsync() 482 | { 483 | string command = ""; 484 | if (!string.IsNullOrEmpty(_arch)) 485 | { 486 | command = string.Format("f -p {0} -a {1} -m {2} -y", _plat, _arch, _mode); 487 | } 488 | else 489 | { 490 | command = string.Format("f -p {0} -m {1} -y", _plat, _mode); 491 | } 492 | return RunCommandAsync(command); 493 | } 494 | 495 | public async Task InitializeAsync(XMakePluginPackage package, CancellationToken cancellationToken) 496 | { 497 | _package = package; 498 | } 499 | 500 | public async Task OnAfterPackageLoadedAsync(CancellationToken cancellationToken, XMakeOptionPage page) 501 | { 502 | _page = page; 503 | _page.OptionChange += OnOptionChanged; 504 | await _package.JoinableTaskFactory.SwitchToMainThreadAsync(); 505 | 506 | if(!CheckIsExecutable()) 507 | { 508 | _isExecutable = false; 509 | await PrintAsync("Access https://xmake.io to download and install xmake first!"); 510 | return; 511 | } 512 | 513 | _isExecutable = true; 514 | IVsSolution solution = await _package.GetServiceAsync(typeof(SVsSolution)) as IVsSolution; 515 | if (solution != null) 516 | { 517 | solution.GetSolutionInfo(out string directory, out string solutionFile, out string optsFile); 518 | if (directory == null) 519 | { 520 | solution.AdviseSolutionEvents(this, out uint cookie); 521 | } 522 | else if (Directory.Exists(solutionFile) && Directory.Exists(directory)) 523 | { 524 | await InitProjectAsync(directory); 525 | } 526 | } 527 | } 528 | 529 | private async Task InitProjectAsync(string folderPath) 530 | { 531 | string[] files = Directory.GetFiles(folderPath, "xmake.lua", SearchOption.AllDirectories).Where(a => !a.Contains(".xmake")).ToArray(); 532 | foreach (string file in files) 533 | { 534 | if (File.Exists(file)) 535 | { 536 | FileInfo fileInfo = new FileInfo(file); 537 | _projDir = fileInfo.Directory.FullName; 538 | 539 | await RefreshAllAsync(); 540 | break; 541 | } 542 | } 543 | } 544 | 545 | private bool CheckIsExecutable() 546 | { 547 | using (var proc = new System.Diagnostics.Process()) 548 | { 549 | proc.StartInfo.WorkingDirectory = _projDir; 550 | proc.StartInfo.FileName = ExecutablePath; 551 | proc.StartInfo.Arguments = "--version"; 552 | proc.StartInfo.UseShellExecute = false; 553 | proc.StartInfo.RedirectStandardOutput = true; 554 | proc.StartInfo.CreateNoWindow = true; 555 | proc.StartInfo.RedirectStandardError = true; 556 | proc.StartInfo.StandardOutputEncoding = System.Text.Encoding.UTF8; 557 | proc.StartInfo.StandardErrorEncoding = System.Text.Encoding.UTF8; 558 | try 559 | { 560 | if (proc.Start()) 561 | { 562 | Regex regex = new Regex("\u001B\\[[;\\d]*m"); 563 | string result = regex.Replace(proc.StandardOutput.ReadToEnd(), ""); 564 | if (!string.IsNullOrEmpty(result)) 565 | return true; 566 | } 567 | } 568 | catch(Exception e) 569 | { 570 | return false; 571 | } 572 | 573 | } 574 | 575 | return false; 576 | } 577 | 578 | private void OnOptionChanged(string option, object value) 579 | { 580 | if(option == "CustomExecutablePath") 581 | { 582 | if (!CheckIsExecutable()) 583 | { 584 | _isExecutable = false; 585 | _package.JoinableTaskFactory.RunAsync(async ()=>{ 586 | await PrintAsync("Access https://xmake.io to download and install xmake first!"); 587 | }); 588 | } 589 | else 590 | { 591 | _isExecutable = true; 592 | } 593 | 594 | _package.JoinableTaskFactory.Run(RefreshEnableAsync); 595 | } 596 | } 597 | 598 | private async Task LoadConfigAsync() 599 | { 600 | if (string.IsNullOrEmpty(_projDir)) 601 | return; 602 | 603 | 604 | string result = await RunScriptAsync("\"import(\\\"core.project.config\\\"); config.load(); print(\\\"$(plat) $(arch) $(mode)\\\")\""); 605 | string[] cache = null; 606 | if (!string.IsNullOrEmpty(result)) 607 | { 608 | Regex regex = new Regex("\u001B\\[[;\\d]*m"); 609 | result = regex.Replace(result, ""); 610 | cache = result.Trim().Split(' '); 611 | } 612 | 613 | string platform = cache != null && cache.Length > 0 && !string.IsNullOrEmpty(cache[0]) ? cache[0] : null; 614 | if (!string.IsNullOrEmpty(platform)) 615 | _plat = platform; 616 | else 617 | _plat = _allPlatAndArchs[0].Item1; 618 | 619 | string arch = cache != null && cache.Length > 1 && !string.IsNullOrEmpty(cache[1]) ? cache[1] : null; 620 | if (!string.IsNullOrEmpty(arch)) 621 | _arch = arch; 622 | else 623 | _arch = _allPlatAndArchs[0].Item2[0]; 624 | 625 | string mode = cache != null && cache.Length > 2 && !string.IsNullOrEmpty(cache[2]) ? cache[2] : null; 626 | if (!string.IsNullOrEmpty(mode)) 627 | _mode = mode; 628 | else 629 | _mode = _allModes[0]; 630 | } 631 | 632 | private async Task LoadTargetAsync() 633 | { 634 | if (string.IsNullOrEmpty(_projDir)) 635 | return; 636 | 637 | _allTargets.Clear(); 638 | _allTargets.Add("default"); 639 | string result = await RunScriptAsync("\"import(\\\"core.project.config\\\"); import(\\\"core.project.project\\\"); config.load(); for name, _ in pairs((project.targets())) do print(name) end\""); 640 | if (!string.IsNullOrEmpty(result)) 641 | { 642 | Regex regex = new Regex("\u001B\\[[;\\d]*m"); 643 | result = regex.Replace(result, ""); 644 | string[] targets = result.Trim().Split('\n'); 645 | bool find = false; 646 | foreach (var item in targets) 647 | { 648 | if (string.IsNullOrEmpty(item)) 649 | continue; 650 | 651 | string t = item.Trim(); 652 | _allTargets.Add(t); 653 | 654 | if (!find && _target != null && _target == t) 655 | find = true; 656 | } 657 | 658 | if (!find) 659 | { 660 | _target = _allTargets.Count > 1 ? _allTargets[1] : _allTargets[0]; 661 | } 662 | } 663 | } 664 | 665 | private async Task PrintAsync(string msg) 666 | { 667 | await _package.JoinableTaskFactory.SwitchToMainThreadAsync(); 668 | 669 | DTE dte = await _package.GetServiceAsync(typeof(DTE)) as DTE; 670 | Window outputWindow = dte.Windows.Item(EnvDTE.Constants.vsWindowKindOutput); 671 | outputWindow.Activate(); 672 | 673 | IVsOutputWindow vso = await _package.GetServiceAsync(typeof(SVsOutputWindow)) as IVsOutputWindow; 674 | Guid paneGuid = new Guid("f015d5ab-25d8-4ed3-8a3c-38fce53a0baf"); 675 | vso.CreatePane(ref paneGuid, "XMake", 1, 1); 676 | 677 | IVsOutputWindowPane vsOutputWindowPane; 678 | vso.GetPane(ref paneGuid, out vsOutputWindowPane); 679 | vsOutputWindowPane.Activate(); 680 | 681 | if (vsOutputWindowPane != null) 682 | vsOutputWindowPane.OutputString(msg + "\r\n"); 683 | } 684 | 685 | private Task RunCommandAsync(string command) 686 | { 687 | var tcs = new TaskCompletionSource(); 688 | 689 | if (string.IsNullOrEmpty(_projDir)) 690 | { 691 | tcs.SetResult(-1); 692 | return tcs.Task; 693 | } 694 | 695 | _package.JoinableTaskFactory.RunAsync(async () => 696 | { 697 | await PrintAsync(command); 698 | }); 699 | 700 | var proc = new System.Diagnostics.Process(); 701 | proc.StartInfo.WorkingDirectory = _projDir; 702 | proc.StartInfo.FileName = ExecutablePath; 703 | proc.StartInfo.Arguments = command; 704 | proc.StartInfo.UseShellExecute = false; 705 | proc.StartInfo.RedirectStandardOutput = true; 706 | proc.StartInfo.CreateNoWindow = true; 707 | proc.StartInfo.RedirectStandardError = true; 708 | proc.StartInfo.StandardOutputEncoding = Encoding.GetEncoding("GBK"); 709 | proc.StartInfo.StandardErrorEncoding = Encoding.GetEncoding("GBK"); 710 | proc.OutputDataReceived += (s, e)=> 711 | { 712 | if (!string.IsNullOrEmpty(e.Data)) 713 | { 714 | Regex regex = new Regex("\u001B\\[[;\\d]*m"); 715 | string result = regex.Replace(e.Data, ""); 716 | _package.JoinableTaskFactory.RunAsync(async () => 717 | { 718 | await PrintAsync(result); 719 | }); 720 | } 721 | }; 722 | proc.EnableRaisingEvents = true; 723 | proc.Exited += (s, e)=> 724 | { 725 | tcs.SetResult(proc.ExitCode); 726 | proc.Dispose(); 727 | }; 728 | proc.Start(); 729 | proc.BeginOutputReadLine(); 730 | proc.BeginErrorReadLine(); 731 | 732 | return tcs.Task; 733 | } 734 | 735 | private async Task RunScriptAsync(string script) 736 | { 737 | if (string.IsNullOrEmpty(_projDir)) 738 | return ""; 739 | 740 | string result = ""; 741 | using (var proc = new System.Diagnostics.Process()) 742 | { 743 | proc.StartInfo.WorkingDirectory = _projDir; 744 | proc.StartInfo.FileName = ExecutablePath; 745 | proc.StartInfo.Arguments = "lua -c " + script; 746 | proc.StartInfo.UseShellExecute = false; 747 | proc.StartInfo.RedirectStandardOutput = true; 748 | proc.StartInfo.CreateNoWindow = true; 749 | proc.StartInfo.RedirectStandardError = true; 750 | proc.StartInfo.StandardOutputEncoding = System.Text.Encoding.UTF8; 751 | proc.StartInfo.StandardErrorEncoding = System.Text.Encoding.UTF8; 752 | try 753 | { 754 | if (proc.Start()) 755 | result = await proc.StandardOutput.ReadToEndAsync(); 756 | } 757 | catch(Exception e) 758 | { 759 | return string.Empty; 760 | } 761 | 762 | } 763 | 764 | return result; 765 | } 766 | 767 | private async Task RefreshTargetAsync() 768 | { 769 | await Microsoft.VisualStudio.Shell.ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); 770 | await LoadTargetAsync(); 771 | XMakeToolWindow window = await _package.FindToolWindowAsync(typeof(XMakeToolWindow), 772 | 0, 773 | true, 774 | _package.DisposalToken) as XMakeToolWindow; 775 | if (window != null) 776 | { 777 | ((IVsWindowFrame)window.Frame).Show(); 778 | window.RefreshTarget(); 779 | } 780 | } 781 | 782 | private async Task RefreshConfigAsync() 783 | { 784 | await Microsoft.VisualStudio.Shell.ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); 785 | await LoadConfigAsync(); 786 | 787 | XMakeToolWindow window = await _package.FindToolWindowAsync(typeof(XMakeToolWindow), 788 | 0, 789 | true, 790 | _package.DisposalToken) as XMakeToolWindow; 791 | if (window != null) 792 | { 793 | ((IVsWindowFrame)window.Frame).Show(); 794 | window.RefreshConfig(); 795 | } 796 | } 797 | 798 | private async Task RefreshEnableAsync() 799 | { 800 | XMakeToolWindow window = await _package.FindToolWindowAsync(typeof(XMakeToolWindow), 801 | 0, 802 | true, 803 | _package.DisposalToken) as XMakeToolWindow; 804 | if (window != null) 805 | { 806 | ((IVsWindowFrame)window.Frame).Show(); 807 | window.RefreshEnable(); 808 | } 809 | } 810 | 811 | private async Task RefreshAllAsync() 812 | { 813 | await Microsoft.VisualStudio.Shell.ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); 814 | await LoadConfigAsync(); 815 | await LoadTargetAsync(); 816 | XMakeToolWindow window = await _package.FindToolWindowAsync(typeof(XMakeToolWindow), 817 | 0, 818 | true, 819 | _package.DisposalToken) as XMakeToolWindow; 820 | if (window != null) 821 | { 822 | ((IVsWindowFrame)window.Frame).Show(); 823 | window.RefreshEnable(); 824 | window.RefreshConfig(); 825 | window.RefreshTarget(); 826 | } 827 | } 828 | 829 | public void OnAfterOpenFolder(string folderPath) 830 | { 831 | _package.JoinableTaskFactory.RunAsync(async () => 832 | { 833 | await InitProjectAsync(folderPath); 834 | }); 835 | } 836 | 837 | public void OnBeforeCloseFolder(string folderPath) 838 | { 839 | } 840 | 841 | public void OnQueryCloseFolder(string folderPath, ref int pfCancel) 842 | { 843 | } 844 | 845 | public void OnAfterCloseFolder(string folderPath) 846 | { 847 | } 848 | 849 | public void OnAfterLoadAllDeferredProjects() 850 | { 851 | } 852 | 853 | public int OnAfterOpenProject(IVsHierarchy pHierarchy, int fAdded) 854 | { 855 | return 0; 856 | } 857 | 858 | public int OnQueryCloseProject(IVsHierarchy pHierarchy, int fRemoving, ref int pfCancel) 859 | { 860 | return 0; 861 | } 862 | 863 | public int OnBeforeCloseProject(IVsHierarchy pHierarchy, int fRemoved) 864 | { 865 | return 0; 866 | } 867 | 868 | public int OnAfterLoadProject(IVsHierarchy pStubHierarchy, IVsHierarchy pRealHierarchy) 869 | { 870 | return 0; 871 | } 872 | 873 | public int OnQueryUnloadProject(IVsHierarchy pRealHierarchy, ref int pfCancel) 874 | { 875 | return 0; 876 | } 877 | 878 | public int OnBeforeUnloadProject(IVsHierarchy pRealHierarchy, IVsHierarchy pStubHierarchy) 879 | { 880 | return 0; 881 | } 882 | 883 | public int OnAfterOpenSolution(object pUnkReserved, int fNewSolution) 884 | { 885 | return 0; 886 | } 887 | 888 | public int OnQueryCloseSolution(object pUnkReserved, ref int pfCancel) 889 | { 890 | return 0; 891 | } 892 | 893 | public int OnBeforeCloseSolution(object pUnkReserved) 894 | { 895 | return 0; 896 | } 897 | 898 | public int OnAfterCloseSolution(object pUnkReserved) 899 | { 900 | return 0; 901 | } 902 | } 903 | } 904 | -------------------------------------------------------------------------------- /XMake.VisualStudio.VS2022/XMake.VisualStudio/XMakeToolWindow.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.Shell; 2 | using Microsoft.VisualStudio.Shell.Interop; 3 | using System; 4 | using System.Runtime.InteropServices; 5 | 6 | namespace XMake.VisualStudio 7 | { 8 | /// 9 | /// This class implements the tool window exposed by this package and hosts a user control. 10 | /// 11 | /// 12 | /// In Visual Studio tool windows are composed of a frame (implemented by the shell) and a pane, 13 | /// usually implemented by the package implementer. 14 | /// 15 | /// This class derives from the ToolWindowPane class provided from the MPF in order to use its 16 | /// implementation of the IVsUIElementPane interface. 17 | /// 18 | /// 19 | [Guid(WindowGuidString)] 20 | public class XMakeToolWindow : ToolWindowPane 21 | { 22 | public const string WindowGuidString = "2d408b97-42a2-4227-8e28-4a28bd1b2da2"; // Replace with new GUID in your own code 23 | public const string Title = "XMake Tool Window"; 24 | 25 | private XMakeService _service; 26 | private XMakeToolWindowControl _control; 27 | 28 | 29 | /// 30 | /// Initializes a new instance of the class. 31 | /// 32 | public XMakeToolWindow(XMakeService service) : base(null) 33 | { 34 | this.Caption = "XMakeToolWindow"; 35 | _service = service; 36 | 37 | // This is the user control hosted by the tool window; Note that, even if this class implements IDisposable, 38 | // we are not calling Dispose on this object. This is because ToolWindowPane calls Dispose on 39 | // the object returned by the Content property. 40 | _control = new XMakeToolWindowControl(); 41 | Content = _control; 42 | 43 | _control.platChanged += OnPlatChanged; 44 | _control.archChanged += OnArchChanged; 45 | _control.modeChanged += OnModeChanged; 46 | _control.targetChanged += OnTargetChanged; 47 | _control.build += Build; 48 | _control.quickStart = QuickStart; 49 | _control.run = Run; 50 | _control.clean = Clean; 51 | _control.cleanConfig = CleanConfig; 52 | _control.updateIntellisense = UpdateIntellisense; 53 | _control.updateLaunch = UpdateLaunch; 54 | 55 | for (int i = 0; i < _service.AllModes.Length; i++) 56 | { 57 | _control.ModeComboBox.Items.Add(_service.AllModes[i]); 58 | } 59 | 60 | 61 | for (int i = 0; i < _service.AllPlats.Length; i++) 62 | { 63 | _control.PlatformComboBox.Items.Add(_service.AllPlats[i]); 64 | } 65 | 66 | RefreshEnable(); 67 | RefreshConfig(); 68 | RefreshTarget(); 69 | } 70 | 71 | public void RefreshEnable() 72 | { 73 | _control.IsEnabled = _service.IsValid; 74 | } 75 | 76 | public void RefreshConfig() 77 | { 78 | _control.PlatformComboBox.SelectedItem = _service.Plat; 79 | // _control.ArchComboBox.SelectedItem = _service.Arch; 80 | _control.ModeComboBox.SelectedItem = _service.Mode; 81 | } 82 | 83 | public void RefreshTarget() 84 | { 85 | _control.TargetComboBox.Items.Clear(); 86 | 87 | for (int i = 0; i < _service.AllTargets.Count; i++) 88 | { 89 | _control.TargetComboBox.Items.Add(_service.AllTargets[i]); 90 | } 91 | 92 | _control.TargetComboBox.SelectedItem = _service.Target; 93 | } 94 | 95 | private void UpdateIntellisense() 96 | { 97 | _service.UpdateIntellisense(); 98 | } 99 | 100 | private void UpdateLaunch() 101 | { 102 | _service.UpdateLaunch(); 103 | } 104 | 105 | private void CleanConfig() 106 | { 107 | _service.CleanConfig(); 108 | } 109 | 110 | private void Clean() 111 | { 112 | _service.Clean(); 113 | } 114 | 115 | private void Run() 116 | { 117 | _service.Run(); 118 | } 119 | 120 | private void QuickStart() 121 | { 122 | _service.QuickStart(); 123 | } 124 | 125 | private void Build() 126 | { 127 | _service.Build(); 128 | } 129 | 130 | private void OnTargetChanged(string obj) 131 | { 132 | _service.Target = obj; 133 | } 134 | 135 | private void OnModeChanged(string obj) 136 | { 137 | _service.Mode = obj; 138 | } 139 | 140 | private void OnArchChanged(string obj) 141 | { 142 | _service.Arch = obj; 143 | } 144 | 145 | private void OnPlatChanged(string obj) 146 | { 147 | _service.Plat = obj; 148 | 149 | _control.ArchComboBox.Items.Clear(); 150 | for (int i = 0; i < _service.AllArchs.Length; i++) 151 | { 152 | _control.ArchComboBox.Items.Add(_service.AllArchs[i]); 153 | } 154 | 155 | if(string.IsNullOrEmpty(_service.Arch)) 156 | { 157 | _control.ArchComboBox.IsEnabled = false; 158 | } 159 | else 160 | { 161 | _control.ArchComboBox.IsEnabled = true; 162 | _control.ArchComboBox.SelectedItem = _service.Arch; 163 | } 164 | } 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /XMake.VisualStudio.VS2022/XMake.VisualStudio/XMakeToolWindowControl.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 | 51 | 52 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /XMake.VisualStudio.VS2022/XMake.VisualStudio/XMakeToolWindowControl.xaml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.Shell; 2 | using Microsoft.VisualStudio.Shell.Interop; 3 | using System; 4 | using System.Diagnostics.CodeAnalysis; 5 | using System.IO; 6 | using System.Windows; 7 | using System.Windows.Controls; 8 | 9 | namespace XMake.VisualStudio 10 | { 11 | /// 12 | /// Interaction logic for XMakeToolWindowControl. 13 | /// 14 | public partial class XMakeToolWindowControl : UserControl 15 | { 16 | 17 | internal Action modeChanged; 18 | internal Action platChanged; 19 | internal Action archChanged; 20 | internal Action targetChanged; 21 | internal Action quickStart; 22 | internal Action build; 23 | internal Action run; 24 | internal Action clean; 25 | internal Action cleanConfig; 26 | internal Action updateIntellisense; 27 | internal Action updateLaunch; 28 | 29 | /// 30 | /// Initializes a new instance of the class. 31 | /// 32 | public XMakeToolWindowControl() 33 | { 34 | this.InitializeComponent(); 35 | } 36 | 37 | private void ModeComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) 38 | { 39 | if (ModeComboBox.SelectedItem == null) 40 | return; 41 | 42 | modeChanged.Invoke(ModeComboBox.SelectedItem.ToString()); 43 | } 44 | 45 | private void PlatformComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) 46 | { 47 | if (PlatformComboBox.SelectedItem == null) 48 | return; 49 | 50 | platChanged.Invoke(PlatformComboBox.SelectedItem.ToString()); 51 | } 52 | 53 | private void ArchComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) 54 | { 55 | if (ArchComboBox.SelectedItem == null) 56 | return; 57 | 58 | archChanged.Invoke(ArchComboBox.SelectedItem.ToString()); 59 | } 60 | 61 | private void TargetComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) 62 | { 63 | if (TargetComboBox.SelectedItem == null) 64 | return; 65 | 66 | targetChanged.Invoke(TargetComboBox.SelectedItem.ToString()); 67 | } 68 | 69 | private void Build_Click(object sender, RoutedEventArgs e) 70 | { 71 | build.Invoke(); 72 | } 73 | 74 | private void Run_Click(object sender, RoutedEventArgs e) 75 | { 76 | run.Invoke(); 77 | } 78 | 79 | private void Clean_Click(object sender, RoutedEventArgs e) 80 | { 81 | clean.Invoke(); 82 | } 83 | 84 | private void CleanConfig_Click(object sender, RoutedEventArgs e) 85 | { 86 | cleanConfig.Invoke(); 87 | } 88 | 89 | private void Intellisense_Click(object sender, RoutedEventArgs e) 90 | { 91 | updateIntellisense.Invoke(); 92 | } 93 | 94 | private void Launch_Click(object sender, RoutedEventArgs e) 95 | { 96 | updateLaunch.Invoke(); 97 | } 98 | 99 | private void QuickStart_Click(object sender, RoutedEventArgs e) 100 | { 101 | quickStart.Invoke(); 102 | } 103 | 104 | 105 | } 106 | } -------------------------------------------------------------------------------- /XMake.VisualStudio.VS2022/XMake.VisualStudio/XMakeToolWindowResources.xaml: -------------------------------------------------------------------------------- 1 |  8 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /XMake.VisualStudio.VS2022/XMake.VisualStudio/XMakeToolWindowResources.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | namespace XMake.VisualStudio 4 | { 5 | public partial class XMakeToolWindowResources : ResourceDictionary 6 | { 7 | public XMakeToolWindowResources() 8 | { 9 | this.InitializeComponent(); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /XMake.VisualStudio.VS2022/XMake.VisualStudio/source.extension.vsixmanifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | XMake.VisualStudio.VS2022 6 | XMake VSIX Plugin for visual studio 2022. 7 | Icon.png 8 | 9 | 10 | 11 | amd64 12 | 13 | 14 | amd64 15 | 16 | 17 | amd64 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /XMake.VisualStudio/XMake.VisualStudio.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.31912.275 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XMake.VisualStudio", "XMake.VisualStudio\XMake.VisualStudio.csproj", "{78787B01-1917-4C1E-B997-270DB5AB58A0}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Debug|x86 = Debug|x86 12 | Release|Any CPU = Release|Any CPU 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {78787B01-1917-4C1E-B997-270DB5AB58A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {78787B01-1917-4C1E-B997-270DB5AB58A0}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {78787B01-1917-4C1E-B997-270DB5AB58A0}.Debug|x86.ActiveCfg = Debug|x86 19 | {78787B01-1917-4C1E-B997-270DB5AB58A0}.Debug|x86.Build.0 = Debug|x86 20 | {78787B01-1917-4C1E-B997-270DB5AB58A0}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {78787B01-1917-4C1E-B997-270DB5AB58A0}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {78787B01-1917-4C1E-B997-270DB5AB58A0}.Release|x86.ActiveCfg = Release|x86 23 | {78787B01-1917-4C1E-B997-270DB5AB58A0}.Release|x86.Build.0 = Release|x86 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {FA52575F-CBE3-4FE3-9708-90552B1EF9E2} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /XMake.VisualStudio/XMake.VisualStudio/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloWorld886/xmake-visualstudio/04c5dc325d8ce53cf332711ef1f50cd0d5299e73/XMake.VisualStudio/XMake.VisualStudio/Icon.png -------------------------------------------------------------------------------- /XMake.VisualStudio/XMake.VisualStudio/Key.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloWorld886/xmake-visualstudio/04c5dc325d8ce53cf332711ef1f50cd0d5299e73/XMake.VisualStudio/XMake.VisualStudio/Key.snk -------------------------------------------------------------------------------- /XMake.VisualStudio/XMake.VisualStudio/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("XMake.VisualStudio")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("XMake.VisualStudio")] 12 | [assembly: AssemblyCopyright("")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | // Version information for an assembly consists of the following four values: 22 | // 23 | // Major Version 24 | // Minor Version 25 | // Build Number 26 | // Revision 27 | // 28 | // You can specify all the values or you can default the Build and Revision Numbers 29 | // by using the '*' as shown below: 30 | // [assembly: AssemblyVersion("1.0.*")] 31 | [assembly: AssemblyVersion("1.0.0.0")] 32 | [assembly: AssemblyFileVersion("1.0.0.0")] 33 | -------------------------------------------------------------------------------- /XMake.VisualStudio/XMake.VisualStudio/VSPackage.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // 此代码由工具生成。 4 | // 运行时版本:4.0.30319.42000 5 | // 6 | // 对此文件的更改可能会导致不正确的行为,并且如果 7 | // 重新生成代码,这些更改将会丢失。 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace XMake.VisualStudio { 12 | using System; 13 | 14 | 15 | /// 16 | /// 一个强类型的资源类,用于查找本地化的字符串等。 17 | /// 18 | // 此类是由 StronglyTypedResourceBuilder 19 | // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 20 | // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen 21 | // (以 /str 作为命令选项),或重新生成 VS 项目。 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | public class VSPackage { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal VSPackage() { 33 | } 34 | 35 | /// 36 | /// 返回此类使用的缓存的 ResourceManager 实例。 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | public static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("XMake.VisualStudio.VSPackage", typeof(VSPackage).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// 重写当前线程的 CurrentUICulture 属性,对 51 | /// 使用此强类型资源类的所有资源查找执行重写。 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | public static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// 查找类似 XMakeCommand Extension 的本地化字符串。 65 | /// 66 | public static string _110 { 67 | get { 68 | return ResourceManager.GetString("110", resourceCulture); 69 | } 70 | } 71 | 72 | /// 73 | /// 查找类似 XMakeCommand Visual Studio Extension Detailed Info 的本地化字符串。 74 | /// 75 | public static string _112 { 76 | get { 77 | return ResourceManager.GetString("112", resourceCulture); 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /XMake.VisualStudio/XMake.VisualStudio/VSPackage.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | XMakeCommand Extension 122 | 123 | 124 | XMakeCommand Visual Studio Extension Detailed Info 125 | 126 | -------------------------------------------------------------------------------- /XMake.VisualStudio/XMake.VisualStudio/XMake.VisualStudio.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 15.0 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | 7 | 8 | true 9 | 10 | 11 | Key.snk 12 | 13 | 14 | 15 | Debug 16 | AnyCPU 17 | 2.0 18 | {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 19 | {78787B01-1917-4C1E-B997-270DB5AB58A0} 20 | Library 21 | Properties 22 | XMake.VisualStudio 23 | XMake.VisualStudio 24 | v4.7.2 25 | true 26 | true 27 | true 28 | false 29 | false 30 | true 31 | true 32 | Program 33 | $(DevEnvDir)devenv.exe 34 | /rootsuffix Exp 35 | 36 | 37 | true 38 | full 39 | false 40 | bin\Debug\ 41 | DEBUG;TRACE 42 | prompt 43 | 4 44 | 45 | 46 | pdbonly 47 | true 48 | bin\Release\ 49 | TRACE 50 | prompt 51 | 4 52 | 53 | 54 | 55 | 56 | True 57 | True 58 | VSPackage.resx 59 | 60 | 61 | 62 | 63 | 64 | 65 | XMakeToolWindowControl.xaml 66 | 67 | 68 | XMakeToolWindowResources.xaml 69 | 70 | 71 | 72 | 73 | 74 | Designer 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | Menus.ctmenu 97 | 98 | 99 | 100 | 101 | true 102 | VSPackage 103 | PublicResXFileCodeGenerator 104 | VSPackage.Designer.cs 105 | 106 | 107 | 108 | 109 | Always 110 | true 111 | 112 | 113 | 114 | 115 | Designer 116 | MSBuild:Compile 117 | 118 | 119 | Designer 120 | MSBuild:Compile 121 | 122 | 123 | 124 | 125 | 126 | 133 | -------------------------------------------------------------------------------- /XMake.VisualStudio/XMake.VisualStudio/XMakeCommand.cs: -------------------------------------------------------------------------------- 1 | using EnvDTE; 2 | using EnvDTE80; 3 | using Microsoft.VisualStudio.Shell; 4 | using Microsoft.VisualStudio.Shell.Interop; 5 | using System; 6 | using System.ComponentModel.Design; 7 | using Task = System.Threading.Tasks.Task; 8 | 9 | namespace XMake.VisualStudio 10 | { 11 | /// 12 | /// Command handler 13 | /// 14 | internal sealed class XMakeCommand 15 | { 16 | /// 17 | /// Command ID. 18 | /// 19 | public const int CommandId = 257; 20 | 21 | /// 22 | /// Command menu group (command set GUID). 23 | /// 24 | public static readonly Guid CommandSet = new Guid("94e59d17-8ff5-49c2-b8cc-5b4b6c9b92f2"); 25 | 26 | /// 27 | /// VS Package that provides this command, not null. 28 | /// 29 | private readonly AsyncPackage package; 30 | 31 | /// 32 | /// Initializes a new instance of the class. 33 | /// Adds our command handlers for menu (commands must exist in the command table file) 34 | /// 35 | /// Owner package, not null. 36 | /// Command service to add command to, not null. 37 | private XMakeCommand(AsyncPackage package, OleMenuCommandService commandService) 38 | { 39 | this.package = package ?? throw new ArgumentNullException(nameof(package)); 40 | commandService = commandService ?? throw new ArgumentNullException(nameof(commandService)); 41 | 42 | var menuCommandID = new CommandID(CommandSet, CommandId); 43 | var menuItem = new MenuCommand(this.Execute, menuCommandID); 44 | commandService.AddCommand(menuItem); 45 | } 46 | 47 | /// 48 | /// Gets the instance of the command. 49 | /// 50 | public static XMakeCommand Instance 51 | { 52 | get; 53 | private set; 54 | } 55 | 56 | /// 57 | /// Gets the service provider from the owner package. 58 | /// 59 | private Microsoft.VisualStudio.Shell.IAsyncServiceProvider ServiceProvider 60 | { 61 | get 62 | { 63 | return this.package; 64 | } 65 | } 66 | 67 | /// 68 | /// Initializes the singleton instance of the command. 69 | /// 70 | /// Owner package, not null. 71 | public static async Task InitializeAsync(AsyncPackage package) 72 | { 73 | // Switch to the main thread - the call to AddCommand in XMakeCommand's constructor requires 74 | // the UI thread. 75 | await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(package.DisposalToken); 76 | 77 | OleMenuCommandService commandService = await package.GetServiceAsync(typeof(IMenuCommandService)) as OleMenuCommandService; 78 | Instance = new XMakeCommand(package, commandService); 79 | } 80 | 81 | 82 | private void Execute(object sender, EventArgs e) 83 | { 84 | package.JoinableTaskFactory.RunAsync(async () => 85 | { 86 | await Microsoft.VisualStudio.Shell.ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); 87 | 88 | ToolWindowPane window = await package.FindToolWindowAsync( 89 | typeof(XMakeToolWindow), 90 | 0, 91 | create: true, 92 | cancellationToken: package.DisposalToken); 93 | ((IVsWindowFrame)window.Frame).Show(); 94 | }); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /XMake.VisualStudio/XMake.VisualStudio/XMakePluginPackage.cs: -------------------------------------------------------------------------------- 1 | using EnvDTE; 2 | using EnvDTE80; 3 | using Microsoft; 4 | using Microsoft.VisualStudio.Shell; 5 | using Microsoft.VisualStudio.Shell.Interop; 6 | using System; 7 | using System.Diagnostics.CodeAnalysis; 8 | using System.IO; 9 | using System.Linq; 10 | using System.Runtime.InteropServices; 11 | using System.Threading; 12 | using System.Threading.Tasks; 13 | using Task = System.Threading.Tasks.Task; 14 | using System.ComponentModel; 15 | 16 | namespace XMake.VisualStudio 17 | { 18 | /// 19 | /// This is the class that implements the package exposed by this assembly. 20 | /// 21 | /// 22 | /// 23 | /// The minimum requirement for a class to be considered a valid package for Visual Studio 24 | /// is to implement the IVsPackage interface and register itself with the shell. 25 | /// This package uses the helper classes defined inside the Managed Package Framework (MPF) 26 | /// to do it: it derives from the Package class that provides the implementation of the 27 | /// IVsPackage interface and uses the registration attributes defined in the framework to 28 | /// register itself and its components with the shell. These attributes tell the pkgdef creation 29 | /// utility what data to put into .pkgdef file. 30 | /// 31 | /// 32 | /// To get loaded into VS, the package must be referred by <Asset Type="Microsoft.VisualStudio.VsPackage" ...> in .vsixmanifest file. 33 | /// 34 | /// 35 | [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)] 36 | [ProvideMenuResource("Menus.ctmenu", 1)] 37 | [Guid(XMakePluginPackage.PackageGuidString)] 38 | [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "pkgdef, VS and vsixmanifest are valid VS terms")] 39 | [ProvideToolWindow(typeof(XMake.VisualStudio.XMakeToolWindow))] 40 | [ProvideOptionPage(typeof(XMakeOptionPage), "XMake", "General", 0, 0, true)] 41 | public sealed class XMakePluginPackage : AsyncPackage 42 | { 43 | /// 44 | /// XMakeCommandPackage GUID string. 45 | /// 46 | public const string PackageGuidString = "f015d5ab-25d8-4ed3-8a3c-38fce53a0baf"; 47 | 48 | /// 49 | /// Initializes a new instance of the class. 50 | /// 51 | public XMakePluginPackage() 52 | { 53 | // Inside this method you can place any initialization code that does not require 54 | // any Visual Studio service because at this point the package object is created but 55 | // not sited yet inside Visual Studio environment. The place to do all the other 56 | // initialization is the Initialize method. 57 | } 58 | 59 | #region Package Members 60 | 61 | /// 62 | /// Initialization of the package; this method is called right after the package is sited, so this is the place 63 | /// where you can put all the initialization code that rely on services provided by VisualStudio. 64 | /// 65 | /// A cancellation token to monitor for initialization cancellation, which can occur when VS is shutting down. 66 | /// A provider for progress updates. 67 | /// A task representing the async work of package initialization, or an already completed task if there is none. Do not return null from this method. 68 | protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress progress) 69 | { 70 | AddService(typeof(XMakeService), CreateXMakeAsync); 71 | // When initialized asynchronously, the current thread may be a background thread at this point. 72 | // Do any initialization that requires the UI thread after switching to the UI thread. 73 | await this.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); 74 | await XMakeCommand.InitializeAsync(this); 75 | 76 | XMakeService service = await GetServiceAsync(typeof(XMakeService)) as XMakeService; 77 | XMakeOptionPage page = (XMakeOptionPage)GetDialogPage(typeof(XMakeOptionPage)); 78 | if (service != null && page != null) 79 | await service.OnAfterPackageLoadedAsync(cancellationToken, page); 80 | } 81 | 82 | private async Task CreateXMakeAsync(IAsyncServiceContainer container, CancellationToken cancellationToken, Type serviceType) 83 | { 84 | var svc = new XMakeService(); 85 | await svc.InitializeAsync(this, cancellationToken); 86 | return svc; 87 | } 88 | 89 | //protected override async Task OnAfterPackageLoadedAsync(CancellationToken cancellationToken) 90 | //{ 91 | // XMakeService service = await GetServiceAsync(typeof(XMakeService)) as XMakeService; 92 | // if (service != null) 93 | // await service.OnAfterPackageLoadedAsync(cancellationToken); 94 | 95 | // await base.OnAfterPackageLoadedAsync(cancellationToken); 96 | //} 97 | 98 | public override IVsAsyncToolWindowFactory GetAsyncToolWindowFactory(Guid toolWindowType) 99 | { 100 | return toolWindowType.Equals(Guid.Parse(XMakeToolWindow.WindowGuidString)) ? this : null; 101 | } 102 | 103 | protected override string GetToolWindowTitle(Type toolWindowType, int id) 104 | { 105 | return toolWindowType == typeof(XMakeToolWindow) ? XMakeToolWindow.Title : base.GetToolWindowTitle(toolWindowType, id); 106 | } 107 | 108 | protected override async Task InitializeToolWindowAsync(Type toolWindowType, int id, CancellationToken cancellationToken) 109 | { 110 | XMakeService service = await GetServiceAsync(typeof(XMakeService)) as XMakeService; 111 | return service; 112 | } 113 | 114 | #endregion 115 | } 116 | 117 | [Guid("5183218B-3091-4919-A875-1BB034166D22")] 118 | public class XMakeOptionPage : DialogPage 119 | { 120 | public Action OptionChange; 121 | 122 | private string _customExecutablePath; 123 | 124 | [Category("XMake")] 125 | [DisplayName("Custom Executable Path")] 126 | [Description("Where is xmake.exe")] 127 | public string CustomExecutablePath 128 | { 129 | get { return _customExecutablePath; } 130 | set 131 | { 132 | if (_customExecutablePath == value) 133 | return; 134 | 135 | _customExecutablePath = value; 136 | if (OptionChange != null) 137 | OptionChange("CustomExecutablePath", _customExecutablePath); 138 | } 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /XMake.VisualStudio/XMake.VisualStudio/XMakePluginPackage.vsct: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 9 | 10 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 25 | 26 | 33 | 34 | 43 | 44 | 48 | 53 | 54 | 55 | 57 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /XMake.VisualStudio/XMake.VisualStudio/XMakeService.cs: -------------------------------------------------------------------------------- 1 | using EnvDTE; 2 | using Microsoft.VisualStudio.Shell; 3 | using Microsoft.VisualStudio.Shell.Interop; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Diagnostics; 7 | using System.IO; 8 | using System.Linq; 9 | using System.Resources; 10 | using System.Runtime.InteropServices; 11 | using System.Text; 12 | using System.Text.RegularExpressions; 13 | using System.Threading; 14 | using System.Threading.Tasks; 15 | using Task = System.Threading.Tasks.Task; 16 | 17 | namespace XMake.VisualStudio 18 | { 19 | internal struct Intellisense 20 | { 21 | public string name; 22 | public List inheritEnvironments; 23 | public List includePath; 24 | public List defines; 25 | public string intelliSenseMode; 26 | } 27 | 28 | internal struct Launch 29 | { 30 | public string type; 31 | public string name; 32 | public string project; 33 | public string cwd; 34 | public string program; 35 | public string MIMode; 36 | public string miDebuggerPath; 37 | public bool externalConsole; 38 | } 39 | 40 | [ComVisible(true)] 41 | public class XMakeService : IVsSolutionEvents7, IVsSolutionEvents 42 | { 43 | private static string _updateIntellisense = @"""import('core.project.config') 44 | import('core.project.project') 45 | import('core.project.rule') 46 | config.load() 47 | for k,t in pairs(project:targets()) do 48 | local rules = t:get('rules') 49 | if rules and type(rules) == 'string' then 50 | local on_config = rules:script('config') 51 | if on_config then 52 | utils.trycall(on_config, nil, t) 53 | end 54 | elseif rules and type(rules) == 'table' then 55 | for k, r in pairs(rules) do 56 | local rule = rule.rule(r) 57 | local on_config = rule:script('config') 58 | if on_config then 59 | utils.trycall(on_config, nil, t) 60 | end 61 | end 62 | end 63 | local name = t:name() or '' 64 | local define = '' 65 | local defines = t:get('defines') or '' 66 | if type(defines) == 'string' then 67 | define = defines 68 | elseif type(defines) == 'table' then 69 | for k, p in pairs(defines) do 70 | define = define == '' and p or (define.. ';' .. p) 71 | end 72 | end 73 | local arch = t:get('arch') or 'x86' 74 | local includes = '' 75 | for k, p in pairs(t:pkgs()) do 76 | local dir = p:get('sysincludedirs') 77 | if dir then 78 | includes = includes == '' and dir or (includes .. ';' .. dir) 79 | end 80 | end 81 | local includedirs = t:get('includedirs') 82 | if type(includedirs) == 'string' then 83 | includes = includes == '' and includedirs or (includes .. ';' .. includedirs) 84 | elseif type(includedirs) == 'table' then 85 | for k, p in pairs(includedirs) do 86 | includes = includes == '' and p or (includes .. ';' .. p) 87 | end 88 | end 89 | local sysincludedirs = t:get('sysincludedirs') 90 | if type(sysincludedirs) == 'string' then 91 | includes = includes == '' and sysincludedirs or (includes .. ';' .. sysincludedirs) 92 | elseif type(sysincludedirs) == 'table' then 93 | for k, p in pairs(sysincludedirs) do 94 | includes = includes == '' and p or (includes .. ';' .. p) 95 | end 96 | end 97 | local toolchains = t:toolchains() 98 | if toolchains then 99 | for k, toolchain in pairs(toolchains) do 100 | local runenvs = toolchain:runenvs() 101 | if runenvs then 102 | local include = runenvs.INCLUDE 103 | if include then 104 | includes = includes == '' and include or (includes .. ';' .. include) 105 | end 106 | end 107 | end 108 | end 109 | print(string.format('name=%s|define=%s|arch=%s|includes=%s', name, define, arch, includes)) 110 | end"""; 111 | 112 | private static string _updateLaunch = @""" 113 | import('core.project.config') 114 | import('core.project.project') 115 | config.load() 116 | for k,t in pairs(project:targets()) do 117 | if t:is_binary() then 118 | print(t:targetfile()) 119 | end 120 | end 121 | """; 122 | 123 | private XMakePluginPackage _package; 124 | private readonly List _allTargets = new List() { "default" }; 125 | private readonly string[] _allModes = new string[] 126 | { 127 | "debug", 128 | "release" 129 | }; 130 | private readonly Tuple[] _allPlatAndArchs = new Tuple[] 131 | { 132 | new Tuple("windows", new string[]{"x86", "x64"}), 133 | new Tuple("mingw", new string[]{"i386", "x86_64", "arm", "arm64"}), 134 | new Tuple("android", new string[]{"armeabi-v7a", "arm64-v8a"}), 135 | new Tuple("wasm", new string[] { }) 136 | }; 137 | 138 | private string[] _allPlats; 139 | private string[] _allArchs; 140 | private string _projDir; 141 | private string _target; 142 | private string _mode; 143 | private string _plat; 144 | private string _arch; 145 | private XMakeOptionPage _page; 146 | private bool _configChanged = false; 147 | private bool _isExecutable = false; 148 | 149 | public string ProjDir 150 | { 151 | get => _projDir; 152 | } 153 | 154 | public string Target 155 | { 156 | get => _target; 157 | set 158 | { 159 | if (!string.IsNullOrEmpty(_target) && _target != value) 160 | _configChanged = true; 161 | _target = value; 162 | } 163 | } 164 | 165 | public string[] AllModes { get => _allModes; } 166 | 167 | public string[] AllPlats 168 | { 169 | get 170 | { 171 | if(_allPlats == null) 172 | { 173 | _allPlats = new string[_allPlatAndArchs.Length]; 174 | for (int i = 0; i < _allPlatAndArchs.Length; i++) 175 | { 176 | _allPlats[i] = _allPlatAndArchs[i].Item1; 177 | } 178 | } 179 | 180 | return _allPlats; 181 | } 182 | } 183 | 184 | public string[] AllArchs 185 | { 186 | get { return _allArchs; } 187 | } 188 | 189 | public IReadOnlyList AllTargets { get => _allTargets; } 190 | 191 | public string Mode 192 | { 193 | get => _mode; 194 | set 195 | { 196 | if (!string.IsNullOrEmpty(_mode) && _mode != value) 197 | _configChanged = true; 198 | _mode = value; 199 | } 200 | } 201 | 202 | public string Plat 203 | { 204 | get => _plat; 205 | set 206 | { 207 | if (!string.IsNullOrEmpty(_plat) && _plat != value) 208 | _configChanged = true; 209 | _plat = value; 210 | 211 | for(int i = 0; i < _allPlats.Length; ++i) 212 | { 213 | if(_allPlats[i] == _plat) 214 | { 215 | _allArchs = _allPlatAndArchs[i].Item2; 216 | if(_allArchs.Length != 0) 217 | { 218 | _arch = _allArchs[0]; 219 | } 220 | else 221 | { 222 | _arch = string.Empty; 223 | } 224 | break; 225 | } 226 | } 227 | } 228 | } 229 | 230 | public string Arch 231 | { 232 | get => _arch; 233 | set 234 | { 235 | if (!string.IsNullOrEmpty(_arch) && _arch != value) 236 | _configChanged = true; 237 | _arch = value; 238 | } 239 | } 240 | 241 | public string CppProperties { 242 | get 243 | { 244 | return Path.Combine(_projDir, "CppProperties.json"); 245 | } 246 | } 247 | 248 | public string LaunchVS 249 | { 250 | get 251 | { 252 | return Path.Combine(_projDir, "Launch.vs.json"); 253 | } 254 | } 255 | 256 | public bool IsValid 257 | { 258 | get 259 | { 260 | if (string.IsNullOrEmpty(_projDir)) 261 | return false; 262 | return _isExecutable; 263 | } 264 | } 265 | 266 | public string ExecutablePath 267 | { 268 | get 269 | { 270 | if (string.IsNullOrEmpty(_page.CustomExecutablePath)) 271 | return "xmake"; 272 | return _page.CustomExecutablePath; 273 | } 274 | } 275 | 276 | public void QuickStart() 277 | { 278 | _package.JoinableTaskFactory.RunAsync(async () => 279 | { 280 | string command = ""; 281 | if (!string.IsNullOrEmpty(_arch)) 282 | { 283 | command = string.Format("f -p {0} -a {1} -m {2} -y", _plat, _arch, _mode); 284 | } 285 | else 286 | { 287 | command = string.Format("f -p {0} -m {1} -y", _plat, _mode); 288 | } 289 | if (await RunCommandAsync(command) != -1) 290 | await RefreshTargetAsync(); 291 | }); 292 | } 293 | 294 | public void Build() 295 | { 296 | _package.JoinableTaskFactory.RunAsync(async () => 297 | { 298 | if (string.IsNullOrEmpty(_target)) 299 | return; 300 | 301 | string command = "build -v -y"; 302 | if (_target != "default") 303 | command += " " + _target; 304 | else 305 | command += " -a"; 306 | 307 | if (_configChanged) 308 | { 309 | await UpdateConfigAsync(); 310 | _configChanged = false; 311 | } 312 | 313 | if(await RunCommandAsync(command) != -1) 314 | await RefreshTargetAsync(); 315 | }); 316 | } 317 | 318 | public void Run() 319 | { 320 | _package.JoinableTaskFactory.RunAsync(async () => 321 | { 322 | if (string.IsNullOrEmpty(_target)) 323 | return; 324 | 325 | string command = "r"; 326 | if (_target != "default") 327 | command += " " + _target; 328 | await RunCommandAsync(command); 329 | }); 330 | } 331 | 332 | public void Clean() 333 | { 334 | _package.JoinableTaskFactory.RunAsync(async () => 335 | { 336 | string command = "c"; 337 | if (_target != "default") 338 | command += " " + _target; 339 | 340 | if (await RunCommandAsync(command) != -1) 341 | await RefreshAllAsync(); 342 | }); 343 | } 344 | 345 | public void CleanConfig() 346 | { 347 | _package.JoinableTaskFactory.RunAsync(async () => 348 | { 349 | if(await RunCommandAsync("f -c -y") != -1) 350 | await RefreshAllAsync(); 351 | }); 352 | } 353 | 354 | public void UpdateIntellisense() 355 | { 356 | _package.JoinableTaskFactory.RunAsync(async () => 357 | { 358 | if(await RunCommandAsync("f -y") != -1) 359 | await RefreshTargetAsync(); 360 | 361 | string result = await RunScriptAsync(_updateIntellisense); 362 | result = result.Trim(); 363 | Dictionary intellisenseDict = new Dictionary(); 364 | Intellisense intellisense = new Intellisense() 365 | { 366 | name = "", 367 | inheritEnvironments = new List(1) { "msvc_x64" }, 368 | includePath = new List(2) {"${env.INCLUDE}", "${workspaceRoot}\\**"}, 369 | defines = new List(), 370 | intelliSenseMode = "windows-msvc-x64" 371 | }; 372 | DirectoryInfo directoryInfo = new DirectoryInfo(_projDir); 373 | intellisense.name = directoryInfo.Name; 374 | intellisenseDict.Add("configurations", new Intellisense[] { intellisense}); 375 | using (StringReader reader = new StringReader(result)) 376 | { 377 | while (reader.Peek() > -1) 378 | { 379 | string line = await reader.ReadLineAsync(); 380 | line = line.Trim(); 381 | if (string.IsNullOrEmpty(line)) 382 | continue; 383 | string[] fields = line.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries); 384 | for (int i = 0; i < fields.Length; i++) 385 | { 386 | string field = fields[i].Trim(); 387 | string[] kv = field.Split(new char[] { '=' }); 388 | if (kv.Length != 2) 389 | continue; 390 | switch (kv[0]) 391 | { 392 | case "includes": 393 | string includes = kv[1].Trim(); 394 | if (!string.IsNullOrEmpty(includes)) 395 | { 396 | string[] includePaths = includes.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries); 397 | intellisense.includePath.AddRange(includePaths); 398 | } 399 | break; 400 | case "define": 401 | string define = kv[1].Trim(); 402 | if (!string.IsNullOrEmpty(define)) 403 | { 404 | string[] defines = define.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries); 405 | intellisense.defines.AddRange(defines); 406 | } 407 | break; 408 | } 409 | } 410 | } 411 | } 412 | 413 | string json = Newtonsoft.Json.JsonConvert.SerializeObject(intellisenseDict); 414 | try 415 | { 416 | File.WriteAllText(CppProperties, json, Encoding.UTF8); 417 | } 418 | catch (Exception ex) 419 | { 420 | await PrintAsync(ex.Message); 421 | } 422 | }); 423 | 424 | } 425 | 426 | public void UpdateLaunch() 427 | { 428 | _package.JoinableTaskFactory.RunAsync(async () => 429 | { 430 | if(await RunCommandAsync("f -y") != -1) 431 | await RefreshTargetAsync(); 432 | 433 | string result = await RunScriptAsync(_updateLaunch); 434 | result = result.Trim(); 435 | Dictionary lanuchDict = new Dictionary(); 436 | lanuchDict.Add("version", "0.2.1"); 437 | lanuchDict.Add("defaults", new Dictionary()); 438 | List launches = new List(0); 439 | lanuchDict.Add("configurations", launches); 440 | using (StringReader reader = new StringReader(result)) 441 | { 442 | while (reader.Peek() > -1) 443 | { 444 | string line = await reader.ReadLineAsync(); 445 | line = line.Trim(); 446 | if (string.IsNullOrEmpty(line)) 447 | continue; 448 | 449 | string targetFile = Path.Combine(_projDir, line); 450 | Launch launch = new Launch() 451 | { 452 | name = Path.GetFileName(targetFile), 453 | type = "cppvsdbg", 454 | project = targetFile, 455 | cwd = Path.GetDirectoryName(targetFile), 456 | program = targetFile, 457 | MIMode = "gdb", 458 | miDebuggerPath = "", 459 | externalConsole = true, 460 | }; 461 | launches.Add(launch); 462 | } 463 | } 464 | 465 | string json = Newtonsoft.Json.JsonConvert.SerializeObject(lanuchDict); 466 | try 467 | { 468 | File.WriteAllText(LaunchVS, json, Encoding.UTF8); 469 | } 470 | catch (Exception ex) 471 | { 472 | await PrintAsync(ex.Message); 473 | } 474 | }); 475 | } 476 | 477 | public Task UpdateConfigAsync() 478 | { 479 | string command = ""; 480 | if (!string.IsNullOrEmpty(_arch)) 481 | { 482 | command = string.Format("f -p {0} -a {1} -m {2} -y", _plat, _arch, _mode); 483 | } 484 | else 485 | { 486 | command = string.Format("f -p {0} -m {1} -y", _plat, _mode); 487 | } 488 | return RunCommandAsync(command); 489 | } 490 | 491 | public async Task InitializeAsync(XMakePluginPackage package, CancellationToken cancellationToken) 492 | { 493 | _package = package; 494 | } 495 | 496 | public async Task OnAfterPackageLoadedAsync(CancellationToken cancellationToken, XMakeOptionPage page) 497 | { 498 | _page = page; 499 | _page.OptionChange += OnOptionChanged; 500 | await _package.JoinableTaskFactory.SwitchToMainThreadAsync(); 501 | 502 | if(!CheckIsExecutable()) 503 | { 504 | _isExecutable = false; 505 | await PrintAsync("Access https://xmake.io to download and install xmake first!"); 506 | return; 507 | } 508 | 509 | _isExecutable = true; 510 | IVsSolution solution = await _package.GetServiceAsync(typeof(SVsSolution)) as IVsSolution; 511 | if (solution != null) 512 | { 513 | solution.GetSolutionInfo(out string directory, out string solutionFile, out string optsFile); 514 | if (directory == null) 515 | { 516 | solution.AdviseSolutionEvents(this, out uint cookie); 517 | } 518 | else if (Directory.Exists(solutionFile) && Directory.Exists(directory)) 519 | { 520 | await InitProjectAsync(directory); 521 | } 522 | } 523 | } 524 | 525 | private async Task InitProjectAsync(string folderPath) 526 | { 527 | string[] files = Directory.GetFiles(folderPath, "xmake.lua", SearchOption.AllDirectories).Where(a => !a.Contains(".xmake")).ToArray(); 528 | foreach (string file in files) 529 | { 530 | if (File.Exists(file)) 531 | { 532 | FileInfo fileInfo = new FileInfo(file); 533 | _projDir = fileInfo.Directory.FullName; 534 | 535 | await RefreshAllAsync(); 536 | break; 537 | } 538 | } 539 | } 540 | 541 | private bool CheckIsExecutable() 542 | { 543 | using (var proc = new System.Diagnostics.Process()) 544 | { 545 | proc.StartInfo.WorkingDirectory = _projDir; 546 | proc.StartInfo.FileName = ExecutablePath; 547 | proc.StartInfo.Arguments = "--version"; 548 | proc.StartInfo.UseShellExecute = false; 549 | proc.StartInfo.RedirectStandardOutput = true; 550 | proc.StartInfo.CreateNoWindow = true; 551 | proc.StartInfo.RedirectStandardError = true; 552 | proc.StartInfo.StandardOutputEncoding = System.Text.Encoding.UTF8; 553 | proc.StartInfo.StandardErrorEncoding = System.Text.Encoding.UTF8; 554 | try 555 | { 556 | if (proc.Start()) 557 | { 558 | Regex regex = new Regex("\u001B\\[[;\\d]*m"); 559 | string result = regex.Replace(proc.StandardOutput.ReadToEnd(), ""); 560 | if (!string.IsNullOrEmpty(result)) 561 | return true; 562 | } 563 | } 564 | catch(Exception e) 565 | { 566 | return false; 567 | } 568 | 569 | } 570 | 571 | return false; 572 | } 573 | 574 | private void OnOptionChanged(string option, object value) 575 | { 576 | if(option == "CustomExecutablePath") 577 | { 578 | if (!CheckIsExecutable()) 579 | { 580 | _isExecutable = false; 581 | _package.JoinableTaskFactory.RunAsync(async ()=>{ 582 | await PrintAsync("Access https://xmake.io to download and install xmake first!"); 583 | }); 584 | } 585 | else 586 | { 587 | _isExecutable = true; 588 | } 589 | 590 | _package.JoinableTaskFactory.Run(RefreshEnableAsync); 591 | } 592 | } 593 | 594 | private async Task LoadConfigAsync() 595 | { 596 | if (string.IsNullOrEmpty(_projDir)) 597 | return; 598 | 599 | 600 | string result = await RunScriptAsync("\"import(\\\"core.project.config\\\"); config.load(); print(\\\"$(plat) $(arch) $(mode)\\\")\""); 601 | string[] cache = null; 602 | if (!string.IsNullOrEmpty(result)) 603 | { 604 | Regex regex = new Regex("\u001B\\[[;\\d]*m"); 605 | result = regex.Replace(result, ""); 606 | cache = result.Trim().Split(' '); 607 | } 608 | 609 | string platform = cache != null && cache.Length > 0 && !string.IsNullOrEmpty(cache[0]) ? cache[0] : null; 610 | if (!string.IsNullOrEmpty(platform)) 611 | _plat = platform; 612 | else 613 | _plat = _allPlatAndArchs[0].Item1; 614 | 615 | string arch = cache != null && cache.Length > 1 && !string.IsNullOrEmpty(cache[1]) ? cache[1] : null; 616 | if (!string.IsNullOrEmpty(arch)) 617 | _arch = arch; 618 | else 619 | _arch = _allPlatAndArchs[0].Item2[0]; 620 | 621 | string mode = cache != null && cache.Length > 2 && !string.IsNullOrEmpty(cache[2]) ? cache[2] : null; 622 | if (!string.IsNullOrEmpty(mode)) 623 | _mode = mode; 624 | else 625 | _mode = _allModes[0]; 626 | } 627 | 628 | private async Task LoadTargetAsync() 629 | { 630 | if (string.IsNullOrEmpty(_projDir)) 631 | return; 632 | 633 | _allTargets.Clear(); 634 | _allTargets.Add("default"); 635 | string result = await RunScriptAsync("\"import(\\\"core.project.config\\\"); import(\\\"core.project.project\\\"); config.load(); for name, _ in pairs((project.targets())) do print(name) end\""); 636 | if (!string.IsNullOrEmpty(result)) 637 | { 638 | Regex regex = new Regex("\u001B\\[[;\\d]*m"); 639 | result = regex.Replace(result, ""); 640 | string[] targets = result.Trim().Split('\n'); 641 | bool find = false; 642 | foreach (var item in targets) 643 | { 644 | if (string.IsNullOrEmpty(item)) 645 | continue; 646 | 647 | string t = item.Trim(); 648 | _allTargets.Add(t); 649 | 650 | if (!find && _target != null && _target == t) 651 | find = true; 652 | } 653 | 654 | if (!find) 655 | { 656 | _target = _allTargets.Count > 1 ? _allTargets[1] : _allTargets[0]; 657 | } 658 | } 659 | } 660 | 661 | private async Task PrintAsync(string msg) 662 | { 663 | await _package.JoinableTaskFactory.SwitchToMainThreadAsync(); 664 | 665 | DTE dte = await _package.GetServiceAsync(typeof(DTE)) as DTE; 666 | Window outputWindow = dte.Windows.Item(EnvDTE.Constants.vsWindowKindOutput); 667 | outputWindow.Activate(); 668 | 669 | IVsOutputWindow vso = await _package.GetServiceAsync(typeof(SVsOutputWindow)) as IVsOutputWindow; 670 | Guid paneGuid = new Guid("f015d5ab-25d8-4ed3-8a3c-38fce53a0baf"); 671 | vso.CreatePane(ref paneGuid, "XMake", 1, 1); 672 | 673 | IVsOutputWindowPane vsOutputWindowPane; 674 | vso.GetPane(ref paneGuid, out vsOutputWindowPane); 675 | vsOutputWindowPane.Activate(); 676 | 677 | if (vsOutputWindowPane != null) 678 | vsOutputWindowPane.OutputString(msg + "\r\n"); 679 | } 680 | 681 | private Task RunCommandAsync(string command) 682 | { 683 | var tcs = new TaskCompletionSource(); 684 | 685 | if (string.IsNullOrEmpty(_projDir)) 686 | { 687 | tcs.SetResult(-1); 688 | return tcs.Task; 689 | } 690 | 691 | _package.JoinableTaskFactory.RunAsync(async () => 692 | { 693 | await PrintAsync(command); 694 | }); 695 | 696 | var proc = new System.Diagnostics.Process(); 697 | proc.StartInfo.WorkingDirectory = _projDir; 698 | proc.StartInfo.FileName = ExecutablePath; 699 | proc.StartInfo.Arguments = command; 700 | proc.StartInfo.UseShellExecute = false; 701 | proc.StartInfo.RedirectStandardOutput = true; 702 | proc.StartInfo.CreateNoWindow = true; 703 | proc.StartInfo.RedirectStandardError = true; 704 | proc.StartInfo.StandardOutputEncoding = Encoding.GetEncoding("GBK"); 705 | proc.StartInfo.StandardErrorEncoding = Encoding.GetEncoding("GBK"); 706 | proc.OutputDataReceived += (s, e)=> 707 | { 708 | if (!string.IsNullOrEmpty(e.Data)) 709 | { 710 | Regex regex = new Regex("\u001B\\[[;\\d]*m"); 711 | string result = regex.Replace(e.Data, ""); 712 | _package.JoinableTaskFactory.RunAsync(async () => 713 | { 714 | await PrintAsync(result); 715 | }); 716 | } 717 | }; 718 | proc.EnableRaisingEvents = true; 719 | proc.Exited += (s, e)=> 720 | { 721 | tcs.SetResult(proc.ExitCode); 722 | proc.Dispose(); 723 | }; 724 | proc.Start(); 725 | proc.BeginOutputReadLine(); 726 | proc.BeginErrorReadLine(); 727 | 728 | return tcs.Task; 729 | } 730 | 731 | private async Task RunScriptAsync(string script) 732 | { 733 | if (string.IsNullOrEmpty(_projDir)) 734 | return ""; 735 | 736 | string result = ""; 737 | using (var proc = new System.Diagnostics.Process()) 738 | { 739 | proc.StartInfo.WorkingDirectory = _projDir; 740 | proc.StartInfo.FileName = ExecutablePath; 741 | proc.StartInfo.Arguments = "lua -c " + script; 742 | proc.StartInfo.UseShellExecute = false; 743 | proc.StartInfo.RedirectStandardOutput = true; 744 | proc.StartInfo.CreateNoWindow = true; 745 | proc.StartInfo.RedirectStandardError = true; 746 | proc.StartInfo.StandardOutputEncoding = System.Text.Encoding.UTF8; 747 | proc.StartInfo.StandardErrorEncoding = System.Text.Encoding.UTF8; 748 | try 749 | { 750 | if (proc.Start()) 751 | result = await proc.StandardOutput.ReadToEndAsync(); 752 | } 753 | catch(Exception e) 754 | { 755 | return string.Empty; 756 | } 757 | 758 | } 759 | 760 | return result; 761 | } 762 | 763 | private async Task RefreshTargetAsync() 764 | { 765 | await Microsoft.VisualStudio.Shell.ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); 766 | await LoadTargetAsync(); 767 | XMakeToolWindow window = await _package.FindToolWindowAsync(typeof(XMakeToolWindow), 768 | 0, 769 | true, 770 | _package.DisposalToken) as XMakeToolWindow; 771 | if (window != null) 772 | { 773 | ((IVsWindowFrame)window.Frame).Show(); 774 | window.RefreshTarget(); 775 | } 776 | } 777 | 778 | private async Task RefreshConfigAsync() 779 | { 780 | await Microsoft.VisualStudio.Shell.ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); 781 | await LoadConfigAsync(); 782 | 783 | XMakeToolWindow window = await _package.FindToolWindowAsync(typeof(XMakeToolWindow), 784 | 0, 785 | true, 786 | _package.DisposalToken) as XMakeToolWindow; 787 | if (window != null) 788 | { 789 | ((IVsWindowFrame)window.Frame).Show(); 790 | window.RefreshConfig(); 791 | } 792 | } 793 | 794 | private async Task RefreshEnableAsync() 795 | { 796 | XMakeToolWindow window = await _package.FindToolWindowAsync(typeof(XMakeToolWindow), 797 | 0, 798 | true, 799 | _package.DisposalToken) as XMakeToolWindow; 800 | if (window != null) 801 | { 802 | ((IVsWindowFrame)window.Frame).Show(); 803 | window.RefreshEnable(); 804 | } 805 | } 806 | 807 | private async Task RefreshAllAsync() 808 | { 809 | await Microsoft.VisualStudio.Shell.ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); 810 | await LoadConfigAsync(); 811 | await LoadTargetAsync(); 812 | XMakeToolWindow window = await _package.FindToolWindowAsync(typeof(XMakeToolWindow), 813 | 0, 814 | true, 815 | _package.DisposalToken) as XMakeToolWindow; 816 | if (window != null) 817 | { 818 | ((IVsWindowFrame)window.Frame).Show(); 819 | window.RefreshEnable(); 820 | window.RefreshConfig(); 821 | window.RefreshTarget(); 822 | } 823 | } 824 | 825 | public void OnAfterOpenFolder(string folderPath) 826 | { 827 | _package.JoinableTaskFactory.RunAsync(async () => 828 | { 829 | await InitProjectAsync(folderPath); 830 | }); 831 | } 832 | 833 | public void OnBeforeCloseFolder(string folderPath) 834 | { 835 | } 836 | 837 | public void OnQueryCloseFolder(string folderPath, ref int pfCancel) 838 | { 839 | } 840 | 841 | public void OnAfterCloseFolder(string folderPath) 842 | { 843 | } 844 | 845 | public void OnAfterLoadAllDeferredProjects() 846 | { 847 | } 848 | 849 | public int OnAfterOpenProject(IVsHierarchy pHierarchy, int fAdded) 850 | { 851 | return 0; 852 | } 853 | 854 | public int OnQueryCloseProject(IVsHierarchy pHierarchy, int fRemoving, ref int pfCancel) 855 | { 856 | return 0; 857 | } 858 | 859 | public int OnBeforeCloseProject(IVsHierarchy pHierarchy, int fRemoved) 860 | { 861 | return 0; 862 | } 863 | 864 | public int OnAfterLoadProject(IVsHierarchy pStubHierarchy, IVsHierarchy pRealHierarchy) 865 | { 866 | return 0; 867 | } 868 | 869 | public int OnQueryUnloadProject(IVsHierarchy pRealHierarchy, ref int pfCancel) 870 | { 871 | return 0; 872 | } 873 | 874 | public int OnBeforeUnloadProject(IVsHierarchy pRealHierarchy, IVsHierarchy pStubHierarchy) 875 | { 876 | return 0; 877 | } 878 | 879 | public int OnAfterOpenSolution(object pUnkReserved, int fNewSolution) 880 | { 881 | return 0; 882 | } 883 | 884 | public int OnQueryCloseSolution(object pUnkReserved, ref int pfCancel) 885 | { 886 | return 0; 887 | } 888 | 889 | public int OnBeforeCloseSolution(object pUnkReserved) 890 | { 891 | return 0; 892 | } 893 | 894 | public int OnAfterCloseSolution(object pUnkReserved) 895 | { 896 | return 0; 897 | } 898 | } 899 | } 900 | -------------------------------------------------------------------------------- /XMake.VisualStudio/XMake.VisualStudio/XMakeToolWindow.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.Shell; 2 | using Microsoft.VisualStudio.Shell.Interop; 3 | using System; 4 | using System.Runtime.InteropServices; 5 | 6 | namespace XMake.VisualStudio 7 | { 8 | /// 9 | /// This class implements the tool window exposed by this package and hosts a user control. 10 | /// 11 | /// 12 | /// In Visual Studio tool windows are composed of a frame (implemented by the shell) and a pane, 13 | /// usually implemented by the package implementer. 14 | /// 15 | /// This class derives from the ToolWindowPane class provided from the MPF in order to use its 16 | /// implementation of the IVsUIElementPane interface. 17 | /// 18 | /// 19 | [Guid(WindowGuidString)] 20 | public class XMakeToolWindow : ToolWindowPane 21 | { 22 | public const string WindowGuidString = "2d408b97-42a2-4227-8e28-4a28bd1b2da2"; // Replace with new GUID in your own code 23 | public const string Title = "XMake Tool Window"; 24 | 25 | private XMakeService _service; 26 | private XMakeToolWindowControl _control; 27 | 28 | 29 | /// 30 | /// Initializes a new instance of the class. 31 | /// 32 | public XMakeToolWindow(XMakeService service) : base(null) 33 | { 34 | this.Caption = "XMakeToolWindow"; 35 | _service = service; 36 | 37 | // This is the user control hosted by the tool window; Note that, even if this class implements IDisposable, 38 | // we are not calling Dispose on this object. This is because ToolWindowPane calls Dispose on 39 | // the object returned by the Content property. 40 | _control = new XMakeToolWindowControl(); 41 | Content = _control; 42 | 43 | _control.platChanged += OnPlatChanged; 44 | _control.archChanged += OnArchChanged; 45 | _control.modeChanged += OnModeChanged; 46 | _control.targetChanged += OnTargetChanged; 47 | _control.build += Build; 48 | _control.quickStart = QuickStart; 49 | _control.run = Run; 50 | _control.clean = Clean; 51 | _control.cleanConfig = CleanConfig; 52 | _control.updateIntellisense = UpdateIntellisense; 53 | _control.updateLaunch = UpdateLaunch; 54 | 55 | for (int i = 0; i < _service.AllModes.Length; i++) 56 | { 57 | _control.ModeComboBox.Items.Add(_service.AllModes[i]); 58 | } 59 | 60 | 61 | for (int i = 0; i < _service.AllPlats.Length; i++) 62 | { 63 | _control.PlatformComboBox.Items.Add(_service.AllPlats[i]); 64 | } 65 | 66 | RefreshEnable(); 67 | RefreshConfig(); 68 | RefreshTarget(); 69 | } 70 | 71 | public void RefreshEnable() 72 | { 73 | _control.IsEnabled = _service.IsValid; 74 | } 75 | 76 | public void RefreshConfig() 77 | { 78 | _control.PlatformComboBox.SelectedItem = _service.Plat; 79 | // _control.ArchComboBox.SelectedItem = _service.Arch; 80 | _control.ModeComboBox.SelectedItem = _service.Mode; 81 | } 82 | 83 | public void RefreshTarget() 84 | { 85 | _control.TargetComboBox.Items.Clear(); 86 | 87 | for (int i = 0; i < _service.AllTargets.Count; i++) 88 | { 89 | _control.TargetComboBox.Items.Add(_service.AllTargets[i]); 90 | } 91 | 92 | _control.TargetComboBox.SelectedItem = _service.Target; 93 | } 94 | 95 | private void UpdateIntellisense() 96 | { 97 | _service.UpdateIntellisense(); 98 | } 99 | 100 | private void UpdateLaunch() 101 | { 102 | _service.UpdateLaunch(); 103 | } 104 | 105 | private void CleanConfig() 106 | { 107 | _service.CleanConfig(); 108 | } 109 | 110 | private void Clean() 111 | { 112 | _service.Clean(); 113 | } 114 | 115 | private void Run() 116 | { 117 | _service.Run(); 118 | } 119 | 120 | private void QuickStart() 121 | { 122 | _service.QuickStart(); 123 | } 124 | 125 | private void Build() 126 | { 127 | _service.Build(); 128 | } 129 | 130 | private void OnTargetChanged(string obj) 131 | { 132 | _service.Target = obj; 133 | } 134 | 135 | private void OnModeChanged(string obj) 136 | { 137 | _service.Mode = obj; 138 | } 139 | 140 | private void OnArchChanged(string obj) 141 | { 142 | _service.Arch = obj; 143 | } 144 | 145 | private void OnPlatChanged(string obj) 146 | { 147 | _service.Plat = obj; 148 | 149 | _control.ArchComboBox.Items.Clear(); 150 | for (int i = 0; i < _service.AllArchs.Length; i++) 151 | { 152 | _control.ArchComboBox.Items.Add(_service.AllArchs[i]); 153 | } 154 | 155 | if(string.IsNullOrEmpty(_service.Arch)) 156 | { 157 | _control.ArchComboBox.IsEnabled = false; 158 | } 159 | else 160 | { 161 | _control.ArchComboBox.IsEnabled = true; 162 | _control.ArchComboBox.SelectedItem = _service.Arch; 163 | } 164 | } 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /XMake.VisualStudio/XMake.VisualStudio/XMakeToolWindowControl.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 | 51 | 52 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /XMake.VisualStudio/XMake.VisualStudio/XMakeToolWindowControl.xaml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.Shell; 2 | using Microsoft.VisualStudio.Shell.Interop; 3 | using System; 4 | using System.Diagnostics.CodeAnalysis; 5 | using System.IO; 6 | using System.Windows; 7 | using System.Windows.Controls; 8 | 9 | namespace XMake.VisualStudio 10 | { 11 | /// 12 | /// Interaction logic for XMakeToolWindowControl. 13 | /// 14 | public partial class XMakeToolWindowControl : UserControl 15 | { 16 | 17 | internal Action modeChanged; 18 | internal Action platChanged; 19 | internal Action archChanged; 20 | internal Action targetChanged; 21 | internal Action quickStart; 22 | internal Action build; 23 | internal Action run; 24 | internal Action clean; 25 | internal Action cleanConfig; 26 | internal Action updateIntellisense; 27 | internal Action updateLaunch; 28 | 29 | /// 30 | /// Initializes a new instance of the class. 31 | /// 32 | public XMakeToolWindowControl() 33 | { 34 | this.InitializeComponent(); 35 | } 36 | 37 | private void ModeComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) 38 | { 39 | if (ModeComboBox.SelectedItem == null) 40 | return; 41 | 42 | modeChanged.Invoke(ModeComboBox.SelectedItem.ToString()); 43 | } 44 | 45 | private void PlatformComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) 46 | { 47 | if (PlatformComboBox.SelectedItem == null) 48 | return; 49 | 50 | platChanged.Invoke(PlatformComboBox.SelectedItem.ToString()); 51 | } 52 | 53 | private void ArchComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) 54 | { 55 | if (ArchComboBox.SelectedItem == null) 56 | return; 57 | 58 | archChanged.Invoke(ArchComboBox.SelectedItem.ToString()); 59 | } 60 | 61 | private void TargetComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) 62 | { 63 | if (TargetComboBox.SelectedItem == null) 64 | return; 65 | 66 | targetChanged.Invoke(TargetComboBox.SelectedItem.ToString()); 67 | } 68 | 69 | private void Build_Click(object sender, RoutedEventArgs e) 70 | { 71 | build.Invoke(); 72 | } 73 | 74 | private void Run_Click(object sender, RoutedEventArgs e) 75 | { 76 | run.Invoke(); 77 | } 78 | 79 | private void Clean_Click(object sender, RoutedEventArgs e) 80 | { 81 | clean.Invoke(); 82 | } 83 | 84 | private void CleanConfig_Click(object sender, RoutedEventArgs e) 85 | { 86 | cleanConfig.Invoke(); 87 | } 88 | 89 | private void Intellisense_Click(object sender, RoutedEventArgs e) 90 | { 91 | updateIntellisense.Invoke(); 92 | } 93 | 94 | private void Launch_Click(object sender, RoutedEventArgs e) 95 | { 96 | updateLaunch.Invoke(); 97 | } 98 | 99 | private void QuickStart_Click(object sender, RoutedEventArgs e) 100 | { 101 | quickStart.Invoke(); 102 | } 103 | 104 | 105 | } 106 | } -------------------------------------------------------------------------------- /XMake.VisualStudio/XMake.VisualStudio/XMakeToolWindowResources.xaml: -------------------------------------------------------------------------------- 1 |  8 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /XMake.VisualStudio/XMake.VisualStudio/XMakeToolWindowResources.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | namespace XMake.VisualStudio 4 | { 5 | public partial class XMakeToolWindowResources : ResourceDictionary 6 | { 7 | public XMakeToolWindowResources() 8 | { 9 | this.InitializeComponent(); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /XMake.VisualStudio/XMake.VisualStudio/source.extension.vsixmanifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | XMake.VisualStudio 6 | XMake VSIX Plugin. 7 | Icon.png 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /screenshot/options.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloWorld886/xmake-visualstudio/04c5dc325d8ce53cf332711ef1f50cd0d5299e73/screenshot/options.png -------------------------------------------------------------------------------- /screenshot/tools.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloWorld886/xmake-visualstudio/04c5dc325d8ce53cf332711ef1f50cd0d5299e73/screenshot/tools.png --------------------------------------------------------------------------------