├── .gitignore
├── LICENSE
├── MSBuildTargetsVsExtension.sln
├── MSBuildTargetsVsExtension
├── Constants.cs
├── EventArgsFormatter.cs
├── GlobalSuppressions.cs
├── Helpers.cs
├── Key.snk
├── MSBuildTargetsVsExtension.csproj
├── MSBuildTargetsVsExtension.csproj.user
├── MSBuildTargetsVsExtensionPackage.cs
├── MSBuildTargetsVsExtensionPackage.vsct
├── OutputWindowLoggerAdaptor.cs
├── PkgCmdID.cs
├── Properties
│ └── AssemblyInfo.cs
├── Resources
│ ├── Images.png
│ ├── package.ico
│ └── target.png
├── SelectTargetsWindow.xaml
├── SelectTargetsWindow.xaml.cs
├── VSPackage.resx
├── packages.config
└── source.extension.vsixmanifest
├── README.md
└── img2.png
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Mono auto generated files
17 | mono_crash.*
18 |
19 | # Build results
20 | [Dd]ebug/
21 | [Dd]ebugPublic/
22 | [Rr]elease/
23 | [Rr]eleases/
24 | x64/
25 | x86/
26 | [Aa][Rr][Mm]/
27 | [Aa][Rr][Mm]64/
28 | bld/
29 | [Bb]in/
30 | [Oo]bj/
31 | [Ll]og/
32 |
33 | # Visual Studio 2015/2017 cache/options directory
34 | .vs/
35 | # Uncomment if you have tasks that create the project's static files in wwwroot
36 | #wwwroot/
37 |
38 | # Visual Studio 2017 auto generated files
39 | Generated\ Files/
40 |
41 | # MSTest test Results
42 | [Tt]est[Rr]esult*/
43 | [Bb]uild[Ll]og.*
44 |
45 | # NUNIT
46 | *.VisualState.xml
47 | TestResult.xml
48 |
49 | # Build Results of an ATL Project
50 | [Dd]ebugPS/
51 | [Rr]eleasePS/
52 | dlldata.c
53 |
54 | # Benchmark Results
55 | BenchmarkDotNet.Artifacts/
56 |
57 | # .NET Core
58 | project.lock.json
59 | project.fragment.lock.json
60 | artifacts/
61 |
62 | # StyleCop
63 | StyleCopReport.xml
64 |
65 | # Files built by Visual Studio
66 | *_i.c
67 | *_p.c
68 | *_h.h
69 | *.ilk
70 | *.meta
71 | *.obj
72 | *.iobj
73 | *.pch
74 | *.pdb
75 | *.ipdb
76 | *.pgc
77 | *.pgd
78 | *.rsp
79 | *.sbr
80 | *.tlb
81 | *.tli
82 | *.tlh
83 | *.tmp
84 | *.tmp_proj
85 | *_wpftmp.csproj
86 | *.log
87 | *.vspscc
88 | *.vssscc
89 | .builds
90 | *.pidb
91 | *.svclog
92 | *.scc
93 |
94 | # Chutzpah Test files
95 | _Chutzpah*
96 |
97 | # Visual C++ cache files
98 | ipch/
99 | *.aps
100 | *.ncb
101 | *.opendb
102 | *.opensdf
103 | *.sdf
104 | *.cachefile
105 | *.VC.db
106 | *.VC.VC.opendb
107 |
108 | # Visual Studio profiler
109 | *.psess
110 | *.vsp
111 | *.vspx
112 | *.sap
113 |
114 | # Visual Studio Trace Files
115 | *.e2e
116 |
117 | # TFS 2012 Local Workspace
118 | $tf/
119 |
120 | # Guidance Automation Toolkit
121 | *.gpState
122 |
123 | # ReSharper is a .NET coding add-in
124 | _ReSharper*/
125 | *.[Rr]e[Ss]harper
126 | *.DotSettings.user
127 |
128 | # JustCode is a .NET coding add-in
129 | .JustCode
130 |
131 | # TeamCity is a build add-in
132 | _TeamCity*
133 |
134 | # DotCover is a Code Coverage Tool
135 | *.dotCover
136 |
137 | # AxoCover is a Code Coverage Tool
138 | .axoCover/*
139 | !.axoCover/settings.json
140 |
141 | # Visual Studio code coverage results
142 | *.coverage
143 | *.coveragexml
144 |
145 | # NCrunch
146 | _NCrunch_*
147 | .*crunch*.local.xml
148 | nCrunchTemp_*
149 |
150 | # MightyMoose
151 | *.mm.*
152 | AutoTest.Net/
153 |
154 | # Web workbench (sass)
155 | .sass-cache/
156 |
157 | # Installshield output folder
158 | [Ee]xpress/
159 |
160 | # DocProject is a documentation generator add-in
161 | DocProject/buildhelp/
162 | DocProject/Help/*.HxT
163 | DocProject/Help/*.HxC
164 | DocProject/Help/*.hhc
165 | DocProject/Help/*.hhk
166 | DocProject/Help/*.hhp
167 | DocProject/Help/Html2
168 | DocProject/Help/html
169 |
170 | # Click-Once directory
171 | publish/
172 |
173 | # Publish Web Output
174 | *.[Pp]ublish.xml
175 | *.azurePubxml
176 | # Note: Comment the next line if you want to checkin your web deploy settings,
177 | # but database connection strings (with potential passwords) will be unencrypted
178 | *.pubxml
179 | *.publishproj
180 |
181 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
182 | # checkin your Azure Web App publish settings, but sensitive information contained
183 | # in these scripts will be unencrypted
184 | PublishScripts/
185 |
186 | # NuGet Packages
187 | *.nupkg
188 | # The packages folder can be ignored because of Package Restore
189 | **/[Pp]ackages/*
190 | # except build/, which is used as an MSBuild target.
191 | !**/[Pp]ackages/build/
192 | # Uncomment if necessary however generally it will be regenerated when needed
193 | #!**/[Pp]ackages/repositories.config
194 | # NuGet v3's project.json files produces more ignorable files
195 | *.nuget.props
196 | *.nuget.targets
197 |
198 | # Microsoft Azure Build Output
199 | csx/
200 | *.build.csdef
201 |
202 | # Microsoft Azure Emulator
203 | ecf/
204 | rcf/
205 |
206 | # Windows Store app package directories and files
207 | AppPackages/
208 | BundleArtifacts/
209 | Package.StoreAssociation.xml
210 | _pkginfo.txt
211 | *.appx
212 | *.appxbundle
213 | *.appxupload
214 |
215 | # Visual Studio cache files
216 | # files ending in .cache can be ignored
217 | *.[Cc]ache
218 | # but keep track of directories ending in .cache
219 | !?*.[Cc]ache/
220 |
221 | # Others
222 | ClientBin/
223 | ~$*
224 | *~
225 | *.dbmdl
226 | *.dbproj.schemaview
227 | *.jfm
228 | *.pfx
229 | *.publishsettings
230 | orleans.codegen.cs
231 |
232 | # Including strong name files can present a security risk
233 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
234 | #*.snk
235 |
236 | # Since there are multiple workflows, uncomment next line to ignore bower_components
237 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
238 | #bower_components/
239 |
240 | # RIA/Silverlight projects
241 | Generated_Code/
242 |
243 | # Backup & report files from converting an old project file
244 | # to a newer Visual Studio version. Backup files are not needed,
245 | # because we have git ;-)
246 | _UpgradeReport_Files/
247 | Backup*/
248 | UpgradeLog*.XML
249 | UpgradeLog*.htm
250 | ServiceFabricBackup/
251 | *.rptproj.bak
252 |
253 | # SQL Server files
254 | *.mdf
255 | *.ldf
256 | *.ndf
257 |
258 | # Business Intelligence projects
259 | *.rdl.data
260 | *.bim.layout
261 | *.bim_*.settings
262 | *.rptproj.rsuser
263 | *- Backup*.rdl
264 |
265 | # Microsoft Fakes
266 | FakesAssemblies/
267 |
268 | # GhostDoc plugin setting file
269 | *.GhostDoc.xml
270 |
271 | # Node.js Tools for Visual Studio
272 | .ntvs_analysis.dat
273 | node_modules/
274 | package-lock.json
275 |
276 | # Visual Studio 6 build log
277 | *.plg
278 |
279 | # Visual Studio 6 workspace options file
280 | *.opt
281 |
282 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
283 | *.vbw
284 |
285 | # Visual Studio LightSwitch build output
286 | **/*.HTMLClient/GeneratedArtifacts
287 | **/*.DesktopClient/GeneratedArtifacts
288 | **/*.DesktopClient/ModelManifest.xml
289 | **/*.Server/GeneratedArtifacts
290 | **/*.Server/ModelManifest.xml
291 | _Pvt_Extensions
292 |
293 | # Paket dependency manager
294 | .paket/paket.exe
295 | paket-files/
296 |
297 | # FAKE - F# Make
298 | .fake/
299 |
300 | # CodeRush personal settings
301 | .cr/personal
302 |
303 | # Python Tools for Visual Studio (PTVS)
304 | __pycache__/
305 | *.pyc
306 |
307 | # Cake - Uncomment if you are using it
308 | # tools/**
309 | # !tools/packages.config
310 |
311 | # Tabs Studio
312 | *.tss
313 |
314 | # Telerik's JustMock configuration file
315 | *.jmconfig
316 |
317 | # BizTalk build output
318 | *.btp.cs
319 | *.btm.cs
320 | *.odx.cs
321 | *.xsd.cs
322 |
323 | # OpenCover UI analysis results
324 | OpenCover/
325 |
326 | # Azure Stream Analytics local run output
327 | ASALocalRun/
328 |
329 | # MSBuild Binary and Structured Log
330 | *.binlog
331 |
332 | # NVidia Nsight GPU debugger configuration file
333 | *.nvuser
334 |
335 | # MFractors (Xamarin productivity tool) working folder
336 | .mfractor/
337 |
338 | # Local History for Visual Studio
339 | .localhistory/
340 |
341 | # BeatPulse healthcheck temp database
342 | healthchecksdb
343 |
344 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
345 | MigrationBackup/
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 George Samartzidis
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/MSBuildTargetsVsExtension.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.2.32526.322
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MSBuildTargetsVsExtension", "MSBuildTargetsVsExtension\MSBuildTargetsVsExtension.csproj", "{D7DE3193-010B-4D7B-811B-0C86AC5C9297}"
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 | {D7DE3193-010B-4D7B-811B-0C86AC5C9297}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {D7DE3193-010B-4D7B-811B-0C86AC5C9297}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {D7DE3193-010B-4D7B-811B-0C86AC5C9297}.Debug|x86.ActiveCfg = Debug|x86
19 | {D7DE3193-010B-4D7B-811B-0C86AC5C9297}.Debug|x86.Build.0 = Debug|x86
20 | {D7DE3193-010B-4D7B-811B-0C86AC5C9297}.Release|Any CPU.ActiveCfg = Release|Any CPU
21 | {D7DE3193-010B-4D7B-811B-0C86AC5C9297}.Release|Any CPU.Build.0 = Release|Any CPU
22 | {D7DE3193-010B-4D7B-811B-0C86AC5C9297}.Release|x86.ActiveCfg = Release|x86
23 | {D7DE3193-010B-4D7B-811B-0C86AC5C9297}.Release|x86.Build.0 = Release|x86
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | GlobalSection(ExtensibilityGlobals) = postSolution
29 | SolutionGuid = {B7098CA6-8554-43E7-A823-1D31D78DFAD6}
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/MSBuildTargetsVsExtension/Constants.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace MSBuildTargetsVsExtension
8 | {
9 | static class Constants
10 | {
11 | public const string GuidMsBuildTargetsVsExtensionPkgString = "137da963-074e-4dcf-a87a-34857204d497";
12 | public const string GuidMsBuildTargetsVsExtensionCmdSetString = "9fc10e11-28c8-45b9-abac-8aa4ec3a4346";
13 | public static readonly Guid GuidMsBuildTargetsVsExtensionCmdSet = new Guid(GuidMsBuildTargetsVsExtensionCmdSetString);
14 | public const string ProductName = "MSBuildTargets";
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/MSBuildTargetsVsExtension/EventArgsFormatter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 | using System.Text;
4 | using Microsoft.Build.Framework;
5 |
6 | namespace MSBuildTargetsVsExtension
7 | {
8 | internal static class EventArgsFormatter
9 | {
10 | ///
11 | /// Escape the carriage Return from a string
12 | ///
13 | ///
14 | /// String with carriage returns escaped as \\r
15 | internal static string EscapeCarriageReturn(string stringWithCarriageReturn)
16 | {
17 | if (!string.IsNullOrEmpty(stringWithCarriageReturn))
18 | return stringWithCarriageReturn.Replace("\r", "\\r");
19 |
20 | // If the string is null or empty or then we just return the string
21 | return stringWithCarriageReturn;
22 | }
23 |
24 | ///
25 | /// Format the error event message and all the other event data into
26 | /// a single string.
27 | ///
28 | /// Error to format
29 | /// The formatted message string.
30 | internal static string FormatEventMessage(BuildErrorEventArgs e)
31 | {
32 | return FormatEventMessage(e, false);
33 | }
34 |
35 | ///
36 | /// Format the error event message and all the other event data into
37 | /// a single string.
38 | ///
39 | /// Error to format
40 | /// The formatted message string.
41 | internal static string FormatEventMessage(BuildErrorEventArgs e, bool removeCarriageReturn)
42 | {
43 | return FormatEventMessage("error", e.Subcategory, removeCarriageReturn ? EscapeCarriageReturn(e.Message) : e.Message,
44 | e.Code, e.File, null, e.LineNumber, e.EndLineNumber,
45 | e.ColumnNumber, e.EndColumnNumber, e.ThreadId);
46 | }
47 |
48 | ///
49 | /// Format the error event message and all the other event data into
50 | /// a single string.
51 | ///
52 | /// Error to format
53 | /// The formatted message string.
54 | internal static string FormatEventMessage(BuildErrorEventArgs e, bool removeCarriageReturn, bool showProjectFile)
55 | {
56 | return FormatEventMessage("error", e.Subcategory, removeCarriageReturn ? EscapeCarriageReturn(e.Message) : e.Message,
57 | e.Code, e.File, showProjectFile ? e.ProjectFile : null, e.LineNumber, e.EndLineNumber,
58 | e.ColumnNumber, e.EndColumnNumber, e.ThreadId);
59 | }
60 |
61 | ///
62 | /// Format the warning message and all the other event data into a
63 | /// single string.
64 | ///
65 | /// Warning to format
66 | /// The formatted message string.
67 | internal static string FormatEventMessage(BuildWarningEventArgs e)
68 | {
69 | return FormatEventMessage(e, false);
70 | }
71 |
72 | ///
73 | /// Format the warning message and all the other event data into a
74 | /// single string.
75 | ///
76 | /// Warning to format
77 | /// The formatted message string.
78 | internal static string FormatEventMessage(BuildWarningEventArgs e, bool removeCarriageReturn)
79 | {
80 | return FormatEventMessage("warning", e.Subcategory, removeCarriageReturn ? EscapeCarriageReturn(e.Message) : e.Message,
81 | e.Code, e.File, null, e.LineNumber, e.EndLineNumber,
82 | e.ColumnNumber, e.EndColumnNumber, e.ThreadId);
83 | }
84 |
85 | ///
86 | /// Format the warning message and all the other event data into a
87 | /// single string.
88 | ///
89 | /// Warning to format
90 | /// The formatted message string.
91 | internal static string FormatEventMessage(BuildWarningEventArgs e, bool removeCarriageReturn, bool showProjectFile)
92 | {
93 | return FormatEventMessage("warning", e.Subcategory, removeCarriageReturn ? EscapeCarriageReturn(e.Message) : e.Message,
94 | e.Code, e.File, showProjectFile ? e.ProjectFile : null, e.LineNumber, e.EndLineNumber,
95 | e.ColumnNumber, e.EndColumnNumber, e.ThreadId);
96 | }
97 |
98 | ///
99 | /// Format the message and all the other event data into a
100 | /// single string.
101 | ///
102 | /// Message to format
103 | /// The formatted message string.
104 | internal static string FormatEventMessage(BuildMessageEventArgs e)
105 | {
106 | return FormatEventMessage(e, false);
107 | }
108 |
109 | ///
110 | /// Format the message and all the other event data into a
111 | /// single string.
112 | ///
113 | /// Message to format
114 | /// Escape CR or leave as is
115 | /// The formatted message string.
116 | internal static string FormatEventMessage(BuildMessageEventArgs e, bool removeCarriageReturn)
117 | {
118 | return FormatEventMessage(e, removeCarriageReturn, false);
119 | }
120 |
121 | ///
122 | /// Format the message and all the other event data into a
123 | /// single string.
124 | ///
125 | /// Message to format
126 | /// Escape CR or leave as is
127 | /// Show project file or not
128 | /// The formatted message string.
129 | internal static string FormatEventMessage(BuildMessageEventArgs e, bool removeCarriageReturn, bool showProjectFile)
130 | {
131 | return FormatEventMessage("message", e.Subcategory, removeCarriageReturn ? EscapeCarriageReturn(e.Message) : e.Message,
132 | e.Code, e.File, showProjectFile ? e.ProjectFile : null, e.LineNumber, e.EndLineNumber, e.ColumnNumber, e.EndColumnNumber, e.ThreadId);
133 | }
134 |
135 | ///
136 | /// Format the event message and all the other event data into a
137 | /// single string.
138 | ///
139 | /// category ("error" or "warning")
140 | /// subcategory
141 | /// event message
142 | /// error or warning code number
143 | /// file name
144 | /// line number (0 if n/a)
145 | /// end line number (0 if n/a)
146 | /// column number (0 if n/a)
147 | /// end column number (0 if n/a)
148 | /// thread id
149 | /// The formatted message string.
150 | internal static string FormatEventMessage
151 | (
152 | string category,
153 | string subcategory,
154 | string message,
155 | string code,
156 | string file,
157 | int lineNumber,
158 | int endLineNumber,
159 | int columnNumber,
160 | int endColumnNumber,
161 | int threadId
162 | )
163 | {
164 | return FormatEventMessage(category, subcategory, message, code, file, null, lineNumber, endLineNumber, columnNumber, endColumnNumber, threadId);
165 | }
166 |
167 | ///
168 | /// Format the event message and all the other event data into a
169 | /// single string.
170 | ///
171 | /// category ("error" or "warning")
172 | /// subcategory
173 | /// event message
174 | /// error or warning code number
175 | /// file name
176 | /// the project file name
177 | /// line number (0 if n/a)
178 | /// end line number (0 if n/a)
179 | /// column number (0 if n/a)
180 | /// end column number (0 if n/a)
181 | /// thread id
182 | /// The formatted message string.
183 | internal static string FormatEventMessage
184 | (
185 | string category,
186 | string subcategory,
187 | string message,
188 | string code,
189 | string file,
190 | string projectFile,
191 | int lineNumber,
192 | int endLineNumber,
193 | int columnNumber,
194 | int endColumnNumber,
195 | int threadId
196 | )
197 | {
198 | var format = new StringBuilder();
199 |
200 | // Uncomment these lines to show show the processor, if present.
201 | /*
202 | if (threadId != 0)
203 | {
204 | format.Append("{0}>");
205 | }
206 | */
207 |
208 | if ((file == null) || (file.Length == 0))
209 | {
210 | format.Append("MSBUILD : "); // Should not be localized.
211 | }
212 | else
213 | {
214 | format.Append("{1}");
215 |
216 | if (lineNumber == 0)
217 | {
218 | format.Append(" : ");
219 | }
220 | else
221 | {
222 | if (columnNumber == 0)
223 | {
224 | if (endLineNumber == 0)
225 | {
226 | format.Append("({2}): ");
227 | }
228 | else
229 | {
230 | format.Append("({2}-{7}): ");
231 | }
232 | }
233 | else
234 | {
235 | if (endLineNumber == 0)
236 | {
237 | if (endColumnNumber == 0)
238 | {
239 | format.Append("({2},{3}): ");
240 | }
241 | else
242 | {
243 | format.Append("({2},{3}-{8}): ");
244 | }
245 | }
246 | else
247 | {
248 | if (endColumnNumber == 0)
249 | {
250 | format.Append("({2}-{7},{3}): ");
251 | }
252 | else
253 | {
254 | format.Append("({2},{3},{7},{8}): ");
255 | }
256 | }
257 | }
258 | }
259 | }
260 |
261 | if ((subcategory != null) && (subcategory.Length != 0))
262 | {
263 | format.Append("{9} ");
264 | }
265 |
266 | // The category as a string (should not be localized)
267 | format.Append("{4} ");
268 |
269 | // Put a code in, if available and necessary.
270 | if (code == null)
271 | {
272 | format.Append(": ");
273 | }
274 | else
275 | {
276 | format.Append("{5}: ");
277 | }
278 |
279 | // Put the message in, if available.
280 | if (message != null)
281 | {
282 | format.Append("{6}");
283 | }
284 |
285 | // If the project file was specified, tack that onto the very end.
286 | if (projectFile != null && !String.Equals(projectFile, file))
287 | {
288 | format.Append(" [{10}]");
289 | }
290 |
291 | // A null message is allowed and is to be treated as a blank line.
292 | if (null == message)
293 | {
294 | message = String.Empty;
295 | }
296 |
297 | var finalFormat = format.ToString();
298 |
299 | // If there are multiple lines, show each line as a separate message.
300 | var lines = SplitStringOnNewLines(message);
301 | var formattedMessage = new StringBuilder();
302 |
303 | for (int i = 0; i < lines.Length; i++)
304 | {
305 | formattedMessage.Append(String.Format(
306 | CultureInfo.CurrentCulture, finalFormat,
307 | threadId, file,
308 | lineNumber, columnNumber, category, code,
309 | lines[i], endLineNumber, endColumnNumber,
310 | subcategory, projectFile));
311 |
312 | if (i < (lines.Length - 1))
313 | {
314 | formattedMessage.AppendLine();
315 | }
316 | }
317 |
318 | return formattedMessage.ToString();
319 | }
320 |
321 |
322 | ///
323 | /// Splits strings on 'newLines' with tolerance for Everett and Dogfood builds.
324 | ///
325 | /// String to split.
326 | private static string[] SplitStringOnNewLines(string s)
327 | {
328 | var subStrings = s.Split(s_newLines, StringSplitOptions.None);
329 | return subStrings;
330 | }
331 |
332 | ///
333 | /// The kinds of newline breaks we expect.
334 | ///
335 | /// Currently we're not supporting "\r".
336 | private static readonly string[] s_newLines = { "\r\n", "\n" };
337 | }
338 | }
339 |
--------------------------------------------------------------------------------
/MSBuildTargetsVsExtension/GlobalSuppressions.cs:
--------------------------------------------------------------------------------
1 | // This file is used by Code Analysis to maintain SuppressMessage
2 | // attributes that are applied to this project. Project-level
3 | // suppressions either have no target or are given a specific target
4 | // and scoped to a namespace, type, member, etc.
5 | //
6 | // To add a suppression to this file, right-click the message in the
7 | // Error List, point to "Suppress Message(s)", and click "In Project
8 | // Suppression File". You do not need to add suppressions to this
9 | // file manually.
10 |
11 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1017:MarkAssembliesWithComVisible")]
12 |
--------------------------------------------------------------------------------
/MSBuildTargetsVsExtension/Helpers.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace MSBuildTargetsVsExtension
9 | {
10 | public static class Helpers
11 | {
12 | public static bool IsPathSubpathOf(string parentPath, string childPath)
13 | {
14 | var pp = Path.GetFullPath(parentPath);
15 | var cp = Path.GetFullPath(childPath);
16 | return cp.StartsWith(pp, StringComparison.OrdinalIgnoreCase);
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/MSBuildTargetsVsExtension/Key.snk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/samartzidis/MSBuildTargetsVsExtension/536e5a5dc0234e864fe75b3593033aa9a3de3663/MSBuildTargetsVsExtension/Key.snk
--------------------------------------------------------------------------------
/MSBuildTargetsVsExtension/MSBuildTargetsVsExtension.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 | {D7DE3193-010B-4D7B-811B-0C86AC5C9297}
20 | Library
21 | Properties
22 | MSBuildTargetsVsExtension
23 | MSBuildTargetsVsExtension
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 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | SelectTargetsWindow.xaml
64 |
65 |
66 |
67 |
68 |
69 | Designer
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 | compile; build; native; contentfiles; analyzers; buildtransitive
89 |
90 |
91 | runtime; build; native; contentfiles; analyzers; buildtransitive
92 | all
93 |
94 |
95 |
96 |
97 | MSBuild:Compile
98 | Designer
99 |
100 |
101 |
102 |
103 |
104 | Menus.ctmenu
105 |
106 |
107 | PreserveNewest
108 | true
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
124 |
--------------------------------------------------------------------------------
/MSBuildTargetsVsExtension/MSBuildTargetsVsExtension.csproj.user:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | C:\Program Files\Microsoft Visual Studio\2022\Professional\Common7\IDE\devenv.exe
5 |
6 |
7 |
--------------------------------------------------------------------------------
/MSBuildTargetsVsExtension/MSBuildTargetsVsExtensionPackage.cs:
--------------------------------------------------------------------------------
1 | using EnvDTE80;
2 | using Microsoft.Build.Execution;
3 | using Microsoft.VisualStudio.Shell;
4 | using Microsoft.VisualStudio.Shell.Interop;
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Runtime.InteropServices;
8 | using System.Threading;
9 | using Microsoft.Build.Evaluation;
10 | using Microsoft.Build.Framework;
11 | using System.ComponentModel.Design;
12 | using EnvDTE;
13 | using System.IO;
14 | using System.Windows.Input;
15 | using System.Linq;
16 | using System.Threading.Tasks;
17 |
18 | namespace MSBuildTargetsVsExtension
19 | {
20 | [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)]
21 | [InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)] // This attribute is used to register the information needed to show this package in the Help/About dialog of Visual Studio.
22 | [ProvideMenuResource("Menus.ctmenu", 1)] // This attribute is needed to let the shell know that this package exposes some menus.
23 | [Guid(Constants.GuidMsBuildTargetsVsExtensionPkgString)]
24 | [ProvideMenuResource("Menus.ctmenu", 1)]
25 | public sealed class MSBuildTargetsVsExtensionPackage : AsyncPackage
26 | {
27 | public bool ShowMessages { get; set; }
28 |
29 | private DTE2 _dte;
30 |
31 | ///
32 | /// Default constructor of the package.
33 | /// Inside this method you can place any initialization code that does not require
34 | /// any Visual Studio service because at this point the package object is created but
35 | /// not sited yet inside Visual Studio environment. The place to do all the other
36 | /// initialization is the Initialize method.
37 | ///
38 | public MSBuildTargetsVsExtensionPackage()
39 | {
40 |
41 | }
42 |
43 |
44 | ///
45 | /// Initialization of the package; this method is called right after the package is sited, so this is the place
46 | /// where you can put all the initialization code that rely on services provided by VisualStudio.
47 | ///
48 | /// A cancellation token to monitor for initialization cancellation, which can occur when VS is shutting down.
49 | /// A provider for progress updates.
50 | /// 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.
51 | protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress progress)
52 | {
53 | // When initialized asynchronously, the current thread may be a background thread at this point.
54 | // Do any initialization that requires the UI thread after switching to the UI thread.
55 | await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
56 |
57 | _dte = await GetServiceAsync(typeof(DTE)) as DTE2;
58 | if (_dte == null)
59 | {
60 | VsShellUtilities.ShowMessageBox(
61 | this,
62 | "Extension failed to load.",
63 | Constants.ProductName,
64 | OLEMSGICON.OLEMSGICON_CRITICAL,
65 | OLEMSGBUTTON.OLEMSGBUTTON_OK,
66 | OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST);
67 |
68 | return;
69 | }
70 |
71 | var commandService = await GetServiceAsync(typeof(IMenuCommandService)) as OleMenuCommandService;
72 | if (commandService == null)
73 | {
74 | VsShellUtilities.ShowMessageBox(
75 | this,
76 | "Extension failed to load.",
77 | Constants.ProductName,
78 | OLEMSGICON.OLEMSGICON_CRITICAL,
79 | OLEMSGBUTTON.OLEMSGBUTTON_OK,
80 | OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST);
81 |
82 | return;
83 | }
84 |
85 | // Add menu command handlers (commands must exist in the .vsct file)
86 | // NOTE: AddCommand() must be called from the UI thread
87 |
88 | var menuCommandIdStart = new CommandID(Constants.GuidMsBuildTargetsVsExtensionCmdSet, (int)PkgCmdID.CmdStart);
89 | var menuItemStart = new MenuCommand(MenuItemCallback, menuCommandIdStart);
90 | commandService.AddCommand(menuItemStart);
91 |
92 | var menuCommandIdStartDebugging = new CommandID(Constants.GuidMsBuildTargetsVsExtensionCmdSet, (int)PkgCmdID.CmdStartDebugging);
93 | var menuItemStartDebugging = new MenuCommand(MenuItemCallback, menuCommandIdStartDebugging);
94 | commandService.AddCommand(menuItemStartDebugging);
95 |
96 | var menuCommandIdSelectTarget = new CommandID(Constants.GuidMsBuildTargetsVsExtensionCmdSet, (int)PkgCmdID.CmdSelectTarget);
97 | var menuItemSelectTarget = new MenuCommand(ShowToolWindow, menuCommandIdSelectTarget);
98 | commandService.AddCommand(menuItemSelectTarget);
99 | }
100 |
101 | private void ShowToolWindow(object sender, EventArgs e)
102 | {
103 | ThreadHelper.ThrowIfNotOnUIThread();
104 |
105 | var targetNamesCount = new SortedDictionary();
106 | var selectedProjects = new List();
107 |
108 | //Ignore standard targets as we dont want to display a huge target list
109 | var winDrive = Path.GetPathRoot(Environment.GetFolderPath(Environment.SpecialFolder.Windows));
110 | var winDir = Environment.GetFolderPath(Environment.SpecialFolder.Windows);
111 | var ignoreFolders = new[] {
112 | Path.Combine(winDir, "Microsoft.NET"),
113 | Path.Combine(winDrive, "Program Files (x86)"),
114 | Path.Combine(winDrive, "Program Files")
115 | };
116 |
117 | var projCount = _dte.SelectedItems.Count;
118 | for (var k = 1; k <= projCount; k++) //Iterate through all selected items
119 | {
120 | var selectedProject = _dte.SelectedItems.Item(k).Project;
121 | selectedProjects.Add(selectedProject); //Add to selectedProjects
122 |
123 | var evalProject = ProjectCollection.GlobalProjectCollection.LoadProject(selectedProject.FullName);
124 | var execProject = evalProject.CreateProjectInstance();
125 |
126 | foreach (var target in execProject.Targets)
127 | {
128 | var targetName = $"{target.Value.Name}";
129 | var targetFilePath = Path.GetDirectoryName(target.Value.FullPath);
130 |
131 | if (ignoreFolders.Any(t => Helpers.IsPathSubpathOf(t, targetFilePath)))
132 | continue; // Ignore
133 |
134 | if (targetNamesCount.ContainsKey(targetName))
135 | targetNamesCount[targetName]++;
136 | else
137 | targetNamesCount[targetName] = 1;
138 | }
139 | }
140 |
141 | var commonTargets = targetNamesCount.Where(t => t.Value == projCount).Select(t => t.Key);
142 |
143 | var selectTargetsWindow = new SelectTargetsWindow { DataContext = this };
144 | foreach (var item in commonTargets)
145 | selectTargetsWindow.ItemsComboBox.Items.Add(item);
146 | selectTargetsWindow.ItemsComboBox.SelectedIndex = 0;
147 |
148 | if (selectTargetsWindow.ShowDialog() == true)
149 | {
150 | var targetName = (string)selectTargetsWindow.ItemsComboBox.SelectedItem;
151 |
152 | ExecuteTarget(selectedProjects, targetName);
153 | }
154 | }
155 |
156 | private void ExecuteTarget(IEnumerable selectedProjects, string targetName)
157 | {
158 | ThreadHelper.ThrowIfNotOnUIThread();
159 |
160 | var buildInfo = new List();
161 | foreach (var project in selectedProjects)
162 | {
163 | project.Save(); //Save the EnvDTE project instance
164 |
165 | // Add to ProjectBackgroundBuildInfo list
166 | var config = project.ConfigurationManager.ActiveConfiguration;
167 | buildInfo.Add(new ProjectBackgroundBuildInfo {
168 | ProjectName = project.Name,
169 | ConfigName = config.ConfigurationName,
170 | PlatformName = config.PlatformName,
171 | BuildParameters = CreateBuildParameters(),
172 | BuildRequestData = CreateBuildRequestData(project, targetName)
173 | });
174 | }
175 |
176 | var output = new OutputWindowLoggerAdaptor(true);
177 | output.Activate();
178 |
179 | // Run build in background thread
180 | var previousCursor = Mouse.OverrideCursor;
181 | Mouse.OverrideCursor = Cursors.Wait;
182 | Task.Run(() =>
183 | {
184 | var succeeded = 0;
185 | var failed = 0;
186 |
187 | using (var buildManager = new BuildManager())
188 | {
189 | foreach (var bi in buildInfo)
190 | {
191 | var res = buildManager.Build(bi.BuildParameters, bi.BuildRequestData);
192 |
193 | if (res.OverallResult == BuildResultCode.Failure)
194 | {
195 | output.OutputString($"========== '{bi.ProjectName} -> {targetName}' FAILED ==========");
196 | failed++;
197 | }
198 | else
199 | {
200 | output.OutputString($"========== '{bi.ProjectName} -> {targetName}' succeeded ==========");
201 | succeeded++;
202 | }
203 |
204 | if (failed == 0)
205 | output.OutputString($"========== {targetName}: {succeeded} project(s) succeeded ==========");
206 | else
207 | output.OutputString($"========== {targetName}: {succeeded} project(s) succeeded, {failed} project(s) FAILED ==========");
208 | }
209 | }
210 |
211 | }).ContinueWith(t =>
212 | {
213 | Mouse.OverrideCursor = previousCursor;
214 | }, TaskScheduler.FromCurrentSynchronizationContext());
215 | }
216 |
217 | private void MenuItemCallback(object sender, EventArgs e)
218 | {
219 | ThreadHelper.ThrowIfNotOnUIThread();
220 |
221 | var menuCommand = (MenuCommand)sender;
222 | var debugging = menuCommand.CommandID.ID == PkgCmdID.CmdStartDebugging;
223 | var selectedItemCount = _dte.SelectedItems.Count;
224 |
225 | if (selectedItemCount > 1) //Multiple projects selected?
226 | {
227 | var projects = new List();
228 | for (int k = 0; k < selectedItemCount; k++)
229 | {
230 | var selectedProject = _dte.SelectedItems.Item(k + 1).Project;
231 | projects.Add(selectedProject);
232 | }
233 |
234 | var sb = _dte.Solution.SolutionBuild;
235 | var newStartups = projects.Select(t => t.FullName).ToArray