resolve cyclic dependency with zstd
[platform/upstream/cmake.git] / Source / cmCallVisualStudioMacro.cxx
1 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2    file Copyright.txt or https://cmake.org/licensing for details.  */
3 #include "cmCallVisualStudioMacro.h"
4
5 #include <sstream>
6
7 #include "cmStringAlgorithms.h"
8 #include "cmSystemTools.h"
9
10 #if defined(_MSC_VER)
11 #  define HAVE_COMDEF_H
12 #endif
13
14 // Just for this file:
15 //
16 static bool LogErrorsAsMessages;
17
18 #if defined(HAVE_COMDEF_H)
19
20 #  include <comdef.h>
21
22 // Copied from a correct comdef.h to avoid problems with deficient versions
23 // of comdef.h that exist in the wild... Fixes issue #7533.
24 //
25 #  ifdef _NATIVE_WCHAR_T_DEFINED
26 #    ifdef _DEBUG
27 #      pragma comment(lib, "comsuppwd.lib")
28 #    else
29 #      pragma comment(lib, "comsuppw.lib")
30 #    endif
31 #  else
32 #    ifdef _DEBUG
33 #      pragma comment(lib, "comsuppd.lib")
34 #    else
35 #      pragma comment(lib, "comsupp.lib")
36 #    endif
37 #  endif
38
39 //! Use ReportHRESULT to make a cmSystemTools::Message after calling
40 //! a COM method that may have failed.
41 #  define ReportHRESULT(hr, context)                                          \
42     if (FAILED(hr)) {                                                         \
43       if (LogErrorsAsMessages) {                                              \
44         std::ostringstream _hresult_oss;                                      \
45         _hresult_oss.flags(std::ios::hex);                                    \
46         _hresult_oss << context << " failed HRESULT, hr = 0x" << hr << '\n';  \
47         _hresult_oss.flags(std::ios::dec);                                    \
48         _hresult_oss << __FILE__ << "(" << __LINE__ << ")";                   \
49         cmSystemTools::Message(_hresult_oss.str());                           \
50       }                                                                       \
51     }
52
53 //! Using the given instance of Visual Studio, call the named macro
54 HRESULT InstanceCallMacro(IDispatch* vsIDE, const std::string& macro,
55                           const std::string& args)
56 {
57   HRESULT hr = E_POINTER;
58
59   _bstr_t macroName(macro.c_str());
60   _bstr_t macroArgs(args.c_str());
61
62   if (0 != vsIDE) {
63     DISPID dispid = (DISPID)-1;
64     wchar_t execute_command[] = L"ExecuteCommand";
65     OLECHAR* name = execute_command;
66
67     hr =
68       vsIDE->GetIDsOfNames(IID_NULL, &name, 1, LOCALE_USER_DEFAULT, &dispid);
69     ReportHRESULT(hr, "GetIDsOfNames(ExecuteCommand)");
70
71     if (SUCCEEDED(hr)) {
72       VARIANTARG vargs[2];
73       DISPPARAMS params;
74       VARIANT result;
75       EXCEPINFO excep;
76       UINT arg = (UINT)-1;
77
78       // No VariantInit or VariantClear calls are necessary for
79       // these two vargs. They are both local _bstr_t variables
80       // that remain in scope for the duration of the Invoke call.
81       //
82       V_VT(&vargs[1]) = VT_BSTR;
83       V_BSTR(&vargs[1]) = macroName;
84       V_VT(&vargs[0]) = VT_BSTR;
85       V_BSTR(&vargs[0]) = macroArgs;
86
87       params.rgvarg = &vargs[0];
88       params.rgdispidNamedArgs = 0;
89       params.cArgs = sizeof(vargs) / sizeof(vargs[0]);
90       params.cNamedArgs = 0;
91
92       VariantInit(&result);
93
94       memset(&excep, 0, sizeof(excep));
95
96       hr = vsIDE->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT,
97                          DISPATCH_METHOD, &params, &result, &excep, &arg);
98
99       std::ostringstream oss;
100       /* clang-format off */
101       oss << "\nInvoke(ExecuteCommand)\n"
102              "  Macro: " << macro << "\n"
103              "  Args: " << args << '\n';
104       /* clang-format on */
105
106       if (DISP_E_EXCEPTION == hr) {
107         /* clang-format off */
108         oss << "DISP_E_EXCEPTION EXCEPINFO:" << excep.wCode << "\n"
109                "  wCode: " << excep.wCode << "\n"
110                "  wReserved: " << excep.wReserved << '\n';
111         /* clang-format on */
112         if (excep.bstrSource) {
113           oss << "  bstrSource: " << (const char*)(_bstr_t)excep.bstrSource
114               << '\n';
115         }
116         if (excep.bstrDescription) {
117           oss << "  bstrDescription: "
118               << (const char*)(_bstr_t)excep.bstrDescription << '\n';
119         }
120         if (excep.bstrHelpFile) {
121           oss << "  bstrHelpFile: " << (const char*)(_bstr_t)excep.bstrHelpFile
122               << '\n';
123         }
124         /* clang-format off */
125         oss << "  dwHelpContext: " << excep.dwHelpContext << "\n"
126                "  pvReserved: " << excep.pvReserved << "\n"
127                "  pfnDeferredFillIn: "
128             << reinterpret_cast<void*>(excep.pfnDeferredFillIn) << "\n"
129                "  scode: " << excep.scode << '\n';
130         /* clang-format on */
131       }
132
133       std::string exstr(oss.str());
134       ReportHRESULT(hr, exstr.c_str());
135
136       VariantClear(&result);
137     }
138   }
139
140   return hr;
141 }
142
143 //! Get the Solution object from the IDE object
144 HRESULT GetSolutionObject(IDispatch* vsIDE, IDispatchPtr& vsSolution)
145 {
146   HRESULT hr = E_POINTER;
147
148   if (0 != vsIDE) {
149     DISPID dispid = (DISPID)-1;
150     wchar_t solution[] = L"Solution";
151     OLECHAR* name = solution;
152
153     hr =
154       vsIDE->GetIDsOfNames(IID_NULL, &name, 1, LOCALE_USER_DEFAULT, &dispid);
155     ReportHRESULT(hr, "GetIDsOfNames(Solution)");
156
157     if (SUCCEEDED(hr)) {
158       DISPPARAMS params;
159       VARIANT result;
160       EXCEPINFO excep;
161       UINT arg = (UINT)-1;
162
163       params.rgvarg = 0;
164       params.rgdispidNamedArgs = 0;
165       params.cArgs = 0;
166       params.cNamedArgs = 0;
167
168       VariantInit(&result);
169
170       memset(&excep, 0, sizeof(excep));
171
172       hr = vsIDE->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT,
173                          DISPATCH_PROPERTYGET, &params, &result, &excep, &arg);
174       ReportHRESULT(hr, "Invoke(Solution)");
175
176       if (SUCCEEDED(hr)) {
177         vsSolution = V_DISPATCH(&result);
178       }
179
180       VariantClear(&result);
181     }
182   }
183
184   return hr;
185 }
186
187 //! Get the FullName property from the Solution object
188 HRESULT GetSolutionFullName(IDispatch* vsSolution, std::string& fullName)
189 {
190   HRESULT hr = E_POINTER;
191
192   if (0 != vsSolution) {
193     DISPID dispid = (DISPID)-1;
194     wchar_t full_name[] = L"FullName";
195     OLECHAR* name = full_name;
196
197     hr = vsSolution->GetIDsOfNames(IID_NULL, &name, 1, LOCALE_USER_DEFAULT,
198                                    &dispid);
199     ReportHRESULT(hr, "GetIDsOfNames(FullName)");
200
201     if (SUCCEEDED(hr)) {
202       DISPPARAMS params;
203       VARIANT result;
204       EXCEPINFO excep;
205       UINT arg = (UINT)-1;
206
207       params.rgvarg = 0;
208       params.rgdispidNamedArgs = 0;
209       params.cArgs = 0;
210       params.cNamedArgs = 0;
211
212       VariantInit(&result);
213
214       memset(&excep, 0, sizeof(excep));
215
216       hr = vsSolution->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT,
217                               DISPATCH_PROPERTYGET, &params, &result, &excep,
218                               &arg);
219       ReportHRESULT(hr, "Invoke(FullName)");
220
221       if (SUCCEEDED(hr)) {
222         fullName = (std::string)(_bstr_t)V_BSTR(&result);
223       }
224
225       VariantClear(&result);
226     }
227   }
228
229   return hr;
230 }
231
232 //! Get the FullName property from the Solution object, given the IDE object
233 HRESULT GetIDESolutionFullName(IDispatch* vsIDE, std::string& fullName)
234 {
235   IDispatchPtr vsSolution;
236   HRESULT hr = GetSolutionObject(vsIDE, vsSolution);
237   ReportHRESULT(hr, "GetSolutionObject");
238
239   if (SUCCEEDED(hr)) {
240     GetSolutionFullName(vsSolution, fullName);
241     ReportHRESULT(hr, "GetSolutionFullName");
242   }
243
244   return hr;
245 }
246
247 //! Get all running objects from the Windows running object table.
248 //! Save them in a map by their display names.
249 HRESULT GetRunningInstances(std::map<std::string, IUnknownPtr>& mrot)
250 {
251   // mrot == Map of the Running Object Table
252
253   IRunningObjectTablePtr runningObjectTable;
254   IEnumMonikerPtr monikerEnumerator;
255   IMonikerPtr moniker;
256   ULONG numFetched = 0;
257
258   HRESULT hr = GetRunningObjectTable(0, &runningObjectTable);
259   ReportHRESULT(hr, "GetRunningObjectTable");
260
261   if (SUCCEEDED(hr)) {
262     hr = runningObjectTable->EnumRunning(&monikerEnumerator);
263     ReportHRESULT(hr, "EnumRunning");
264   }
265
266   if (SUCCEEDED(hr)) {
267     hr = monikerEnumerator->Reset();
268     ReportHRESULT(hr, "Reset");
269   }
270
271   if (SUCCEEDED(hr)) {
272     while (S_OK == monikerEnumerator->Next(1, &moniker, &numFetched)) {
273       std::string runningObjectName;
274       IUnknownPtr runningObjectVal;
275       IBindCtxPtr ctx;
276
277       hr = CreateBindCtx(0, &ctx);
278       ReportHRESULT(hr, "CreateBindCtx");
279
280       if (SUCCEEDED(hr)) {
281         LPOLESTR displayName = 0;
282         hr = moniker->GetDisplayName(ctx, 0, &displayName);
283         ReportHRESULT(hr, "GetDisplayName");
284         if (displayName) {
285           runningObjectName = (std::string)(_bstr_t)displayName;
286           CoTaskMemFree(displayName);
287         }
288
289         hr = runningObjectTable->GetObject(moniker, &runningObjectVal);
290         ReportHRESULT(hr, "GetObject");
291         if (SUCCEEDED(hr)) {
292           mrot.insert(std::make_pair(runningObjectName, runningObjectVal));
293         }
294       }
295
296       numFetched = 0;
297       moniker = 0;
298     }
299   }
300
301   return hr;
302 }
303
304 //! Do the two file names refer to the same Visual Studio solution? Or are
305 //! we perhaps looking for any and all solutions?
306 bool FilesSameSolution(const std::string& slnFile, const std::string& slnName)
307 {
308   if (slnFile == "ALL" || slnName == "ALL") {
309     return true;
310   }
311
312   // Otherwise, make lowercase local copies, convert to Unix slashes, and
313   // see if the resulting strings are the same:
314   std::string s1 = cmSystemTools::LowerCase(slnFile);
315   std::string s2 = cmSystemTools::LowerCase(slnName);
316   cmSystemTools::ConvertToUnixSlashes(s1);
317   cmSystemTools::ConvertToUnixSlashes(s2);
318
319   return s1 == s2;
320 }
321
322 //! Find instances of Visual Studio with the given solution file
323 //! open. Pass "ALL" for slnFile to gather all running instances
324 //! of Visual Studio.
325 HRESULT FindVisualStudioInstances(const std::string& slnFile,
326                                   std::vector<IDispatchPtr>& instances)
327 {
328   std::map<std::string, IUnknownPtr> mrot;
329
330   HRESULT hr = GetRunningInstances(mrot);
331   ReportHRESULT(hr, "GetRunningInstances");
332
333   if (SUCCEEDED(hr)) {
334     std::map<std::string, IUnknownPtr>::iterator it;
335     for (it = mrot.begin(); it != mrot.end(); ++it) {
336       if (cmHasLiteralPrefix(it->first, "!VisualStudio.DTE.")) {
337         IDispatchPtr disp(it->second);
338         if (disp != (IDispatch*)0) {
339           std::string slnName;
340           hr = GetIDESolutionFullName(disp, slnName);
341           ReportHRESULT(hr, "GetIDESolutionFullName");
342
343           if (FilesSameSolution(slnFile, slnName)) {
344             instances.push_back(disp);
345
346             // std::cout << "Found Visual Studio instance." << std::endl;
347             // std::cout << "  ROT entry name: " << it->first << std::endl;
348             // std::cout << "  ROT entry object: "
349             //          << (IUnknown*) it->second << std::endl;
350             // std::cout << "  slnFile: " << slnFile << std::endl;
351             // std::cout << "  slnName: " << slnName << std::endl;
352           }
353         }
354       }
355     }
356   }
357
358   return hr;
359 }
360
361 #endif // defined(HAVE_COMDEF_H)
362
363 int cmCallVisualStudioMacro::GetNumberOfRunningVisualStudioInstances(
364   const std::string& slnFile)
365 {
366   int count = 0;
367
368   LogErrorsAsMessages = false;
369
370 #if defined(HAVE_COMDEF_H)
371   HRESULT hr = CoInitialize(0);
372   ReportHRESULT(hr, "CoInitialize");
373
374   if (SUCCEEDED(hr)) {
375     std::vector<IDispatchPtr> instances;
376     hr = FindVisualStudioInstances(slnFile, instances);
377     ReportHRESULT(hr, "FindVisualStudioInstances");
378
379     if (SUCCEEDED(hr)) {
380       count = static_cast<int>(instances.size());
381     }
382
383     // Force release all COM pointers before CoUninitialize:
384     instances.clear();
385
386     CoUninitialize();
387   }
388 #else
389   (void)slnFile;
390 #endif
391
392   return count;
393 }
394
395 //! Get all running objects from the Windows running object table.
396 //! Save them in a map by their display names.
397 int cmCallVisualStudioMacro::CallMacro(const std::string& slnFile,
398                                        const std::string& macro,
399                                        const std::string& args,
400                                        const bool logErrorsAsMessages)
401 {
402   int err = 1; // no comdef.h
403
404   LogErrorsAsMessages = logErrorsAsMessages;
405
406 #if defined(HAVE_COMDEF_H)
407   err = 2; // error initializing
408
409   HRESULT hr = CoInitialize(0);
410   ReportHRESULT(hr, "CoInitialize");
411
412   if (SUCCEEDED(hr)) {
413     std::vector<IDispatchPtr> instances;
414     hr = FindVisualStudioInstances(slnFile, instances);
415     ReportHRESULT(hr, "FindVisualStudioInstances");
416
417     if (SUCCEEDED(hr)) {
418       err = 0; // no error
419
420       std::vector<IDispatchPtr>::iterator it;
421       for (it = instances.begin(); it != instances.end(); ++it) {
422         hr = InstanceCallMacro(*it, macro, args);
423         ReportHRESULT(hr, "InstanceCallMacro");
424
425         if (FAILED(hr)) {
426           err = 3; // error attempting to call the macro
427         }
428       }
429
430       if (instances.empty()) {
431         // no instances to call
432
433         // cmSystemTools::Message(
434         //  "cmCallVisualStudioMacro::CallMacro no instances found to call",
435         //  "Warning");
436       }
437     }
438
439     // Force release all COM pointers before CoUninitialize:
440     instances.clear();
441
442     CoUninitialize();
443   }
444 #else
445   (void)slnFile;
446   (void)macro;
447   (void)args;
448   if (LogErrorsAsMessages) {
449     cmSystemTools::Message("cmCallVisualStudioMacro::CallMacro is not "
450                            "supported on this platform");
451   }
452 #endif
453
454   if (err && LogErrorsAsMessages) {
455     std::ostringstream oss;
456     oss << "cmCallVisualStudioMacro::CallMacro failed, err = " << err;
457     cmSystemTools::Message(oss.str());
458   }
459
460   return 0;
461 }